Gözlem, hipotez, deney

Hata ayıklamada önce beklenen ve gözlenen davranışı yazın; ardından tek bir açıklamayı sınayacak deney seçin. Bir testin seri çalıştırılınca geçmesi, paylaşılan durum hipotezini destekleyebilir ama tek başına kök nedeni kanıtlamaz. Zamanlama, kaynak sınırı ve başka paralellik etkileri de aynı sonucu üretebilir.

Aşağıdaki örnekte hata özellikle görünür ve deterministik biçimde oluşturuldu. Node.js v24.19.0 üzerinde çalıştırıldı; gerçek bir müşteri arızasının raporu değil. Rastgele bekleme ya da 'on denemeden biri' gibi doğrulanmamış bir başarısızlık oranı kullanılmıyor.

Hatalı fonksiyon

brokenRender her çağrıda aynı sharedTitle değişkenine yazıyor, sonra bir microtask sınırında bekliyor. Alice ve Bob çağrıları birlikte başlatılınca ikinci çağrı ilk çağrının değerini değiştiriyor; iki çağrı da Bob sonucunu döndürüyor. Beklenen sonuç ise Alice ve Bob'un kendi değerleri.

Hipotezimiz bu fonksiyonun çağrılar arası ortak değişken okuması. Önce paralel başarısızlığı kaydediyoruz; ardından sadece yürütme biçimini seri yapıyoruz. Bu ikinci deney semptomu daraltıyor. Asıl müdahale, ortak değişkeni kaldırıp değeri çağrıya özel parametreden döndürmek.

Üç deneyi birlikte çalıştırın

Kaynak bağlantısından debugging-shared-state.mjs dosyasını kaydedin ve Node.js 24 ile node debugging-shared-state.mjs komutunu çalıştırın. Program yalnızca bellekte çalışır; ağ çağrısı, dosya değişikliği veya ek paket yok. Assertion'lar beklenen bozuk çıktıyı ve düzeltilmiş davranışı ayrı doğrular.

JavaScript
import assert from 'node:assert/strict';
let sharedTitle = '';
async function brokenRender(title) {
  sharedTitle = title;
  await Promise.resolve();
  return sharedTitle;
}
const expected = ['Alice', 'Bob'];
const parallel = await Promise.all(expected.map(brokenRender));
assert.deepEqual(parallel, ['Bob', 'Bob']);
console.log('parallel broken:', JSON.stringify(parallel));
const serial = [];
for (const title of expected) serial.push(await brokenRender(title));
assert.deepEqual(serial, expected);
console.log('serial broken:', JSON.stringify(serial));
async function fixedRender(title) {
  await Promise.resolve();
  return title; // Keep state local to each invocation.
}
const fixed = await Promise.all(expected.map(fixedRender));
assert.deepEqual(fixed, expected);
console.log('parallel fixed:', JSON.stringify(fixed));
console.log('PASS: deterministic reproduction and regression checks');

Deney kaydı ve gerçek çıktı

Paralel bozuk sürüm: [Bob, Bob]. Seri bozuk sürüm: [Alice, Bob]. Paralel düzeltilmiş sürüm: [Alice, Bob]. Seri çalıştırmayı kalıcı çözüm diye kabul etmedik; fonksiyon düzeltilince aynı paralel koşulu tekrar sınadık.

Bu örnekte kök nedeni destekleyen kanıt yalnızca seri testin geçmesi değil: ortak değişkene yazan kodu görüyoruz, gözlenen değer çakışmasını yeniden üretiyoruz ve ortak okumayı kaldırınca paralel regresyon kontrolünün geçtiğini doğruluyoruz. Gerçek bir projede benzer deneyleri kendi veriniz ve yürütme modeliniz üzerinde kurmalısınız.

Terminal
parallel broken: ["Bob","Bob"]
serial broken: ["Alice","Bob"]
parallel fixed: ["Alice","Bob"]
PASS: deterministic reproduction and regression checks

Bu test neyi kanıtlamaz?

Bu bir tek süreçli JavaScript örneği. Worker thread, veritabanı izolasyonu, HTTP istekleri ve dağıtık sistem yarışları test edilmiyor. Bir microtask örneğinin geçmesi uygulamanın bütün eşzamanlılık hatalarını ortadan kaldırdığını göstermez. Sonuçları kapsamıyla birlikte yazmak, 'artık çalışıyor' ifadesinden daha değerlidir.

Üretim loglarına gerçek anahtar veya kişisel veri eklemeyin. İstek kimliği, modül sürümü ve veri biçimi gibi güvenli izlerle başlayın. Birden fazla değişikliği aynı anda yapmak hangi müdahalenin etkili olduğunu belirsizleştirir; yeniden başlatmanın sonucu değiştirmesi de tek başına neden değildir.

Kendi hatanıza uyarlayın

Beklenen/gözlenen çıktıyı kaydedin; en küçük yeniden üretimi çıkarın; hipotezi deneyden önce yazın; tek değişken değiştirin; karşı örnekleri deneyin ve düzeltilmiş davranış için regresyon kontrolü bırakın. Kodun etki alanını anlamak için kod okuma rehberini, yeniden teslimat kaynaklı tekrarlar için idempotency örneğini inceleyin.

Kaynak: Çalıştırılabilir debugging örneği

Kaynak: Node.js assertion belgeleri

Kaynak: İlgili rehber: kodu yazmadan önce okumak

Kaynak: İlgili rehber: event idempotency