جب بھی کوئی صارف سینڈ (send) بٹن کو تیزی سے دباتا، بوٹ ایک ہی جواب دو یا تین بار دینے لگتا۔ یہ تکرار صرف ان لوگوں کے ساتھ ہوتی تھی جو اتنی تیزی سے ٹائپ کرتے تھے کہ AI کے سوچنے شروع کرنے سے پہلے ہی کئی پیغامات بھیج دیتے، اور چونکہ یہ صورتحال کم پیش آتی تھی، اس لیے یہ مسئلہ طویل عرصے تک پروڈکشن میں چھپا رہا۔ ڈیٹا بیس کا ایک قبل از وقت لاک (lock) – جو لینے کے چند ملی سیکنڈ بعد ہی ختم ہو جاتا تھا – گفتگو کو غیر محفوظ چھوڑ دیتا تھا، جس کی وجہ سے متعدد پروسیسز ایک ہی پرامپٹ (prompt) کا جواب دینے لگتے تھے۔
لاک کیوں ناکام ہوا
کوڈ نے ایک ہی ڈیٹا بیس کال میں لاک حاصل کیا، اور پھر فوری طور پر کنٹرول ریکویسٹ ہینڈلر (request handler) کو واپس دے دیا۔ لاک کی مدت ملی سیکنڈز میں تھی، جو کہ AI ماڈل کے جواب تیار کرنے کے لیے درکار وقت سے کہیں زیادہ کم تھی۔ جب تک ماڈل نے کام شروع کیا، لاک پہلے ہی ختم ہو چکا تھا، اس لیے کوئی چیز دوسرے ریکویسٹ کو اسی گفتگو کے ریکارڈ کو حاصل کرنے اور ایک اور جواب جاری کرنے سے نہیں روک سکتی تھی۔
دو علامات سامنے آئیں:
- ایک جیسے جوابات لگاتار بھیجے گئے۔
- ایک ہی سوال کے لیے تھوڑے بدلے ہوئے الفاظ والے جوابات نظر آتے، کیونکہ ہر پروسیس صارف کے ایک ہی ان پٹ سے اپنا پرامپٹ تیار کر رہا تھا۔
چونکہ زیادہ تر صارفین پیغامات کے درمیان وقفہ لیتے ہیں، اس لیے یہ بگ نظروں سے اوجھل رہا۔ صرف تیز ٹائپ کرنے والے ہی 'ریس کنڈیشن' (race condition) کو متحرک کر پاتے تھے، اور ایسے واقعات بہت کم ہوتے تھے۔
وہ ادھورا حل جو کام نہ آیا
پہلا ردعمل پیغام آنے کے بعد ایک مختصر تاخیر شامل کرنا تھا، اس امید کے ساتھ کہ تیز ان پٹ کو "ڈی باؤنس" (debounce) کیا جا سکے۔ اس سے اس وقت مدد ملی جب دو پیغامات تیزی سے ایک کے بعد ایک آتے، لیکن اگر AI ابھی متن تیار کر رہا ہو اور تیسرا پیغام آ جائے، تو یہ طریقہ ناکام ہو جاتا۔
ایک دوسرا مسئلہ اس وقت سامنے آیا جب ٹائمرز اور گفتگو کا ڈیٹا ایک ہی اسٹوریج بکٹ (storage bucket) میں موجود تھے۔ جب بوٹ ایک ریکویسٹ پروسیس کرنا مکمل کرتا، تو وہ ٹائمر کے ریکارڈ کو اوور رائٹ (overwrite) کر دیتا، جس سے عملی طور پر اس کا اپنا کاؤنٹ ڈاؤن ہی ختم ہو جاتا۔ سسٹم اس بات کا سراغ کھو دیتا کہ کن پیغامات کے جوابات پہلے ہی دیے جا چکے ہیں، جس سے مزید تکرار کا راستہ کھل جاتا۔
ایک قابل اعتماد حفاظتی نظام کی تعمیر: ورژن کاؤنٹرز، الگ ٹائمرز، اور ایک لیز (lease)
ٹیم نے تین ستونوں کے گرد اس عمل کو دوبارہ ڈیزائن کیا:
- ورژن کاؤنٹر (Version counter) – ہر آنے والا پیغام گفتگو کے ساتھ محفوظ شدہ ایک کاؤنٹر میں اضافہ کرتا ہے۔ یہ کاؤنٹر سسٹم کو بتاتا ہے کہ آخری جواب کے بعد سے کتنے پیغامات آ چکے ہیں، جس سے جواب تیار ہونے کے دوران نئے ان پٹ کا پتہ لگانا آسان ہو جاتا ہے۔
- مخصوص ڈی باؤنس ونڈو (Dedicated debounce window) – ٹائمرز اب ایک الگ اسٹوریج ایریا میں رہتے ہیں، جو گفتگو کے ڈیٹا سے الگ ہوتے ہیں۔ ڈی باؤنس کے دورانیے پر ایک سخت حد (hard cap) مقرر کرنے سے صارف بوٹ کو غیر ضروری طور پر روک نہیں سکتا۔
- سیشن لیز (Session lease) – اصل لاک کو ایک 'لیز' سے بدل دیا گیا ہے جس میں ختم ہونے کا ایک واضح ٹائم اسٹیمپ (timestamp) ہوتا ہے۔ لیز کو 'کمپیئر اینڈ سویپ' (compare-and-swap - CAS) آپریشن کے ذریعے حاصل کیا جاتا ہے: پروسیس موجودہ لیز کی ویلیو پڑھتا ہے، اور نیا ویلیو صرف اسی صورت میں لکھتا ہے اگر پرانی ویلیو مماثل ہو، اور اس طرح گفتگو کے خصوصی حقوق حاصل کر لیتا ہے۔ اگر پروسیس کریش ہو جائے، تو لیز خود بخود ختم ہو جاتی ہے، اور گفتگو اگلے ہینڈلر کے لیے آزاد ہو جاتی ہے۔
نیا پائپ لائن کیسے کام کرتا ہے
- پیغام کی آمد – سسٹم ورژن کاؤنٹر میں اضافہ کرتا ہے اور ڈی باؤنس ٹائمر کو (دوبارہ) سیٹ کرتا ہے۔ یہ AI کو شروع کیے بغیر فوری طور پر کلائنٹ کو جواب دے دیتا ہے۔
- ٹائمر کا ختم ہونا – ٹائمر ہینڈلر لیز حاصل کرنے کی کوشش کرتا ہے۔ اگر CAS کامیاب ہو جائے، تو ہینڈلر آگے بڑھتا ہے؛ ورنہ وہ پیچھے ہٹ جاتا ہے، یہ جانتے ہوئے کہ کوئی دوسرا پروسیس پہلے ہی گفتگو کا مالک ہے۔
- نئے ان پٹ کی جانچ – ہینڈلر موجودہ ورژن کاؤنٹر کا موازنہ اس ویلیو سے کرتا ہے جو اس نے ٹائمر شروع ہوتے وقت ریکارڈ کی تھی۔ اگر کاؤنٹر بڑھ چکا ہو، تو وہ زیر التواء پیغامات کو ایک ہی پرامپٹ میں جمع کر دیتا ہے۔
- جواب تیار کرنا – AI ماڈل ایک بار چلتا ہے، اور ایک ایسا واحد جواب تیار کرتا ہے جو صارف کے تمام حالیہ ان پٹس کا احاطہ کرتا ہے۔
- آخری جانچ (Sanity check) – جواب بھیجنے سے عین پہلے، ہینڈلر دوبارہ ورژن کاؤنٹر پڑھتا ہے۔ اگر جواب تیار کرنے کے دوران کوئی نیا پیغام آیا ہو، تو جواب کو مسترد کر دیا جاتا ہے اور پروسیس ٹائمر کو دوبارہ شروع کر دیتا ہے، تاکہ یہ یقینی بنایا جا سکے کہ کوئی پرانا یا غیر متعلقہ جواب صارف تک نہ پہنچے۔
یہ طریقہ تکراری جوابات کو ختم کرتا ہے، گفتگو کے رکنے کے وقت کو محدود کرتا ہے، اور پروسیس کریش ہونے کی صورت میں خود بخود بحال ہو جاتا ہے کیونکہ لیز خود بخود ختم ہو جاتی ہے۔
حاصلِ کلام
ایسا لاک جو اہم مرحلے (critical section) کے شروع ہونے سے پہلے ہی غائب ہو جائے، کوئی تحفظ فراہم نہیں کرتا۔ ایک عارضی ڈیٹا بیس لاک کو ایک واضح اور ختم ہونے والی لیز سے بدل کر، اور ٹائمرز کو گفتگو کے ڈیٹا سے الگ کر کے، اب بوٹ اس بات کی ضمانت دیتا ہے کہ صارفین کی بجلی جیسی تیز ٹائپنگ کے باوجود انہیں ایک ہی تازہ ترین جواب ملے گا۔ یہ واقعہ ایک لازوال سبق پر زور دیتا ہے: کنکرنسی کے حفاظتی اقدامات (concurrency safeguards) کو اس کام سے زیادہ دیر تک قائم رہنا چاہیے جس کی وہ حفاظت کر رہے ہیں، ورنہ وہ ایسے غیر مرئی رکاوٹ بن جاتے ہیں جن سے بگ آسانی سے گزر جاتے ہیں۔
