ایونت سورسینگ و باگی که مایگریشن بدترش کرد
خلاصهٔ کاملتر
نویسنده با یه سناریوی آشنا شروع میکنه: تو یه سیستم رزرو هتل، شهر مالیات گردشگری رو از ۴٫۵۰ به ۷٫۰۰ افزایش میده. تیم برای پشتیبانی از بازهٔ اعتبار نرخها، نرخ رو یه بار بر اساس تاریخ خروج پیدا میکنه و به همهٔ شبها میزنه. برای اقامتی که ۱ آوریل رو رد میکنه، شبهای مارس هم با نرخ جدید حساب میشن. باگ ۱۹ مارس گزارش و همون روز فیکس میشه؛ ولی کار اصلی تازه از اینجا شروع میشه.
تو مدل state-based، ردیف رزرو فقط total_amount و updated_at داره: نه ریزِ محاسبه، نه نسخهٔ rate plan، نه تاریخ رزرو. پس مایگریشن مجبوره از نو حساب کنه، اون هم با نرخهای امروز. نتیجه اینه که ۱٬۲۴۰ ردیف آپدیت میشن که فقط ۹۰۰تاش واقعاً خراب بودن، و قیمت اتاقِ حدود ۴۰۰ رزرو با نرخ جدید فصل بالا میره. مهمونها ایمیل تأیید میگیرن با مبلغی بیشتر از چیزی که توافق کرده بودن.
مایگریشن دوم بدتره، چون حالا updated_at همهٔ اون ردیفها یکیه و total_amount هم همونیه که خودمون نوشتیم. یعنی ورودی مایگریشن دوم، خروجی مایگریشن اوله. نویسنده میگه چارههای state-based مثل نگهداشتن breakdown تو یه ستون JSON یا جدول price_history جواب میدن، ولی معمولاً بعد از هر حادثه و فقط روی همون جدول اضافه میشن؛ چیزی که در عمل ساخته میشه یه لاگ رویدادِ ناقصه که ستونبهستون و زیر فشار زمان تصمیمگیری شده.
تو نسخهٔ ایونتسورس، استریم همون رزرو رویدادهای RatePlanApplied و ReservationPriceCalculated رو داره، پس معلومه موقع رزرو نرخ نسخهٔ ۴ و ۱۸۰ در شب بوده و فقط cityTax غلط ثبت شده. نیمهٔ دوم ماجرا متادیتاست: نویسنده اضافهکردن buildSha رو توصیه میکنه، یعنی کامیتی که سرویس موقع نوشتن رویداد روش بوده. با یه کوئری روی همون SHA دقیقاً همون ۹۰۰ رویدادِ باینری خراب برمیگرده، نه ۳۴۰ رزروی که فقط تو همون هفته دستخورده بودن.
توصیهٔ اصلی اینه که گذشته رو ویرایش نکنیم؛ رویدادها تغییرناپذیرن و داشتن تاریخچهٔ دقیق، حتی با باگ، خودش یه سناریوی معتبره. بهجاش یه رویداد اصلاحی اضافه میشه، همون کاری که حسابدار با ثبت سند اصلاحی میکنه:
type ReservationPriceCorrected = {
type: 'ReservationPriceCorrected';
data: {
reservationId: string;
previousTotal: number;
correctedTotal: number;
reason: string;
};
};حتی اگه خودِ اسکریپت اصلاح باگ داشته باشه، چون رویدادهای ۱۴ مارس هنوز سر جاشونن، اصلاح بعدی باز هم از روی دادهٔ اصلی حساب میشه نه از روی اصلاح قبلی. مثال آخر نویسنده هم گویاست: یکی از کارمندهای پذیرش قبلاً دستی قیمت رو اصلاح و ۵ درصد تخفیف رضایتی هم اعمال کرده بود؛ یه اصلاح دستهجمعیِ کورکورانه کار اون رو خراب میکرد، ولی با استریم میشه چک کرد که بعد از رویداد خراب، رویداد اصلاحی دیگهای ثبت شده یا نه و اون رزرو رو دستنخورده گذاشت.
نکات کلیدی:
- تو مدل state-based، مایگریشن اصلاحی همون شواهدی رو پاک میکنه که برای بررسی درستیِ فیکس لازمه
- ۱٬۲۴۰ ردیف آپدیت شد ولی فقط ۹۰۰تاش واقعاً از باگ خراب شده بودن
- تو استریم رویدادها ورودیهای محاسبهٔ اولیه میمونن، پس مقدار درست بدون بازسازی حدسی درمیاد
- buildSha تو متادیتا اجازه میده دقیقاً رویدادهای ساختهٔ باینری خراب پیدا بشن
- بهجای ویرایش گذشته یه رویداد اصلاحی اضافه کنید، و بهتره اصلاح رو از اول بهعنوان یه قابلیت با دستور، مجوز و دلیل اجباری بسازید




