Производительность
От Nagle до io_uring: эволюция пакетирования
В 1984 году Джон Нагл смотрел, как одно нажатие клавиши в telnet покидает хост 41-байтовым пакетом: один байт полезных данных, обёрнутый в 40-байтовый TCP/IP-заголовок. На перегруженном ARPANET это были 97.5% потерь, и тысячи таких «крошек» (tinygrams) плавили каналы. Его правка — три строки логики TCP. Сорок лет спустя вы крутите ровно тот же рычаг каждый раз, когда задаёте linger.ms в Kafka или пакуете SQE в один io_uring_enter. Фиксированная стоимость сменила имя; приём не менялся.
Идея пакетирования вне времени: когда фиксированная стоимость на операцию доминирует над переменной стоимостью данных — заголовок TCP, сетевой round-trip, переключение контекста syscall, захват соединения с БД — группируйте операции, чтобы платить фиксированную стоимость один раз вместо N раз. Всё в этом уроке — одна эта идея, применённая на разных слоях стека на протяжении четырёх десятилетий. Единственная настоящая ручка проектирования — окно: как долго ждать или насколько заполниться, прежде чем сбросить.
Алгоритм Нагле: исходный компромисс пакетирования
RFC 896 (1984) ввёл то, что мы теперь зовём алгоритмом Нагле. Правило короткое: пока в полёте есть неподтверждённые данные, придержи любой новый маленький сегмент и объедини его с последующими записями; сбрасывай немедленно лишь когда ACK очистит данные в полёте или когда наберётся целый MSS байт. Мотивация — жёсткая арифметика: 1-байтовое нажатие telnet становилось 41-байтовым пакетом, так что 40 из каждого 41 байта на проводе были заголовком. Правило Нагле превращало серию нажатий в один пакет на round-trip вместо одного пакета на клавишу.
Цена ложится на интерактивный трафик и трафик «запрос/ответ». Если приложение делает маленький write() и затем ждёт ответа, Нагле может усесться на этот последний маленький сегмент в надежде на новые данные, которых не будет — и в итоге ждёт ACK, добавляя до полного round-trip мёртвого времени. Путь отступления — опция сокета TCP_NODELAY, отключающая алгоритм, так что каждая запись уходит немедленно. Поэтому HTTP/2, gRPC, клиенты Redis и по сути любой современный RPC-стек ставят TCP_NODELAY при подключении и делают собственное пакетирование на уровне приложения, где границы сообщений действительно известны.
Дедлок Нагле + delayed-ACK (постмортем-момент)
Знаменитый отказ — не Нагле сам по себе, а Нагле во взаимодействии с TCP delayed ACK. Delayed ACK — зеркало Нагле на стороне получателя: вместо ACK на каждый сегмент получатель ждёт (по умолчанию в Linux до ~40мс), надеясь подсадить ACK на ответ или объединить его со следующим. Теперь сложите оба. Отправитель пишет ответ чуть больше одного MSS: первый полный сегмент уходит, но маленький хвостовой сегмент держит Нагле, потому что первый ещё не подтверждён. Получатель получил первый сегмент, но держит свой ACK под delayed-ACK, ожидая подсадки. Ни одна сторона не двинется. Дедлок разрывается лишь когда срабатывает 40мс таймер delayed-ACK.
Симптом в проде безошибочен и бесит: протокол, который должен делать тысячи транзакций в секунду, загадочно упирается в ~25/сек, а гистограммы латентности всплескивают на подозрительно круглых 40мс (или 200мс на некоторых стеках). Фраза Марка Брукера — «Это всегда TCP_NODELAY. Каждый чёртов раз.» — фольклор не зря. Правка — одна опция сокета; диагноз — вот что трудно, потому что эти 40мс не платит ничей CPU, и они проявляются лишь как стояние по настенным часам.
Граница Парето латентность-пропускная способность
Сними слой-специфичные детали — и каждая пакетирующая система описывает одну кривую. На одной оси: окно пакета (время или размер). На двух других: пропускная способность и латентность на элемент, которые движутся в противоположные стороны.
| Рабочая точка | Окно | Латентность на элемент | Пропускная способность |
|---|---|---|---|
| Без пакетирования | окно = 0 | Минимум (шлём сразу) | Упёрта в фиксированную стоимость на операцию |
| Рабочая точка SLO | наибольшее окно с p99 < SLO | У потолка SLO | Почти максимум под этим потолком |
| Бесконечное пакетирование | окно = latency = ∞ | Неограниченно (первый элемент не сбрасывается) | Максимум теоретический |
Старший рабочий процесс — не «выбери число», а: сначала определи потолок латентности SLO (скажем, p99 < 50ms), затем найди наибольшее окно пакета, которое всё ещё умещается под ним, потому что это окно даёт максимум пропускной способности, который можно купить, не ломая контракт. Статические системы крутят эту ручку один раз и живут с этим. Адаптивные системы отслеживают кривую в рантайме: при лёгкой нагрузке сжимают окно к нулю (важна латентность, и пакетировать всё равно нечего); при тяжёлой — дают пакетам наполниться (важна пропускная способность, а элементы прибывают так быстро, что ожидание дёшево). Kafka 4.0 тихо закодировала эту мудрость: дефолт продюсера linger.ms сдвинулся с 0 на 5ms, потому что выигрыш в эффективности от более полных пакетов обычно окупает 5мс ожидания — нередко давая меньшую сквозную латентность, а не большую, за счёт сокращения накладных расходов на запрос.
Coalescing пакетов и deduplication запросов
Есть более острый вариант для нагрузок кеш/поиск, где многим вызывающим нужен один и тот же результат, а не просто пропускная способность. Когда конкурентные запросы промахиваются мимо кеша по одному ключу, их можно схлопнуть в единственную загрузку в полёте вместо N дублирующих. Работник А промахивается по ключу K и стартует запрос к БД; работник Б (и В, и Г…) тоже промахиваются по K, видят, что запрос А уже в полёте, и прицепляются к нему вместо собственного. Один результат раздаётся всем.
Это лекарство от cache stampede / thundering herd: горячий ключ протух, 100 запросов прибывают в одну миллисекунду, и без coalescing все 100 долбят БД разом — нередко достаточно, чтобы её уронить ровно когда трафик на пике. С coalescing эти 100 промахов становятся 1 запросом и 99 «попутчиками». Реализации повсюду под разными именами: golang.org/x/sync/singleflight в Go, AsyncLoadingCache Caffeine в Java, request collapsing в Varnish и большинстве CDN. DataLoader из GraphQL идёт ещё на шаг дальше, комбинируя coalescing с пакетной загрузкой по окну: каждый уникальный ключ, запрошенный в один tick, дедуплицируется и уникальные ключи связываются в один пакетный backend-запрос, что заодно — как DataLoader убивает проблему N+1 запросов.
Почему это работает
Coalescing и Нагле выглядят по-разному, но у них общий хребет. Нагле сливает записи во времени на одном соединении, амортизируя стоимость заголовка. Singleflight сливает чтения между вызывающими по одному ключу, амортизируя backend-запрос. Оба отвечают на один вопрос — «несколько маленьких вещей хотят одну дорогую операцию; может ли один поход обслужить всех?» — просто по разным измерениям (время против идентичности).
Почему важна эта родословная
Весь смысл видеть Нагле, Kafka и io_uring одной семьёй в том, что метод настройки переносится. Каждое — лишь другая фиксированная стоимость, обёрнутая в тот же рычаг, поэтому диагностический вопрос всякий раз одинаков.
| Эпоха | Система | Амортизируемая фиксированная стоимость | Ручка окна |
|---|---|---|---|
| 1984 | Nagle / RFC 896 | 40-байтовый TCP/IP-заголовок на сегмент | ACK по данным в полёте или полный MSS |
| 2011 | Kafka producer linger.ms | сетевой round-trip + запрос к брокеру | linger.ms + batch.size |
| 2019 | io_uring (Linux 5.1) | syscall + переключение контекста в ядро | SQE в очереди до одного io_uring_enter |
io_uring — чистейшее современное эхо: вместо одного syscall на каждый I/O вы заполняете кольцо записей очереди отправки (SQE) в разделяемой памяти и отправляете весь пакет одним io_uring_enter — амортизируя пересечение границы ядра на множество операций, ровно как Нагле амортизировал заголовок на множество нажатий. Тот же рычаг, новая фиксированная стоимость. Когда видишь паттерн, работа всегда одна и та же в три шага: измерь фиксированную стоимость против переменной, подтверди, что фиксированная действительно доминирует, затем подбери окно к наибольшему значению, какое позволяет твой SLO по латентности.
Сервис запрос/ответ поверх TCP загадочно упирается в ~25 транзакций/сек, а латентности сгруппированы ровно на 40мс. Какая причина наиболее вероятна?
Горячий ключ кеша протух, и 100 конкурентных запросов промахиваются по нему в одну миллисекунду. Что делает coalescing запросов (singleflight)?
Упорядочьте старший рабочий процесс настройки любого окна пакетирования:
- 1 Измерь фиксированную стоимость на операцию против переменной стоимости данных
- 2 Подтверди, что фиксированная стоимость действительно доминирует (иначе пакетирование даст мало)
- 3 Сначала определи потолок латентности SLO (например, p99 ниже 50мс)
- 4 Найди наибольшее окно пакета, которое всё ещё умещается под этим потолком
- 5 Реши: статика или адаптив — фиксированная ручка либо сжатие/рост окна с нагрузкой
Низконагруженный внутренний RPC-сервис делает маленькие вызовы запрос/ответ и упирается в фиксированный пол латентности ~40мс на вызов. Выберите правку, которую защитит сеньор.
- 01Какую проблему решил алгоритм Нагле, каков механизм и как возникает классический дедлок?
- 02Объясните родословную Nagle → Kafka linger.ms → io_uring как один паттерн и рабочий процесс настройки окна.
Пакетирование — один рычаг, применённый сквозь четыре десятилетия: когда фиксированная стоимость на операцию (заголовок TCP, round-trip, syscall) доминирует над переменной стоимостью данных, группируй операции, чтобы платить её один раз. Алгоритм Нагле (1984) сливал мелкие TCP-записи до ACK или полного MSS, а его знаменитый дедлок с delayed ACK прибивает латентность к 40мс, пока TCP_NODELAY его не отключит. Kafka linger.ms и пакетная отправка SQE в io_uring — тот же приём на более высоких слоях, поэтому рабочий процесс настройки не меняется: измерь фиксированную против переменной стоимости, определи потолок латентности SLO, затем возьми наибольшее окно под ним — статически или адаптивно. Coalescing запросов (singleflight, Caffeine, DataLoader) применяет идею между вызывающими, а не во времени, схлопывая давку одинаковых промахов кеша в единственную backend-загрузку.
встречается в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