69 / DISTRIBUTED RELIABILITY PATTERNS / PRODUCTION FABRIC
69 / PRODUCTION / IDEMPOTENCY · DELIVERY · OUTBOX · LEASES · CONSISTENCY

DISTRIBUTED
RELIABILITY PATTERNS.

Распределённая система ломается не потому, что «сервер упал», а потому что часть операции могла успеть выполниться, часть — нет, ответ потерялся, сообщение пришло дважды, события поменялись местами или два исполнителя одновременно решили, что владеют одной работой.

Главный принцип: distributed reliability строится не на надежде на exactly-once, а на сочетании stable operation identity, at-least-once delivery, idempotent effects, atomic local transactions, outbox/inbox, optimistic concurrency, leases/fencing, reconciliation и explicit consistency contracts.
00. ARCHITECTURAL STATUS

НУЖЕН, КОГДА ОДНА ЛОГИЧЕСКАЯ ОПЕРАЦИЯ ПЕРЕСЕКАЕТ PROCESS / QUEUE / SERVICE / DB / PROVIDER BOUNDARIES

Для одного процесса и одной ACID-базы многие проблемы можно решить обычной transaction. Но как только workflow пишет в DB и публикует message, worker вызывает внешний API, несколько replicas конкурируют за один resource или состояние размазано по сервисам — появляются distributed seams.
TYPEPRODUCTIONCross-service correctness patterns.
DEFAULTCONDITIONALВключать на distributed boundaries.
ENABLE WHENMULTI-BOUNDARY EFFECTSQueue/service/provider/replicas.
SEPARATE COMPONENTYESLogical reliability fabric/pattern set.
LIVES INPRODUCTION FABRICCross-cutting infrastructure.
COMPLEXITYMEDIUM → HIGHLocal transaction first; distribution last.
IMPLEMENT: WHERE DISTRIBUTION EXISTS
Минимум 80% ценности: operation_id/idempotency key, unique constraints, transactional outbox, inbox dedupe, state_version/CAS, leases with expiry, fencing tokens for dangerous shared resources, retry-safe side effects, monotonic state transitions, dead-letter + quarantine, reconciliation jobs and failure-injection tests around every commit/publish boundary.
01A. ARCHITECTURE BOUNDARIES & OPERATIONS

EXPLICIT SYSTEM CONTRACT

A. BOUNDARY WITH NEIGHBORS

№57 Queues & Workers владеет backlog/claim/ack/worker execution; №69 определяет cross-boundary correctness: duplicate delivery, inbox dedupe, outbox, leases, fencing, ordering and reconciliation. №59 Broker транспортирует messages/events; transport does not magically make business effects exactly-once. №63 Retry решает, когда повторять transient operation; №69 делает repeated attempt безопасным. №68 Durable Workflow хранит process history and resumes workflow; №69 описывает reliability patterns между workflow engine, queue, DB и external systems. №42 Events задаёт event semantics and trigger rules; №69 deals with duplicate/out-of-order/delivery behavior.

B. PREREQUISITES / CROSS-REFERENCES

Prerequisites: №10 State Management, №42 Events & Triggers, №46 Observability, №50 Contracts, №57 Queues & Workers, №59 Broker, №60 Artifact Store, №63 Retry, №68 Workflow Engine. Forward references: №70 Model Serving, №76 Data Governance, №77 Production Architecture.

C. PLANE PLACEMENT

REQUEST-TIME: idempotency, CAS, leases, dedupe, transactional writes. CONTROL PLANE: consistency classes, retry/dedupe windows, lease durations, partition/ordering keys, compensation policy. DATA PLANE: operation ledger, outbox/inbox rows, versions, leases, fencing tokens, delivery attempts. OFFLINE: reconciliation, repair, stuck/duplicate scans, DLQ analysis, invariant tests.

D. FAILURE & OPERATIONS CONTRACT

Success: one logical operation produces the intended business state despite duplicate delivery/process crash/retry. Retryable: transport/service/lock-store transient failures. Permanent: invariant violation, stale fencing token, invalid state transition, policy failure. Idempotency: stable operation identity required across attempts. Persist: operation status, dedupe record, state version, outbox/inbox, lease owner/expiry/token, compensation/reconciliation status. Security: idempotency keys and correlation IDs must be scoped to tenant/principal so one caller cannot collide with another.

E. WHAT THIS TOPIC DOES NOT OWN

№69 не владеет queue implementation, broker transport, workflow business logic, scheduler semantics, dependency retry policy or database replication algorithms generally. Она владеет CROSS-BOUNDARY CORRECTNESS UNDER DUPLICATION, REORDERING, PARTIAL FAILURE AND CONCURRENT OWNERSHIP.

01. THE DISTRIBUTED FAILURE MODEL

«НЕ ПОЛУЧИЛ ОТВЕТ» НЕ ОЗНАЧАЕТ «НИЧЕГО НЕ ПРОИЗОШЛО»

LOST RESPONSE

Effect succeeded

Remote service committed, network dropped reply. Caller sees timeout and may retry.

DUPLICATE DELIVERY

Same work twice

Consumer crashes after side effect but before ACK; broker redelivers.

REORDERING

B arrives before A

Parallel partitions, retries or different channels change observed order.

CONCURRENT OWNERS

Two workers act

Lease expires, old worker still alive, new worker takes over.

PARTIAL COMMIT

DB yes, message no

Local state changed but downstream notification was never published.

MESSAGE YES, DB NO

External observer ahead

Message published, transaction rolled back or crashed before commit.

PARTITION

Can't coordinate

Services/regions temporarily cannot agree on shared state.

STALE READ

Old state observed

Replica/cache returns previous version and caller makes obsolete decision.

Architectural question is not «как исключить сбой», а какие invariants должны оставаться true, когда сбой происходит в любой точке между двумя commits.
02. DELIVERY SEMANTICS

AT-MOST-ONCE, AT-LEAST-ONCE И EXACTLY-ONCE — НЕ MARKETING LABELS, А TRADE-OFFS

SemanticWhat may happenTypical use
AT-MOST-ONCEMessage may be lost, but won't be retried by transport.Low-value telemetry where duplicates worse than loss or data can be recomputed.
AT-LEAST-ONCEMessage should eventually arrive, duplicates possible.Most reliable queues/workers/events; pair with idempotent consumers.
EXACTLY-ONCE PROCESSINGUsually means exactly-once within a bounded transactional substrate.Possible inside some stream/DB systems, but external effects still require idempotency.
Практический default: at-least-once delivery + idempotent processing + dedupe ledger.
03. OPERATION IDENTITY

ПОВТОРНАЯ ПОПЫТКА ДОЛЖНА ОСТАВАТЬСЯ ТОЙ ЖЕ ЛОГИЧЕСКОЙ ОПЕРАЦИЕЙ

{
  "operation_id": "OP-01J...",
  "tenant_id": "tenant_A",
  "operation_type": "publish.post",
  "resource_ref": "post://42",
  "input_hash": "sha256:...",
  "status": "COMPLETED",
  "attempts": 3,
  "effect_ref": "provider://post/9981",
  "created_at": "...",
  "completed_at": "..."
}
IDENTITY RULE

Attempt ≠ operation

  • operation_id stays stable across retries;
  • attempt_id changes each physical attempt;
  • input hash detects accidental key reuse with different parameters;
  • scope includes tenant/principal/resource where needed;
  • completion result can be returned to duplicate callers.
04. IDEMPOTENCY

ПОВТОРНОЕ ВЫПОЛНЕНИЕ ДАЁТ ТОТ ЖЕ ЛОГИЧЕСКИЙ РЕЗУЛЬТАТ

NATURALLY IDEMPOTENT

Set value

SET status='ACTIVE' can often be repeated safely under version/state checks.

KEYED IDEMPOTENCY

Create once

INSERT ... UNIQUE(operation_id) or provider idempotency key collapses duplicates.

LEDGER-BASED

Effect tracking

Before/after external action, persist business operation state and reconcile unknown outcome.

Нельзя просто сказать «endpoint idempotent», если внутри он делает несколько неатомарных side effects without ledger.
05. IDEMPOTENCY KEY SCOPE

КЛЮЧ ДОЛЖЕН ОЗНАЧАТЬ КОНКРЕТНЫЙ BUSINESS INTENT

Bad keyProblemBetter
timestampRetry generates a new value, so duplicate is invisible.Stable operation UUID created before first attempt.
user_id onlyBlocks unrelated operations by same user.tenant + operation type + client operation ID.
payload hash onlyTwo legitimate identical requests may collide.Explicit intent identity + hash used only for validation.
global unscoped keyCross-tenant collision/security issue.Tenant/principal scope included in unique constraint.
Server should reject reuse of same idempotency key with materially different payload, not silently return old result for a different intended action.
06. DEDUPE INBOX

CONSUMER ПЕРВЫМ ДЕЛОМ ДОЛЖЕН ПОНЯТЬ: «Я УЖЕ ВИДЕЛ ЭТО MESSAGE?»

MESSAGE

message_id / event_id / operation_id.

INBOX INSERT

INSERT ... ON CONFLICT DO NOTHING inside local transaction.

PROCESS ONCE LOGICALLY

Existing inbox row → duplicate; skip/reuse previous result.

Dedupe window должен соответствовать business risk. Если message может redeliver через 30 дней, хранить dedupe IDs 5 минут недостаточно.
07. DUAL-WRITE PROBLEM

«COMMIT DB, ПОТОМ PUBLISH» СОЗДАЁТ НЕУСТРАНИМУЮ ЩЕЛЬ МЕЖДУ ДВУМЯ СИСТЕМАМИ

APPLICATION 1. update order 2. publish event DB COMMIT succeeds state = PAID PROCESS CRASH before broker publish BROKER NO "OrderPaid" downstream stale never reached
Нельзя атомарно commit-нуть обычную SQL transaction и внешний broker publish без общей distributed transaction. Transactional Outbox removes this seam.
08. TRANSACTIONAL OUTBOX

BUSINESS STATE И «НАДО ОТПРАВИТЬ MESSAGE» КОММИТЯТСЯ В ОДНОЙ LOCAL TRANSACTION

BEGIN TXOne local DB transaction.
UPDATE STATEorder.status = PAID.
+
INSERT OUTBOXevent_id + payload/ref.
COMMITBoth or neither.
RELAYRead unpublished outbox rows.
PUBLISHBroker may receive duplicate.
MARK SENTIdempotent relay loop.
Outbox guarantees eventual publication of committed local intent, not duplicate-free transport. Consumer still needs inbox/idempotency.
09. OUTBOX CONTRACT

OUTBOX ROW — ЭТО DURABLE COMMAND/EVENT INTENT

outbox(
  event_id           text primary key,
  aggregate_type     text,
  aggregate_id       text,
  aggregate_version  bigint,
  event_type         text,
  payload_json       jsonb,
  payload_ref        text,
  tenant_id          text,
  created_at         timestamptz,
  published_at       timestamptz null,
  attempts           int,
  last_error         jsonb
)
RELAY RULES

At-least-once publisher

  • select unpublished rows in bounded batches;
  • lease/claim rows safely;
  • publish with stable event_id;
  • mark published after broker acknowledgement;
  • if crash occurs after publish but before mark, republish same event_id;
  • consumer dedupes.
10. INBOX + OUTBOX TOGETHER

СТАНДАРТНЫЙ RELIABLE SERVICE BOUNDARY

SERVICE A
  local transaction:
    update A state
    insert outbox event E1
  commit
    ↓
OUTBOX RELAY
    ↓
BROKER
    ↓
message E1
    ↓
SERVICE B
  local transaction:
    insert inbox(E1) UNIQUE
      if duplicate → no-op
    update B state
    optionally insert B outbox event E2
  commit
    ↓
ACK E1

RESULT:
  transport may duplicate
  relay may retry
  consumer may crash
  but each service applies
  each event once logically
  to its own local state
Это базовый composition pattern для event-driven distributed systems без глобальной ACID-транзакции.
11. OPTIMISTIC CONCURRENCY CONTROL

VERSION CHECK ЧАСТО ЛУЧШЕ DISTRIBUTED LOCK

resource:
  id = 42
  state = READY
  version = 7

worker A reads version 7
worker B reads version 7

worker A:
  UPDATE resource
  SET state='RUNNING', version=8
  WHERE id=42 AND version=7
  → 1 row updated

worker B:
  same WHERE version=7
  → 0 rows updated

B knows its decision was stale
and must reload/re-evaluate.
USE CAS WHEN

State is in one authoritative DB

Compare-and-swap / optimistic locking works especially well when:

  • one row/aggregate owns state;
  • conflicts are relatively rare;
  • caller can retry from fresh state;
  • you need atomic state transition, not long exclusive ownership.
Distributed lock is frequently overused where a version column + atomic update would be simpler and safer.
12. MONOTONIC STATE TRANSITIONS

ПОЗДНЕЕ СТАРОЕ EVENT НЕ ДОЛЖНО ОТКАТЫВАТЬ НОВОЕ СОСТОЯНИЕ

Incoming eventCurrent stateCorrect behavior
PROCESSING v4READY v3Apply if transition/version valid.
READY v3PROCESSING v4Reject/ignore stale event.
COMPLETED v5PROCESSING v4Apply.
PROCESSING v4 duplicateCOMPLETED v5No rollback; dedupe/stale handling.
Используйте aggregate version, sequence, logical clock or domain transition rules so out-of-order messages cannot revert state.
13. ORDERING

GLOBAL ORDER ДОРОГ И ЧАСТО НЕ НУЖЕН — НУЖЕН ORDER ТОЛЬКО ВНУТРИ КОНКРЕТНОЙ DOMAIN KEY

AGGREGATE KEY

order_id / workflow_id

Messages for one business entity go through same ordering partition where practical.

SEQUENCE

Detect gaps/stale

Per-aggregate monotonically increasing version reveals missing/out-of-order events.

NO GLOBAL ORDER

Scale-friendly

Order A events independently from unrelated order B events.

BUFFER?

Conditional

Short reorder buffer can wait for missing sequence only if latency and completeness justify it.

Ordering guarantees should be minimal and explicit. Global total order turns into coordination bottleneck.
14. LEASES

LEASE — ЭТО ВРЕМЕННОЕ ПРАВО СЧИТАТЬ СЕБЯ OWNER

OWNER

worker_id

Current claimant.

EXPIRES_AT

Bounded ownership

If owner dies, lease becomes reclaimable.

RENEW

Heartbeat

Long work periodically extends lease while healthy.

Lease alone не предотвращает старого owner-а от продолжения работы после expiry. Для dangerous shared effects нужен fencing token.
15. FENCING TOKENS

НОВЫЙ OWNER ПОЛУЧАЕТ БОЛЬШИЙ TOKEN; RESOURCE ОТКЛОНЯЕТ ДЕЙСТВИЯ СТАРЫХ OWNERS

WORKER A lease token = 41 GC pause / network stall WORKER B new token = 42 valid current owner SHARED RESOURCE last_seen_fence = 42 accept token >= 42 reject token 41 EFFECT only newest owner can mutate safely
Fencing works only if the protected resource actually validates the token/version. «We obtained Redis lock» without downstream fencing can still allow stale owner writes.
16. DISTRIBUTED LOCKS

LOCK — ПОСЛЕДНИЙ ИНСТРУМЕНТ, НЕ ПЕРВЫЙ

PREFER

DB uniqueness / CAS

Use authoritative datastore constraints for short critical transitions.

LEASE

Long ownership

If task needs exclusive processing, use bounded lease + expiry + fencing where effect matters.

AVOID

Unbounded distributed mutex

Can deadlock, suffer stale ownership, coordinator outage and hidden liveness problems.

Ask first: can this operation be made idempotent or version-checked instead of serializing the whole world?
17. UNIQUE CONSTRAINTS AS RELIABILITY PRIMITIVE

DATABASE UNIQUE KEY ЧАСТО НАДЁЖНЕЕ APPLICATION-LEVEL «CHECK THEN INSERT»

BAD:
  if not exists(operation_id):
      insert(effect)

RACE:
  worker A sees absent
  worker B sees absent
  both insert

GOOD:
  INSERT INTO effect_ledger(
    tenant_id,
    operation_id,
    ...
  )
  VALUES (...)
  ON CONFLICT (tenant_id, operation_id)
  DO NOTHING

DATABASE decides atomically
which logical operation wins.
Reliability should lean on authoritative atomic primitives whenever possible instead of reimplementing concurrency in application code.
18. SAGA / COMPENSATION

НЕ ВСЕ РАСПРЕДЕЛЁННЫЕ ОПЕРАЦИИ МОЖНО ROLLBACK-НУТЬ — НУЖНА BUSINESS COMPENSATION

STEP AReserve resource.
STEP BCreate external object.
STEP C FAILSNotification/payment-like dependency unavailable.
COMPENSATE BCancel object if domain allows.
COMPENSATE ARelease reservation.
Saga can be orchestrated by №68 durable workflow or choreographed through events. Compensation itself must be idempotent and observable.
19. ORCHESTRATED VS CHOREOGRAPHED SAGA

ЦЕНТРАЛЬНЫЙ PROCESS ИЛИ EVENT-DRIVEN REACTIONS

ORCHESTRATED

One durable coordinator

Workflow knows steps/compensations and commands services. Easier to inspect, reason about and recover for complex processes.

CHOREOGRAPHED

Services react to events

Loose coupling, but process semantics become distributed across consumers; harder to see global state and failure compensation.

Для complex AI/business process with approvals and external actions orchestrated saga через №68 обычно понятнее. Choreography подходит simple local reactions where no central process lifecycle is needed.
20. RECONCILIATION

НИ ОДИН ONLINE PROTOCOL НЕ УБИРАЕТ ПОТРЕБНОСТЬ В PERIODIC REPAIR

OUTBOX

Unpublished rows

Committed intents not yet acknowledged by broker.

EFFECT LEDGER

Unknown outcomes

Operations stuck IN_PROGRESS beyond timeout.

STATE DIFF

Cross-system mismatch

Local says ACTIVE, provider says absent/closed.

GAP DETECTION

Missing sequence

Observed versions 17 and 19 but 18 never arrived.

Reconciliation turns rare timing failures from silent corruption into detectable repairable states.
21. EFFECT LEDGER

ДЛЯ ВАЖНЫХ ВНЕШНИХ ACTIONS ХРАНИТЬ СОБСТВЕННЫЙ BUSINESS RECORD

effect_ledger(
  tenant_id         text,
  operation_id      text,
  effect_type       text,
  resource_ref      text,
  input_hash        text,
  state             text,
  provider_ref      text,
  started_at        timestamptz,
  completed_at      timestamptz,
  last_checked_at   timestamptz,
  error_json        jsonb,
  primary key(
    tenant_id,
    operation_id
  )
)

state:
  RESERVED
  EXECUTING
  UNKNOWN
  COMPLETED
  FAILED
  COMPENSATED
WHY LEDGER

External system isn't your transaction log

Own ledger allows:

  • dedupe;
  • unknown-outcome reconciliation;
  • compensation tracking;
  • audit;
  • retry decisions;
  • correlating provider resource IDs with logical operation IDs.
22. DEAD LETTER / QUARANTINE

НЕПЕРЕРАБАТЫВАЕМОЕ MESSAGE НЕ ДОЛЖНО БЕСКОНЕЧНО БЛОКИРОВАТЬ PIPELINE

RETRY EXHAUSTED

Move aside

After bounded retry, message/work enters DLQ/quarantine with full error context.

POISON DATA

Schema/business defect

One malformed event should not crash every consumer restart forever.

REPLAY TOOLING

After fix

Operators can inspect, patch policy/code, then replay with same message/operation identity.

DLQ is not a trash can. It needs ownership, age SLA, metrics and replay/reject resolution path.
23. POISON MESSAGE IS NOT TRANSIENT

RETRY НЕ ИСПРАВИТ JSON, КОТОРЫЙ НИКОГДА НЕ ПРОЙДЁТ SCHEMA VALIDATION

FailureRetry?Reliability action
Network timeoutYES boundedRetry same operation_id; idempotent consumer/effect.
Schema invalidNOQuarantine + producer/contract fix.
Stale versionNO blind retryReload state; ignore/recompute transition.
Duplicate eventNO new workInbox dedupe and ACK.
Unknown external effectRECONCILE FIRSTQuery provider by operation/resource ID.
24. CONSISTENCY CONTRACT

НЕ ВСЕ ДАННЫЕ ДОЛЖНЫ БЫТЬ STRONGLY CONSISTENT

STRONG

Must be current before effect

Permissions, spend reservations, unique business ownership, workflow transition.

READ-YOUR-WRITES

Session/local correctness

User should see own recent update immediately where UX requires.

EVENTUAL

Can converge later

Analytics dashboards, secondary indexes, search replicas, derived summaries.

STALE-BOUNDED

Age contract

Cache/read model may lag by ≤N seconds if domain accepts it.

Consistency should be chosen per invariant. Making everything strong is expensive; making everything eventual is dangerous.
25. SOURCE OF TRUTH

У КАЖДОГО BUSINESS FACT ДОЛЖЕН БЫТЬ AUTHORITATIVE OWNER

AUTHORITATIVE STATE

One owner

For a given aggregate/fact, define which DB/service owns writes.

READ MODELS

Derived copies

Search/vector/cache/analytics views may lag and are rebuilt from authoritative facts.

RECONCILE

Detect drift

Derived views compare version/checksum/watermark against authoritative owner.

Multi-master ownership without conflict semantics is a hidden distributed-consistency problem waiting to happen.
26. READ MODELS & EVENTUAL CONSISTENCY

SEARCH / VECTOR / ANALYTICS МОГУТ ОТСТАВАТЬ, ЕСЛИ ЭТО ЯВНО И ДОПУСТИМО

AUTHORITATIVE DBversion 42 committed.
OUTBOX EVENTaggregate_version=42.
BROKERat-least-once.
INDEXERinbox dedupe.
READ MODELapplied_version=42.
WATERMARKfreshness observable.
Version/watermark lets the system know whether derived state is current enough instead of pretending all replicas are instantly consistent.
27. SPLIT BRAIN

ДВА УЗЛА НЕ ДОЛЖНЫ ОБА СЧИТАТЬ СЕБЯ ЕДИНСТВЕННЫМ LEADER/OWNER ДЛЯ НЕОБРАТИМОГО EFFECT

RISK

Partitioned leaders

Old leader loses coordination but continues writing; new leader is elected and also writes.

FENCING

Epoch/token

Every leadership term gets higher epoch; protected resources reject stale epochs.

AUTHORITATIVE QUORUM

Where required

Consensus/DB service can serialize leadership, but application effects still need token validation.

Leader election alone does not solve stale leader effects unless downstream mutations can distinguish newer term from older.
28. CLOCKS

WALL CLOCK — ПЛОХОЙ ИСТОЧНИК DISTRIBUTED ORDER

CLOCK SKEW

Machines disagree

Two timestamps don't reliably prove causal order.

DB VERSION

Logical order

Aggregate version/sequence is stronger for business transitions.

MONOTONIC TIMER

Local duration

Use monotonic clocks for local timeout measurement where available.

UTC TIME

Human/audit time

Useful for observability/expiry, but not a universal concurrency ordering primitive.

29. EVENT VERSIONING

СООБЩЕНИЕ МОЖЕТ ЖИТЬ ДОЛЬШЕ, ЧЕМ DEPLOYMENT PRODUCER-А

SCHEMA VERSION

Typed payload

Consumers know how to parse/migrate old event shape.

AGGREGATE VERSION

Business order

Detect stale/gap/out-of-order updates.

PRODUCER VERSION

Debug/provenance

Useful for tracing semantic change and replay incidents.

Contract version and aggregate state version solve different problems; store both when events represent evolving state.
30. AI-SPECIFIC FAILURE AMPLIFICATION

AGENTS УМНОЖАЮТ DISTRIBUTED RISKS ЧЕРЕЗ FAN-OUT, RETRIES И TOOLS

TOOL REPEAT

Duplicate side effect

Agent retries «send email» after ambiguous response.

SUBAGENT FAN-OUT

Shared resource race

Several workers update same record or publish same entity simultaneously.

MODEL NONDETERMINISM

Retry changes intent

Regenerating action arguments can create a new business operation instead of retrying the old one.

HOST CONTROL

Freeze operation contract

Once an external action is approved/scheduled, persist exact arguments hash + operation_id and retry that contract, not a fresh model generation.

AI controller may decide to create a new operation, but transport retry must never silently ask the model to invent new arguments for the same failed side effect.
31. APPROVAL + IDEMPOTENCY

HUMAN APPROVAL ДОЛЖЕН БЫТЬ ПРИВЯЗАН К EXACT OPERATION, КОТОРАЯ ПОТОМ RETRY-ИТСЯ

PREPARE

operation_id + exact resource + args_hash.

APPROVE

Human approves that exact immutable intent.

EXECUTE / RETRY

Same operation_id and same approved args. If args change → new approval.

Это соединяет №49 HITL, №63 retry, №68 durable workflow and №69 operation identity into one safe external-action contract.
32. SECURITY

RELIABILITY KEYS САМИ ЯВЛЯЮТСЯ SECURITY-SENSITIVE CONTROL DATA

KEY SPOOFING

Collision attack

Attacker reuses another principal's idempotency key to retrieve/suppress operation.

TENANT SCOPE

Composite uniqueness

Unique constraints include trusted tenant/principal scope.

SIGNAL AUTH

Commands/events

Only authorized producer/principal may mutate workflow/resource state.

REPLAY AUTH

DLQ/manual repair

Replaying old effectful messages requires operator permissions and policy revalidation where needed.

A duplicate message may be old enough that permissions/policy changed. Reliability replay does not automatically bypass current security requirements for sensitive effects.
33. REPLAY POLICY

НЕ КАЖДОЕ СТАРОЕ EVENT БЕЗОПАСНО ПЕРЕИГРЫВАТЬ СЕГОДНЯ

ItemReplay defaultReason
Derived search index updateYESRebuildable from authoritative source.
Analytics eventYES with dedupeUsually side-effect-light and recomputable.
Email / publish actionONLY same idempotent operationDuplicate external effect is visible to user/world.
Permission-sensitive actionREVALIDATEAuthority may have changed since original attempt.
Irreversible destructive operationMANUAL/STRICTReplay risk may exceed automation benefit.
34. OBSERVABILITY

TRACE ДОЛЖЕН ПОКАЗЫВАТЬ ЛОГИЧЕСКУЮ ОПЕРАЦИЮ И ВСЕ ЕЁ PHYSICAL ATTEMPTS

DUP

Duplicate Delivery Rate

Messages/events observed more than once.

DED

Dedupe Hit Rate

Duplicates safely suppressed/reused by inbox/operation ledger.

OUT

Outbox Lag

Commit → successful broker publish delay.

GAP

Sequence Gaps

Missing aggregate versions/events needing repair.

UNK

Unknown Effects

Operations stuck awaiting reconciliation.

FEN

Stale Fence Rejects

Writes blocked from expired/stale owners.

DLQ

Quarantine Age

Oldest unresolved dead-letter item and count by reason.

DRIFT

Reconciliation Drift

Cross-system mismatches detected per scan.

35. FAILURE INJECTION

УБИВАТЬ PROCESS МЕЖДУ КАЖДЫМИ ДВУМЯ SIDE-EFFECT BOUNDARIES

DB COMMIT → CRASH

Before publish

Outbox eventually emits message after restart.

PUBLISH → CRASH

Before outbox mark

Republish duplicate; inbox suppresses logical reprocessing.

EFFECT → CRASH

Before ACK/result

Same operation_id reconciles/dedupes external effect.

LEASE EXPIRES

Old worker resumes

Fencing rejects stale owner mutation.

OUT-OF-ORDER

v9 before v8

Consumer detects version gap/stale order and follows policy.

DUP SIGNAL

Same event twice

State advances once logically.

BROKER DOWN

Outbox accumulation

Business transaction stays valid; relay catches up after recovery.

RECONCILE

Injected mismatch

Repair job detects and resolves/alerts deterministic inconsistency.

36. FAILURE MODES

КАК РАСПРЕДЕЛЁННАЯ СИСТЕМА ТИХО ПОРТИТ ДАННЫЕ

CHECK THEN INSERT
Concurrent workers both observe absence and create duplicates.
UNIQUE CONSTRAINT
DB THEN PUBLISH
Crash leaves committed state without event.
TRANSACTIONAL OUTBOX
ACK AFTER EFFECT WITHOUT IDEMPOTENCY
Consumer crash causes duplicate external effect on redelivery.
OPERATION LEDGER
LOCK WITHOUT FENCING
Expired old owner resumes and overwrites newer owner.
LEASE + FENCING TOKEN
TIMESTAMP ORDER
Clock skew misorders distributed state transitions.
LOGICAL VERSION
GLOBAL ORDER
Coordination bottleneck for unrelated entities.
PER-AGGREGATE ORDER
RETRY POISON FOREVER
One invalid event burns CPU and blocks progress.
DLQ / QUARANTINE
NO RECONCILIATION
Rare partial failures remain silent forever.
PERIODIC REPAIR
MODEL REGENERATES RETRY ARGS
Retry accidentally becomes a different real-world action.
FREEZE OPERATION CONTRACT
37. MVP IMPLEMENTATION

POSTGRES + OUTBOX + INBOX + OPERATION LEDGER ЗАКРЫВАЮТ БОЛЬШИНСТВО REAL-WORLD FAILURES

reliability/
├── idempotency.py
├── outbox.py
├── inbox.py
├── versions.py
├── leases.py
├── fencing.py
├── effects.py
├── reconcile.py
└── tests/

processed_messages(
  tenant_id      text,
  message_id     text,
  processed_at   timestamptz,
  primary key(tenant_id, message_id)
)

outbox(
  event_id       text primary key,
  tenant_id      text,
  aggregate_id   text,
  aggregate_ver  bigint,
  event_type     text,
  payload_json   jsonb,
  published_at   timestamptz null,
  attempts       int
)

operation_ledger(
  tenant_id      text,
  operation_id   text,
  operation_type text,
  input_hash     text,
  status         text,
  result_ref     text,
  primary key(tenant_id, operation_id)
)

leases(
  resource_key   text primary key,
  owner_id       text,
  expires_at     timestamptz,
  fence_token    bigint
)
80% VALUE MVP

Boring reliability wins

  • Stable operation_id for every effectful command.
  • Unique DB constraints for dedupe.
  • Transactional outbox for DB→message seam.
  • Inbox table for message dedupe.
  • State version / CAS on mutable aggregates.
  • At-least-once queue assumptions documented.
  • Leases for worker ownership.
  • Fencing for high-risk shared resources.
  • DLQ/quarantine with replay tooling.
  • Reconciliation job for unknown/stuck states.
  • Failure injection around commit/publish/ACK boundaries.

Do not start with multi-region consensus, custom distributed lock service or global event ordering unless the actual deployment requires them.

38. WHEN TO UPGRADE

УСЛОЖНЯТЬ RELIABILITY ПО РЕАЛЬНЫМ FAILURE DOMAINS

SignalPotential upgrade
Very high message volumePartitioned outbox relays, CDC-based outbox publication, scalable inbox retention.
Multiple regions write same aggregateExplicit ownership, consensus/leader epochs, conflict-free or merge semantics.
Long cross-service business transactionsDurable saga orchestration integrated with №68.
External effects lack idempotency APIsStronger effect ledger + read-back reconciliation + human repair path.
High lock contentionRedesign ownership/sharding/CAS before adding more lock complexity.
Derived read models are criticalWatermarks, sequence-gap recovery, automated rebuild/reconciliation.
39. PRACTICAL DECISION

СТОИТ ЛИ ДЕЛАТЬ ОТДЕЛЬНЫЙ КОМПОНЕНТ?

ВопросОтвет
Стоит ли реализовывать?Да на distributed boundaries. Если всё живёт в одной transaction, использовать ACID и не усложнять.
Separate Component?YES логически. Но это чаще shared libraries/tables/protocols, а не один giant «reliability service».
Минимум 80% ценности?Operation identity, unique constraints, outbox/inbox, CAS, leases/fencing, DLQ, reconciliation, failure-injection tests.
Когда overkill?Consensus, distributed locks, global order и complex sagas для single-process/single-DB application.
Trigger?Operation crosses transaction boundaries, messages can duplicate/reorder, replicas compete, or external effects can have unknown outcomes.
Как измерить uplift?Duplicate-effect rate, reconciliation drift, outbox lag, DLQ age, stale-write rejects, manual repair incidents, data-consistency incidents.
Можно ли rule/tool/code вместо LLM-agent?Не просто можно — нужно. Distributed correctness must be deterministic. LLM can help analyze incidents, but cannot be the concurrency-control protocol.
40. DESIGN RULES

ПРАВИЛА ДЛЯ РЕАЛЬНОЙ СИСТЕМЫ

RULE 01

Assume duplicates

Any queue/event/effectful retry path must tolerate repeated delivery.

RULE 02

Stable operation identity

Retry the same logical intent, not a newly generated action.

RULE 03

One local transaction first

Use DB atomicity/uniqueness/CAS before distributed coordination.

RULE 04

Outbox for dual writes

Persist state and publication intent atomically.

RULE 05

Inbox for consumers

Dedupe before applying repeated message to local state.

RULE 06

Version state transitions

Stale/out-of-order work cannot overwrite newer truth.

RULE 07

Lease is not enough

Use fencing when stale owners can still mutate protected resources.

RULE 08

Reconcile forever

Online protocols reduce failure probability; reconciliation catches the remainder.

RULE 09

Test crash boundaries

Correctness is proven when process dies between commit, publish, effect and ACK.

41. FINAL MAP

DISTRIBUTED CORRECTNESS IS BUILT FROM COMPOSABLE LOCAL GUARANTEES

LOGICAL OPERATION
  operation_id
  tenant scope
  exact input hash
        ↓
LOCAL SERVICE TRANSACTION
        ↓
STATE UPDATE
  version = N+1
        +
OUTBOX INSERT
  event_id
  aggregate_version
        ↓
COMMIT
        ↓
OUTBOX RELAY
        ↓
BROKER / QUEUE
  at-least-once
  duplicate possible
  reorder possible
        ↓
CONSUMER
        ↓
INBOX DEDUPE
  UNIQUE(message_id)
        ↓
CHECK VERSION / STATE
        ↓
APPLY LOCAL EFFECT
        ↓
OPTIONAL NEXT OUTBOX
        ↓
COMMIT
        ↓
ACK

EXTERNAL SIDE EFFECT:

prepare exact operation
        ↓
operation ledger
        ↓
execute with idempotency key
        ↓
response?

YES:
  record COMPLETED

TIMEOUT / UNKNOWN:
  do not invent a new operation
        ↓
  reconcile by operation/resource ID
        ↓
  already happened?
    yes → record success
    no  → safe retry same operation_id

CONCURRENT OWNERS:

lease owner A / token 41
        ↓
lease expires
        ↓
owner B / token 42
        ↓
shared resource accepts 42
rejects stale 41

OUT-OF-ORDER:

aggregate v9 arrives before v8
        ↓
version/gap policy
        ↓
buffer / reload / ignore stale / reconcile
        ↓
NEVER silently revert v9 to v8

PARTIAL MULTI-SERVICE PROCESS:

forward steps
        ↓
failure
        ↓
durable saga
        ↓
idempotent compensations
        ↓
explicit final state

BACKGROUND SAFETY NET:

reconciliation
  outbox backlog
  unknown effects
  stale leases
  sequence gaps
  source/read-model drift
  DLQ age
        ↓
repair / alert / manual review

BOUNDARIES:

№57 QUEUE
  transports runnable work

№59 BROKER
  transports messages/events

№63 RETRY
  decides when to repeat

№68 DURABLE WORKFLOW
  remembers process lifecycle

№69 DISTRIBUTED RELIABILITY
  ensures retries, duplicates,
  ordering and partial failures
  do not corrupt business state

CORE PRINCIPLE:

DO NOT TRY TO MAKE
THE NETWORK "EXACTLY ONCE".

MAKE THE BUSINESS OPERATION
IDENTIFIABLE,
IDEMPOTENT,
VERSIONED,
RECONCILABLE
AND SAFE TO REDELIVER.

THE NETWORK MAY DUPLICATE.
THE WORKER MAY DIE.
THE ACK MAY DISAPPEAR.
THE OLD OWNER MAY WAKE UP.

THE SYSTEM SHOULD STILL
CONVERGE TO ONE VALID
LOGICAL BUSINESS STATE.

ECC RETROFIT / PRACTICAL HARNESS INTEGRATION

A. Related ECC ideas. Context-as-cache, scoped memory, lifecycle hooks, selective capabilities, feature flags, deterministic enforcement, provider-neutral adapters and eval-gated learning are applied only where relevant to №69 Distributed Reliability Patterns.

B–E. Existing boundary and placement. The existing conceptual boundary, class PRODUCTION, default CONDITIONAL and owner Production Fabric remain authoritative. Runtime/control/data/offline placement is unchanged; durable state stays outside model context.

F–H. Hooks and contracts. Use bounded PRE_MODEL/POST_MODEL, PRE_TOOL/POST_TOOL, CHECKPOINT and TASK_COMPLETED events as applicable. Illustrative fields and canonical contracts are defined in NEW_CONTRACTS_SPEC.md; no universal schema is implied.

I–J. Security and evaluation. Host-side schema, permission, secret, budget, idempotency and audit checks take precedence over LLM output. Optional mechanisms require a feature flag and WITH/WITHOUT ablation; measure quality, acceptance, correction, latency, cost, escalations and severe errors.

K–L. Task profiles and cross-references. A TaskProfile selects the relevant skill, tool/context slice, memory scope and enforcement profile independently from FAST/STANDARD/DEEP. See cross-reference map, hook spec and ablation plan. Provider adapters remain outside the core.