Базы данных
Акт 7 в глубину: шардинг, co-location и семиуровневый каскад трейдоффов
Citus развёрнут. Shard key — tenant_id. Большинство tenants быстры. Tenant Acme — 40% всех запросов — насыщает свой шард, пока пять других простаивают. Кластер фактически одношардовый для Acme. Сбой hot shard был не вызван шардингом; он был спроектирован в день выбора shard key.
Акт 7 — Три независимых решения шардинга
Шардинг в год 3 включает три независимых решения:
Решение 1: Shard key. Shard key определяет распределение данных и локальность запросов. Для B2B multi-tenant SaaS tenant_id — естественный выбор: все запросы для одного tenant остаются на одном шарде. Для B2C — user_id. Для time-series — time bucket. Ключ должен иметь высокую кардинальность (для равномерного распределения нагрузки), равномерные паттерны доступа (чтобы ни один шард не был горячим) и быть дешёвым в вычислении (чтобы routing overhead был минимальным).
Решение 2: Метод распределения.
- Hash: строки хешируются на шарды — равномерное распределение, range queries веером расходятся по всем шардам.
- Range: строки в диапазоне ключей идут на конкретный шард — range queries остаются локальными, но горячий диапазон создаёт горячий шард.
- List: ручное назначение — гибко, высокие операционные издержки.
- Directory: lookup table маппит значения ключей на шарды — максимальная гибкость, максимальные операционные издержки.
Решение 3: Co-location. Таблицы, участвующие в join-ах, должны быть размещены на одном shard key, чтобы cross-shard join-ы оставались локальными. В Citus: users, orgs, events, audit_log — все шардированные по tenant_id — co-located. Join между users и events для tenant 42 попадает на один шард и возвращается с локальной латентностью. Без co-location тот же join веером идёт по всем шардам, собирает результаты на coordinator и мёрджит.
Federation tax
После шардирования запросы, охватывающие несколько шардов, должны быть federated: отправить запрос на каждый шард, собрать результаты и смёрджить на стороне приложения или на Citus coordinator.
Наивный outer join через два шарда становится двумя inner queries (по одному на шард) и client-side join. GROUP BY, затрагивающий шарды, становится GROUP BY на каждом шарде, затем второй GROUP BY по агрегатам. Каждый cross-shard запрос имеет latency = max(latency по шарду) + merge time; хвостовая латентность доминирует.
Citus обрабатывает federation с distributed query planning, но только для co-located таблиц (с одинаковым shard key). Reference tables (реплицированные на каждый worker-узел) бесплатны — маленькая dimension table типа countries или currencies может быть реплицирована один раз и join-иться локально везде.
Исправление hot shard
Когда Acme даёт 40% всех запросов на tenant_id-шардированном кластере:
- Вариант A — логическое разбиение: Направить запросы Acme на выделенный набор логических шардов. Tenant-aware router мультиплексирует Acme по shard ID, маппящимся на выделенный физический worker. Остальные tenants делят оставшиеся workers.
- Вариант B — физический кластер: Переместить Acme на свой собственный Postgres кластер. Запросы к Acme маршрутизируются туда; общий кластер обрабатывает все остальные tenants.
Оба варианта требуют онлайн-решардинга (citus_rebalance_table_shards), использующего логическую репликацию для суб-секундных пауз записи на шард. Планируй окно — догоняющая репликация добавляет нагрузку.
Семиуровневый каскад трейдоффов
Каждый акт открывает следующий, но только если предыдущий в порядке:
- Пропусти Акт 1 (схема) → Акты 2–7 воюют с неэффективными join-ами и изменяемыми натуральными ключами.
- Пропусти Акт 2 (индексы) → решения планирования Акта 3 принимаются вслепую; статистика бесполезна, если планировщику нечего выбирать.
- Пропусти Акт 3 (статистика) → vacuum Акта 4 единственный рычаг; таблицы раздуваются пока ты угадываешь мощность.
- Пропусти Акт 4 (bloat) → потоки пула Акта 5 видят table scans, замедленные мёртвыми tuples в heap pages.
- Пропусти Акт 5 (размер пула) → миграции Акта 6 задыхаются от connection storms; backlog handshakes соединений мешает миграции получить блокировки вовремя.
- Пропусти Акт 6 (lock safety) → шардинг Акта 7 невозможен без downtime; каждый shard rebalance требует эксклюзивной блокировки если миграции не были разработаны для онлайн-работы.
- Пропусти Акт 7 (когда нужен) → один tenant доминирует в кластере, и остальные ждут в очереди.
Порядок — ограничение, наложенное физикой и внутренностями Postgres, а не рекомендация.
- Single-node ceiling, write-heavy OLTP (железо 2026)
- 10–50K writes/с sustained
- Полный скан таблицы 1B строк на SSD
- 5–10 мин
- Онлайн-перемещение шарда, пауза записи (Citus 11.1+)
- менее 1 секунды на шард
- Schema-based sharding: практический лимит tenants на кластер
- 1000–3000 tenants
- Проект изменения shard key
- месяцы (dual-write + backfill + cutover)
Почему это работает
Schema-based multi-tenancy («одна схема на tenant») хорошо работает при малом числе tenants: чистая изоляция, простое резервное копирование, простой экспорт данных. Но catalog-таблицы Postgres (pg_class, pg_attribute, pg_constraint) растут линейно с числом схем × таблиц на схему. При 10000 схем × 50 таблиц в pg_class полмиллиона строк; время обхода планировщика в запросах, затрагивающих несколько схем, вырастает до секунд. Практический потолок — 1000–3000 tenants на кластер при schema-based sharding. После этого row-level multi-tenancy (колонка tenant_id в каждой таблице) с Citus shard distribution масштабируется дальше.
Кластер Citus шардирован по tenant_id. Запрос join-ит users и events для одного tenant. Заплатит ли этот запрос federation tax?
Команда пропустила Акт 6 (паттерны безопасных миграций). Теперь нужно добавить колонку в шардированную таблицу на 1B строк. Каково последствие?
Почему изменение shard key после запуска описывается как 'одна из самых дорогих операций в базах данных'?
- 01Назови три независимых решения шардинга и объясни почему shard key наиболее значим.
- 02Что такое federation tax и когда co-location его устраняет?
- 03Проследи каскад трейдоффов: выбери Акты 1 и 6, опиши как их пропуск делает Акт 7 дороже.
Акт 7 включает три решения: shard key (в основном необратимый — определяет распределение данных и локальность join-ов), метод распределения (hash для равномерного распределения, range для локальности range-запросов, list/directory для ручного управления) и co-location (таблицы, join-ящиеся вместе, должны разделять shard key, чтобы избежать federation tax). Failure mode hot shard — один tenant насыщает шард — исправляется логическим разбиением tenant или физической изоляцией кластера, оба через онлайн-решардинг. Каскад трейдоффов доказывает: акты компонуются — пропусти Акт 1 и выбор shard key отравлен неверной схемой; пропусти Акт 6 и каждая операция шарда — downtime event. Прагматичный дефолт 2026 для Postgres-шопа, масштабирующегося за один узел: declarative partitioning сначала, Citus когда пределы одного узла измерены и доказаны, Aurora DSQL или Spanner только когда глобальные writes — реальное product requirement.
встречается в263
- Почему 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
- HTTP: язык запрос-ответ в вебеjunior
- HTTP/2: потоки, фреймы и HPACKmiddle
- HTTP/3 и QUIC: изоляция потерь на уровне потокаmiddle
- HTTP/3 в продакшне: внутренности QUIC, fallback и наблюдаемостьsenior
- HTTP дизайн: приоритеты, WebTransport и семантическая корректностьsenior
- 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