Қайталау стратегиялары мен
1) Бұл не үшін қажет
Желілердегі істен шығулар - норма: таймауттар, transient-қателер, желілік флаппингтер, артық жүктеме. Ретраялар сенімділікті тек қана:1. қайталау қауіпсіз (іспеттес),
2. қайталаулар арасында ұстамдар сақталады,
3. тәуелділіктің лимиттері/квоталары және «денсаулығы» құрметтеледі.
Мақсаты - жалған дубльдерсіз және жарыссыз бизнес-операциялар деңгейіндегі effectively-once мінез-құлық.
2) Жеткізу семантикасының таксономиясы
At-most-once: қайталаусыз, жоғалту тәуекелі (логизация, fire-and-forget).
At-least-once: дубликаттар болуы мүмкін → тұтынушының демпотенттігі қажет (көптеген кезектер, вебхактар).
Effectively-once: дубликаттар мүмкін, бірақ дұрыс дедуплицияланады (кілттер, транзакциялар, outbox).
3) Қашан ретрациялау, ал қашан емес
Ретраиттің мағынасы бар: '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 Джиттер үлгілері
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
Ретрациялардың үлесін шектеңіз:- `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: бизнес-оқиғаны жазу және фон релейері арқылы сол БД-транзакциядан хабар жіберу; тұтынушы іспеттес.
Тұтынушыда Inbox/Processed-table: дубльді елемеу үшін 'event _ id' сақталады.
Kafka-да exactly-once бизнесте ≠ exactly-once: тіпті EOS продюсер/консумер кезінде де қолданбалы логика бәрібір идемотенттік болуы тиіс.
Компенсациялық транзакциялар (Saga): егер қадамдар кері қайтарылса және жанама әсерлер туындаса, жүйені инварианттарға қайтарамыз.
8) Жеке жағдайлар: төлемдер және қаржылық операциялар
Strong idempotency: кілт әрекеттің логикасына байланған (мысалы, 'external _ payment _ id').
PSP дедупликациясы: 'merchant _ reference' → бағдарламасын сақтаңыз.
«Клиенттен» ретрайлері: 'Idempotency-Key' кезінде ғана рұқсат етіледі, әйтпесе екі рет есептен шығару тәуекелі.
Бәсекелестік: орындау уақытында «аккаунтқа/құралға/шартқа» бұғаттау; қайталағанда 409/423 қайтарыңыз.
Бақылау мүмкіндігі: 'idempo _ replay _ total', 'idempo _ conflict _ total' өлшемдері.
9) Вебхактар және сыртқы шақырулар
HMAC қолтаңбалары және уақыт терезесі; алдымен тексеру, содан кейін өңдеу.
Жіберушінің ретрайлері: экспоненциалды backoff + джиттер, 'max _ attempts' және DLQ.
Тұтынушы - іспеттес: 'event _ id' → кесте/in-memory cache; «ұқыпты» тәртіпке кепілдік берілмеген.
Кодтар: 2xx = табысты, 4xx = қайталанбасын, 5хх/таймаут = қайталанбасын.
10) Кезектер және фондық міндеттер
Әдепкі бойынша At-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' -ден ерте келеді.
Таймауттарды инъекциялау/' 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 «табысқа дейінгі уақыт» ретрайлерімен.
Алерттар: ретрайлардың burn-rate бюджеті, дау-дамайлардың өсуі, DLQ өсуі.
15) Антипаттерндер
Қателерді ретке келтіру.
Джиттердің жоқтығы → ретрайлардың синхронды толқындары.
TTL және тазалаусыз ұзақ өмір сүретін кілттер.
Жанама әсер коммитінен кейін нәтижені сақтау (outbox бұзылуы).
'trace _ id '/' idempotency _ key' → логтары форензия мүмкін емес.
Write-операциялардағы агрессивті параллель ретрациялар.
16) Prod-дайындық чек-парағы
- Бірыңғай саясат: не ретраим, не жоқ; клиенттің кодтары мен кеңестері.
- Экспоненциалды backoff + full jitter; берілген 'retry _ budget'.
- «Idempotency-Key» келісімшарты + TTL-мен нәтижелерді сақтау.
- Оқиғалар үшін Outbox/Inbox; DLQ; бәсекелестік лимиттері.
- circuit breaker, respect 'Retry-After' бағдарламасымен біріктіру.
- Ретрациялар/дубликаттар/қайшылықтар бойынша метриктер/алерттар.
- Хаос-тесттер жиынтығы және желілік іркілістерді эмуляциялау.
- Клиенттерге арналған құжаттама: бэк-офф мысалдары мен мәртебелері.
17) TL; DR
Ретраялар демпотенттілігімен бірге ғана пайдалы. 'Idempotency-Key' және нәтижелер қоймасын енгізіңіз, джиттермен экспоненциалды backoff және retry-budget қолданыңыз, 'Retry-After' -ді құрметтеңіз, circuit breaker-мен интеграцияланыңыз. Оқиғалар үшін - outbox/inbox; төлемдер үшін - қатаң дедупликация және блокадалау. Ретрациялар мен қайшылықтарды өлшеңіз, дубликаттар мен таймауттарды тестілеңіз.