Демпотенттик жана ачкычтар
Демпотенттик деген эмне
Идемпотенттүүлүк - ошол эле идентификатор менен кайталоо жыйынтыктоочу эффектти өзгөртпөгөн операциянын касиети. Бөлүштүрүлгөн системаларда бул натыйжаны ретрага, кайталанган билдирүүлөргө жана таймауттарга карабастан "так бир иштетүүгө" барабар кылуунун негизги жолу.
Негизги идея: ар бир потенциалдуу кайталануучу операция система "буга чейин жасалган" деп тааныган жана жыйынтыкты бир жолудан ашык эмес колдонгон ачкыч менен белгилениши керек.
Кайда маанилүү
Төлөмдөр жана баланстар: 'operation _ id' боюнча эсептен чыгаруу/чегерүү.
Брондоолор/квоталар/лимиттер: бир эле слот/ресурс.
Webhook/эскертмелер: кайра жеткирүү таасири кайталанышы керек.
Импорт/миграция: файлдарды/пакеттерди кайра иштетүү.
Стрим-процессинг: брокерден/CDCден дубль.
Ачкычтардын түрлөрү жана алардын чөйрөсү
1. Operation key - бизнес-операциянын конкреттүү аракетинин идентификатору
Мисалы: 'idempotency _ key' (HTTP), 'operation _ id' (RPC).
Область: сервис/агрегат; таблицада сакталат.
2. Event key - уникалдуу окуя/билдирүү идентификатору
Мисалдар: 'event _ id' (UUID), '(producer_id, sequence)'.
Аймак: керектөөчү/керектөөчүлөр тобу; проекцияларды коргойт.
3. Business Key - предмет чөйрөсүнүн табигый ачкычы
Мисалдар: 'payment _ id', 'invoice _ number', '(user_id, day)'.
Аймак: агрегат; уникалдуулугун/версиясын текшерүүдө колдонулат.
TTL жана сактоо саясаты
TTL ачкычтар мүмкүн терезе кайталоо ≥: Retention Логин + тармак/процесстик кечигүү.
Критикалык домендер үчүн (төлөмдөр) TTL - күндөр/жумалар; телеметрия үчүн - саат.
Deadup жадыбалдарды background джобами менен тазалаңыз; текшерүү үчүн - архивдөө.
Ачкыч сактагычтар (дедупликация)
Транзакциялык БД (сунушталат): ишенимдүү upsert/unique-индекстер, эффект менен биргелешкен транзакция.
KV/Redis: тез, кыска TTL үчүн ыңгайлуу, бирок ALTP менен биргелешкен транзакциясыз - этият.
State store стрим процессору: жергиликтүү + брокерде чейнджлог; Flink/KStreams жакшы.
- idempotency_keys
`consumer_id` (или `service`), `op_id` (PK на пару), `applied_at`, `ttl_expires_at`, `result_hash`/`response_status` (опц.) .
Индекстер: '(consumer_id, op_id)' - уникалдуу.
Негизги ишке ашыруу ыкмалары
1) Транзакция "эффект + прогресс"
Натыйжаны жазуу жана окуу/позиция прогрессин жазуу - бир транзакцияда.
pseudo begin tx if not exists(select 1 from idempotency_keys where consumer=:c and op_id=:id) then
-- apply effect atomically (upsert/merge/increment)
apply_effect(...)
insert into idempotency_keys(consumer, op_id, applied_at)
values(:c,:id, now)
end if
-- record reading progress (offset/position)
upsert offsets set pos=:pos where consumer=:c commit
2) Optimistic Concurrency (агрегатынын версия)
Жарышта кош таасирден коргойт:sql update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
-- if 0 rows are updated → retry/conflict
3) Idempotent sinks (upsert/merge)
Бир жолу эсептөө операциясы:sql insert into bonuses(user_id, op_id, amount)
values(:u,:op,:amt)
on conflict (user_id, op_id) do nothing;
Протоколдордо Идемпотенттүүлүк
HTTP/REST
'Idempotency-Key' аталышы: <uid 'hash>'.
Сервер ачкычтын жазуусун сактайт жана ошол эле жоопту кайра кайтарат (же инварианттар кагылышында '409 '/' 422' коду).
"Кооптуу" POST үчүн - милдеттүү 'Idempotency-Key' + туруктуу таймаут/ретрай саясаты.
gRPC/RPC
Метадеректер 'idempotency _ key', 'request _ id' + deadline.
Сервердик ишке ашыруу - REST сыяктуу: транзакциядагы Дедуп таблицасы.
Брокерлер/агымы (Kafka/NATS/Pulsar)
Продюсер: туруктуу 'event _ id '/idempotent продюсер (колдоого алынган жерде).
Консюмер: дедуп боюнча '(consumer_id, event_id)' жана/же бизнес-версия агрегаты.
Indempotent эмес/бузулган билдирүүлөр үчүн өзүнчө DLQ.
Webhook жана тышкы өнөктөштөр
келишимде 'Idempotency-Key '/' event _ id' талап; кайра жеткирүү коопсуз болушу керек.
'notification _ id' жана жөнөтүү статусун сактаңыз; ретрада - кайталабаңыз.
Ачкычтарды долбоорлоо
Детерминация: ретрайлер ошол эле ачкычты жөнөтүшү керек (кардарга/оркестрге алдын ала түзүңүз).
Көрүү аянты: 'op _ id' катары 'service: aggregate: id: purpose' түзүңүз.
Чыр-чатактар: бизнес параметрлери UUIDv7/ULID же хэш колдонуу (керек болсо туз менен).
Иерархия: фронтто жалпы 'operation _ id' → бардык субоперацияларда көрсөтүлөт (демпотенттик чынжыр).
UX жана азык-түлүк аспектилери
Ачкыч боюнча кайталап суроо-талап ошол эле натыйжаны (анын ичинде дене/статус) же ачык "аткарылды".
Колдонуучуга "ийгилик үчүн" кайталап аракет кылуунун ордуна "операция иштетилет/аяктады" статусун көрсөтүңүз.
Узак операциялар үчүн - ачкыч боюнча поллинг ('GET/operations/{ op _ id}').
Байкоо
Логин 'op _ id', 'event _ id', 'trace _ id', жыйынтык: 'APPLIED '/' ALREADY _ APPLIED'.
Метрика: кайталоолордун үлүшү, дедуп-таблицалардын өлчөмү, транзакциялардын убактысы, версиялардын карама-каршылыктары, DLQ-коюм.
Trace: ачкыч командасы аркылуу өтүүгө тийиш → окуя → проекция → тышкы чакырык.
Коопсуздук жана комплаенс
PIIди ачкычта сактабаңыз; ачкыч - ID, эмес payload.
Узакка созулган TTL менен чоң атанын жазууларында сезимтал талааларды шифрлеңиз.
Сактоо саясаты: TTL жана Archives; унутуу укугу - жоопторду/метадерилерди крипто-өчүрүү аркылуу (эгерде алар PIIди камтыса).
Сыноо
1. кайталанышы: 2-5 жолу бир билдирүү/суроо-талап - так бир таасири.
2. Кадамдардын ортосунда түшүү: эффектти жазууга чейин/кийин, офсетти бекитүүгө чейин/кийин.
3. Кайра/керектөөчүлөрдүн балансы: эки жолу колдонуу жок.
4. Атаандаштык: бир менен параллелдүү суроо 'op _ id' → бир таасир, экинчи - 'ALREADY _ APPLIED/409'.
5. Узак мөөнөттүү ачкычтар: TTL мөөнөтү жана калыбына келтирүү кийин кайталап текшерүү.
Антипаттерндер
Ар бир retray үчүн кокустук жаңы ачкыч: система кайталоону тааныбайт.
Эки өзүнчө коммита: биринчи эффект, андан кийин офсет - алардын ортосундагы түшүү эффектти кайталайт.
Бир гана брокерге ишеним: көк/агрегатта чоң атанын жоктугу.
Агрегаттын версиясы жок: кайталанган окуя экинчи жолу абалын өзгөртөт.
Fat keys: ачкыч бизнес талаалар/PII → агып жана татаал индекстерди камтыйт.
Кайталануучу жооптордун жоктугу: кардар коопсуз ретрациялай албайт.
Мисалдар
Төлөм POST
Кардар: 'POST/payments' + 'Idempotency-Key: k-789'.
Server: транзакция - 'payment' жана 'idempotency _ keys' жазуусун түзөт.
Кайталоо: ошол эле '201 '/орган кайтарат; карама-каршылыкта - '409'.
Бонус эсептөө (sink)
sql insert into credits(user_id, op_id, amount, created_at)
values(:u,:op,:amt, now)
on conflict (user_id, op_id) do nothing;
Окуялардын проекциясы
Консюмер 'seen (event_id)' жана 'version' агрегатын сактайт; кайталоо - ignor/idempotent upsert.
Окуу прогресси проекцияны жаңыртуу менен бирдей транзакцияда белгиленет.
Production чек тизмеси
- Бардык кооптуу операциялар үчүн idempotent ачкычы жана анын көрүү чөйрөсү аныкталган.
- TTL жана уникалдуу индекстер менен таятасынын таблицалары бар.
- Okuma таасири жана прогресс атомдук биригет.
- Write модели оптимисттик атаандаштыкты камтыйт (версия/sequence).
- API келишимдери 'Idempotency-Key '/' operation _ id' жана кайталоо жүрүм-турумун белгилейт.
- Метриктер жана логиндер 'op _ id '/' event _ id '/' trace _ id'.
- дубликат тесттер, кулап жана жарыш - CI.
- TTL/Archive саясаты жана PII коопсуздук сакталат.
FAQ
'Idempotency-Key' 'Request-Id' дегенден эмнеси менен айырмаланат?
'Request-Id' - жол; "Idempotency-Key" - операциянын семантикалык идентификатору.
БДсыз идемпотенттикти жасоого болобу?
Кыска терезе үчүн - ооба (Redis/процессордук кэш), бирок биргелешкен транзакциясыз дубль коркунучу күчөйт. Критикалык домендерде - бир DD транзакциясында жакшыраак.
Тышкы өнөктөштөр менен эмне кылуу керек?
Ачкычтар жана кайталануучу жооптор жөнүндө сүйлөшүңүз. Эгерде өнөктөш колдобосо, анда чакырыкты сиздин демпотенттик катмарыңызга буруп, "буга чейин колдонулуп келген".
Кантип TTL тандоо керек?
Максималдуу кечигүүлөрдү жалпылаңыз: логдун ретенциясы + worst-case network/ребаланс + буфер. запасын кошуу (× 2).
Жыйынтык
Демпотенттик - ачкычтардын, транзакциялардын жана версиялардын дисциплинасы. Туруктуу иш идентификаторлору + атомдук окуу эффектин жана прогрессти бекитүү + демпотенттик sinks/проекциялар транспорттук деңгээлдеги сыйкырсыз "так бир эффектти" берет. Ачкычтарды аныктагыла, TTL реалдуу, ал эми тесттерди зыяндуу кылгыла. Ошондо ретра жана дубликаттар окуя эмес, күнүмдүк болуп калат.