صبح کے تین بجے آپ کے ڈیٹا بیس کا CPU 100% پر پہنچ جاتا ہے۔ ٹریفک معمول کے مطابق نظر آتی ہے۔ درخواستوں (requests) کی تعداد غیر معمولی طور پر زیادہ نہیں ہوتی۔ پھر بھی آپ کا پرائمری اسٹور (primary store) جواب دے رہا ہوتا ہے کیونکہ ان میں سے ہر ایک درخواست بالکل ایک ہی وقت میں بالکل ایک ہی چیز مانگ رہی ہوتی ہے۔

یہ "thundering herd" کا مسئلہ ہے۔

کسی کنسرٹ کے بعد اسٹیڈیم کا تصور کریں۔ ایک گھنٹے کے دوران، وہی ایک ایگزٹ ڈور (exit door) آرام سے ہر کسی کو باہر نکلنے دے سکتا ہے۔ لیکن اگر پوری بھیڑ اسی دس سیکنڈ کے وقفے میں اسی ایک دروازے سے نکلنے کا فیصلہ کرے، تو دروازہ ٹوٹتا نہیں ہے۔ وہ بس اس کنکرنسی (concurrency) کو سنبھال نہیں پاتا۔ اصل مسئلہ ہجوم کا دباؤ ہے، دروازہ نہیں ہے۔

ہجوم کہاں بنتا ہے

یہ پیٹرن تب ظاہر ہوتا ہے جب بہت سے پروسیسز (processes) کسی ایک ایونٹ پر ہم آہنگ (synchronize) ہو جاتے ہیں۔ اس کے لیے آپ کو کسی نقصان دہ ٹریفک کی ضرورت نہیں ہے۔ عام سسٹمز خود ہی یہ کر لیتے ہیں۔

کیش کی میعاد ختم ہونا (Cache expiration)۔ ایک ہاٹ کیش کی (hot cache key) ختم ہو جاتی ہے۔ یہ پروڈکٹ کیٹلاگ، فیچر فلیگ، یا صارف کی اجازتوں کی فہرست ہو سکتی ہے۔ ہزاروں ایپلی کیشن سرورز بیک وقت خالی جگہ کو محسوس کرتے ہیں۔ ہر سرور، نیک نیتی کے ساتھ، اپنا ڈیٹا بیس کنکشن کھولتا ہے اور ویلیو کو دوبارہ بنانے کے لیے وہی بھاری کوئری (query) چلاتا ہے۔ ڈیٹا بیس کو کبھی بھی ایک ہی مہنگے سوال کے دو ہزار جوابات متوازی (in parallel) دینے کے لیے کنفیگر نہیں کیا گیا تھا۔ ایک ایکسپائرڈ کی (expired key) پورے کلسٹر کو گرا دیتی ہے۔

کنکشن پول کے جاگنا (Connection pool wakeups)۔ کچھ آرکیٹیکچرز میں، بہت سے ورکر پروسیسز کسی ایک شرط پر رک جاتے ہیں اور کام کے آنے کا انتظار کرتے ہیں۔ جب وہ شرط پوری ہوتی ہے، تو آپریٹنگ سسٹم ہر سوئے ہوئے ورکر کو جگا دیتا ہے۔ صرف ایک ورکر اصل میں کنکشن یا ٹاسک حاصل کرتا ہے۔ باقی سب جاگتے ہیں، یہ دیکھتے ہیں کہ وہ ریس ہار گئے ہیں، اور دوبارہ سو جاتے ہیں۔ یہ چکر کنٹیکسٹ سوئچز (context switches) میں CPU کو ضائع کرتا ہے اور اصل پیداواری کام کو روک سکتا ہے۔

ری ٹرائی اسٹرمز (Retry storms)۔ کوئی ڈاؤن اسٹریم سروس (downstream service) تھوڑی دیر کے لیے رکتی ہے۔ ہر کلائنٹ ٹائم آؤٹ کو پکڑتا ہے اور دوبارہ کوشش کرنے سے پہلے ایک مقررہ وقفے، مثلاً ٹھیک ایک سیکنڈ کا انتظار کرتا ہے۔ جب سروس بحال ہوتی ہے اور دوبارہ ٹریفک قبول کرتی ہے، تو ہر کلائنٹ اسی لمحے اس پر حملہ کرتا ہے۔ سروس، جو ابھی ٹھنڈی اور بحال ہو رہی ہوتی ہے، دوبارہ گر جاتی ہے۔ یہ چکر بار بار دہرایا جاتا ہے۔

کرون کولیژنز (Cron collisions)۔ اگر آپ انسٹنسز کے ایک پورے گروپ میں مینٹیننس، رپورٹ جنریشن، یا کیش وارمنگ (cache warming) کو ٹھیک 00:00 UTC پر چلانے کا شیڈول بناتے ہیں، تو آپ گھڑی کی درستگی کے ساتھ ٹریفک میں اچانک اضافہ پیدا کر دیتے ہیں۔ بوجھ کا اندازہ لگایا جا سکتا ہے، لیکن اس کا ارتکاز جان لیوا ہوتا ہے۔

ریکویسٹ کوالسنگ (Request Coalescing)

کیش اسٹیمپڈز (cache stampedes) کا سب سے مؤثر حل ایک ہی سوال کے ایک سے زیادہ بار جواب دینا بند کرنا ہے۔ جب کیش مس (cache miss) ہو، تو آپ نہیں چاہیں گے کہ 2,500 تھریڈز میں سے ہر ایک ڈیٹا بیس کوئری چلا رہا ہو۔ آپ چاہتے ہیں کہ ایک تھریڈ کوئری چلائے جبکہ باقی 2,499 اس کے نتیجے کا انتظار کریں۔

اس پیٹرن کو اکثر ریکویسٹ کوالسنگ (request coalescing) کہا جاتا ہے۔ Go میں، golang.org/x/sync میں موجود singleflight پیکیج اس کا ایک معیاری نفاذ (implementation) فراہم کرتا ہے۔ کسی مخصوص کی (key) کے لیے Do کو کال کرنے والا پہلا goroutine کام شروع کرتا ہے۔ اسی کی (key) کے ساتھ آنے والے اگلے کالرز اسی Do کال پر رک جاتے ہیں۔ جب کام مکمل ہو جاتا ہے، تو تمام انتظار کرنے والے ایک ساتھ نتیجہ حاصل کر لیتے ہیں۔ آپ نے ایک کوئری چلائی اور 2,500 درخواستوں کو پورا کیا۔

آپ پرومیسز (promises)، فیوچرز (futures)، یا چینلز (channels) کے ان میموری کنکرنٹ میپ (in-memory concurrent map) کے ذریعے اسی طرح کا طرز عمل بنا سکتے ہیں۔ ترکیب یہ ہے کہ چلنے والی درخواست (in-flight request) کو ایٹمیکلی (atomically) چیک اور رجسٹر کیا جائے۔ اگر درخواست ناکام ہو جائے تو سب کو ایرر ملتا ہے، جو کہ عام طور پر درست رویہ ہے۔ اگر یہ کامیاب ہو جائے تو سب کو کیش شدہ ویلیو مل جاتی ہے، اور بعد کی درخواستیں براہ راست کیش پر جاتی ہیں۔ ڈیٹا بیس کا بوجھ ایک اچانک اضافے (vertical spike) سے کم ہو کر ایک سیدھی لائن (flat line) میں بدل جاتا ہے۔

پروببیلسٹک ارلی ایکسپائریشن (Probabilistic Early Expiration)

بعض اوقات آپ اسٹیمپڈ (stampede) کو سنبھالنے کے بجائے اس سے مکمل طور پر بچنا چاہتے ہیں۔ یہاں پروببیلسٹک ارلی ایکسپائریشن (probabilistic early expiration) کا تصور آتا ہے، جسے XFetch جیسے الگورتھم کے ذریعے نافذ کیا جاتا ہے۔

ایک سخت میعاد ختم ہونے کے وقت (hard expiration time) کے بجائے، ہر کیش شدہ ویلیو کے ساتھ ایک نرم میعاد کا وقفہ (soft expiration window) ہوتا ہے۔ جب کوئی درخواست آتی ہے، تو ایپلی کیشن ایک رینڈم نمبر تیار کرتی ہے اور ایک سادہ پروببیلیٹی چیک (probability check) لگاتی ہے۔ اگر ڈائس (dice) کا نتیجہ 'ہاں' ہو، تو وہ ایک درخواست کیش کو وقت سے پہلے ریفریش کر دیتی ہے۔ اگر نہیں، تو درخواست تھوڑی پرانی (stale) ویلیو فراہم کرتی ہے۔

چونکہ ہر درخواست اپنا ڈائس خود چلاتی ہے، اس لیے ریفریشز پورے سافٹ ونڈو (soft window) میں پھیل جاتے ہیں۔ ایک کلائنٹ ہارڈ ایکسپائریشن سے چالیس سیکنڈ پہلے ریفریش کر سکتا ہے، دوسرا بارہ سیکنڈ پہلے، اور باقی زیادہ تر بالکل نہیں۔ کام ایک واحد ہم آہنگ اضافے (synchronized spike) سے بدل کر ایک نرم...