Bot tersebut mula mengeluarkan jawapan yang sama dua atau tiga kali setiap kali pengguna menekan butang hantar berkali-kali dengan pantas. Penduplikatan ini hanya berlaku kepada mereka yang menaip cukup laju untuk menghantar beberapa mesej sebelum AI mula berfikir, dan ia kekal tersembunyi dalam persekitaran produksi untuk masa yang lama kerana corak ini jarang berlaku. Kunci pangkalan data yang pramatang – yang dilepaskan beberapa milisaat selepas diambil – menyebabkan perbualan tidak dilindungi, membolehkan pelbagai proses menjawab prom yang sama.
Mengapa kunci tersebut gagal
Kod tersebut memperoleh kunci dalam satu panggilan pangkalan data, kemudian segera menyerahkan semula kawalan kepada pengendali permintaan. Jangka hayat kunci tersebut diukur dalam milisaat, jauh lebih singkat daripada masa yang diperlukan oleh model AI untuk menjana maklum balas. Menjelang masa model mula bekerja, kunci tersebut telah pun hilang, jadi tiada apa yang menghalang permintaan kedua daripada mengambil rekod perbualan yang sama dan mengeluarkan balasan lain.
Dua simptom muncul:
- Balasan yang serupa dihantar berturut-turut.
- Balasan yang diolah semula sedikit muncul untuk soalan yang sama, kerana setiap proses membina promnya sendiri daripada input pengguna yang sama.
Oleh kerana kebanyakan pengguna berhenti seketika di antara mesej, pepijat ini tidak disedari. Hanya pengetik pantas yang mencetuskan keadaan perlumbaan (race condition), dan kes-kes tersebut adalah jarang berlaku.
Penyelesaian separuh jalan yang tidak memadai
Tindakan pertama yang diambil adalah menambah sedikit lengah (delay) selepas mesej tiba, dengan harapan dapat melakukan "debounce" pada input yang pantas. Ini membantu apabila dua mesej tiba berturut-turut, tetapi ia gagal jika mesej ketiga muncul semasa AI masih menjana teks.
Isu kedua timbul apabila pemasa dan data perbualan berada dalam bekas storan yang sama. Apabila bot selesai memproses permintaan, ia menulis semula rekod pemasa, yang secara berkesan memadamkan kiraan undurnya sendiri. Sistem hilang jejak tentang mesej mana yang telah dijawab, membuka ruang kepada penduplikatan lanjut.
Membina pelindung yang boleh dipercayai: pengira versi, pemasa terasing, dan pajakan (lease)
Pasukan telah mereka bentuk semula aliran kerja berdasarkan tiga tonggak:
- Pengira versi (Version counter) – setiap mesej yang masuk akan menambah pengira yang disimpan bersama perbualan. Pengira ini memberitahu sistem berapa banyak mesej yang telah tiba sejak balasan terakhir, menjadikannya mudah untuk mengesan input baharu semasa maklum balas sedang dijana.
- Tetingkap debounce khusus – pemasa kini berada dalam kawasan storan yang berasingan, terasing daripada muatan (payload) perbualan. Had tetap pada durasi debounce menghalang pengguna daripada melengah-lengahkan bot tanpa had.
- Pajakan sesi (Session lease) – kunci asal digantikan dengan pajakan yang membawa cap masa tamat tempoh yang jelas. Pajakan tersebut dituntut menggunakan operasi compare-and-swap (CAS): proses tersebut membaca nilai pajakan semasa, menulis nilai baharu hanya jika nilai lama sepadan, dan dengan itu memperoleh hak eksklusif ke atas perbualan tersebut. Jika proses terhenti (crash), pajakan akan tamat secara automatik, membebaskan perbualan untuk pengendali seterusnya.
Bagaimana saluran paip (pipeline) baharu berfungsi
- Ketibaan mesej – sistem menambah pengira versi dan (menetapkan semula) pemasa debounce. Ia memulangkan respons kepada klien dengan segera, tanpa memulakan AI.
- Tamat tempoh pemasa – pengendali pemasa cuba memperoleh pajakan. Jika CAS berjaya, pengendali akan meneruskan; jika tidak, ia akan berhenti seketika, mengetahui bahawa proses lain sudah memiliki perbualan tersebut.
- Semak input baharu – pengendali membandingkan pengira versi semasa dengan nilai yang direkodkan semasa pemasa bermula. Jika pengira telah meningkat, ia menggabungkan mesej-mesej yang tertangguh ke dalam satu prom tunggal.
- Jana balasan – model AI berjalan sekali sahaja, menghasilkan satu jawapan tunggal yang merangkumi semua input pengguna terkini.
- Semakan akhir – sejurus sebelum balasan dihantar, pengendali membaca semula pengira versi. Jika mesej yang lebih baharu tiba semasa penjanaan, balasan tersebut akan dibuang dan proses akan memulakan semula pemasa, bagi memastikan tiada jawapan lapuk sampai kepada pengguna.
Pendekatan ini menghapuskan balasan pendua, mengehadkan masa perbualan boleh tergendala, dan pulih secara automatik daripada kegagalan proses kerana pajakan tamat dengan sendirinya.
Pengajaran
Kunci yang hilang sebelum bahagian kritikal (critical section) bermula tidak memberikan sebarang perlindungan. Dengan menggantikan kunci pangkalan data yang seketika dengan pajakan yang mempunyai tempoh tamat yang jelas dan mengasingkan pemasa daripada data perbualan, bot kini menjamin satu balasan tunggal yang terkini walaupun pengguna menaip dengan sangat pantas. Episod ini menekankan pengajaran yang abadi: perlindungan konkurensi mesti bertahan lebih lama daripada kerja yang dilindunginya, atau ia akan menjadi penghalang halimunan yang membiarkan pepijat terlepas masuk.
