Exactly-once vs At-least-once
1) Эмне үчүн семантиканы талкуулоо керек
Жеткирүү семантикасы алуучу каталар жана ретрациялар учурунда билдирүүнү канчалык көп көрөрүн аныктайт:- At-most-once - кайталоо жок, бирок жоготуу мүмкүн (сейрек кабыл алынат).
- At-least-once - биз жоготпойбуз, бирок дубликаттар болушу мүмкүн (көпчүлүк брокерлердин/кезектердин дефолту).
- Exactly-once - ар бир билдирүү байкалган таасири боюнча так бир жолу иштетилет.
Негизги чындык: глобалдык бүтүмдөр жана синхрондуу ырааттуулук жок бөлүштүрүлгөн дүйнөдө "таза" end-to-end exactly-once кол жеткис. Биз натыйжалуу exactly-once куруп: транспорт боюнча кайталоо жол, бирок байкалган таасири "бир жолу сыяктуу" болгон жол менен empotent иштетүү.
2) Бузулуулардын модели жана дубликаттар кайда пайда болот
Кайталоолор улам пайда болот:- Жоготуулар ack/commit (продюсер/брокер/консюмер "уккан жок" ырастоо).
- Лидерлерди кайра шайлоо/репликалар, тармактык ажырымдардан кийин калыбына келтирүү.
- Ар кандай сайттарда таймауттар/ретрайлер (кардар → брокер → консюмер → синк).
Тергөө: транспорттун "уникалдуулугуна" таянууга болбойт. Эффекттерди башкарабыз: БДга жазуу, акчаны эсептен чыгаруу, кат жөнөтүү ж.б.
3) жөнөтүүчүлөр жана чынында эмне үчүн Exactly-once
3. 1 Kafka
кирпич берет:- Idempotent Producer (`enable. idempotence = true ') - өндүрүүчүнүн ретрага тарапта дубль болтурбоо.
- Транзакциялар - атомдук түрдө бир нече партияларга билдирүүлөрдү жарыялайт жана керектөө офсеттерин (үлгү read-process-write "өткөрбөстөн").
- Compaction - ачкычтын акыркы маанисин сактайт.
Бирок "чынжыр аягы" (Синк: BD/төлөм/почта) дагы эле боштукту талап кылат. Болбосо, иштетүү эки эсе таасир алып келет.
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 ачкычы (idempotency key)
Ар бир команда/окуя табигый ачкычын алып: 'payment _ id', 'order _ id #step', 'saga _ id #n'. Иштеп чыгуучу:- "Көрдүңбү?" - Дедуп-стор (Redis/DD) менен TTL/retenshn.
- Эгерде көрдүм - мурда эсептелген натыйжаны кайталайт же no-op кылат.
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 базасы (Emempotent Синк)
Жазуулар 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: бизнес-бүтүм жана жазуу "жарыялоо окуялар" бир бүтүм DD өтөт. Фон жарыялоочу 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) Качан жетиштүү ат-least-once
Кэш/материалдаштырылган идеяларды ачкыч компакциясы менен жаңыртуу.
Кайра инкрементация алгылыктуу болгон эсептегичтер/метриктер (же версия менен дельталарды сактайбыз).
экинчи кат маанилүү эмес ноталар (жакшы дагы ачкычын коюп).
Эреже: Эгер дубль бизнес маанисин өзгөртпөсө же оңой табылса → at-least-once + жарым-жартылай коргоо.
6) аткаруу жана наркы
Exactly-once (ал тургай, "натыйжалуу") кымбат турат: кошумча жазуу (Inbox/Outbox), сактоо ачкычтары, бүтүмдөр, татаал диагностика.
At-least-once арзан/жөнөкөй, жакшы throughput/p99.
баа: эки эсе баасы × эки эсе ыктымалдыгы vs коргоо наркы.
7) Конфигурация жана код мисалдары
7. 1 Kafka Продюсер (idempotentity + бүтүмдөр)
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 менен Consumer (psevdocode)
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/DD) - кайтарымдардын үлүшү.
'outbox _ backlog' - жарыялоонун артта калышы.
'exactly _ once _ path _ latency {p95, p99}' vs 'at _ least _ once _ path _ latency' - кошумча чыгымдар.
Логдордун аудити: 'message _ id', 'idempotency _ key', 'saga _ id', 'attempt'.
9) Test Playbook (Оюн күндөрү)
Кайталап жөнөтүү: жасалма тайм менен өндүрүүчүнүн ретрасы.
"Синк жана ACK" ортосунда Crash: Inbox/Upsert кош алдын алуу үчүн.
Кайра жеткирүү: брокердин redelivery көбөйтүү; дедуп текшерүү.
Тышкы APIs ыктымалдыгы: ошол эле ачкыч менен кайталап POST - бирдей жооп.
Лидер өзгөртүү/тармактын үзүлүшү: Кафка транзакцияларын/консультанттардын жүрүм-турумун текшерүү.
10) Анти-үлгүлөрү
транспорт таянуу: "Биз exactly-once менен Kafka, анда ачкычы жок болот" - жок.
No-op ack чейин жазуу: ачылып, бирок синк түшүп → жоготуу.
Жок DLQ/retrains менен Jitter: чексиз кайталоо жана бороон.
Табигый ачкычтардын ордуна кокустук UUID: deduplication эч нерсе жок.
Индекстери жок прод-таблицалар менен Inbox/Outbox аралаштыруу: ысык блоктор жана p99-куйруктары.
Тышкы провайдерлерде идемпотенттик API жок бизнес-операциялар.
11) Тандоо чек-тизмеси
1. double баасы (акча/юридикалык/UX) vs коргоо баасы (жашыруун/татаалдыгы/наркы).
2. Иш-чаранын/операциянын табигый ачкычы барбы? Эгер жок болсо - туруктуу келип.
3. Sink Upsert/чыгаруу колдойт? Болбосо - Inbox + ордун толтуруу.
4. Глобалдык транзакциялар керекпи? Эгер жок болсо - SAGA боюнча сегментти.
5. реплика/узак retenshn талап кылынат? 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: Fast Store (Redis) менен TTL, же таблица Inbox (PK = message _ id). Төлөмдөр үчүн - узак (күн/жума).
Q: TTL дедуп ачкычтарды кантип тандоо керек?
A: минималдуу = максималдуу кайра жеткирүү убактысы + иш запасы (адатта 24-72 саат). Каржы үчүн - көбүрөөк.
Q: Мен Kafka ачкычы боюнча compaction болсо, ачкыч керекпи?
A: Ооба. Compaction сактоону азайтат, бирок сиздин синк болбогон кылат.
13) Натыйжалары
At-least-once - транспорттун негизги, ишенимдүү семантикасы.
Exactly-once бизнес таасири иштеп деъгээлинде жетишилет: Idempotency-Key, Inbox/Outbox, Upsert/версия, SAGA/ордун толтуруу.
Тандоо - бул компромисс баасы тобокелдик эки эсе жөнөкөй иш. Табигый ачкычтарды долбоорлоо, көгүчкөндөрдү демпотенттик кылуу, байкоону кошуу жана оюн күндөрүн үзгүлтүксүз өткөрүү - анда сиздин пайплайндарыңыз ретрациялардын жана ийгиликсиздиктердин бороонунда да алдын ала айтууга жана коопсуз болот.