زمان، سادهترین چیزی که خرابش میکنیم
خلاصهٔ کاملتر
این مقاله قسمت دوم مجموعهای به اسم «فراتر از مسیر خوشحال»ه که هر بار یکی از وابستگیهای بدیهیِ سیستم رو زیر سؤال میبره. نویسنده میگه زمان از بیرون سادهترین وابستگیه: لحظهٔ فعلی رو میگیری، با یه لحظهٔ دیگه مقایسهاش میکنی و تصمیم میگیری. مثال ثابت مقاله یه فرایند پرداخته که کالا رو پونزده دقیقه برای کاربر رزرو نگه میداره.
اولین ترک تو این مدل اینه که زمان یک ابزار واحد نیست. زمان دیواری (wall-clock) برای ثبت اینکه کاربر کِی کاری کرده خوبه، زمان یکنواخت (monotonic) به این سؤال جواب میده که چقدر طول کشید، زمان منطقی دربارهٔ ترتیبه نه ساعت، و زمان کسبوکار همون معناییـه که تقویم و قوانین دامنه به یه لحظه میدن. این چهارتا شبیه هم به نظر میرسن ولی مسئلهٔ مهندسی متفاوتیان.
اختلاف ساعت بین ماشینها (clock skew) دقیقاً همونجاییه که مثال رزرو میشکنه. سفارشگیری رزروی مینویسه که ساعت ۱۰:۱۵ منقضی میشه؛ کاربر یک دقیقه مونده به ددلاین هنوز داره پرداخت میکنه، ولی ساعتِ ماشینی که جاب انقضا روش اجرا میشه دو دقیقه جلوتره و همون ردیف رو منقضی حساب میکنه. به گفتهٔ نویسنده همگامسازی ساعت این اختلاف رو کم میکنه، ولی باید یه وابستگی عملیاتی حسابش کرد، نه اثبات درستی.
برای اندازهگیری مدت زمان هم ساعت دیواری ابزار غلطیه. اگه ساعت سیستم وسط کار به عقب اصلاح بشه، تفریق ساده میتونه بگه عملیات «منفی پنج ثانیه» طول کشیده و اگه برعکس بپره، یه کار سریع دقیقهها به نظر میرسه. تأخیرسنجی، بودجهٔ تایماوت، فاصلهٔ تلاش مجدد و مدت اجارهٔ قفل، همه سؤال «چقدر گذشت»ان و جاشون روی ساعت یکنواخته.
تله بعدی استفاده از تایماستمپ برای تشخیص ترتیب رویدادهاست. اگه ساعت ورکری که «آزادسازی» رو ثبت میکنه عقب باشه، مرتبسازی لاگها داستان رو برعکس تعریف میکنه: اول آزاد شد، بعد رزرو. برای تصمیمهای حساس باید سراغ نسخه، تراکنش، آفست استریم یا ماشین وضعیت رفت؛ سؤال واقعی اینه که از وضعیت فعلی چه گذاری مجازه، نه اینکه کدوم رکورد عدد ساعتش بزرگتره.
نتیجهٔ عملی اینه که انقضا خودش یه گذار وضعیته، نه یه مقایسهٔ زمانی. تایماستمپ فقط میگه این رزرو کاندیدای انقضاست؛ نوشتن نهایی باید شرطی باشه تا ورکرِ دیرکرده یا تکراری نتونه چیزی رو خراب کنه:
update reservations
set state = "expired"
where id = reservationID
and state = "reserved"
and paymentAuthorizationID is nullهمین شرط باعث میشه اگه پرداخت یا لغو زودتر رزرو رو جلو برده باشن، بهروزرسانی انقضا اصلاً با هیچ رکوردی مطابقت نکنه.
کار زمانبندیشده هم به گفتهٔ نویسنده «فقط برنامهٔ شروع کاره»، نه تضمین اینکه سر وقت و دقیقاً یک بار اجرا شده. اگه ورکر حین یه استقرار چند دقیقه متوقف بشه، اجرای بعدی همهٔ کار عقبافتاده رو یکجا پیدا میکنه و اون چیزی که از دید زمانبند «جبران عقبماندگی»ـه، از دید بقیهٔ سیستم یه انفجار ناگهانی از آپدیت دیتابیس و نوتیفیکیشنه. راهحل: دستههای محدود، مالکیت مشخص برای هر دسته و ثبت پایدار پیشرفت.
لایهٔ آخر، زمانِ کاربره. مشتری به لحظهٔ UTC کاری نداره؛ براش مهمه که سفارشش قبل از کاتآف تحویل ثبت شده یا نه. برای رویدادهای گذشته لحظه رو ذخیره کن، ولی برای قرار آیندهٔ یه آدم، تاریخ و ساعت محلی بههمراه منطقهٔ زمانی رو نگه دار؛ چون Europe/London قواعد تغییر ساعت رو با خودش میآره ولی یه آفست مثل +01:00 فقط یه لحظه رو توصیف میکنه. باگهای زمانی هم معمولاً سروصدا ندارن: سرویس بالاست، داشبورد سبزه و سیستم بیصدا داره تصمیمهای کمی غلط میگیره.
نکات کلیدی:
- زمان چهار مسئلهٔ جداست: مدت، لحظه، ترتیب، و قاعدهٔ کسبوکار — یک تایماستمپ نباید هر پنج نقش رو بازی کنه
- اندازهگیری مدت و ددلاین با ساعت یکنواخت، نه ساعت دیواری که ممکنه وسط کار اصلاح بشه
- تایماستمپ ترتیب رویدادها رو ثابت نمیکنه؛ نسخه، تراکنش یا ماشین وضعیت لازمه
- انقضا یه گذار وضعیت شرطیه؛ نوشتن باید به وضعیت فعلی مقید باشه تا ورکر دیرکرده بیخطر بمونه
- جاب زمانبندیشده تضمین اجرا نیست؛ برای جبران عقبماندگی، دستهبندی و ثبت پیشرفت لازمه
- برای قرارهای آینده منطقهٔ زمانی رو ذخیره کن، نه فقط آفست
- تصمیمهای زمانمحور رو مستقیم رصد کنید: تعداد انقضاها، تأخیر جابها، اختلاف ساعت و سن صف




