dbit.one© 2026
000
booting_
Loading experience0%
dbit.one

Test uch yuz yurishda bir marta yiqiladi. Bu ob-havo emas

Qisqasi: uch yuz yurishda bir marta yiqiladigan test haqiqiy poygani allaqachon topgan — siz uni takrorlay olmayapsiz, xolos. Tasodifiy narsa xato emas, operatsion tizim tanlagan bajarilish tartibi. Bu tanlovni undan tortib oling — va yiqilish latifadan manzilga aylanadi.

7 daqiqa o‘qish

Qisqacha

  • Beqaror test — bu manzilsiz topilgan xato. `retry(3)` xatoni tuzatmaydi, u signalni oʻchiradi.
  • Asinxron kodda tasodifiy narsa nosozlik emas, jadval: tayyor kolbeklardan qaysi birini ishga tushirish.
  • Deterministik simulyatsiya jadvalni oʻziga oladi: vaqt, taymerlar va tartib siddan olinadi, yurish esa istalgan mashinada bayt-baytigacha takrorlanadi.
  • 200 jadval tanlab olish «topilmadi»ni isbotlaydi. Toʻliq sanab chiqish «mavjud emas»ni — lekin faqat kichik test uchun.

Beqaror test aslida nima

Beqaror deb kodga tegilmagan holda goho yiqiladigan testni aytishadi. Odatda aybdor test deb hisoblanadi va unga retry qoʻyiladi. Xulosa notoʻgʻri: koʻpchilik hollarda test haqiqiy poygani ushlagan va uni takrorlay olmagan.

Asinxron kod bitta tartibda bajarilmaydi. Bir vaqtning oʻzida bir nechta kolbek tayyor boʻlganda, kimdir qaysi biri birinchi ketishini hal qilishi kerak — buni ijro muhiti operatsion tizim bilan birga hal qiladi. Variantlar mingta, sizni bittasi buzadi. Sizning mashinangizda u hech qachon chiqmaydi, tunda yigʻish serverida esa bir marta chiqadi va qaytmaydi.

Bu prodda qancha turadi

Retray bilan oʻchirilgan poyga yoʻqolmaydi — u ishlab chiqarishga koʻchadi, u yerda jadvalni sizning noutbukingiz emas, yuklama tanlaydi. Va u yerda bu pul turadi.

Poyga sinfi
Nima bilan tugaydi
Taymautdan keyin toʻlov qayta yuborilishi
ikki marta yechib olish va qoʻlda qaytarish
Puldan bitta ulanish ikki chaqiruvchiga berilishi
javob boshqa mijozga ketadi
Ikkita kesh yangilanishi, sekinrogʻi oxirida keladi
eski maʼlumot yangisini bosib ketadi
Ikki oʻtkazma ikkita qulfni teskari tartibda oladi
oʻzaro blokirovka, xizmat javob bermaydi

Birinchi qator eng qimmat va eng koʻp uchraydigani. Toʻlov tizimlarida amallarning idempotentligi aynan shuning uchun bor, va oddiy test aynan shuni oʻtkazib yuboradi: tashlab ketilgan soʻrov test ketganidan keyin yetib keladi.

Nega retry(3) yechim emas

Retray «nega yiqildi» savolini «necha marta qayta ishga tushirsa oʻtadi» savoliga almashtiradi. Bu qizil yigʻish hech kimni ajablantirmay qoʻygan kungacha ishlaydi — oʻsha kundan boshlab testlar toʻplami hech narsani himoya qilmaydi.

  • Retray beqarorlikni emas, xatolar sinfini yashiradi: har qanday haqiqiy poyga xuddi shunday koʻrinadi va xuddi shunday «tuzatiladi».
  • Yigʻish vaqti oʻsadi, ishonch tushadi: jamoa logni oʻqishdan oldin qayta ishga tushiradi.
  • Xato kodda qoladi va kamdan-kam jadvalni tez-tez uchraydiganga aylantiradigan yuklamani kutadi.

Jadvalni operatsion tizimdan tortib olish

Deterministik simulyatsiya gʻoyasi oddiy: agar noaniqlik siz boshqarmaydigan narsadan kelsa — uni oʻzingizga oling. Yurish davomida setTimeout, setInterval, setImmediate, Date, performance.now va Math.random almashtiriladi. Kutishning barcha yoʻllari bitta virtual taymerlar navbatiga tushadi va «keyin nima boʻladi» savolining yagona egasi paydo boʻladi.

Bir virtual lahzada bir nechta kolbek tayyor boʻlsa, tartib siddan tanlanadi va yozib qoʻyiladi. Bir sid — istalgan mashinada bayt-baytigacha bir xil yurish. Qadamlar orasida invariantlar qayta tekshiriladi, shuning uchun buzilish uni keltirib chiqargan jadvalda ushlanadi.

Bir yon taʼsirni ortiqcha baholab boʻlmaydi: virtual vaqt bepul. Olti soatlik chekinishli qayta urinish siyosati mikrosoniyalarda tekshiriladi, osilgan tizim esa sekinidan farqlanadi — chunki simulyator uygʻotadigan hech kim qolmaganini biladi.

Bu kodda qanday koʻrinadi

Buning uchun ochiq kodli vosita yozdik — unflake. Test oddiy koʻrinadi, faqat tanasi boshqariladigan dunyoda bajariladi, shart esa har bir rejalashtirish qadamidan keyin qayta tekshiriladi.

ts
import { check } from 'unflake';
import { it } from 'vitest';

it('ulanish ikki marta berilmaydi', async () => {
  await check('bitta ulanish — bitta egasi', async (sim) => {
    const pool = createPool(sim, { size: 2 });
    const held = new Map<string, number>();

    // Test oxirida emas, har bir qadamdan keyin qayta tekshiriladi.
    sim.invariant('ulanish ikkovida emas', () =>
      [...held.values()].every((n) => n <= 1),
    );

    await sim.parallel(4, async () => {
      const conn = await pool.acquire();
      held.set(conn, (held.get(conn) ?? 0) + 1);
      await sim.io('query', { latency: [1, 6] });
      held.set(conn, (held.get(conn) ?? 0) - 1);
      pool.release(conn);
    });
  }, { runs: 200 });
});
Ulanishlar puli: bitta ulanish ikki marta berilmasligini tekshiramiz

Ikki yuz jadvaldan biri invariantni buzsa, yiqilish buzishda davom etadigan eng kichik jadvalgacha siqiladi va vaqt chizigʻi bilan chop etiladi: kim nimani ushlab turgan, kim nimani kutgan va qaysi lahzada uygʻotadigan hech kim qolmagan. U sid bilan emas, yozib olingan reja bilan qayta ijro etiladi — yaʼni begona mashinada ham.

Tanlab olish va isbotlash — har xil narsa

Ikki yuz tasodifiy jadval — bu fazzing: toza natija «topilmadi» degani, «boʻlmaydi» degani emas. Yetarlicha kichik test uchun koʻproq narsa olish mumkin — qaror daraxtini toʻliq sanab chiqish. Bu farq amalda nimani anglatishi model tekshiruvi haqidagi materialda koʻrib chiqilgan.

ts
const report = await explore('bitta ulanish — bitta egasi', body);
// { ok: true, schedules: 24, exhaustive: true }

exhaustive: true — bu makon toʻliq aylanib chiqilgani, va shunda oʻtish «buzilish mavjud emas» deb oʻqiladi. Chegara halol: daraxt koʻpaytma tarzida oʻsadi, keng latency: [1, 25] har bir kiritish-chiqarishda yigirma besh tarmoq beradi, toʻliq sanash esa kichik testlar nasibasi boʻlib qoladi.

Begona kodga qaratganda nima chiqdi

Hech narsa topmaydigan vosita shubha uygʻotadi — shuning uchun uni begona paketlarga qaratdik va natijani toʻliq eʼlon qildik: yettita mashhur kutubxonada 19 ta hujjatlashtirilgan shartnoma (p-limit, p-queue, async-mutex, async-sema, generic-pool, p-retry, bottleneck), har biriga yuzlab jadval.

Birorta shartnoma buzilmadi. Bu halol sarlavha va uni jimgina aytmay qoʻyish oʻrniga ovoz chiqarib aytish kerak. Bu kutilgan natija ham: bu paketlar haftasiga millionlab yuklanadi, ularning asosiy kafolatlarini har bir foydalanuvchi har kuni ishlatadi. Birinchi kechadayoq p-limit chegarasini «buzadigan» vosita oʻz kalibrlanishi haqida gapiradi, p-limit haqida emas.

Bitta topilma haqiqiydek koʻrindi: bottleneck minTime: 10 da ikkita vazifani 9 ms farq bilan ishga tushirardi. U simulyatordan tashqarida ham takrorlandi — lekin farq toʻrt yuz jadvalning hammasida aynan 1 ms edi, yaʼni bu rejalashtirish nuqsoni emas, ishga tushish narxi. Bunday hisobot yuborish — mayntenerning kechasini bir millisekund uchun olib qoʻyish. Yubormadik va sababini yozib qoʻydik.

Usul nimani qila olmaydi

Kafolatlarini oshirib koʻrsatadigan test vositasi yoʻqligidan yomonroq. Chegaralar bular.

  • Faqat oʻzi boshqargan narsani koʻradi. Haqiqiy soket, fayl yoki nativ drayver rejalashtiruvchiga koʻrinmaydi, bajariladigan narsa qolmagan yurish esa oʻzaro blokirovkaga oʻxshaydi. Haqiqiy kiritish-chiqarishni simulyator orqali oʻtkazish kerak.
  • Perebor makonini qisqartirmaydi. Vosita qaysi amallar umumiy holatga tegishini bilmaydi, shuning uchun ikkita tartib ekvivalentligini isbotlab, birini tashlab keta olmaydi. Rustda buni loom uddalaydi — chunki test ostidagi kod uning oʻz primitivlarini ishlatadi.
  • Parallellik haqida emas. Node bir oqimli, simulyator ham: vorkerlar, jarayonlar va nativ modullar ichidagi poygalar qamrovdan tashqarida.
  • Mikrovazifalar tartibiga tegilmaydi. Promislarning hal boʻlishi allaqachon deterministik; uni qayta tartiblash — dvigatel yarata olmaydigan poygalarni oʻylab topish, yolgʻon signal esa oʻtkazib yuborilgan xatodan qimmatroq.

Bir soatda qanday boshlash mumkin

Ish tartibi
  • Yigʻishlar tarixidan retray qoʻyilgan yoki kodga tegilmay yiqilgan testni toping.
  • U aslida nimani tekshirayotganini invariant sifatida ifodalang: «bitta ulanish — bitta egasi», «yechib olish bir marta boʻladi».
  • Test tanasini haqiqiy taymerlar va tarmoq emas, simulyator orqali kutadigan qilib qayta yozing.
  • Ikki yuz jadvalni yuriting. Yiqilsa — sizda eng kichik jadval va qayta ijro bor; yiqilmasa — retrayni olib tashlashga asos bor.
  • Yurishni CIda qoldiring: tuzatilgan poyga endi jimgina qaytmaydi.

Agar gap pul sanaydigan tizim haqida ketayotgan boʻlsa, testlardan emas, amallar arxitekturasidan boshlash kerak: tafsilotlar toʻlovlar idempotentligi tahlilida va fintech ishlab chiqish sahifasida. Qolgan muhandislik tahlillari «Muhandislik» boʻlimida.

Ko‘p so‘raladigan savollar

Bu fake-timers kabi taymerlarni almashtirishdan nimasi bilan farq qiladi?

Taymerlarni almashtirish vaqtni boshqaradi, lekin bir nechta tayyor kolbekdan qaysi biri birinchi ketishini hal qilmaydi. Poygalar aynan shu tanlovda yashaydi. Deterministik simulyatsiya vaqtni ham, tartibni ham boshqaradi va qilingan tanlovni yozib qoʻyadi.

Bu bizning oddiy testlarimizni almashtiradimi?

Yoʻq. Bu konkurent kod uchun alohida qatlam: pullar, navbatlar, qayta urinishlar, tranzaksiyalar — bir nechta vazifa umumiy holatga tegadigan hamma joy. Yunit va integratsion testlar oʻz oʻrnida qoladi.

Nechta jadval yuritish kerak?

Ikki yuz — maʼqul standart: vaqt virtual boʻlgani uchun yurish arzon. Kichik testlar uchun toʻliq sanash mantiqan toʻgʻri — u «topilmadi»ni «mavjud emas»ga aylantiradi.

Biz TypeScriptda yozmaymiz. Nima qilish kerak?

Yondashuv tilga bogʻliq emas: Rustda loom va shuttle, taqsimlangan tizimlarda Jepsen, maʼlumotlar bazalarida oʻz simulyatorlari. Qoida bitta: noaniqlik manbasini oʻzingizniki qiling va qilingan tanlovni yozib boring.

Manbalar

Maqoladagi da’volarni tekshirish mumkin: quyida qayta hikoya emas, birlamchi manbalar.

  1. 01FoundationDB: TestingToʻgʻriligi deterministik simulyatsiyaga tayanadigan maʼlumotlar bazasi
  2. 02TigerBeetle: VOPRXuddi shu yondashuv moliyaviy registr uchun
  3. 03loom (Rust)Makonni qisqartirish bilan toʻliq sanash — JavaScriptda hali yoʻq
  4. 04unflakeMaqoladagi vosita: MIT, bogʻliqliklarsiz, ichida yettita paket auditi
Muallif
Doniyor Botirov
dbit.one asoschisi · maqola muallifi

Bu maqola, boshqalaridan farqli oʻlaroq, ism bilan imzolangan: uning ortida mijoz yoʻq. Gap ketayotgan kod toʻliq ochiq va oʻsha ismda chop etilgan — uni oʻqish, ishga tushirish va maqoladagi daʼvolarni soʻzga ishonmasdan tekshirish mumkin.

Tegishli xizmatlar

Keyingi o‘qish

Loyihangiz bo‘yicha savollar bormi?

Vazifangizni tasvirlang — 24 soat ichida baho, muddat va reja bilan qaytamiz.

[email protected]