Tekrarlama stratejileri ve idempotency
1) Neden ihtiyacınız var
Ağlarda, arızalar normdur: zaman aşımları, geçici hatalar, ağ flappingleri, aşırı yük. Geri çekilmeler yalnızca aşağıdaki durumlarda güvenilirliği artırır:1. Güvenli tekrarlayın (idempotent),
2. Tekrarlar arasındaki gecikmeler gözlenir,
3. sınırlara/kotalara ve bağımlılıklara'sağlık "saygı gösterilir.
Amaç, bir kez etkili bir şekilde, yanlış alımlar ve yarışlar olmadan iş operasyonları düzeyinde davranıştır.
2) Teslimat semantiğinin taksonomisi
En çok bir kez: tekrar yok, kayıp riski (günlüğe kaydetme, ateş etme ve unutma).
En az bir kez: kopyalar mümkündür - tüketici idempotence gereklidir (çoğu kuyruk, webhook).
Etkili bir kez: kopyalar mümkündür, ancak doğru şekilde tekilleştirilir (anahtarlar, işlemler, giden kutusu).
3) Ne zaman geri çekilmeli ve ne zaman değil
Geri çekilme mantıklı: '408', '429' ('Retry-After' gözlemleme), '425' (Çok Erken), '499' (istemci çevre üzerinde kapalı), '5xx', '504', ağ zaman aşımları/sonları, ağ geçidinde '502', 'bağlantı sıfırlama'.
Sorguyu değiştirmeden geri çekmeyin: '400/ 401/403/404/422'.
Tartışmalı durumlar: '409 Çatışma' (genellikle retrayim değil; Önce operasyonun durumunu okuyoruz/niyeti yeniden teyit ediyoruz).
4) Zaman aşımları, backoff ve jitter
4. 1 Kurallar
Önce zaman aşımı, sonra retro: Her isteğin bir "son tarihi" olmalıdır.
Üstel geri dönüş: 'delay _ n = base 2 ^ n', limit 'max _ delay'.
Jitter gereklidir: "donuk senkron dalgaları" ayırmak için rastgelelik ekleyin.
4. 2 Jitter desenleri
Full jitter: 'sleep = rand (0, base2 ^ n)'en iyi genel seçimdir.
Dekore edilmiş jitter: 'Uyku = min (max_delay, rand (taban, sleep_prev3))' - uzun diyaloglar için.
Eşit titreme: 'uyku = base2 ^ n/2 + rand (0, base2 ^ n/2)' - yumuşak varyasyon.
4. 3 Yeniden deneme bütçesi
Retrays oranını sınırlayın:- 'retry _ budget _ per _ min = max (α success_rps, kat β)'; Genellikle 'α = 0. 1–0. 2`.
- Bütçe tükenmişse, hızlı/devre kesici "açık'a geçin.
5) Hız sınırlaması ve Devre Kesici ile etkileşim
'Retry-After', 'RateLimit-Reset'e saygı gösterin ve geri planda sayın.
Yüksek '5xx'/zaman aşımlarında - retray frekansını ve genel eşzamanlılığı düşürün.
- Yarı açık: Sınırlı örneklemeye izin verir.
- Aç: anında reddeder (kaynak tasarrufu sağlar).
- Kapalı: Sıradan iş.
- Yazma işlemlerinde, 409/503'ü büküm agresif retrays yerine net bir ipucu ile döndürmek tercih edilir.
6) Yazma işlemlerinin idempotensi
6. 1 Genel fikir
Aynı niyetler - bir sonuç. Temel, idempotence anahtarı ve yürütme kayıtlarının depolanmasıdır.
6. 2 HTTP sözleşmesi
İstemci başlığı gönderir:
Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
Sunucu:
- İlk başarıda kaydeder (anahtar, sonuç - durum, vücut karması)
- Tekrarlanırsa, eski yanıtı ve 'Idempotency-Replay: true' başlığını döndürür;
- Bir vücut çatışması durumunda (aynı anahtar, ancak farklı bir yük) - '409 Çatışma'.
6. 3 Depolama ve TTL
Tablo/değer anahtarı: 'idempotency _ key', 'request _ hash', 'result', 'status', 'expiry _ at'.
TTL = olası tekrarlar ve geç teslimatlar penceresi (ödemeler için genellikle 24-72 saat).
'Idempotency _ key'ile endeksler; Yüksek yük için - hash sharding.
6. 4 Örnek Şema (SQL)
sql
CREATE TABLE idempo_store (
key UUID PRIMARY KEY,
req_hash BYTEA NOT NULL,
status INT NOT NULL,
response JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expiry_at TIMESTAMPTZ NOT NULL
);
6. 5 İşleyici pseudocode
pseudo handle_write(req):
k = req. headers["Idempotency-Key"]
h = hash(req. body)
rec = idempo_store. get(k)
if rec and rec. req_hash == h:
return rec. status, rec. response, {"Idempotency-Replay": "true"}
if rec and rec. req_hash!= h:
return 409, problem("IDEMPOTENT_CONFLICT")
begin tx result = apply_business_mutation (req) # change status upsert once (idempo_store, key = k, req_hash=h, status = 201, response = result, expiry = now () + 2d)
commit
return 201, result
7) "etkili-bir kez" desenleri
İşlem Giden Kutusu: Bir iş olayını kaydetmek ve aynı veritabanı işleminden arka plan rölesi aracılığıyla bir mesaj göndermek; Tüketici idempotenttir.
Gelen kutusu/Tüketicide İşlenmiş tablo: kopyaları yoksaymak için 'event _ id' değerini kaydedin.
Tam olarak bir kez Kafka'da ≠ tam olarak bir kez iş dünyasında: Üretici/tüketici EOS ile bile, uygulanan mantık hala idempotent olmalıdır.
Telafi edici işlemler (Saga): Adımlar geri çekilirse ve yan etkilere neden olursa, sistemi değişmeze geri döndürürüz.
8) Özel durumlar: ödemeler ve finansal işlemler
Güçlü idempotency: Anahtar çalışma mantığına bağlıdır (örn. 'dış _ ödeme _ id').
PSP'de veri tekilleştirme - Mağaza 'merchant _ reference' - tekrarlanırsa, PSP aynı sonucu döndürür.
Retrays "istemciden": Yalnızca 'Idempotency-Key' olduğunda izin verin, aksi takdirde çift yazma riski.
Rekabet: Yürütme süresi boyunca "hesapta/araçta/sözleşmede" kilitler; Tekrarlandığında, 409/423 döndürün.
Gözlemlenebilirlik: metrikler 'idempo _ replay _ total', 'idempo _ conflict _ total'.
9) Webhooks ve dış zorluklar
HMAC imzaları ve zaman penceresi; Önce doğrulama, sonra işleme.
Gönderen geri alır: üstel geri alma + jitter, 'max _ trials've DLQ.
Tüketici - idempotent: 'event _ id' - tablo/bellek içi önbellek; "Düzenli" sipariş garanti edilmez.
Kodlar: 2xx = başarılı, 4xx = tekrar etme, 5xx/zaman aşımı = tekrar.
10) Kuyruklar ve arka plan görevleri
Varsayılan olarak en az bir kez - kopyalar kaçınılmazdır.
'Task _ id'/' event _ id've yürütme durumunu saklayın; kopyalarla - kısa yol "yeniden oynatma".
DLQ ve zehir mesajları: sayaç, karantina, manuel ayrıştırma girişimi.
Rekabet limitleri (semaforlar) ve idempotent işçiler.
11) Sürüm oluşturma ve "doğal" anahtarlar
Doğal anahtarlar (hesap numarası + tarih + belge numarası) tekrarlamaya karşı direnci artırır.
Şemayı/sürümü değiştirirken, sürüm anahtarını 'Idempotency-Key' veya sorgu karmasına ekleyin.
12) HTTP başlıkları ve istemciye sorar
'Idempotency-Key', 'Idempotency-Replay', 'Retry-After', 'Prefer: wait = <sec>' (uzun işlemlerde), 'If-Match'/' ETag' (iyimser kilitler).
Geçerli 'Retry-After'ile 425/429/503 önemli bir çakışma için 409.
"Uzun" işlemler için - asenkron durumun alınması (durum kaynağı başına '202 Kabul Edildi' + 'Konum').
13) Test ve kaos senaryoları
Negatif testler: çift gönderme, başka bir gövdeyle tekrarlama, saat uyumsuzluğu.
Sıra dışı: 't2', 't1'den önce gelir.
Zaman aşımı enjeksiyonu/' RST'/' EOF ', yarım istekler (yavaş POST).
Düşen idempotency depolama - başarısızlık-kapalı davranış (çift yazma-off daha iyi başarısızlık).
14) Metrikler ve uyarılar
'retries _ total {reason}', 'retry _ budget _ used {route}', 'backoff _ seconds _ bucket'.
'idempo _ replay _ total', 'idempo _ conflict _ total', 'duplicate _ detected _ total'.
Rotalara göre 409/425/429/5xx paylaşın; p95/p99 geri çekilmelerle "başarı zamanı".
Uyarılar: yakma oranı bütçeyi yeniden ödeme, idempotence çatışmalarında artış, DLQ büyümesi.
15) Antipatterns
Bir satırda tüm hataları geri çekin.
Jitter eksikliği - retraces senkron dalgaları.
TTL ve temizlik olmadan uzun ömürlü anahtarlar.
Bir yan etki işlendikten sonra sonucun kaydedilmesi (giden kutusu ihlali).
'Trace _ id'/' idempotency _ key' olmayan günlüklerin oluşturulması imkansızdır.
Yazma işlemlerinde agresif paralel retrays.
16) Prod Hazırlık Kontrol Listesi
- Birleşik politika: ne retrayim, ne değil; Kodlar ve müşteri istemleri.
- Üstel geri dönüş + tam jitter; 'retry _ budget' belirtilmiş.
- Sözleşme 'Idempotency-Key' + TTL ile sonuçları depolamak.
- Olaylar için giden kutusu/gelen kutusu; DLQ; rekabet sınırları.
- Devre kesici ile entegrasyon, saygı 'Retry-After'.
- Retray/Duplicate/Conflict tarafından Metrikler/Uyarılar.
- Bir dizi kaos testi ve ağ hatası öykünmesi.
- Müşteri belgeleri - yedekleme ve durum örnekleri.
17) TL; DR
Geri çekilmeler sadece idempotency ile birlikte yararlıdır. 'Idempotency-Key' girin ve depolama sonucu, jitter ve retry-budget ile üstel geri dönüş uygulayın, 'Retry-After' saygı, devre kesici ile entegre. Olaylar için - giden kutusu/gelen kutusu; Ödemeler, sıkı veri tekilleştirme ve kilitler için. Geri çekilmeleri ve çakışmaları ölçün, kopyaları ve zaman aşımlarını test edin.