ریست کردن sticky connection بین جابهای صف در لاراول ۱۳.۳۶
خلاصهٔ کاملتر
خیلی از اپهای لاراولی ترافیک دیتابیس رو بین یه سرور write و یه یا چندتا read replica (یعنی کپیهای فقطخوندنی دیتابیس) پخش میکنن. وقتی گزینه sticky روشن باشه، بعد از یه نوشتن، خوندنهای بعدی هم از سرور write انجام میشه تا درخواست بتونه دادهای رو که همین الان ذخیره کرده بخونه. توی یه درخواست وب این حالت با فرستادن response تموم میشه، ولی توی worker صف ممکنه تا ریاستارت worker بمونه.
ماجرا اینه که connection یه فلگ داره که ثبت میکنه چیزی نوشته شده یا نه. یه insert، یا update و delete که حداقل یه ردیف رو عوض کنه، این فلگ رو روشن میکنه و تا وقتی کسی forgetRecordModificationState() رو صدا نزنه روشن میمونه. Octane این کارو بین درخواستها انجام میده، ولی queue:work تا قبل از این نسخه نمیکرد. نویسنده مثال میزنه: یه جاب سفارش رو shipped میکنه و ۱۰ هزار جاب فقطخوندنیِ بعدی همه از سرور write میخونن، تا وقتی worker به --max-jobs یا --max-time یا سقف حافظه برسه یا queue:restart بزنی.
توی لاراول ۱۳.۳۶، Jack Bayliss یه property استاتیک به worker اضافه کرده که توی AppServiceProvider روشنش میکنی:
public function boot(): void
{
Worker::$flushStickyConnections = true;
}با این فلگ، worker قبل از برداشتن جاب بعدی روی همه connectionهای باز forgetRecordModificationState() رو صدا میزنه. داخل خود جاب، sticky مثل قبل کار میکنه؛ یعنی جابی که یه ردیف مینویسه و بعد میخونه، هنوز از سرور write میخونه. این فلگ توی لاراول ۱۳ پیشفرض خاموشه، چون ممکنه جابی به دادهای وابسته باشه که جاب قبلی توی همون worker نوشته. نویسنده میگه جابهای وابسته باید chain یا batch باشن، ولی بهتره قبل از روشن کردنش این جابها رو چک کنی.
یه نکته مهم برای درایور صف database: این درایور برای رزرو کردن جاب یه update روی جدول jobs میزنه. اگه این جدول روی همون connection دادههای اپ باشه، این update بعد از ریست و قبل از اجرای جاب دوباره فلگ رو روشن میکنه و عملاً فلگ جدید بیاثر میشه. راهحل اینه که با DB_QUEUE_CONNECTION صف رو به یه connection جدا (مثلاً queue) بفرستی، چون فلگ sticky برای هر connection جدا نگه داشته میشه. درایورهای Redis و SQS این مشکلو ندارن.
برای کدهای طولانیمدت دیگه، مثل یه دستور Artisan که توی حلقه بینهایت میچرخه، میتونی خودت DB::forgetRecordModificationState() رو اول هر دور صدا بزنی. متد DB::hasModifiedRecords() هم مقدار فعلی فلگ رو برمیگردونه که برای تست کردن اینکه یه جاب از replica خونده یا نه به درد میخوره.
نکات کلیدی:
- تنظیم جدید Worker::$flushStickyConnections از لاراول ۱۳.۳۶ اضافه شده (PR شماره 61915)
- پیشفرضش خاموشه و باید توی AppServiceProvider روشنش کنی
- قبل از هر جاب forgetRecordModificationState() روی همه connectionهای باز صدا زده میشه
- با درایور database، جدول jobs باید روی connection جدا باشه وگرنه فلگ دوباره روشن میشه
- Redis و SQS به دیتابیس اپ چیزی نمینویسن و این مشکلو ندارن
- DB::hasModifiedRecords() وضعیت فعلی فلگ رو برای تست برمیگردونه




