Təkrarlama strategiyaları və idempotentlik
1) Niyə lazımdır
Şəbəkələrdə nasazlıqlar normadır: taymaut, transient-səhvlər, şəbəkə flappinqləri, həddindən artıq yükləmə. Retrajlar yalnız etibarlılığı artırır:1. təkrar təhlükəsiz (idempotenten),
2. təkrar arasında saxlanılır,
3. Limitlərə/kvotalara və asılılığın «sağlamlığına» hörmət edilir.
Məqsəd - saxta dubl və yarış olmadan biznes əməliyyatları səviyyəsində effectively-once davranış.
2) Çatdırılma semantikasının taksonomiyası
At-most-once: təkrarsız, itki riski (loging, fire-and-forget).
At-least-once: dublikatlar mümkündür → istehlakçının idempotantlığına ehtiyacı var (əksər növbələr, webhucks).
Effectively-once: dublikatlar mümkündür, lakin düzgün deduplizasiya (açarlar, əməliyyatlar, outbox).
3) Nə zaman retraj və nə zaman deyil
Retraitin mənası var: '408', '429' (riayət edərək 'Retry-After'), '425' (Too Early), '499' (perimetrdə client closed), '5xx', '504', şəbəkə vaxtları/fasilələri, '502' şlüz, 'connection reset ".
Sorğu dəyişdirilmədən geri çəkilmir: '400/ 401/403/404/422'.
Mübahisəli hallar: '409 Conflict' (adətən retraim deyil; əvvəlcə əməliyyatın vəziyyətini oxuyuruq/niyyəti yenidən təsdiqləyirik).
4) Taymaut, backoff və jitter
4. 1 Qaydalar
Əvvəlcə taymaut, sonra retriyalar: hər bir sorğu «deadline» olmalıdır.
Exponential backoff: 'delay _ n = base 2 ^ n', məhdud 'max _ delay'.
Jitter tələb olunur: «axmaq sinxron dalğaları» aradan qaldırmaq üçün bir təsadüf əlavə edin.
4. 2 Jitter şablonları
Full jitter: 'sleep = rand (0, base2 ^ n)' - ən yaxşı ümumi seçimdir.
Decorrelated jitter: 'sleep = min (max_delay, rand (base, sleep_prev3))' - uzun dialoqlar üçün.
Equal jitter: 'sleep = base2 ^ n/2 + rand (0, base2 ^ n/2)' - yumşaq variasiya.
4. 3 Retry-budget
Retrajların payını məhdudlaşdırın:- `retry_budget_per_min = max(α success_rps, floor β)`; adətən 'α = 0. 1–0. 2`.
- Büdcə tükəndikdə - fail-fast/circuit breaker «açıq» keçid.
5) rate limiting və Circuit Breaker ilə qarşılıqlı
'Retry-After', 'RateLimit-Reset' -ə hörmət edin və bunu geri çevrilmə hesab edin.
Yüksək '5xx '/taymautlarda - retrajların tezliyini və ümumi paralelliyi azaltın.
- Half-open: məhdud sınağa imkan verir.
- Açıq: Dərhal rədd edir (resurslara qənaət edir).
- Closed: adi iş.
- Write əməliyyatlarında 409/503-i aqressiv retrayları çevirməkdən daha aydın bir ipucu ilə geri qaytarmaq daha yaxşıdır.
6) Write əməliyyatlarının idempotentliyi
6. 1 Ümumi fikir
Eyni niyyət → bir nəticə. Əsas idempotentlik açarı və icra qeydlərinin anbarıdır.
6. 2 HTTP müqaviləsi
Müştəri başlıq göndərir:
Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
Server:
- ilk uğurla yerinə yetirildikdə saxlayır (açar → nəticə, status, bədən hash);
- təkrar edildikdə əvvəlki cavabı və 'Idempotency-Replay: true' başlığını qaytarır;
- bədən münaqişə (eyni açar, lakin digər payload) - '409 Conflict'.
6. 3 Saxlama və TTL
Cədvəl/açar dəyəri: 'idempotency _ key', 'request _ hash', 'result', 'status', 'expiry _ at'.
TTL = mümkün təkrarlama və gec çatdırılma pəncərəsi (adətən ödənişlər üçün 24-72 saat).
Indekslər 'idempotency _ key'; yüksək yük üçün - heş şarding.
6. 4 Nümunə sxemi (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 Prosessor saxta kodu
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) «effectively-once» nümunələri
Transactional Outbox: Biznes hadisəsini qeyd etmək və fon releyeri vasitəsilə eyni DB əməliyyatından mesaj göndərmək; istehlakçı idempotentdir.
İstehlakçıda Inbox/Processed-table: dubllara məhəl qoymamaq üçün 'event _ id' saxlayın.
Kafka-da Exactly-once ≠ biznesdə exactly-once: hətta EOS prodüser/konsumer tətbiqi məntiq hələ də idempotent olmalıdır.
Kompensasiya əməliyyatları (Saga): addımlar geri çəkilərsə və yan təsirlərə səbəb olarsa, sistemi dəyişməz vəziyyətə qaytarırıq.
8) Xüsusi hallar: ödənişlər və maliyyə əməliyyatları
Strong idempotency: açar əməliyyatın məntiqinə bağlıdır (məsələn, 'external _ payment _ id').
PSP-də duplikasiya: 'merchant _ reference' → saxlayın, PSP təkrarlandıqda əvvəlki nəticəni qaytaracaq.
«Müştəridən» retraisi: yalnız 'Idempotency-Key' zamanı icazə verilir, əks halda ikiqat silinmə riski.
Rəqabət qabiliyyəti: icra müddətində «hesab/alət/müqavilə» bloklanması; təkrarlandıqda 409/423 qaytarın.
Müşahidə: metriklər 'idempo _ replay _ total', 'idempo _ conflict _ total'.
9) Vebhuk və xarici çağırışlar
HMAC imzaları və vaxt pəncərəsi; əvvəlcə yoxlama, sonra emal.
Göndəricinin retrayları: eksponensial backoff + jitter, 'max _ attempts' və DLQ.
İstehlakçı - idempotent: 'event _ id' → cədvəl/in-memory cache; «səliqəli» qaydaya zəmanət verilmir.
Kodlar: 2xx = müvəffəqiyyətlə, 4xx = təkrarlamayın, 5xx/taym = təkrarlayın.
10) Növbələr və fon tapşırıqları
At-least-once default → təkrarları qaçılmazdır.
'task _ id '/' event _ id' və icra statusunu saxlayın; dubli - qısa yol «replay».
DLQ və poison-messages: cəhd sayğacı, karantin, əl təhlili.
Rəqabət limitləri (semaforlar) və idempotent işçilər.
11) Version və «təbii» açarları
Natural açarlar (hesab nömrəsi + tarix + sənəd nömrəsi) təkrarlara davamlılığı artırır.
Sxem/versiyanı dəyişdirərkən, 'Idempotency-Key' və ya hash sorğusuna versiya açarını daxil edin.
12) HTTP başlıqları və müştəri ipuçları
'Idempotency-Key', 'Idempotency-Replay', 'Retry-After', 'Prefer: wait = <sec>' (uzun əməliyyatlarda), 'If-Match '/' ETag' (optimist bloklar).
409 «Retry-After» valid ilə 425/429/503 açar toqquşması zamanı.
«Uzun» əməliyyatlar üçün - asinxron statusun qəbulu (status resursuna '202 Accepted' + 'Location').
13) Test və xaos ssenariləri
Negative testləri: ikiqat göndərmə, başqa bir bədən ilə təkrarlama, rasinkron saat.
Qaydanın pozulması: 't2' 't1' -dən əvvəl gəlir.
Enjeksiyon vaxtı/' RST '/' EOF ', yarım sorğular (slow-POST).
Düşmüş idempotency saxlama → davranış fail-closed (ikiqat silinməkdən daha yaxşı imtina).
14) Metriklər və alertlər
`retries_total{reason}`, `retry_budget_used{route}`, `backoff_seconds_bucket`.
`idempo_replay_total`, `idempo_conflict_total`, `duplicate_detected_total`.
Marşrutlar üzrə 409/425/429/5xx payı; p95/p99 retras ilə «uğur üçün vaxt».
Alertlər: retrajın burn-rate büdcəsi, idempotentlik münaqişələrinin artması, DLQ artımı.
15) Antipattern
Bütün səhvləri geri çəkin.
Jitter yoxluğu → sinxron retraj dalğaları.
TTL və təmizləmə olmadan uzun ömürlü açarlar.
Yan təsir kommit sonra nəticəni saxlamaq (outbox pozulması).
'trace _ id '/' idempotency _ key' → olmadan forensika mümkün deyil.
Write əməliyyatlarında aqressiv paralel retralar.
16) Prod hazırlıq yoxlama siyahısı
- Vahid siyasət: nə retraim, nə yoxdur; kodlar və müştəri ipuçları.
- Eksponensial backoff + full jitter; 'retry _ budget' tərəfindən verilmişdir.
- 'Idempotency-Key' müqaviləsi + TTL ilə nəticələrin saxlanması.
- Hadisələr üçün Outbox/Inbox; DLQ; rəqabət limitləri.
- circuit breaker ilə inteqrasiya, respect 'Retry-After'.
- Retralar/dublikatlar/münaqişələr üzrə metriklər/alertlər.
- Xaos testləri və şəbəkə uğursuzluqlarının emulyasiyası.
- Müştərilər üçün sənədləşmə: backoff nümunələri və statuslar.
17) TL; DR
Retrajlar yalnız idempotentlik ilə birlikdə faydalıdır. 'Idempotency-Key' daxil edin və nəticələr saxlayın, jitter və retry-budget ilə eksponensial backoff tətbiq edin, 'Retry-After' hörmət edin, circuit breaker ilə inteqrasiya edin. Hadisələr üçün - outbox/inbox; ödənişlər üçün - ciddi dekuplikasiya və bloklama. Retrajları və münaqişələri ölçün, təkrarları və vaxtları sınayın.