awesome-everything EN
↑ Обратно к восхождению

Очереди, потоки, события

Капстоун очередей: чтение кода и конфигурации

Суть Читай реальную конфигурацию pipeline и код консьюмера — outbox плюс CDC, порядок коммита оффсета, dedup и обработку DLQ — предскажи поведение и выбери фикс, который senior сделает первым.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Баги pipeline живут не в прозе — они живут в границе транзакции, в порядке коммита, в отсутствующем уникальном ограничении и в конфиге коннектора. Читай каждый сниппет по pipeline заказов и выбирай фикс, к которому senior-инженер тянется первым.

Цель

Потренируй чтение швов event-driven pipeline так, как читаешь инцидент: замечай, где атомарность, порядок, идемпотентность или обработка DLQ тихо сломаны, и называй коррекцию с наибольшим рычагом.

Сниппет 1 — запись в outbox

-- хендлер заказа, одна транзакция БД
BEGIN;
  INSERT INTO orders (id, customer_id, status, total_cents)
    VALUES ('ord_9f', 'cust_3a', 'placed', 4200);
  INSERT INTO outbox (id, aggregate_id, type, payload, created_at)
    VALUES ('evt_7c', 'ord_9f', 'order.placed', '{"order_id":"ord_9f"}', now());
COMMIT;
-- отдельно, позже, relay:
--   SELECT * FROM outbox WHERE sent = false ...
--   публикация в Kafka, затем UPDATE outbox SET sent = true
Викторина

Какую гарантию даёт эта структура и что в результате должны делать консьюмеры ниже по потоку?

Сниппет 2 — взаимодействие outbox и CDC

// Debezium outbox-event-router connector
{
  "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
  "table.include.list": "public.outbox",
  "transforms": "outbox",
  "transforms.outbox.type":
    "io.debezium.transforms.outbox.EventRouter",
  "transforms.outbox.route.by.field": "aggregate_id",
  "slot.name": "order_outbox_slot",
  "heartbeat.interval.ms": "0"
}
Викторина

Этот коннектор читает таблицу outbox и маршрутизирует события в Kafka-топик, ключуя по aggregate_id. Две настройки плохо взаимодействуют с низкотрафиковым развёртыванием плюс залипшим консьюмером. Что — настоящая production-опасность?

Сниппет 3 — порядок коммита у консьюмера

for msg in consumer:                      # at-least-once Kafka-консьюмер
    event = parse(msg.value)
    consumer.commit()                     # коммит оффсета СНАЧАЛА
    charge_payment(event.order_id,        # затем side effect
                   event.amount_cents)
Викторина

Этот консьюмер оплаты коммитит оффсет до списания. В чём сбой и какой порядок правильный?

Сниппет 4 — обработка DLQ

def handle(event):
    try:
        process(event)                       # идемпотентный side effect
    except Exception:
        send_to_dlq(event)                    # любой сбой -> DLQ
        consumer.commit()
Викторина

Этот хендлер отправляет каждый сбой сразу в DLQ при первом исключении. Что идёт не так и какой senior-фикс?

Итог

Каждый шов pipeline читается в коде и конфиге: outbox делает два INSERT атомарными, но publish-затем-mark у relay — at-least-once, так что консьюмеры должны делать dedup; CDC-коннектор, ключующий по aggregate_id, хранит порядок по заказу, но heartbeat.interval.ms = 0 без cap на slot — это простой из-за заполнения диска, ждущий залипания; коммит оффсета до side effect — at-most-once и тихо теряет списания, так что коммить после обработки и оставайся идемпотентным; а DLQ при первом исключении хоронит poison-сообщения под восстановимыми транзиентами, так что ретрай с бюджетом и карантин только при исчерпании. Читай границу транзакции, порядок коммита, уникальное ограничение и конфиг коннектора — именно там и живёт корректность pipeline.

Продолжить восхождение ↑Капстоун очередей: собери pipeline обработки заказов
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.