بازجویی در دیتاسنتر! - کشف باگ در سیستم‌های پیچیده

برنامه‌نویسی شغلیه پر از استرس. خیلی وقت‌ها تلاش می‌کنیم یه محصولی رو تولید کنیم ولی نمیشه. رسوندن محصول به خط تولید یه پروسه زمانبر، طولانی و پرچالشه. احتمالا برای اکثر برنامه‌نویس‌ها پیش میاد که از ابتدا در روند توسعه یک محصول مشارکت کنن ولی اون محصول هیچوقت به خط تولید نمیرسه.

interrogation  in datacenter (1) copy

تو این مقاله با چالش‌های رسوندن محصول به خط تولید کاری ندارم. تمرکزم روی یکی از مهم‌ترین یا شاید هم مهم‌ترین معیار کیفیت یک محصول نرم‌افزاری پس از رسیدن به خط تولید و بعدش فروشه. منظور از فروش زمانیه که کاربران واقعی از محصول نرم‌افزاری شما استفاده می‌‌کنه. اینجا نقطه‌ایه که دیگه خیلی از کاراییه که تا قبلش به عنوان یه مهندس نرم‌افزار راحت می‌تونستیم انجام بدیم، ولی دیگه نمی‌تونیم انجام بدیم. مثلا اگه برنامه‌نویس بکند باشید در طول روند توسعه بارها شده که ساختار جداول دیتابیس رو زیر و رو می‌کنید بدون اینکه دغدغه خاصی داشته باشید. اما به محض اینکه اولین کاربر واقعی از محصول شما استفاده کرد و اطلاعات اون کاربر در دیتابیس ذخیره شد دیگه همه چیز عوض میشه!

اصل «یکپارچگی داده» اگه مهم‌ترین معیار کیفیت یه محصول نرم‌افزاری نباشه حتما جز مهم‌تریناشه. برای من به شخصه که مهم‌ترین اصله. این که کد تمیز هست یا نه، عملکرد سیستم کند هست یا نه، تکنولوژی و زبان استفاده شده قابل دفاع هست یا نه، همه و همه در اولویت‌های خیلی پایین‌تری قرار می‌گیرن. مهم‌ترین فاکتور برای اینکه کاربر به محصول شما اعتماد کنه اینه که مطمئن باشه اطلاعاتش به درستی ذخیره میشه. اگه رفتار محصول شما به گونه‌ای باشه که این اعتماد رو از کاربر سلب کنه، احتمالا خیلی زود کاربر شاکی میشه و شاید حتی بزودی استفاده از محصول شما رو متوقف می‌کنه.

در فرآیند توسعه محصول نرم‌افزاری باید به خیلی چیزا فکر کنید: معماری سیستم، اصول نگهداری کد، نحوه اجرای کد محصول نهایی در سرورهای اصلی و غیره. اما یکی از مهم‌ترین چیزهایی که در لحظه لحظه فرآیند توسعه باید بهش فکر کنید «یکپارچه نگه داشتن داده‌ها» ست.

روش مرسومی که همه ما باهاش آشنا هستیم روش محتاطانه ایه که سعی می‌کنیم به نقطه نقطه سیستم فکر کنیم و ببینیم در قسمت‌های مختلف سیستم چه خطا یا باگی ممکنه وجود داشته باشه، یا در آینده بوجود بیاد که بعدش بتونیم به فکر راه حل باشیم براش.

مثلا وقتی یه کاربری در سایت ثبت نام می‌کنه ما باید اطلاعات کاربر جدید رو در دیتابیس ذخیره کنیم. خب برای این کار لازمه که همه فرآیند ثبت‌نام رو بررسی کنیم و در تمامی نقاطی که در کد ممکنه خطایی رخ بده سعی کنیم خطا رو مدیریت کنیم. مدیریت کردن یعنی اینکه در قدم اول از رخ دادنش جلوگیری کنیم و در قدم دوم کاربر و سیستم رو متوجه خطا کنیم. همچنین در این فرآیند باید مطمئن باشیم «ناسازگاری داده» رخ نمیده. یعنی چی؟ یعنی مثلا اینطوری نشه که اطلاعات کاربر ناقص در دیتابیس ذخیره بشه. مثلا اسم و رمز عبورش ثبت بشه، ولی ایمیل یا یوزرنیم ثبت نشه!

نکته چالش برانگیزی که وجود داره اینه که معمولا نیازمندی های سیستم پیچیده‌تر از این حرف‌هاست. مثلا از سیستم انتظار میره که وقتی یه کاربر جدید ثبت‌نام می‌کنه، علاوه بر ثبت اطلاعات کاربری‌ش در جدول کاربران، یک رکورد کیف پول هم در جدول کیف پول براش ثبت بشه. خب اینجا حفظ «یکپارچگی داده» سخت‌تر میشه. ما باید مطمئن بشیم که اگه کاربری ثبت نام می‌کنه، کیف پول هم براش ساخته میشه. اگه این اتفاق به درستی رخ نده، مشکلات متعددی می‌تونه برای کاربر رخ بده، مثلا کاربر می‌تونه همه پروسه خرید در محصول ما رو انجام بده، و حتی اقدام به خرید از درگاه بانکی هم بکنه، اما هیچوقت کیف پولش شارژ نشه! چون کیف پولی براش ساخته نشده.
مثال ثبت نام کاربر و ساخته شدن کیف پول یکی از هزاران نمونه‌ایه هست که در دنیای واقعی رخ میده.

اجازه بدید یه نمونه دیگه از این چالش‌ها رو براتون شرح بدم. فرض کنید یه پلتفرم آموزش برنامه‌نویسی تولید کردید که در این پلتفرم یک سری اساتید به یک سری دانش‌آموز برنامه‌نویسی رو آموزش میدن. تو این پلتفرم یه شیوه رایج استفاده می‌تونه اینطوری باشه که دانش‌آموز یک یا چند جلسه آموزش رو با استاد مورد نظرش خریداری می‌کنه. طبیعتا پولی که دانش‌آموز به پلتفرم پرداخت می‌کنه نباید به حساب استاد بره، چون هنوز جلسه‌ای برگزار نشده و خدمتی دریافت نکرده. پس هزینه کلاس پرداخت شده توسط دانش‌آموز باید نزد پلتفرم محفوظ بمونه تا زمانی که جلسه آموزش برگزار شد. اونوقت سیستم باید پس از کسر کمیسیون پلتفرم، مابقی مبلغ پرداختی دانش‌آموز رو به حساب استاد واریز کنه.
خب در مثال دوم، کار باز هم پیچیده‌تر شد. چون سناریوهای متعدد و متفاوتی ممکنه رخ بده. اگه دانش‌آموز کلاس رو کنسل کرد چی؟ اگه استاد کلاس رو کنسل کرد چی؟ اگه جلسه به تعویق افتاد چی؟ اگه شرایط کنسلی کلاس شرایط متفاوت داشت چی؟ مثلا اگه استاد تا ۱ روز قبل از جلسه کلاس رو کنسل کنه جریمه ای نداره، ولی اگه دو ساعت قبل از جلسه کلاس رو کنسل کنه باید ۱۰ درصد مبلغ کلاس رو به پلتفرم بپردازه. به همچنین دانش‌آموز می‌تونه شرایط کنسلی متفاوتی رو بر اساس زمان کنسل کردن داشته باشه.

خب برگردیم سر اصل موضوع، چیکار کنیم در همه این حالت‌های پیچیده «یکپارچگی داده» سیستم حفظ بشه؟ نقض یکپارچگی در مثال دومی که زدم چی می‌تونه باشه؟ اینکه دانش‌آموز هزینه کلاس رو به پلتفرم پرداخت کنه، اما پلتفرم دستمزد استاد رو به حسابش واریز نکنه. اینکه دانش‌آموز کلاس رو کنسل کنه اما مبلغ کلاس به حسابش برنگرده. اینکه استاد کلاس رو کنسل کنه، اما مبلغ جریمه از حسابش کسر نشه. و کلی سناریو دیگه می‌تونه رخ بده که باعث بشه «یکپارچگی داده» سیستم نقض بشه.

خب چیکار کنیم؟ چطوری کد بزنیم که این مسائل پیش نیاد؟ چطوری دیتابیس رو طراحی کنیم و معماری سیستم رو بچینیم که این مشکلات پیش نیاد؟ جواب روشنه، بیخیالش، راهی نیست که این مسائل پیش نیاد. در نهایت با هر روشی که کد بزنی و از هر ابزاری که استفاده کنی و هر چقدرم معماری سیستم ت خفن باشه و هر چقدرم خوب کد بزنی و با دقت کار رو انجام بدی، در نهایت اتفاقی رخ میده که قابل پیش‌بینی نیست.

خب تا اینجا طبیعیه، به هر حال هر سیستم نرم‌افزاری یک سری مشکل و باگ داره و مجموعه‌ای از این مشکلات و باگ‌ها در طی یک سری سناریو ساده و حتی پیچیده می‌تونن منجر به «ناسازگاری داده» بشن و «یکپارچگی داده» رو نقض کنن. رخ دادن چنین مسائلی قابل پذیرشه و قابل جبران. مسأله وقتی حاد و غیرقابل پذیرش میشه که «ناسازگاری داده» بصورت طولانی مدت رخ بده. اونوقت دیگه به این راحتی ها قابل جبران نیست، به این راحتی‌ها نمیشه از خسارت‌های ناشی از این مشکل چشم‌پوشی کرد. تصور کنید پلتفرم آموزشی در ۱۰ درصد موارد به اشتباه کمیسیون پلتفرم رو قبل از واریز مبلغ هزینه کلاس به حساب استاد، کسر نکنه. در کوتاه مدت این مبلغ برای پلتفرم عددی نمیشه و قابل چشم‌پوشیه. اما فرض کنید این اشکال برای چند ماه یا حتی یکسال در سیستم نرم‌افزاری پلتفرم وجود داشته باشه و کسی هم متوجه‌ش نشه. میدونید چه خسارت هنگفتی متوجه پلتفرم میشه؟!

خب همه این مقدمات رو گفتم که بگم من می‌خوام یه راه حلی معرفی کنم که خیلی به کمتر شدن مشکلات از این دست کمک میکنه. یکی از راه‌هایی که خیلی می‌تونه به ما کمک کنی از «ناسازگاری داده» خیلی زود باخبر بشیم برگزاری «جلسات بازجویی در دیتاسنتر» هست.

همانطور که در ابتدای مقاله عرض کردم اکثر اوقات ما برنامه‌نویس‌ها سعی می‌کنیم «defensive programming» انجام بدیم، یعنی تا جایی که می‌تونیم حالت‌های مختلف خطا رو پیش‌بینی کنیم و برای هر کدوم یه راه حل ارائه بدیم. اما خیلی خیلی کم پیش میاد که به این فکر کنیم خب اگه خطا رخ داد، اگه سیستم به هم ریخت، اونوقت چیکار کنیم؟ اصلاحا بهش میگن نگرش «let it crash»، یعنی شما باید در ذهنت تصور کنی سناریویی رو که نمی‌تونی پیش‌بینی‌ش کنی. باید خودتو برای مواقعی آماده کنی که سیستم به هم میریزه و سعی کنی سیستم رو recover کنی.

یکی از راه‌های عملی استفاده از نگرش «let it crash» استفاده از «جلسات بازجویی در دیتاسنتر» هست.
یعنی چی در دیتاسنتر جلسات بازجویی برگزار کنیم؟ یعنی برای دیتابیس یه سری cron-job بنویسیم که یک مجموعه‌ای از sanity-checkها رو روی داده‌های دیتابیس انجام میدن. ما باید داده‌های مهم رو صحت سنجی و راستی‌آزمایی کنیم.
شما باید مثل یک بازجو ذهنت رو به دیتاسنتر ببری و به همه داده های مهم سیستم فکر کنی و بررسی کنی که در سناریوهای مختلف تو چه حالتی داده‌ها در سازگاری کامل هستند.

برگردیم به مثال‌هایی در این مقاله مطرح شد. مثال اول: مشکل ثبت نام کار بدون اینکه رکورد کیف پول براش ثبت بشه. خب این یک سناریو مهم و حیاتیه. پس خیلی راحت می‌تونیم یه کوئری دیتابیس بنویسیم، یا اگه microserviceهای جدا هستند یک کدی بنویسیم، که بصورت یک cron-job چند ساعت یکبار یا روزی یکبار اجرا میشه و بررسی می‌کنه در روز جاری چه تعداد کاربر ثبت‌نام کردند ولی رکورد کیف پولی براشون ساخته نشده. این گزارش به شکل‌های مختلف می‌تونه در اختیار ما قرار بگیره و خیلی زود مارو متوجه مشکلی در سیستم بکنه.

در مورد مثال دوم: فرض کنید در مواقع کنسلی سیستم یک رکورد تراکنش در جدول تراکنش‌ها ثبت می‌کنه. نوشتن sanity-check احتمالا به این صورت میشه که بره چک کنه ببینه چه تعداد کلاس کنسل شده وجود داره که رکورد تراکنشی برای کنسل شدنشون ثبت نشده.
سناریوها ممکنه پیچیده‌تر باشن، اما یه سری راه‌های ساده وجود داره که صحت اطلاعات کلی سیستم رو تایید کنه. مثلا می‌گم، جمع درآمد روزانه یک استاد باید برابر باشه با جمع هزینه کلاس‌های اون استاد منهای کمیسیون پلتفرم. جمع درآمد پلتفرم باید برابر باشه با هزینه کلاس‌های برگزار شده ضربدر درصد کمیسیون پلتفرم.

دقت کنید که «بازجویی دیتاسنتر» یا sanity-check رو با fraud detection اشتباه نگیرید. در تشخیص جعل هدف «یکپارچگی داده» ها نیست. هدف اینه که رفتار ناسالم کاربران تشخیص داده بشه. و عموما در تشخیص جعل فرض رو بر «سازگاری داده» در نظر میگیرن. مثلا در پلتفرم آموزشی زبان، به دنبال اساتیدی میگردن که فقط یک جلسه با دانش‌آموزان کلاس برگزار کردن، این چه چیزی رو می‌تونه برسونه؟ بله، احتمالا استاد محترم پس از اینکه دانش‌آموز از طریق پلتفرم با ایشان یک جلسه برگزار کرده، دانش‌آموز رو از پلتفرم خارج کرده که کمیسیون نپردازه. خب در سناریو مذکور تمرکز روی رفتار ناسالم کاربران با در فرض سالم بودن داده‌هاست.

اما در بحث «یکپارچگی داده» ما فرض سازگاری داده‌هارو زیر سوال میبریم که بتونیم مشکلات مهم سیستم رو که یکپارچگی داده‌ها رو به هم میزنن زود تشخیص بدیم و اصلاح کنیم.

صحت سنجی داده ها صرفا نوشتن یک سری کوئری دیتابیس نیست و سناریوهای پیچیده تر رو باید براشون راه حل های متفاوت تر در نظر گرفت. اما در کل بحث راستی‌آزمایی داده یک اصل خیلی مهمه که معمولا ما برنامه‌نویس‌ها بهش توجه نمی‌کنیم.

اگه سناریوهای خوب و محکمی برای صحت‌سنجی داده‌هاتون داشته باشید، خیال خودتون نسبت به کدی که می‌نویسید و محصولی که ارائه می‌دید راحت‌تره، چرا؟ چون می‌دونید با وجود مشکلات احتمالی و باگ‌های احتمالی، در نهایت «یکپارچگی داده» های سیستم سرجاشه و مشکلی از این بابت وجود نداره.

7 پسندیده

عالی بود

از این دسته تجربه ها بازم بزارید چیزای خوبی ازش یاد گرفتم

1 پسندیده

خیلی مفید و جالب بود. ممنونم.

1 پسندیده

پروژه قبلی که روش کار میکردم یه پروژه مدیریت مالی بود که کداش بشدت کثیف بود و یکسری باگ ها داشتیم که تا ماه ها نتونستیم بفهمیم دلیل اصلی چی بود. من همیشه به این فکر میکردم که یکسری تسک های دوره‌ای بنویسیم که بیاد این چک کردن دیتا رو انجام بده و خیلی زود متوجه بشیم مشکل یکپارچگی دیتا داریم یا نه و از کجا نشات میگیره.

این نوشته خیلی برام جالب بود چون باعث شد بفهمم که اون فکری که داشتم خیلی هم بیراه نبود.

1 پسندیده

مقاله بسیاری خوبی بود ، ممنون از اینکه تجربیات خودتون رو در اختیار دیگران قرار میدهید

1 پسندیده