صف کارها ساده به نظر میآد، ولی نیست
خلاصهٔ کاملتر
نویسنده تو این پست سراغ چیزی میره که سطحی نگاه کنی خیلی سادهست: صف کارها (job queue). منظورش سیستمیه که کارهای دستهای رو ثبت، زمانبندی و اجرا میکنه، معمولاً هم FIFO یا شبیه اونه. اینجور صفها جایی بهدرد میخورن که توان عملیاتی مهمه ولی تأخیر خیلی حساس نیست — مثل CI یا تولید گزارشهای تحلیلی.
سه «عدسی» رو معرفی میکنه که به گفتهٔ خودش تو طراحی سیستم بهش کمک کردن: بدبینی نسبت به صفها (صفها معمولاً یا نزدیک پرن یا نزدیک خالی)، محدودیتهای صریح (وامگرفته از سبک TigerBeetle: برای همهچی سقف و بودجهٔ مشخص تعریف کن) و مدل خطا یعنی همون فرضهایی که دربارهٔ خرابی و قابلیت اطمینان وابستگیهات داری.
مسئلهٔ واقعیاش اینه: یه جاب پسزمینه مخزنهای مرجع گیت رو بهشکل فشرده repack میکنه. دو حالت داره؛ ریپک کامل که همهچی رو از صفر حساب میکنه و ۷ ساعت طول میکشه، و ریپک تدریجی که فقط تغییرات جدید رو میبینه و ۲ ساعته. خروجی ریپک کامل ۵۰ تا ۶۰ درصد کوچیکتره (یعنی دانلود سریعتر و دیسک کمتر)، ولی ریپک تدریجی نتیجهای بهروزتر میده.
ایدهٔ بهظاهر بدیهی اینه: آخر هفتهها ریپک کامل، روزهای کاری ریپک تدریجی. مشکل اینجاست که کاربرِ یه صف کار، حلقهٔ کنترل رو ننوشته؛ فقط چند تا پیچ تنظیم در اختیار داره. اینجا دو تا پیچ هست: فاصلهٔ زمانبندی و سقف همزمانی. قبلاً فاصله ۹ ساعت بود (برای جاب ۷ ساعته) و سقف همزمانی ۱. حالا وسوسه میشی فاصله رو بذاری ۳ ساعت و فکر کنی سقف همزمانی جلوی دردسر آخر هفته رو میگیره.
برای فهمیدن اینکه چرا این استدلال غلطه، نویسنده مسئله رو برعکس میکنه: اگه خودت داشتی صف رو پیادهسازی میکردی، وقتی جاب اول تموم نشده و وقت جاب دوم رسیده، فقط چهار انتخاب داری. اگه سقف همزمانی اجازه بده، اجرای موازی. وگرنه یکی از این سهتا: ترجیح جدید (جاب اول رو بکش، دومی رو اجرا کن)، انتظار (دومی تو صف بمونه) یا ترجیح قدیمی (دومی رو خودکار کنسل کن و بذار اولی کارش رو تموم کنه). حدس نویسنده اینه که بیشتر آدمها گزینهٔ آخر رو عجیب و غیرمنطقی میدونن.
ولی حساب سرانگشتی برای آخر هفته (جاب ۷ ساعته با فاصلهٔ ۳ ساعته) داستان دیگهای میگه. با ترجیح جدید، هر جاب سر ساعت سوم کشته میشه و هیچی تموم نمیشه: ۴۸ ساعت محاسبه در ۱۶ جاب، دور ریخته. با انتظار، ۱۶ جاب یعنی ۱۱۲ ساعت تقاضا در برابر ۴۸ ساعت ظرفیت، پس دوشنبه با حدود ۶۴ ساعت کار عقبافتاده شروع میکنی و کل هفتهٔ کاری فقط ۴۰ ساعت ظرفیت خالی داره. با ترجیح قدیمی اما تقریباً هفت جاب کامل تموم میشه، ۹ تا از ۱۶ جاب کنسل میشن و دوشنبه فقط ۱ ساعت کار عقبافتاده داری.
نکتهٔ ظریفتری هم هست: با استراتژی انتظار خیلی راحت میشه مشکل رو پنهان کرد بدون اینکه حلش کنی. آخر هفته پیجر میخوره چون صف خالی نمیشه، تو هم سقف همزمانی رو میبری روی یه عدد رند مثل ۴، جابها شروع میکنن به جلو رفتن و ظاهراً حل شد — غافل از اینکه داری چند جاب ۷ ساعتهٔ گرون رو همزمان میدوونی که نتیجهشون فقط ۳ ساعت اعتبار داره و بعد باطل میشه.
درس اصلی نویسنده اینه که این سه عدسی — صف، محدودیت و مدل خطا — رو صریح بنویسید. برای طراح سیستم، صریحنویسی این فرضها به مصرفکننده اجازه میده سریع بفهمه سیستم به درد بارِ کاریاش میخوره یا نه؛ و برای مصرفکننده، پیدا کردن همین فرضها تو مستندات یا کد یعنی فهمیدن اینکه کجا فرض طراح با نیاز تو در تضاده. مخصوصاً وقتی پای فایلهای پیکربندی وسطه که جریان کنترل رو تنظیم میکنن نه فقط داده رو، چون این فایلها دقیقاً همین جزئیات حیاتی رو از چشم پنهان میکنن.
نکات کلیدی:
- وقتی جاب قبلی تموم نشده و تایمر دوباره میزنه، فقط چهار رفتار ممکنه: موازی، ترجیح جدید، انتظار، ترجیح قدیمی
- «ترجیح قدیمی» غیرشهودیه ولی وقتی انتظار داری جاب بعدی هم از فاصلهٔ زمانبندی بگذره، بهترین گزینهست
- در سناریوی آخر هفته، ترجیح جدید ۴۸ ساعت محاسبه رو کاملاً هدر میده
- استراتژی انتظار بدون سقف صف، بدهی کاریای میسازه که هفتهٔ بعد هم صاف نمیشه
- بالا بردن سقف همزمانی میتونه مشکل رو پنهان کنه، نه حل
- سه عدسی مفید برای طراحی: مراقب صفها بودن، محدودیتهای صریح، و مدل خطای روشن




