Кайталоо стратегиялары жана идемпотенттүүлүк
1) Эмне үчүн керек
Тармактардагы мүчүлүштүктөр - норма: таймауттар, transient-каталар, тармактык флаппингдер, ашыкча жүктөө. Retrains ишенимдүүлүгүн гана жогорулатат:1. кайталоо коопсуз (idempotenten),
2. кайталоолордун ортосунда сакталган,
3. көз карандылыктын лимиттери/квоталары жана "ден соолук".
максаты - effectively-once бизнес-бүтүмдөрдүн деъгээлинде жүрүм-туруму эч кандай жалган дубль жана жарыш.
2) Жеткирүү семантикасы таксономиясы
At-most-once: кайталоо жок, жоготуу коркунучу (логин, fire-and-forget).
At-least-once: мүмкүн дубликат → керектөөчүнүн демпотенттик керек (көпчүлүк кезек, Webhuke).
Effectively-once: дубликаттар мүмкүн, бирок туура дедуплициялоо (ачкычтар, бүтүмдөр, outbox).
3) Качан ретрациялоо керек жана качан
Retrait мааниси бар: '408', '429' ('Retry-After' ылайык), '425' (Too Early), '499' (периметрде client closed), '5xx', '504', тармактык таймауттар/ажырымдар, '502' шлюз, "connection reset".
Суроо-талапты өзгөртпөстөн артка кайтарылбайт: '400/ 401/403/404/422'.
Талаштуу учурлар: '409 Conflict' (адатта ретраим эмес; адегенде операциянын статусун окуйбуз/ниетин кайталайбыз).
4) Таймауттар, backoff жана життер
4. 1 Эрежелер
Биринчи таймаут, андан кийин ретраиялар: ар бир суроо-талап "deadline" болушу керек.
Exponential backoff: 'delay _ n = base 2 ^ n', чектөө 'max _ delay'.
Jitter милдеттүү: "келесоо синхрондуу толкундарды" чечүү үчүн кокустук кошуу.
4. 2 Jitter үлгүлөрү
Full jitter: 'sleep = rand (0, base2 ^ n)' - эң жакшы жалпы тандоо.
Decorrelated jitter: 'sleep = min (max_delay, rand (base, sleep_prev3))' - узак диалогдор үчүн.
Equal jitter: 'sleep = base2 ^ n/2 + rand (0, base2 ^ n/2)' - жумшак вариация.
4. 3 Retry-budget
Retrains үлүшүн чектөө:- `retry_budget_per_min = max(α success_rps, floor β)`; адатта 'α = 0. 1–0. 2`.
- Бюджет түгөнгөндө - fail-fast/circuit breaker "open".
5) rate limiting жана Circuit Breaker менен өз ара
Урматтоо 'Retry-After', 'RateLimit-Reset' жана бэк-оффко карап көрөлү.
Жогорку '5хх '/тайм менен - ретрациялардын жыштыгын жана жалпы параллелизмди азайтыңыз.
- Half-open: чектелген үлгү берет.
- Open: дароо четке кагат (ресурстарды үнөмдөйт).
- Closed: кадимки иш.
- Write операцияларында 409/503 агрессивдүү ретрансляторлорду айлантканга караганда так шилтеме менен кайтаруу жакшы.
6) Write-бүтүмдөрдү аныктоо
6. 1 Жалпы идея
Бирдей ниет → бир натыйжасы. Негиздери - идемпотенттүүлүктүн ачкычы жана аткаруу жазууларынын сактагычы.
6. 2 HTTP-контракт
Кардар төмөнкү аталышты жөнөтөт:
Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
Сервер:
- биринчи ийгиликтүү аткарууда сактайт (ачкыч → натыйжасы, абалы, дене хэш);
- кайталаганда мурунку жоопту жана 'Idempotency-Replay: true' аталышын кайтарат;
- дене карама-каршы келгенде (ошол эле ачкыч, бирок башка payload) - '409 Conflict'.
6. 3 сактоо жана TTL
Таблица/ачкыч мааниси: 'idempotency _ key', 'request _ hash', 'result', 'status', 'expiry _ at'.
TTL = Мүмкүн болгон кайталоо жана кеч жеткирүү терезеси (адатта төлөмдөр үчүн 24-72 саат).
'idempotency _ key' боюнча индекстер; жогорку жүк үчүн - хеш боюнча шардана.
6. 4 Схема мисал (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 иштеп псевдокод
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" үлгүлөрү
Transactional Outbox: бизнес-окуяны жазуу жана фон релейери аркылуу ошол эле DB транзакциясынан билдирүү жөнөтүү; керектөөчү idempotenten.
Керектөөчүдө Inbox/Processed-table: double четке 'event _ id' сактоо.
Kafka боюнча Exactly-once ≠ бизнесте exactly-once: ал тургай, EOS өндүрүүчүсү/колдонуучу колдонмо логика дагы эле idempotent болушу керек.
Компенсациялык транзакциялар (Saga): эгерде кадамдар артка чегинип, терс таасирлерди жаратса, системаны инвариантка кайтарабыз.
8) Жеке учурлар: төлөмдөр жана финансылык операциялар
Strong idempotency: ачкыч операциянын логикасына байланган (мисалы, 'external _ payment _ id').
PSP боюнча deduplication: сактагыла 'merchant _ reference' → кайталаганда PSP мурунку натыйжасын кайтарат.
"Кардардан" ретраи: "Idempotency-Key" болгондо гана уруксат берүү, антпесе кош эсептен чыгаруу коркунучу.
Атаандаштык: аткаруу учурунда "эсепке/куралга/келишимге" бөгөт коюу; кайталаганда 409/423 кайтарыңыз.
Байкоо: метрика 'idempo _ replay _ total', 'idempo _ conflict _ total'.
9) Вебхактар жана тышкы чакырыктар
HMAC кол тамгалар жана убакыт терезе; биринчи текшерүү, андан кийин иштетүү.
Жөнөтүүчүнүн Retraes: экспоненциалдык backoff + Jitler, 'max _ attempts' жана DLQ.
Керектөөчү - idempotent: 'event _ id' → таблица/in-memory cache; "тыкан" тартип кепилденген эмес.
коддору: 2xx = ийгиликтүү, 4xx = кайталап жок, 5xx/убакыт = кайталап.
10) Кезектер жана фон милдеттери
Ат-least-once демейки → кайталоо сөзсүз болот.
'task _ id '/' event _ id' жана аткаруу статусун сактаңыз; учурда - кыска жол "replay".
DLQ жана poison-messages: аракет эсептегич, карантин, кол талдоо.
Атаандаштык лимиттер (семафорлор) жана демпотенттик воркерлер.
11) Версиялоо жана "табигый" ачкычтар
Натуралдык ачкычтар (эсеп номери + дата + документ номери) кайталоого туруктуулукту жогорулатат.
Схеманы/версияны алмаштырууда 'Idempotency-Key' версиясынын ачкычын же суроо-талаптын хэшин киргизиңиз.
12) HTTP аталыштары жана кардарга
'Idempotency-Key', 'Idempotency-Replay', 'Retry-After', 'Prefer: wait = <sec>' (узакка созулган операцияларда), 'If-Match '/' ETag' (оптимисттик блоктор).
409 "Retry-After" валиддик менен 425/429/503 ачкычтын кагылышында.
"Узун" операциялар үчүн - асинхрондук статусту кабыл алуу (статус ресурсуна '202 Accepted' + 'Location').
13) сыноо жана башаламандык жагдайлар
Negative-тесттер: эки жолу жөнөтүү, башка орган менен кайталоо, бир нече саат.
тартип бузуу: 't2' мурда келет 't1'.
Enjeksiyon убакыт/' RST '/' EOF ', жарым-жартылай суроолор (slow-POST).
кулаган сактоо idempotency → жүрүм-fail-closed (эки эсепти караганда жакшы баш тартуу).
14) Метрика жана Алерт
`retries_total{reason}`, `retry_budget_used{route}`, `backoff_seconds_bucket`.
`idempo_replay_total`, `idempo_conflict_total`, `duplicate_detected_total`.
409/425/429/5xx маршруттар боюнча үлүшү; p95/p99 "ийгиликке чейин убакыт" ретра менен.
Alerty: burn-rate бюджети retrai, чыр-чатактар демпотенттик өсүшү, DLQ өсүшү.
15) Антипаттерндер
Бардык каталарды ретрациялоо.
Жок Jitter → синхрондуу толкун retrains.
TTL жана тазалоо жок узак мөөнөттүү ачкычтар.
Committee терс таасиринен кийин натыйжасын сактоо (outbox бузуу).
Логи жок 'trace _ id '/' idempotency _ key' → форензия мүмкүн эмес.
Write операцияларында агрессивдүү параллелдүү ретрациялар.
16) Prod-даярдык чек тизмеси
- Бирдиктүү саясат: эмне ретраим, эмне жок; кардарга коддор жана кеңештер.
- экспоненциалдуу backoff + full jitter; берилген 'retry _ budget'.
- Келишим 'Idempotency-Key' + TTL менен натыйжаларды сактоо.
- окуялар үчүн Outbox/Inbox; DLQ; атаандаштык лимиттери.
- circuit breaker менен бириктирүү, respect 'Retry-After'.
- Метрика/retrains/дубликат/чыр-чатактар боюнча алерт.
- Башаламандык сыноолордун топтому жана тармактык каталарды эмуляциялоо.
- Кардарлар үчүн документтер: бэк-офф мисалдары жана статустар.
17) TL; DR
Ретрайдар демпотенттик менен гана пайдалуу. 'Idempotency-Key' жана натыйжаларды сактоо киргизүү, Jitter жана retry-budget менен экспоненциалдык backoff колдонуу, 'Retry-After' урматтоо, circuit breaker менен бириктирүү. окуялар үчүн - outbox/inbox; төлөмдөр үчүн - катуу дедупликация жана бөгөт коюу. Ретраларды жана чыр-чатактарды өлчөө, дубликаттарды жана таймауттарды сыноо.