اولین از پانزدهمین :‌ Single point of Failure vs Single point of Truth

قبل از هرچیزی بهتره این ۲ مفهوم رو تعریف کنیم برای هم:

  • Single Point of Failure
    به قسمتی از سیستم که اگه اون قسمت درست کار نکنه به صورت کلی سیستم کار نمی کنه یا درست کار نمی کنه گفته میشه که به اختصار از این به بعد بهش میگیم SPOF.
  • Single Point of Truth
    به قسمتی از سیستم که اطلاعات یا لاجیک‌ها همگی از اون قسمت گذر می‌کنن و در صورتی که نیاز به تغییر یا بررسی دارند،‌ صرفا در همون قسمت این کارها روشون انجام میشه. که از این به بعد به اختصار بهش میگیم SPOT

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

در تمامی پروژه‌هایی که نیاز به یک بک‌اند دارن ما نیاز به یک برنامه برای ارائه‌ی فایل‌ها و درخواست‌ها داریم. اکثرا در پروژه‌های بزرگ ما با وبسرورهایی مثل nginx یا HA proxy سر کله می‌زنیم تا از بیشترین سرعت و قدرت در پردازش بیشترین درخواست‌های ممکن برخوردار باشیم.
حالا بیاید فکر کنیم که به صورت ناگهانی وبسرور ها دچار اختلال میشه و نمی‌تونه درخواست‌هایی که سمتش میاد رو جواب بده. تو این شرایط تمامی سرویس ما دچار اختلال میشه و تا زمانی که مشکل وبسرورمون رو حل نکنیم هیچ تیکه‌ای از محصول ما کار نمی‌کنه!
قطعا این خبر تیم شرکتمون رو ناراحت می‌کنه مخصوصا زمانی که مدت زمان حل کردن این مشکل طولانی بشه. برای این مسئله هممون میدونیم که پیشنهاد میشه از ساختارهایی که High Availability دارن استفاده کنیم. کاری که این ساختارها برای ما انجام میده اینه که نقطه‌ای از سیستم رو که اگه اون نقطه درست کار نکنه کل سیستم دچار نقص میشه رو مورد هدف قرار میده و سعی می‌کنه با داشتن تئوری‌های بک‌آپ گونه درصورتی که اون قسمت دچار مشکل شد در کسری از ثانیه سیستم بک‌آپ رو وارد مدار کنه تا مشکل به صورت موقت حل بشه و ما زمان داشته باشیم تا نقطه‌ی اصلیمون رو تعمیر کنیم.

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

در دنیای مدیریتی هم این مسائل رو داریم. بیاید با یه مثال ساده این مسئله رو در دنیای مدیریتی هم بررسی کنیم.
فکر کنید که در شرکت شما یک محصول با زبان erlang درحال توسعه هستش، و فقط یک توسعه دهنده در شرکت دارید که با اون زبان توانایی برنامه نویسی داره و همون آدم هم صرفا همون پروژه رو جلو میبره. اگه خدایی نکرده اون یه نفر در یه اتوبوسی باشه که اون اتوبوس تصادف جدی‌ای بکنه،‌ شما باید از فردای اون روز کاسه‌ی چه کنم به دست بگیرید و منتظر باشید تا تیم منابع انسانی یک فرد با توانایی‌های خاصی که می‌خواد براتون پیدا کنه، مصاحبه کنه،‌ استخدام کنه، با فضا شرکت آشنا کنه، یک ماه اول کار رو مانیتور کنه و ...
قطعا هیچ کسی دوست نداره که حداقل ۲ ماه (از روی تجربه دارم میگم) محصولی که خیلی برای شرکت مهم بوده، هیچ توسعه‌ای نداشته باشه! پس قبل از این هم دردسر، بهتر نیست به این فکر کنیم که حداقل ۲ نفر روی پروژه همزمان کار کنن که اگه یه نفر مشکلی براش پیش اومد (یا حتی پیشنهاد کاری بهتری گرفت) حداقل برای پلن‌های شرکت ریسک حساب نشه؟
از مثال بالا کمی کم ریسک‌تر هم می‌تونیم مثال بزنیم: مثلا پروژه‌ای که ۵ نفر از تیم فعلی شرکت توانایی انجامش رو دارن اما به خاطر پیچیده بودن زمان زیادی طول می‌کشه تا روی اون پروژه مسلط بشن.
از سمتی فکر کنید در شرکت، برای تهیه‌‌ی مواد اولیه‌ی خط تولید، فقط با یک شرکت تامین کننده روبرو هستیم، اگه خدایی نکرده اون شرکت دچار مشکلی بشه، کل شرکت شما دچار تامین مواد اولیه میشه!

همه‌ی این مثال‌ها به ما نشون میده که باید در سیستم‌هایی که طراحی می‌کنیم، نقطه‌ای یا قسمتی از سیستم به گونه‌ای نباشه که روی کارکرد کلیت سیستم تاثیر جدی بگذارد.


از سمتی در بعضی از مواقع نیاز متفاوتی در طراحی سیستم وجود دارد که سعی می کنم مثال‌های دیگه ای رو در این راستا بیارم:

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

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

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

در مثال دیگه‌ای در حوزه‌ی مدیریت میشه به مدیریت مالی مجموعه سری زد. در اکثر شرکت‌ها پروسه‌ی واریز حقوق افراد توسط یک نفر در مجموعه انجام میشه. دلیل ساده‌ای داره، اگه این کار توسط چندین نفر قابل انجام باشه، ممکنه هستش یا حقوق‌ها واریز نشه یا دوبار واریز بشه (آشپز ۲ تا بشه غذا یا شور میشه یا بی‌نمک)




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

  • اگه اون یه نقطه کارش رو انجام نده کل سیستم به مشکل بر می خوره و کارها پیش نمیره؟
  • اگه اون نقطه بعضی از کارهاش رو درست انجام نده ترجیح میدید کلا کاری انجام نده یا ترجیح میدید قسمتی از کارهم که شده انجام بشه؟

فکر کنم با جواب دادن به این سوال‌ها متوجه بشید که باید اصل پراکندگی رو پی بگیرید یا اصلی تجمیع رو!