GH GambleHub

Exactly-once vs At-least-once

1) Семантиканы талқылаудың қажеті

Жеткізу семантикасы алушы ақаулар мен ретраялар кезінде хабарды қаншалықты жиі көретінін анықтайды:
  • At-most-once - қайталаусыз, бірақ жоғалуы мүмкін (сирек қолайлы).
  • At-least-once - жоғалтпаймыз, бірақ дубликаттар болуы мүмкін (көптеген брокерлердің/кезектердің дефолты).
  • Exactly-once - әрбір хабарлама бақыланатын әсер тұрғысынан дәл бір рет өңделеді.

Негізгі шындық: таратылған әлемде жаһандық транзакциялар мен синхронды келісусіз «таза» end-to-end exactly-once қол жеткізуге болмайды. Біз тиімді exactly-once жасаймыз: көлікте қайталануға жол береміз, бірақ байқалатын әсері «бір рет сияқты» болатындай етіп, демпотенттік өңдеуді жасаймыз.


2) Істен шығу моделі және телнұсқалар қайда пайда болады

Қайталаулар мыналардан пайда болады:
  • ack/commit шығындары (продюсер/брокер/консюмер растауды «естімеген»).
  • Көшбасшыларды/репликаларды қайта сайлау, желілік алшақтықтардан кейін қалпына келтіру.
  • Кез келген учаскелердегі таймауттар/ретрайлер (клиент → брокер → консюмер → синк).

Нәтижесі: көлікті «жеткізудің бірегейлігіне» сүйенуге болмайды. Әсерлерді басқарамыз: ДБ-ға жазу, ақшаны есептен шығару, хат жіберу және т.б.


3) Жеткізушілерде Exactly-once және ол шын мәнінде не

3. 1 Kafka

Кірпіш береді:
  • Idempotent Producer (`enable. idempotence = true ') - ретраялар кезінде продюсер жағындағы дубльдерді болдырмайды.
  • Транзакциялар - бірнеше партияға арналған хабарламаларды және тұтыну офсеттерін («рұқсатсыз» read-process-write паттерні) атомарлық түрде жариялайды.
  • Compaction - соңғы кілт мәнін сақтайды.

Бірақ «тізбектің соңы» (синк: ДБ/төлем/почта) бәрібір теңсіздікті талап етеді. Әйтпесе, өңдегіш дублі дубль эффектін тудырады.

3. 2 NATS / Rabbit / SQS

Әдепкі - ack/redelivery бар at-least-once. Exactly-once бағдарлама деңгейінде қол жеткізіледі: кілттер, дедуп-стор, upsert.

Қорытынды: Exactly-once көлігімен ≠ exactly-once әсерімен. Соңғысы өңдегіште жасалады.


4) Тиімді exactly-once at-least-once үстінен қалай құру керек

4. 1 Сәйкестік кілт (idempotency key)

Әрбір пәрмен/оқиғада 'payment _ id', 'order _ id #step', 'saga _ id #n' деген табиғи кілт болады. Өңдегіші:
  • «көрдіңіз бе?» - TTL/ретеншн бар дедуп-стор (Redis/ДБ).
  • Егер көрдім - бұрын есептелген нәтижені қайталайды немесе no-op жасайды.
Redis нобайы:
lua
-- SET key if not exists; expires in 24h local ok = redis.call("SET", KEYS[1], ARGV[1], "NX", "EX", 86400)
if ok then return "PROCESS" else return "SKIP" end

4. 2 Upsert базасындағы (іспеттес синк)

Жазбалар UPSERT/ON CONFLICT арқылы нұсқасын/сомасын тексерумен жасалады.

PostgreSQL:
sql
INSERT INTO payments(id, status, amount, updated_at)
VALUES ($1, $2, $3, now())
ON CONFLICT (id) DO UPDATE
SET status = EXCLUDED.status,
updated_at = now()
WHERE payments.status <> EXCLUDED.status;

4. 3 Транзакциялық Outbox/Inbox

Outbox: бизнес-транзакция және «жариялау оқиғалары» жазбасы бір БД транзакциясында болады. Фондық жариялаушы outbox оқиды және брокерге жібереді → жағдай мен оқиға арасында айырмашылық жоқ.
Inbox: кіріс пәрмендері үшін 'message _ id' және нәтиже орындалғанға дейін сақталады; қайта өңдеу жазбаны көреді және жанама әсерлерді қайталамайды.

4. 4 Тізбекті консистенттік өңдеу (read → process → write)

Kafka: транзакция «офсетті оқыды → нәтижелерді жазды → коммит» бір атомдық блокқа.
Транзакциясыз: «алдымен нәтижені/Inbox, содан кейін ack жазыңыз»; crash кезінде телнұсқаны Inbox көреді және no-op аяқталады.

4. 5 SAGA/өтемақы

Идемпотенттілік мүмкін болмаған жағдайда (сыртқы провайдер ақшаны есептен шығарды) өтемдік операцияларды (refund/void) және идемпотенттік сыртқы API (сол 'Idempotency-Key' -мен қайталанған 'POST' сол қорытындыны береді) пайдаланамыз.


5) At-least-once жеткілікті болғанда

Кэштерді/материалданған көріністерді кілт бойынша компакциямен жаңарту.
Қайта инкрементациялау қолайлы санауыштар/метриктер (немесе нұсқасы бар дельталарды сақтаймыз).
Екінші хат сыни емес нотификациялар (бәрібір кілтті қойған дұрыс).

Ереже: егер қосарланған бизнес мағынасын өзгертпесе немесе оңай табылса → at-least-once + ішінара қорғау.


6) Өнімділік және құн

Exactly-once (тіпті «тиімді») қымбат: қосымша жазба (Inbox/Outbox), кілттерді сақтау, транзакциялар, күрделі диагностика.
At-least-once арзан/оңай, throughput/p99 бойынша жақсы.
Бағалаңыз: қосарлану бағасы × қосарлану ықтималдығы vs қорғау құны.


7) Конфигурация және код мысалдары

7. 1 Kafka продюсер (іспеттестік + транзакциялар)

properties enable.idempotence=true acks=all retries=INT_MAX max.in.flight.requests.per.connection=5 transactional.id=orders-writer-1
java producer.initTransactions();
producer.beginTransaction();
producer.send(recordA);
producer.send(recordB);
// также можно atomically commit consumer offsets producer.commitTransaction();

7. 2 Inbox бар консюмер (жалған құжат)

pseudo if (inbox.exists(msg.id)) return inbox.result(msg.id)
begin tx if!inbox.insert(msg.id) then return inbox.result(msg.id)
result = handle(msg)
sink.upsert(result)     # идемпотентный синк inbox.set_result(msg.id, result)
commit ack(msg)

7. 3 HTTP Idempotency-Key (сыртқы API)


POST /payments
Idempotency-Key: 7f1c-42-...
Body: { "payment_id": "p-123", "amount": 10.00 }

Осы кілтпен қайта POST → сол нәтиже/мәртебе.


8) Бақылау және метрика

'duplicate _ attempts _ total' - дубльді қанша рет ұстаған (Inbox/Redis бойынша).
'idempotency _ hit _ rate' - теңсіздікпен «құтқарылған» қайталау үлесі.
'txn _ abort _ rate' (Kafka/ДБ) - қайтару үлесі.
'outbox _ backlog' - жарияланымның артта қалуы.
'exactly _ once _ path _ latency {p95, p99}' vs 'at _ least _ once _ path _ latency' - үстеме шығыстар.
Логтардың аудиті: 'message _ id', 'idempotency _ key', 'saga _ id', 'attempt'.


9) Тест-плейбуктер (Game Days)

Жіберуді қайталау: жасанды таймауттар кезінде продюсер ретрайлері.
«Синк және ack» арасындағы қисаю: Inbox/Upsert қосарлануды болдырмайтынына көз жеткізіңіз.
Қайта жеткізу: брокерде redelivery ұлғайту; дедупты тексеру.
Сыртқы API ұқсастығы: сол кілтпен қайталанған POST - бірдей жауап.
Көшбасшыны ауыстыру/желіні үзу: Kafka транзакцияларын/консумерлердің тәртібін тексеру.


10) Қарсы үлгілер

Көлікке сүйену: «бізде Kafka exactly-once бар, демек кілтсіз болады» - жоқ.
No-op ack жазбаға дейін: іске қосылды, бірақ синк түсті → жоғалту.
DLQ/ретрайлардың болмауы: шексіз қайталаулар және дауыл.
Табиғи кілттердің орнына кездейсоқ UUID: дедуплициялайтын ештеңе жоқ.
Индекстерсіз прод-кестелермен Inbox/Outbox араластыру: ыстық бұғаттау және p99-қалдықтары.
Сыртқы провайдерлерде идемпотенттік API-сыз бизнес-операциялар.


11) Таңдау чек-парағы

1. Double бағасы (ақша/заң/UX) vs қорғау бағасы (жасырындылық/күрделілік/құн).
2. Оқиғаның/операцияның табиғи кілті бар ма? Егер жоқ болса, тұрақтылықты ойлап табыңыз.
3. Синк Upsert/нұсқалауды қолдайды ма? Әйтпесе - Inbox + өтемақы.
4. Жаһандық транзакциялар қажет пе? Егер жоқ болса, SAGA-ға сегменттеңіз.
5. Реплика/ұзақ ретеншн қажет пе? Kafka + Outbox. Жылдам RPC/төмен кідіріс қажет пе? NATS + Idempotency-Key.
6. Көп теңгелілік және квоталар: кілттерді/кеңістіктерді оқшаулау.
7. Бақылау: idempotency және backlog өлшемдері қосылған.


12) FAQ

Q: «Математикалық» exactly-once end-to-end жетуге бола ма?
A: Тек тар сценарийлерде бір консистентті сақтау орны және бүкіл жолдағы транзакциялар. Жалпы жағдайда - жоқ; іспеттілік арқылы exactly-once тиімді пайдаланыңыз.

Q: Не жылдам?
A: At-least-once. Exactly-once транзакцияларды қосады/кілттерді сақтау → жоғары p99 және құны.

Q: іспеттілік кілттерін қайда сақтау керек?
A: TTL немесе Inbox (PK = message _ id) кестесі бар жылдам жол. Төлемдер үшін - ұзақ (күндер/апталар).

Q: TTL дедуп кілттерін қалай таңдауға болады?
А: Минимум = қайта жеткізудің ең көп уақыты + операциялық қор (әдетте 24-72 сағат). Қаржы үшін - одан да көп.

Q: Егер менде Kafka кілті бойынша compaction болса, кілт керек пе?
А: Иә. Compaction сақтауды азайтады, бірақ синкті іспеттес етпейді.


13) Қорытынды

At-least-once - көліктің базалық, сенімді семантикасы.
Exactly-once бизнес әсері ретінде өңдегіш деңгейінде қол жеткізіледі: Idempotency-Key, Inbox/Outbox, Upsert/нұсқасы, SAGA/өтемақы.
Таңдау - бұл компромисс құны, екеуінің тәуекелі, пайдаланудың қарапайымдылығы. Табиғи кілттерді жобалаңыз, синктерді іспотентті жасаңыз, байқауды қосыңыз және тұрақты түрде game days өткізіңіз - сонда ретрациялар мен істен шығулар дауылында да сіздің үлестеріңіз болжамды және қауіпсіз болады.

Contact

Бізбен байланысыңыз

Кез келген сұрақ немесе қолдау қажет болса, бізге жазыңыз.Біз әрдайым көмектесуге дайынбыз!

Интеграцияны бастау

Email — міндетті. Telegram немесе WhatsApp — қосымша.

Сіздің атыңыз міндетті емес
Email міндетті емес
Тақырып міндетті емес
Хабарлама міндетті емес
Telegram міндетті емес
@
Егер Telegram-ды көрсетсеңіз — Email-ге қоса, сол жерге де жауап береміз.
WhatsApp міндетті емес
Пішім: +ел коды және номер (мысалы, +7XXXXXXXXXX).

Батырманы басу арқылы деректерді өңдеуге келісім бересіз.