Производительность
Backpressure, изоляция сбоев и безопасность батчей в продакшене
Ваш Kafka consumer батчит по 500 сообщений за poll. У одного — битый header. Десериализатор бросает исключение. Без изоляции вы только что потеряли — или перепроиграли — 499 хороших сообщений вместе с ним. Батчинг умножает каждый сбой на размер батча, и тот же множитель берёт на вооружение атакующий. Это урок, который пропускают туториалы про throughput.
Уроки 01–05 покрыли плюс: амортизировать фиксированную стоимость, настроить окно, попасть в колено Парето. Это финальный урок, и он про минус — операционные edge case, которые превращают throughput-победу в пейджер в 3 утра. Каждое свойство, делающее батчинг эффективным (атомарное потребление, общая компрессия, одна очередь, одно окно), становится множителем сбоя, как только что-то ломается. Senior, который гонял батчевый пайплайн в продакшене, думает об этих четырёх вещах ещё до того, как смержен счастливый путь.
Изоляция сбоев: проблема отравленного сообщения
Батч потребляется атомарно: вы делаете poll() на 500 записей, обрабатываете их, потом коммитите один offset. Если обработка падает на элементе 47 — что с 1–46 и 48–500? Все наивные ответы — ловушки.
- Ретрай всего батча. Элемент 47 всё ещё отравлен, снова бросает исключение. Consumer никогда не сдвигает offset. Получаем бесконечный цикл, lag растёт без предела, партиция заморожена за одной плохой записью. Это классический столл на отравленном сообщении — тот самый, что будит пейджером, пока дашборды показывают «lag: 4.2M и растёт», а consumer выглядит здоровым.
- Skip батча при ошибке. Сдвигаемся за все 500, теряя 499 хороших, чтобы выбросить один плохой. Для платежей или заказов это потеря данных; для аудит-лога — потенциально инцидент комплаенса.
- Split-and-retry (бисекция). При сбое делим батч пополам и ретраим каждую половину. Падающая половина падает снова — бисектим только её. Рекурсируем, пока батч не станет одной записью, отправляем её в dead-letter и коммитим остальные. Платим
O(log N)лишних запросов, чтобы изолировать один отравленный элемент из N. Для батча в 500 это ~9 ретраев, а не 500.
Облачный примитив для этого — BisectBatchOnFunctionError у AWS Lambda для источников Kinesis и DynamoDB Streams: при ошибке функции Lambda делит батч пополам и ретраит каждую половину независимо, сужаясь до проблемной записи. Сочетайте с ReportBatchItemFailures, где функция возвращает sequence number первого сбоя, и Lambda ретраит только с этой точки вместо повтора всего окна.
Всё это неполно без подстраховки: dead-letter queue (DLQ) плюс re-drive job. Записи, упавшие сверх maximumRetryAttempts, летят в DLQ (SQS-очередь или Kafka DLQ-топик) с оригинальным payload и контекстом ошибки. Оператор разбирает их вне основного потока, чинит схему или баг и re-drive-ит DLQ обратно в основной стрим. Пайплайн без DLQ не имеет изоляции сбоев — у него есть место, где сообщения тихо умирают.
| Режим сбоя | Наивный исход | Senior-фикс |
|---|---|---|
| Один элемент падает в батче из N | Ретрай всего батча → бесконечный цикл, lag взрывается | Split-and-retry для изоляции, потом DLQ одного отравленного |
| Продюсер быстрее consumer | Неограниченная in-mem очередь → OOM kill | Ограниченная очередь + явная политика переполнения (block/drop/spill) |
| Сжатый батч распаковывается огромным | Проверка размера на проводе проходит, брокер OOM-ится при распаковке | Лимит размера ПОСЛЕ декомпрессии, не только на проводе |
| Дубль payload реплеится внутри одного батча | Per-batch dedup видит один ключ, обрабатывает все копии | Per-item idempotency key (nonce), валидируется по записи |
| Статичное окно неверно для текущей нагрузки | Пере-батчим на низкой (латентность) или недо-батчим на высокой | Адаптивное окно: AIMD-контур по p99 vs SLO |
Backpressure и ограниченные очереди
В какой-то момент в любой продакшен-системе скорость продюсера превышает скорость consumer — замедление вниз по потоку, GC-пауза, деплой, удвоивший трафик. Очередь между ними — это место, куда уходит несоответствие. Первое решение — ограничена ли очередь вообще.
- Неограниченная in-memory очередь. Работает прекрасно, пока продюсер не обгонит consumer надолго и не заполнит heap, тогда JVM OOM-килит процесс, и вы теряете всё в полёте. Это классический антипаттерн — очередь, которая «никогда не заполняется», пока однажды не заполнится, в 2 ночи, унося весь узел.
- Ограниченная + block. При заполнении продюсер блокируется. Это сердце Reactive Streams: consumer сигнализирует спрос через
request(n), и продюсер может эмитить только столько — backpressure как протокол первого класса (Akka Streams, Project Reactor, RxJava — все реализуют). Латентность деградирует мягко, throughput оседает на скорости самой медленной стадии. Это правильный дефолт, когда потеря данных дороже добавленной латентности. - Ограниченная + drop. При заполнении выбрасываем новый (или самый старый) элемент. По дизайну с потерями, и корректно, когда свежесть важнее полноты — StatsD по UDP канонический пример: дропнутый сэмпл метрики невидим, а застрявший на полном буфере продюсер ломает ту самую видимость, ради которой он был нужен.
- Ограниченная + spill-to-disk. Страничим переполнение на локальный буфер на диске (write-ahead log), дренируемый за in-memory очередью. Durable при всплесках продюсера ценой disk IOPS и end-to-end латентности. Vector, Fluentd и Filebeat — все предлагают это для шиппинга логов.
Один нюанс, который важно понять верно: spill-to-disk — это рычаг ёмкости, а не сама политика переполнения. Даже дисковый буфер конечен, так что ему по-прежнему нужно правило when_full. Дисковый буфер Vector по дефолту when_full: block, прокидывая backpressure до самого источника, и переключается на drop_newest только когда вы явно выбираете сбрасывать нагрузку. Смысл в том, что «spill» даёт гораздо больший durable-буфер до того, как block-или-drop вообще сработает.
Выбор между ними — продуктовое решение, а не техническое. Вопрос: сколько стоит потеря одного элемента против одной секунды добавленной латентности? Для платежей потерянный элемент недопустим — block. Для метрик-firehose секундный столл ослепляет дашборды — drop. Ответьте один раз на пайплайн и зашейте ответ в конфиг очереди; не позволяйте этому быть случайностью того, какой дефолт привезла библиотека.
Пайплайн платёжных событий: consumer замедляется во время инцидента вниз по потоку, ограниченная очередь заполняется. Выберите политику переполнения, которую защитит senior.
Безопасность: граница батча как примитив атакующего
Граница батча — это рычаг, а рычаг — то, что нужно атакующему. Повторяются три класса эксплойтов.
Бомбы декомпрессии. Маленький сжатый payload, разворачивающийся в гигабайты на получателе. Конкретный инцидент Kafka — CVE-2023-34455 (с follow-up неполного фикса CVE-2023-43642): декомпрессор snappy-java читал 4-байтовое поле длины чанка и выделял байтовый массив этого размера без проверки верхней границы. Подделанный фрейм, объявляющий 0x7FFFFFFF, заставлял брокер пытаться выделить ~2 ГБ и бросать OutOfMemoryError — удалённый DoS, затрагивающий Kafka с 0.8.0 по 3.5.0, исправлено в snappy-java 1.1.10.1+. Общая защита: валидировать размер после декомпрессии, не только на проводе. Сжатый батч в 1 МБ может законно нести гораздо больше, так что лимит на уровне провода — вообще не защита.
Replay внутри батча. Если ваш idempotency key per-batch, а не per-item, атакующий отправляет один батч со 100 копиями одного payload. Проверка dedup на границе батча видит один уникальный ключ и спокойно обрабатывает все 100 копий. Идемпотентность должна быть per-item, с per-item nonce, проверяемым против dedup-стора по мере применения каждой записи.
Переупорядочивание при частичном сбое. Батчевые записи в KV store могут не сохранять порядок отправки, как только включается split-and-retry: переретраенная половина приземляется после успешной. Если порядок несёт смысл — event sourcing, CRDT merge, машина состояний — приложите sequence number per item, и пусть consumer режет out-of-order записи. Никогда не считайте, что порядок батча переживает ретрай.
Объединяющее правило: каждое свойство батча — общий размер, число элементов, idempotency key, порядок — должно валидироваться ПОСЛЕ декомпрессии и per-item, никогда не на границе провода и не per-batch. Сжатый батч — непрозрачный конверт; то, что внутри, и есть поверхность атаки.
Адаптивный батчинг: замыкание петли
Статичное окно — linger.ms=10, batch.size=16384 — неверно при любой нагрузке, кроме той, под которую тюнили. На низком трафике оно добавляет латентность, ожидая батч, который не наполнится; на высоком — упирает throughput ниже того, что система могла бы держать. Адаптивные системы трактуют окно как управляющую переменную, ведомую сигналом обратной связи.
- Сигнал. Наблюдаемая p99 латентность против потолка SLO. Когда
p99 < SLO − margin, растим окно ради throughput. Когдаp99 > SLO, уменьшаем, защищая латентность. - AIMD (additive increase, multiplicative decrease). Тот самый контур, что использует TCP congestion control: толкаем окно вверх малым шагом каждый здоровый интервал, делим пополам при пробитии SLO. Он мягко прощупывает запас и резко отступает под стрессом — тот же инстинкт, что держит интернет стабильным, применяется к окну батча.
- Продакшен-примеры. У Kafka
batch.sizeстатичный, ноlinger.msможно вести sidecar-ом, реагирующим на латентность. Фильтр adaptive concurrency у Envoy гоняет близко родственный контур — gradient controller (алгоритм Netflix), измеряющий minRTT под лёгкой нагрузкой и уменьшающий лимит in-flight конкуренции, как только sampled-латентность поднимается над ним. Та же идея, управляет конкуренцией вместо размера окна. - Цена. Вы добавляете observability-поверхность (per-window гистограммы латентности) и контрольный контур, который сам может осциллировать при плохом тюнинге. Окупается после примерно 100k QPS, где разрыв между правильным окном на пике и в провале велик; ниже — хорошо выбранное статичное окно проще и достаточно.
Почему это работает
Почему переиспользовать AIMD из TCP, а не более умный контроллер? Потому что multiplicative decrease в AIMD гарантирует быстрый уход от перегрузки, а additive increase не даёт врезаться в неё обратно — свойство, делающее его стабильным под общей шумной нагрузкой. PID-контроллер может быть точнее, но его куда легче затюнить в осцилляцию. Для окна батча, где цена перелёта — пробитие SLO, «отступай резко, прощупывай мягко» — консервативный дефолт.
Senior-чеклист перед шипом
Прежде чем батчевый пайплайн уйдёт в шип, senior может ответить на все шесть пунктов. Если хоть один ответ «мы не решили» — это и есть дыра, которая разбудит пейджером.
- Путь восстановления от отравленного сообщения? Split-and-retry для изоляции, DLQ плохого элемента, re-drive job для повтора фиксов.
- Поведение при переполнении? Block, drop или spill — выбранное явно из цены потерянного элемента vs добавленной латентности.
- Максимальный размер payload после декомпрессии? Реальный лимит на распакованный размер, не только на проводе.
- История per-item идемпотентности? Per-item nonce против dedup-стора, никогда не per-batch ключ.
- Статичное окно или адаптивное? Статичное окей ниже ~100k QPS — просто знайте, что выбрали и почему.
- Observability-поверхность? Гистограмма размера батча, gauge глубины очереди, rate DLQ, лаг consumer — четыре сигнала, ловящие всё вышеперечисленное до того, как оно разбудит пейджером.
Батч из 500 Kafka-записей падает на записи 47. Как split-and-retry изолирует отравленный элемент?
Сжатый Kafka-батч в 1 МБ проходит лимит размера на проводе, но OOM-ит брокер. Какая защита верна, согласно CVE-2023-34455?
Упорядочьте шаги изоляции split-and-retry для отравленной записи в батче:
- 1 Обработка падает где-то в батче; весь батч помечен как failed
- 2 Делим батч пополам и ретраим каждую половину независимо
- 3 Рекурсируем только в ту половину, что всё ещё падает, игнорируя прошедшую
- 4 Бисектим до одной отравленной записи
- 5 Отправляем эту запись в dead-letter и коммитим offset-ы всех хороших
- 01Consumer батчит по 500 Kafka-сообщений за poll. Сообщение 47 падает при десериализации. Опишите паттерн split-and-retry изоляции и что делает его дешёвым.
- 02Продюсер быстрее consumer, и ограниченная очередь полна. Что меняется между block, drop и spill-to-disk, и как выбрать?
- 03Почему per-batch идемпотентность небезопасна и как CVE-2023-34455 обобщает урок про границы батча?
Продакшен-батчинг падает способами, которых не покрывают уроки throughput-тюнинга, и каждый из них — эффективность батча, обращённая против вас. Один битый элемент убивает весь батч — фиксим split-and-retry, изолируя отравленную запись за O(log N) запросов, потом dead-letter и re-drive (BisectBatchOnFunctionError у AWS Lambda — это и есть примитив). Один медленный consumer переполняет очередь — ограничьте её и выберите block, drop или spill осознанно, потому что выбор — продуктовое решение о цене потерянного элемента против латентности, а не техническая случайность. Один зловредный батч усиливает атаку на порядки — защищаемся лимитами размера после декомпрессии (CVE-2023-34455), per-item идемпотентностью и sequence numbers, потому что сжатый батч — непрозрачный конверт, который надо валидировать per-item. Статичные окна окей до ~100k QPS; выше — гоните AIMD-контур по наблюдаемой p99 против SLO, та же congestion-логика, что у TCP. Чеклист из шести пунктов перед шипом — путь отравленного сообщения, политика переполнения, лимит декомпрессии, per-item nonce, выбор окна, observability — это то, что отделяет туториал по батчингу от батч-пайплайна.
встречается в260
- Почему 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
- Что такое отношение: таблицы, строки, ключи и ограниченияjunior
- Ограничения, ключи и типы данных Postgresmiddle
- Нормальные формы, денормализация и почему схемы «прилипают»middle
- JSONB, массивы и когда side table побеждаетmiddle
- Heap-хранилище, TOAST и выравнивание колонокsenior
- Целостность схемы: deferral, версионирование и сбои в продакшнеsenior
- Реляционная модель vs документные, wide-column, граф и key-valuesenior
- Что такое индекс и как он ускоряет запросыjunior
- Leading-column rule: почему порядок столбцов в composite-индексе важенmiddle
- Partial, expression и covering-индексыmiddle
- Типы индексов: GIN, GiST, BRIN, Hash, Bloom и HOT-обновленияmiddle
- Index-only scan, Visibility Map и INCLUDEsenior
- Типичные сбои в продакшне и аудит индексовsenior
- Упражнение по проектированию индексов: стратегия полнотекстового поискаsenior
- EXPLAIN и планы выполнения: что решает планировщик и почемуjunior
- Типы сканирования: Seq, Index, Bitmap, Index-Onlymiddle
- Алгоритмы соединения и каскад ошибок оценки строкmiddle
- pg_statistic, ANALYZE и производственная наблюдаемостьmiddle
- Расширенная статистика: исправление ошибок оценки для коррелированных колонокsenior
- Кеш планов, настройка константных стоимостей и внутренности планировщикаsenior
- Производственные режимы отказа и стабильность плановsenior
- MVCC: как Postgres раздаёт согласованные снимкиjunior
- Заголовок tuple и механика снимковmiddle
- HOT-обновления и уровни изоляцииmiddle
- VACUUM, bloat и autovacuummiddle
- CLOG, XID wraparound и MultiXactsenior
- SSI и production-тюнинг autovacuumsenior
- Реальные провалы MVCC, deployment-паттерны и распределённые снимкиsenior
- Connection pool: зачем амортизировать стоимость backend Postgresjunior
- Режимы PgBouncer: session, transaction и statementmiddle
- Размер пула: формула (ядра × 2) + шпинделей и двухуровневый стекmiddle
- Исчерпание пула и idle-in-transaction: сценарий отказа в 3 ночиmiddle
- Миграция на transaction mode: план развёртывания и prepared statements в PgBouncer 1.21middle
- Процессная модель Postgres и почему увеличение max_connections снижает производительностьsenior
- Ландшафт пулеров 2026, serverless connection storms и полная таксономия отказовsenior
- Что такое миграция схемы и почему она заменяет ad-hoc DDLjunior
- ADD COLUMN: мгновенно в PG 11+ против перезаписи в старом Postgresjunior
- Режим отказа очереди блокировок: почему мгновенный DDL может заморозить базуmiddle
- Безопасные DDL-паттерны: NOT VALID, CONCURRENTLY и исправления небезопасных операцийmiddle
- Expand-contract: нулевой простой для ломающих изменений схемыmiddle
- Advisory-блокировки, инструменты миграций и координация деплояsenior
- Таксономия сбоев миграций и дисциплина продакшнаsenior
- Зачем нужно шардирование: потолок одного Postgresjunior
- Выбор ключа шарда: стратегии hash, range, list и directorymiddle
- Партиционирование против шардирования: одно слово, два разных понятияmiddle
- Ко-локация и Citus: инвариант, делающий шардирование пригодным к использованиюmiddle
- Режим отказа hot shard: обнаружение, изоляция и долгосрочная политикаmiddle
- Schema-based шардирование и альтернативы мультиарендностиsenior
- Онлайн-решардинг, 2PC и операционная стоимость шардированияsenior
- Семь актов: от CREATE TABLE до Citusjunior
- Акты 1–3 в глубину: схема, индексы и статистика планировщикаmiddle
- Акты 4–6 в глубину: MVCC bloat, connection pooling и безопасные миграцииmiddle
- Акт 7 в глубину: шардинг, co-location и семиуровневый каскад трейдоффовmiddle
- Наблюдаемость, антипаттерны и производственный триаж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
- 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