Базы данных
Онлайн-решардинг, 2PC и операционная стоимость шардирования
Кластеру Citus нужно добавить воркер-узел для увеличения мощности. Старый подход требовал окна технического обслуживания: остановить записи, скопировать данные, перезапустить. Citus 11.1 изменил это онлайн-ребалансировщиком, перемещающим шарды пока кластер полностью работает — используя тот же механизм логической репликации, что Postgres использует для обновлений.
Онлайн-решардинг: как это работает
Citus 11.1 (сентябрь 2022) представил non-disruptive онлайн-решардинг. Механизм:
1. Запустить ребалансировщик: SELECT citus_rebalance_start();
2. Планировщик решает: какие шарды перемещаются с воркера A на воркер B для балансировки
3. Для каждого перемещаемого шарда:
a. Создать шард на целевом воркере (пустой)
b. Запустить слот логической репликации на источнике для строк этого шарда
c. Логическая репликация копирует существующие строки на цель, затем стримит изменения
d. Когда лаг близок к нулю: приостановить записи на шард (~суб-секунда)
e. Атомарно обновить метаданные карты шардов: шард теперь на цели
f. Удалить исходный шард
g. Возобновить записи — теперь маршрутизируются на цель
4. Начать следующий шард (конвейер параллельных перемещений, настраиваемый concurrency)Профиль производительности:
- Пауза записи на шард: менее 1 секунды
- Общее время ребалансировки: масштабируется с объёмом данных (~10–100 МБ/с пропускная способность сети)
- Влияние на приложение: краткая задержка соединения во время паузы на шард; прозрачно ретраится координатором
Ребалансировщик идемпотентен и возобновляем — если он крашнется в середине перемещения, следующий citus_rebalance_start() продолжит с того места, где остановился, используя состояние pg_stat_subscription.
Опытные команды репетируют ребалансировщик на стейджинге прежде чем запускать в продакшне. Первый запуск не должен быть аварийным.
Распределённые транзакции и 2PC
Транзакция, затрагивающая строки на нескольких шардах, требует двухфазного commit (2PC):
-- Один шард: безопасно, обычная транзакция Postgres
BEGIN;
UPDATE orders SET status = 'shipped' WHERE tenant_id = 42 AND order_id = 99;
UPDATE inventory SET qty = qty - 1 WHERE tenant_id = 42 AND sku = 'ABC';
COMMIT;
-- Обе таблицы ко-локированы на одном шарде → однонодовая транзакция
-- Кросс-шард: требует 2PC
BEGIN;
UPDATE orders SET status = 'refunded' WHERE tenant_id = 42 AND order_id = 99;
UPDATE ledger SET balance = balance + 100 WHERE account_id = 999; -- НЕ в рамках клиента
COMMIT;
-- Citus использует PREPARE TRANSACTION + COMMIT PREPARED по обоим шардамСтоимость 2PC:
- Задержка: минимум 2 round-trip (фаза prepare + фаза commit); часто больше при учёте clock skew.
- Пропускная способность: каждый шард держит своё соединение открытым во время фазы prepare — конкуренция при высоком concurrency.
- In-doubt транзакции: если координатор крашнется между
PREPARE TRANSACTIONиCOMMIT PREPARED, prepared-транзакции сидят вpg_prepared_xactsудерживая блокировки до ручного разрешения. Это режим отказа, который нужно мониторить и иметь runbook для.
Лучшие практики:
- Проектировать транзакции как одношардовые: ко-локированные таблицы делают это естественным для операций в рамках клиента.
- Предпочитать eventual consistency (outbox pattern, saga) там, где бизнес-логика допускает, вместо распределённого 2PC.
- Мониторить
pg_prepared_xactsна каждом воркере; алертить на любую строку старше 5 минут; иметь runbook восстановления (COMMIT PREPARED/ROLLBACK PREPARED).
Операционный множитель N×
| Операция | Один Postgres | Шардированный (N шардов) |
|---|---|---|
| Миграция схемы | Запустить один раз | Citus автопропагирует DDL от координатора к воркерам; проверить согласованность схемы после миграции на всех воркерах |
| Бэкапы | Один pg_basebackup | N координированных бэкапов; все должны целиться в одну логическую точку во времени для согласованного восстановления |
| Минорное обновление версии | Одно rolling update | N rolling updates, координированных; версия Citus должна быть совместимой между координатором и всеми воркерами |
| VACUUM / bloat | Одна конфигурация autovacuum | N отдельных процессов autovacuum; каждый шард независимо накапливает мёртвые строки |
| Мониторинг | Один дашборд | Метрики на шард + агрегаты по кластеру; перекос шардов виден в heatmap |
Опытные команды автоматизируют всё через оркестрацию на шард — Ansible с циклом по хостам воркеров, Terraform for_each, Kubernetes операторы — до запуска в продакшне. Без автоматизации операционный персонал линейно масштабируется с числом шардов, что делает операционный налог шардирования конкретным.
Решардинг ключа: дорогой случай
Иногда исходный ключ шарда оказывается неправильным (шардировали по user_id, но 90% запросов нужен org_id). Решардинг по другому ключу требует переписать каждую строку на новое место:
- Dual-write + backfill: приложение пишет как в старую, так и в новую схему шардирования; заполнить исторические данные; переключить чтения; удалить старые. Месяцы работы.
- Новый параллельный кластер: развернуть новый кластер с правильным ключом, реплицировать через CDC, переключиться. Наиболее гибко; наибольший операционный объём.
- Принять стоимость: на практике команды терпят неправильный ключ шарда вместо того, чтобы платить стоимость миграции — сильный аргумент в пользу тщательного выбора с самого начала.
Опытный вывод: изменение ключа шарда — одна из самых дорогих операций в базах данных. Проектное решение, принятое в момент горизонтального масштабирования, живёт с командой годами.
Почему это работает
Почему онлайн-решардинг использует логическую репликацию, а не физическую? Физическая репликация (pg_basebackup, streaming) копирует целые страницы данных — вы получили бы данные каждого клиента из исходного шарда, а не только перемещаемого. Логическая репликация копирует изменения на уровне строк (INSERT/UPDATE/DELETE), фильтруя по интересующим строкам. Для перемещения шарда логическая репликация может скопировать ровно строки из перемещаемого шарда и стримить только их изменения до переключения — намного эффективнее и гибче физической копии.
Ребалансировщик Citus работает, и координатор крашится в середине перемещения шарда. Что происходит с кластером?
Упорядочьте шаги онлайн-перемещения шарда Citus (с исходного воркера на целевой):
- 1 Создать пустой шард на целевом воркере
- 2 Запустить слот логической репликации на источнике; скопировать существующие строки на цель
- 3 Стримить текущие изменения с источника на цель пока лаг не упадёт почти до нуля
- 4 Приостановить записи на этот шард (суб-секунда)
- 5 Атомарно обновить метаданные карты шардов: шард теперь указывает на целевой воркер
- 6 Возобновить записи — теперь маршрутизируются на цель; удалить исходный шард
- 01Опишите паузу записи на шард во время онлайн-ребалансировки Citus: как долго она длится и почему суб-секундная?
- 02В чём риск in-doubt транзакции в Citus 2PC и как мониторить и восстанавливаться от него?
- 03Почему шардирование описывается как 'дверь в одну сторону' и какие операционные последствия из этого следуют?
Онлайн-ребалансировка Citus 11.1+ использует логическую репликацию для перемещения шардов между воркерами с суб-секундной паузой записи на шард — репетируйте на стейджинге до запуска в продакшне. Кросс-шард транзакции требуют двухфазного commit, который добавляет ~2× задержку, снижает пропускную способность при concurrency и создаёт риск in-doubt-транзакции при краше координатора; мониторьте pg_prepared_xacts и поддерживайте runbook восстановления. Операционный множитель N× означает, что миграции, бэкапы, обновления и VACUUM должны быть автоматизированы на всех шардах до запуска в продакшне — без автоматизации операционный персонал линейно масштабируется с числом шардов. Изменение ключа шарда после запуска — многомесячный проект; проектное решение, принятое в момент горизонтального масштабирования, живёт с командой годами. Шардирование — правильный ответ когда пределы одного Postgres измерены и доказаны — входить намеренно, с автоматизацией, runbook’ами и отработанными операционными процедурами.
встречается в258
- Почему GraphQL получает N+1junior
- Механика DataLoader: батчинг на границе тикаmiddle
- Контракты batch-функции: порядок, формы, ошибкиmiddle
- Federation и lookahead: батчинг за пределами DataLoadermiddle
- Защита сложности запросов: depth, cost, persisted queriesmiddle
- Senior GraphQL API: scheduling-контракт, изоляция арендаторов, наблюдаемостьsenior
- Путь запроса: семь остановок от сокета до ответаjunior
- Accept и парсинг: от очереди ядра до типизированного запросаmiddle
- Маршрутизация и middleware: что выполняется и в каком порядкеmiddle
- Обработчик и ответ: от бизнес-логики до байтов на проводеmiddle
- Стриминг и backpressure: когда клиент читает медленнее, чем вы пишетеsenior
- Таймауты и хвостовая задержка: бюджеты, дедлайны и ловушка fan-outsenior
- Middleware и DI: два паттерна, формирующие любой backendjunior
- Пишем middleware: сигнатуры, next() и три модели фреймворковmiddle
- Инверсия управления: как зависимости добираются до классаmiddle
- Скоупы и время жизни DI: singleton, request, transientmiddle
- DI как шов для тестов: фейки, моки и граница, которая важнаsenior
- DI-контейнеры в продакшене: графы разрешения, циклы и когда не стоитsenior
- Блокирующий vs неблокирующий I/O: два способа ждатьjunior
- Event loop: один поток, упорядоченные фазыmiddle
- Что блокирует цикл: CPU-работа и синхронные вызовыmiddle
- Вынос CPU-работы: worker threads и пул libuvmiddle
- Backpressure и ограниченная конкурентностьsenior
- Пропускная способность под нагрузкой: хвостовая задержка и насыщениеsenior
- Зачем пул: цена создания соединенияjunior
- Размер пула: почему больше не значит быстрееmiddle
- Взятие и таймауты: очередь ожидания — настоящий дроссель задержкиmiddle
- Зачем идемпотентность: безопасные retryjunior
- Серверный state machine: четыре состояния idempotency keymiddle
- Стратегии retry: backoff, jitter и thundering herdmiddle
- Outbox и inbox: effectively-once через dual-write границуmiddle
- Конкурентность и архитектура кеша для идемпотентности на масштабеsenior
- Наблюдаемость, production-инциденты и дизайн для глобального масштабаsenior
- Event loop: один поток, три очередиjunior
- Задачи, микрозадачи и scheduler.yield()middle
- Точность таймеров, троттлинг и фоновая работаmiddle
- Голодание микрозадач, длинные задачи и LoAFsenior
- Event loop Node.js: фазы, nextTick и задержка циклаsenior
- React, Vue и наблюдаемость INP в продакшенеsenior
- Render pipeline: шесть стадий от байтов до пикселейjunior
- Цена стадий и модель процесса рендерераmiddle
- Инвалидация, dirty-биты и containmiddle
- Слои композитора: продвижение, перекрытие и память GPUmiddle
- Флейм-стрип DevTools и жизненный цикл кадраmiddle
- Layout thrash: форсированная синхронная компоновкаsenior
- BeginMainFrame, анимации на потоке compositor и память GPUsenior
- Observability в проде: LoAF, INP и полная поверхность атакиsenior
- Что такое V8 и почему производительность различается в 100 разjunior
- Четырёхуровневый JIT-конвейер V8 и профилированная тиеризацияmiddle
- Hidden classes, деревья переходов и расположение в памятиmiddle
- Inline caches, состояния IC и деоптимизацияmiddle
- Orinoco GC: параллельный scavenger, конкурентная разметка и барьеры записиmiddle
- Спекулятивный движок TurboFan и ловушка deopt-loopsenior
- V8 в production: Isolates, сжатие указателей и реальные аварииsenior
- Жизненный цикл service worker и стратегии кешированияmiddle
- Граничные случаи service worker: version skew, долговременность и ловушка навигацииsenior
- Что делает реконсилер: render vs commitjunior
- Объект fiber и дерево с двойной буферизациейmiddle
- Чистота фазы render и подшаги фазы commitmiddle
- Реконсиляция: эвристики диффа и ловушка ключейmiddle
- Приоритетные lanes, time-slicing и useTransitionmiddle
- Bailout, мемоизация и tearingsenior
- React Profiler, компилятор и продакшн-наблюдаемостьsenior
- Стратегии рендеринга: SSG, SSR, ISR, streaming и гидратацияjunior
- SSG, SSR, ISR, streaming и RSC — как работает каждая стратегияmiddle
- Цена гидратации: selective, progressive, острова, resumabilitymiddle
- Hydration mismatch: причины, обнаружение и правило детерминизмаsenior
- RSC, стратегия на маршрут и production-наблюдаемостьsenior
- Core Web Vitals: что измеряют LCP, INP и CLSjunior
- LCP: четыре фазы, одна доминирующая стоимостьmiddle
- INP: input delay, processing, presentationmiddle
- CLS: почему происходят сдвиги лейаута и как их остановитьmiddle
- Lab vs field: почему они расходятся и как использовать каждыйmiddle
- Трейдоффы метрик, RUM-атрибуция и цикл CI+полеsenior
- Общая картина: от URL до LCP до INP как эстафетаjunior
- Восемь слоёв трассировки: от service worker до второй навигацииmiddle
- Пять канонических поломок: где производство стабильно ломаетсяsenior
- Метод трёх треков: чтение трасс и построение системы мониторингаsenior
- Что такое cache stampede и почему он делает всё хужеjunior
- Лок и single-flight: ограничение параллельных rebuildmiddle
- XFetch: вероятностное раннее истечение без координацииmiddle
- Stale-while-revalidate и CDN request coalescingmiddle
- Детектирование stampede и дизайн TTL для продакшенаmiddle
- Метастабильный сбой, fencing-токены и production-постмортемыsenior
- Роли Raft, term и почему majority-кворум предотвращает split brainjunior
- Как Raft реплицирует log entry и решает, что его безопасно коммититьmiddle
- Выборы лидера в Raft: таймауты, правила голосования и четыре свойства безопасностиmiddle
- Raft в реальном мире: partition, медленный диск и клиентская маршрутизацияmiddle
- Расширения Raft: pre-vote, learner, snapshot и линеаризуемые чтенияsenior
- Raft в production: membership change, Multi-Raft и observabilitysenior
- Где происходит data fetching — и почему это решает LCPjunior
- Fetch waterfall''''ы — диагностика и лечение через Promise.allmiddle
- React Server Components и Suspense streamingmiddle
- Клиентский кэш: TanStack Query, SWR и stale-while-revalidatemiddle
- LCP, prefetch и race conditions в интерактивном fetchingmiddle
- Senior internals: RSC payload, слои кэша и production паденияsenior
- Биты в проводеjunior
- Математика задержкиmiddle
- Bufferbloat и перегрузкаsenior
- Граница физического уровняsenior
- Трёхстороннее рукопожатие TCPjunior
- Номера последовательности и состояние соединенияmiddle
- Управление потоком и перегрузкойmiddle
- BBR, производственная наблюдаемость и за пределами TCPsenior
- DNS: что делает и зачем существуетjunior
- Обход резолвера: перенаправления, типы записей и gluemiddle
- TTL, кеширование и распространение DNSmiddle
- Рукопожатие за 1 RTT: key share и ECDHEmiddle
- Возобновление сессии и 0-RTTmiddle
- CDN: контент по соседствуjunior
- Anycast и GeoDNS: маршрутизация к ближайшему edgemiddle
- Многоуровневый кеш и Cache-Controlmiddle
- Заголовок Vary и cache keysmiddle
- Stale-while-revalidate и cache stampedesenior
- Edge workers и edge-side compositionsenior
- CDN: операции и observabilitysenior
- WebSocket: HTTP-апгрейд до постоянного соединенияjunior
- Формат WebSocket-фрейма: opcodes, маскирование, фрагментацияmiddle
- WebSocket vs SSE vs long-polling: выбор правильного транспортаmiddle
- Backpressure в WebSocket: когда клиенты не успеваютmiddle
- Реконнект: jittered backoff, thundering herd, восстановление сообщенийsenior
- WebSocket в масштабе: HTTP/2 мультиплексирование, permessage-deflate, C10Msenior
- WebSocket в production: прокси, безопасность и распределённая архитектураsenior
- Что делают обратные проксиjunior
- Алгоритмы балансировки: от round-robin до power-of-two-choicesmiddle
- L4 vs L7 балансировка и сохранение IP клиентаmiddle
- Health checks, connection draining и slow startmiddle
- Session affinity, consistent hashing и правильное решениеmiddle
- Retry-бури, circuit breakers и load sheddingsenior
- Устойчивая архитектура LB: anycast, zone-aware маршрутизация и observabilitysenior
- Почему QUIC, а не TCP+TLSjunior
- QUIC-потоки и head-of-line blockingjunior
- Объединённое рукопожатие и 1-RTTmiddle
- Connection ID и миграция сетиmiddle
- Обнаружение потерь и управление перегрузкойmiddle
- Возобновление 0-RTT и шифрование пакетовsenior
- Развёртывание и стоимость CPUsenior
- DDoS: что это и почему работаетjunior
- Атаки усиления и истощение состоянияmiddle
- Ограничение скорости: алгоритмы и архитектураmiddle
- WAF, межсетевые экраны, mTLS и HSTSmiddle
- Отравление DNS-кэша и BGP-перехватsenior
- Эшелонированная защита и экономика атакsenior
- Двенадцать слоёв: один URL, семь действующих лицjunior
- DNS, TCP, TLS по очереди: куда уходят миллисекундыmiddle
- Критический путь рендеринга и Core Web Vitalsmiddle
- Перехват прокси и шлюзы безопасности: rate limiter, WAF, mTLSmiddle
- Альтернативные пути: QUIC 0-RTT, WebSocket upgrade, миграция соединенияmiddle
- Наблюдаемость: распределённые трейсы, USE/RED и семплированиеsenior
- Устойчивость: каскадные повторы, circuit breakers и error budgetsenior
- Что такое три сигнала: метрики, логи, трейсыjunior
- Метрики и cardinality: cost-модель time-series databasemiddle
- Логи и объём: cost-модель структурного логированияmiddle
- Трейсы и сэмплирование: cost-модель distributed tracingmiddle
- Join-ключи и exemplar''''ы: как три сигнала становятся компонуемымиmiddle
- Observability 2.0: широкие события и сдвиг стоимостиsenior
- Режимы сбоя и инженерная практика: cardinality budget''''ы, PII и сэмплированиеsenior
- Зачем нужны структурные логи: дневник против таблицыjunior
- Схема продакшн-лога: поля, которые несёт каждая строкаmiddle
- Log levels и маршрутизация алертовmiddle
- Стратегии sampling и стоимость логовmiddle
- PII-редакция и log injectionsenior
- Propagation trace-контекста в логахsenior
- OTel Logs Data Model и audit-логи как подсистемаsenior
- Сигналы OTel, Semantic Conventions и проводной формат OTLPmiddle
- Авто-инструментирование и ручные спаны: правило 80/20 в OTelmiddle
- Collector OTel: receivers, processors, exporters и паттерны развёртыванияmiddle
- Стратегии сэмплирования: head, tail и parent-basedmiddle
- Vendor-нейтральность, eBPF-инструментирование, Operator и OTel в браузере и serverlesssenior
- Эксплуатация OTel Collector: надёжность, version skew, режимы отказа и управлениеsenior
- RED и USE: два чек-листа, одна дисциплина триажаjunior
- Инструментация RED в Prometheus: счётчики, гистограммы и дисциплина cardinalitymiddle
- USE на Linux: CPU, память, диск, сеть и PSImiddle
- Golden signals, структура дашборда и auto-RED в service meshmiddle
- Cardinality как драйвер затрат: label, PII, exemplars и семплированиеmiddle
- Native histograms, SLO и паттерны production-сбоевmiddle
- SLI, SLO и error budget: надёжность в числахjunior
- Выбор SLI и SLO-целей: отношения, не ощущенияmiddle
- Multi-window multi-burn-rate-алертинг: почему AND лучше ORmiddle
- Error budget policy, latency SLO и составные journeysmiddle
- Iceberg SLI, математика составного SLO и SLA vs SLOsenior
- Продакшн-отказы SLO, самонаблюдаемость, безопасность и общая картинаsenior
- Flame graph: читаем картинку, которая показывает, куда ушло времяjunior
- Sampling vs instrumentation profiling: почему 99 Гц побеждает в productionmiddle
- Типы профилей: CPU, память, off-CPU, mutex — какой когда братьmiddle
- Continuous profiling: always-on flame graphs с eBPF и корреляцией trace-idmiddle
- Как flame graph строится из сэмплов и как использовать его в productionmiddle
- Linux perf, внутренности eBPF, PGO и ограничения sampling''''аsenior
- Profiling в production: безопасность, war stories, OTel profiles и дизайн инфраструктурыsenior
- Debugging-воронка: SLO → RED → trace → profilejunior
- Архитектура OTel: один SDK, четыре сигнала, один wire-форматmiddle
- Экономия на observability: удерживаем затраты в пределах 5% inframiddle
- Петля инцидента: от пейджера до постмортема до предотвращенияmiddle
- Масштаб, безопасность и ROI наблюдаемых системsenior
- Сначала профиль: измерь куда реально уходит времяjunior
- Закон Амдала и self-time: потолок любого ускорения, которое ты можешь выпуститьmiddle
- Измерительный цикл: микробенч, макробенч, prod-профиль, эффект наблюдателяmiddle
- Чтение флейм-графов: формы, профайлеры по языкам и 60-секундный сканmiddle
- Статистические baseline''''ы: почему один запуск — не измерениеmiddle
- История профайлеров и ловушки микробенчей: от Кнута до GWPsenior
- Hardware counters, профили холодного старта и безопасность профилейsenior
- Непрерывное профилирование в масштабе: затраты, CI-гейты, корреляция с трейсами и антипаттерныsenior
- Что делает путь горячим: симптом против причиныjunior
- Пять форм hotspot''''а: CPU, аллокации, кэш, лок, syscallmiddle
- Чтение parent и child chains: где применять правкуmiddle
- JIT deopt, цикл fix-and-verify и PR-time профилированиеmiddle
- Аппаратные счётчики и Intel TMA: диагностика подкатегорийsenior
- False sharing и горячие пути нативных мостовsenior
- Горячие пути в production: безопасность, хвостовая латентность и происхождение инструментовsenior
- Иерархия памяти: почему расстояние важнее числа операцийjunior
- Row-major vs column-major: порядок доступа и разрыв в 9xjunior
- Cache lines и false sharing: когда параллелизм замедляет кодmiddle
- Branch prediction: 10–30 циклов штрафа за неожиданный ifmiddle
- SIMD и data layout: AoS vs SoA и разница в 4–8xmiddle
- Hardware prefetcher, TLB и memory-level parallelismsenior
- Cache-oblivious алгоритмы, PGO и production failuressenior
- Основы GC: за что рантайм берёт налогjunior
- Алгоритмы GC: поколенческая гипотеза, concurrent marking и write barriermiddle
- GC tradeoffs: пауза, throughput, память и давление аллокацийmiddle
- Настройка GC: пейсинг, форма кучи и наблюдаемость аллокацийmiddle
- Внутреннее устройство GC: tri-color инвариант, write barriers и глубокое погружение в рантаймыsenior
- GC в production: наблюдаемость, безопасность, edge cases и управление флотомsenior
- N+1: одна логическая операция, много round-trip''''овjunior
- Семейства фиксов: JOIN, IN, preload и DataLoadermiddle
- Обнаружение N+1: query logs, APM traces и CI gatesmiddle
- DataLoader: батчинг по дереву резолверовmiddle
- Кросс-протокольный N+1: HTTP fan-out и Redis MGETmiddle
- N+1 в масштабе: исчерпание пула, изменения планов и денормализацияsenior
- Batching: амортизируй фиксированную цену каждой операцииjunior
- Окно батчинга: размер и время ожиданияmiddle
- Batching в Kafka и Postgresmiddle
- io_uring и наблюдаемость пакетированияmiddle
- От Nagle до io_uring: эволюция пакетированияmiddle
- Backpressure, изоляция сбоев и безопасность батчей в продакшенеsenior
- Что на самом деле стоит bundle: download, parse, compile, executejunior
- Core Web Vitals: LCP, INP и CLSmiddle
- Code splitting: route-level, component-level, vendor splittingmiddle
- Tree shaking и compression: удаляем то, что не используемmiddle
- Third-party scripts: тихий убийца бюджетаmiddle
- CI enforcement и RUM: делаем бюджеты рабочимиmiddle
- V8 JIT-пайплайн, HTTP-приоритеты и безопасность bundlesenior
- Цикл performance: дисциплина, а не проектjunior
- Классификация и исправление: сопоставление family bottleneck с методамиmiddle
- Observability-стек и CI gates: ловить регрессии до выпускаmiddle
- От инцидента к enforcement: SLO burn до верифицированного исправления за 35 минутmiddle
- Культура, экономика и масштаб performancesenior
- At-most-once, at-least-once, exactly-once: три контракта доставкиjunior
- Три ножки сбоя — где реально происходят дубликаты и потериmiddle
- Consumer-side dedup: самый дешёвый путь к exactly-once processingmiddle
- Kafka exactly-once semantics: idempotent producer и транзакцииmiddle
- SQS visibility timeout, DLQ и outbox patternmiddle
- Exactly-once в production: impossibility-доказательство, гибридные паттерны и реальные инцидентыsenior
- Что такое OAuth и почему пароли — не ответjunior
- Authorization code flow с PKCEmiddle
- Валидация ID-токена и управление JWKS-кешемmiddle
- Ротация refresh-токенов и scope-based least privilegemiddle
- Sender-constrained токены: DPoP и mTLSsenior
- OAuth в production: audience атаки, observability и реальные провалыsenior