Job 2026 md

title: Конспект материала 33 — jepsen.io (Distributed Systems Safety Research, Kyle Kingsbury) source: https://jepsen.io/ (+ классика aphyr.com — оригинальные посты 2013–2015) author: Kyle Kingsbury (aphyr), Jepsen LLC конспект: подготовлено 2026-09-18, TASK-45.37; прочитаны главная страница, индекс анализов (60+ систем, 2013–2026), справочник консистентности jepsen.io/consistency (модели / феномены / зависимости) и 15 анализов полностью: классика aphyr.com 2013–2015 (Redis, Kafka, Cassandra, ZooKeeper, RabbitMQ, Elasticsearch, MariaDB Galera, «Strong consistency models») + современные jepsen.io (etcd 3.4.3, PostgreSQL 12.3, MongoDB 4.2.6, MySQL 8.0.34, Redis-Raft, Redpanda, NATS JetStream, TigerBeetle) статус: P3-источник «после секций», но единственный, где консистентность KB §7 показана «от противного» — на реальных поломках знаменитых БД. Не читать подряд: достаточно таблицы кейсов (§4) + трёх фактов (§6), 20–30 минут. База знаний по консистентности (DDIA гл. 7–9, Alex Xu гл. 6) уже законспектирована — Jepsen даёт к ней живые иллюстрации и числа.


Материал 33: jepsen.io — проверка консистентности реальных БД «сломом»

Jepsen — многолетняя (с 2013) исследовательская программа Kyle Kingsbury (aphyr): реальные кластеры знаменитых БД, очередей и координационных сервисов нагружают, ломают (партиции, крэши, сдвиг часов) и сверяют фактическую консистентность с той, что заявлена в документации. C 2013 года проанализировано 60+ систем; почти в каждом анализе найдены «replica divergence, data loss, stale reads, read skew, lock conflicts». Ценность для нашей секции не в самой теории (вся она уже в KB §5–§7 и DDIA-конспектах), а в числах и формулировках реальных багов, которые превращают абстрактные «аномалии» в конкретные аргументы: «асинхронная репликация + failover — это окно потери подтверждённых записей» перестаёт быть теоретизированием, когда рядом стоят «Redis потерял 56% ack-записей» или «NATS писал на диск раз в 2 минуты».

1. Как устроен Jepsen-тест (методика)

Jepsen — это и открытая библиотека, и серия бесплатных детальных анализов (плюс платные анализы/тренинги по этическому кодексу: кто финансирует — всегда раскрыто, вендор обычно отвечает companion-постом).

Элемент Что это Для интервью
Кластер Обычно 5 реальных нод (LXC/EC2), не «локальный тренажёр» «проверяли на реальном кластере из 5 нод» звучит весомо
Нагрузка Регистры, наборы, bank (переводы, баланс не уходит в минус), list-append, counters, транзакции банковский тест — классика SD
Nemesis Инъекция отказов: сетевые партиции (random-halves, partition-majority), паузы процесса (GC-stop), крэши, clock skew, membership changes тот же словарь, что и в хаос-инженерии: partition, pause, crash, skew
Чекеры Knossos — линейзуемость (алгоритм семейства WGL/Lowe); Elle (с 2020, совместно с Peter Alvaro) — транзакции до strict serializability за линейное время с локализованными контрпримерами «linearizability checker / transactional checker» — словарный минимум
Метод проверки истории операций + зависимостей (write-write, write-read, read-write, realtime) → поиск циклов в графе цикл в графе зависимостей = нарушение сериализуемости
Максима «Jepsen доказывает наличие багов, но не их отсутствие» (experimental approach to safety verification) честная оговорка, если спросят про пределы тестирования

2. Карта консистентности (справочник jepsen.io/consistency)

Справочник — годный одностраничник для ответа «а какая консистентность, собственно, бывает» и для ссылки на модель выше/ниже по силе.

  • Две семьи моделей (multi-object транзакции и single-object операции), которые объединяет вершина — strict serializable (serializable + linearizable одновременно).
  • Multi-object: strict serializable → serializable → repeatable read / snapshot isolation → monotonic atomic view → cursor stability → read committed → read uncommitted.
  • Single-object: strict serializable → linearizable → sequential → causal → writes-follow-reads и PRAM → monotonic reads / monotonic writes / read-your-writes.
  • Связь с доступностью (прямо на картинке): всё от cursor stability / snapshot isolation / sequential и сильнее не может быть полностью доступно в асинхронной сети; от read-your-writes и сильнее — максимум sticky-available; слабее — допускает полную доступность. Это готовая формулировка к разговору про CAP/PACELC (KB §7): «строгая консистентность = платим доступностью/латентностью, потому и существуют каскады ослаблений».

3. Типы аномалий (справочник jepsen.io/consistency/phenomena)

«Феномен — это то, что БД делает, а кто-то, где-то посчитал плохой идеей; легальность зависит от модели» (например, G1a легален под Read Uncommitted и нелегален под Read Committed).

Феномен a.k.a. Смысл Запрещает
G0 Write cycle цикл записи даже RU
G1a Aborted Read прочитали запись откатившейся транзакции RU, RC
G1b Intermediate Read прочитали промежуточное состояние RU, RC
G1c Cyclic Information Flow циклический поток информации между транзакциями RU, RC
G-single Single Anti-dependency Cycle «если бы я знал твоё значение…» по одному объекту RR / SI
G2-item / G2 Anti-dependency Cycle ссора на чтение-запись; G2-item — «предтеча» полного G2 serializable
P0 Dirty Write запись поверх незакоммиченного RU+, все уровни SQL
P1 (A1) Dirty Read чтение незакоммиченного RC+
P2 (A2) Fuzzy/Non-Repeatable Read чтение, потом чужое изменение, потом повторное чтение с другим результатом RR+
P3 (A3) Phantom предикат нестабилен: между двумя чтениями по WHERE добавилась строка serializable
P4 Lost Update потерянное обновление RR/SI (частично)
A5A / A5B Read Skew / Write Skew перекосы чтения и записи — именно write skew остаётся под snapshot isolation serializable
Temporal Long Fork две версии истории, долго не схлопываются causal+
Stale Read / Lost Write устаревшее чтение / потерянная запись linearizable

Практический вывод для KB §7: write skew (A5B) — аномалия, которую snapshot isolation принципиально не закрывает (оттуда SSI в Postgres), а stale read / lost write — база для разговора об асинхронной репликации и failover.

4. Известные кейсы (выжимки с числами)

Система Год Заявленное Найдено Jepsen Ключевое число
Redis + Sentinel 2013 «CP, консистентность» при партиции — split-brain (два мастера), старый мастер продолжал ack'ить; при лечении — потеря подтверждённых записей 56% (1126 из 1998) подтверждённых записей потеряно
Kafka (replication) 2013 «f−1 узлов терпим» ISR может схлопнуться до лидера; при потере им ZK-лида — new лидер с пустым логом; потеря ack-записей потеря произвольных ack-записей
Cassandra 2013 консистентность настраивается LWW (последняя запись побеждает) без векторных часов; даже с QUORUM + идеальными часами + идеальным локером 28% (285 из 1009) подтверждённых записей потеряно; счётчики уплывают до 50%
Riak (упомянут в Cassandra) 2013 чистый LWW без порядка дроп 30–70% записей даже на R=W=ALL
ZooKeeper 2013 linearizable записи линейзуемы (кворум + fsync перед ack); но sync+read — не линейзуем (дополнение 2019) «география» CP-координатор
etcd 0.4 → 3.4.3 2014, 2020 coordination-примитив 0.4: stale reads по умолчанию; 3.4.3: KV-операции строго сериализуемы, но локи фундаментально небезопасны (два процесса держат лок одновременно, даже в здоровом кластере) «locks aren’t real»; решение — fencing tokens
RabbitMQ 2014 очередь queue-мьютекс на отрицательном ack ломается: невозможно отличить упавший процесс от «задумавшегося» — лок выдают второму владельцу; докум. сама предупреждает «партиции инвалидируют гарантии» лок держат два процесса
Elasticsearch 2014 при разделении кластера устаревшие копии шарды, исключённые из кластера, продолжали принимать записи; при воссоединении старые документы затирали свежие расхождение и перезапись данных
MariaDB Galera 2015 snapshot isolation транзакции видят промежуточно закоммиченное состояние; заявленный SI «не совсем корректен» частично закоммиченные чтения
PostgreSQL 12.3 2020 serializable / RR «serializable» нарушал G2-item при нормальной работе (баг конфликт-детекции, патч в 13.08); RR = snapshot isolation (аномалия G2-item легальна там); RC чист повторяющиеся нарушения G2-item
MongoDB 4.2.6 2020 «среди сильнейших гарантий… полные ACID» даже на максимуме read/write concern не держит snapshot isolation; read skew, G1c, дубли записей, internal consistency; слабые дефолты снимают уровень безопасности тихо даунгрейд гарантий «молча»
MySQL 8.0.34 2023 RR RR нарушает формальный RR: G2-item, G-single, lost update, internal consistency, monotonic atomic view (видишь часть эффектов транзакции, потом пропадает остальное); RDS MySQL-кластеры нарушают сериализуемость рутинно «RR несколько сильнее RC, но слабее формального RR»
Redis-Raft 2020 «эффективно CP» (Raft) 21 баг в dev-сборках: бесконечные циклы на любой записи, полная потеря данных при любом failover, stale reads, split-brain, aborted reads; в последующих сборках всё кроме одного закрыто полная потеря на failover
Redpanda 2022 Kafka-совместимость 3 liveness + 7 safety: крэши, aborted reads, несогласованные offset'ы, circular information flow, lost/stale messages 10 проблем, 7 закрыто
NATS JetStream 2025 at-least-once потеря коммитов при повреждении файлов на меньшинстве нод; fsync раз в 2 минуты вместо fsync перед ack → потеря подтверждённых записей и стойкий split-brain при отключении питания ack-записи теряются при питании
TigerBeetle 2025 Strong Serializability контрпример: strong serializability выдержана (0.16.30) — ни одной safety-аномалии на партициях; найдены лишь 7 крэшей, бесконечные ретраи по дизайну, «отсутствие» обработки полной потери ноды «сильные гарантии возможны»

4.1 Redis + Sentinel (2013) — эталон «асинхронная репликация + failover = потеря»

Самая цитируемая цифра Jepsen. Redis на одной ноде — линейзуемое CP-хранилище; Sentinel добавляет failover: Sentineli (внешний процесс) детектят отказ и назначают нового мастера. Jepsen партиционировал кластер: старый мастер остался в меньшинстве и продолжал принимать и подтверждать записи (асинхронная репликация не требует кворума на запись), Sentineli в большинстве выбрали нового мастера. При заживлении сети старый мастер демотируется (это исправили лишь в 2.6.13 — до этого оба мастера жили вечно), и накопленные им ack-записи молча выбрасываются: 1998 ack из 2000, выжило 872, потеряно 1126 = 56%. Плюс все клиенты — часть распределённой системы: «когда корректность зависит от того, с каким узлом клиент говорит в конкретный момент, клиенты упираются в распределённый консенсус — это чертовски сложно делать правильно».

Вывод под слоты (стыковка с KB §5/§7): у асинхронной primary→secondary репликации принципиально есть окно между подтверждением записи клиенту и её долговечным копированием; failover внутри этого окна теряет данные. «Синхронная» репликация/кворум на запись (W+R>N, ISR-ack) — то, что сужает окно; но кворумы не дают линейзуемости без read repair по правилам (см. Kleppmann, материал 32, и KB §7).

4.2 Kafka (2013) — «терпим f−1 отказов» ≠ CP

Kafka 0.8 заявил репликацию, терпящую f−1 отказов (сильнее классического n/2−1), потому что «majority-кворумы в LinkedIn ненадёжны». Механика: лидер шарда держит ISR (in-sync replicas); запись ack-ится, когда её подтвердил весь ISR. ISR может схлопнуться до одного лидера — тогда лидер ack'ит записи, персистентные только локально; если лидер теряет ZK-клейм, выбирается новый лидер, который может быть сколь угодно позади, — и «новый лидер + старый лидер» дают расходящиеся лог-истории; вариант «сохранить оба» нарушает линейный порядок, вариант «сбросить один» уничтожает подтверждённые данные. Достаточно двух согласованных отказов: «лидер изолирован → крэш/перезагрузка».

Вывод: «f−1 отказов» — это терпимость к отказу членов, а не потеря каузальности между лидерами; единственный способ не терять ack-данные — требовать ack от кворума реплик перед подтверждением (то, что Kafka позже сделал min.insync.replicas / acks=all).

4.3 Cassandra (2013) — LWW без векторных часов

Dynamo-стиль (кольцо, N реплик, hinted handoff, антиэнтропия), но вместо векторных часов — last-write-wins (ради 2 round-trips → 1). Jepsen: даже «идеальные» условия — синхронизированные часы, ConsistencyLevel.QUORUM/ALL, внешний идеальный локер, последовательные записи в одну ячейку — дают 28% потери подтверждённых записей (285 из 1009). LWW без строгого внешнего порядка не гарантирует ничего: «единственное условие, при котором запись не будет молча проигнорирована — неизменяемое значение». Векторные часы/CRDT устраняют потери (G-set в Riak — 100% выжили; CQL-append в Cassandra — те же 100%). Счётчики Cassandra — не PN-счётчик: при партиционировании уплывают до 50%.

Вывод: AP-системы требуют thinking в терминах order-free структур (CRDT/коммутативных слияний) — это готовый аргумент в блоке «почему AP и чем платим» (Dynamo, материал 28; KB §5/§7).

4.4 ZooKeeper и etcd (2013/2014/2020) — хорошо там, где узко, и ловушка локов

ZK: linearizable записи (кворум + fsync перед ack), best of small state. etcd 0.4 (2014): stale reads по умолчанию (любой узел читал локально, не проверяя, актуален ли лидер) — «redis-пойнт»: если реплика читает без quorum-флага, linearizability теряется. etcd 3.4.3 (2020): KV-операции и микротранзакции строго сериализуемы (проверено на партициях, паузах, крэшах, clock skew, membership changes), watches отдают каждое изменение в порядке — а вот локи: «locks aren’t real» — даже в здоровом кластере два процесса могут держать etcd-лок одновременно (плюс баг: не проверялась валидность lease после ожидания). Рекомендация Jepsen: локи для производительности (вероятностное ограничение конкурентности) — можно; для безопасности — нельзя: ресурс должен быть безопасен без лока (fencing tokens, например, revision из etcd).

Вывод: это прямая иллюстрация к KB §7 (fencing-токены, zxid/revision/epoch). Ответ «зачем fencing-токен» = «именно это Jepsen и показал на etcd/ZK: лок держат двое, дешевле сделать ресурс безопасным под повторяющиеся фенсинг-токены».

4.5 PostgreSQL 12.3 (2020) — имена isolation-уровней обманывают

Анализ на транзакционном чекере Elle (работа с UCSC, без оплаты): serializable в 12.3 нарушал G2-item при нормальной работе (баг в конфликт-детекции — параллельные update+insert путали, какая транзакция виновата; патч в августе); repeatable read — на самом деле snapshot isolation (подтверждено Kleppmann/Hermitage: G2-item легален там; сильный snapshot isolation при этом держался); read committed чист (ни G0, ни G1a/G1b). Урок про подход: Postgres-тесты isolationtester и Hermitage проверяли только «вручную доказанные» сценарии; Elle генерирует широкий класс транзакций и ловит то, что никто не додумался проверить — property-based тестирование вместо набора хрестоматийных кейсов.

Вывод: «на чём храните деньги/заказы» — уровень изоляции надо называть по феноменам, которые он реально запрещает, а не по имени ANSI-уровня.

4.6 MongoDB 4.2.6 (2020) — «+ACID» на бумаге

Документация: «среди сильнейших гарантий консистентности… полные ACID-транзакции». Jepsen: даже на максимальных read/write concern нарушается snapshot isolation; наблюдались read skew, G1c (циклический поток), дубли записей, внутренние рассинхроны; слабые дефолты тихо снижали уровень безопасности (транзакции могут терять записи и видеть dirty reads без единой ошибки), а snapshot read concern «включал» snapshot только в паре с write concern=majority — даже для read-only транзакций. May 2020: MongoDB заявила, что нашла баг в механизме ретраев транзакций (патч 4.2.8).

Вывод: «ACID-транзакции» — маркетинг; сильные гарантии требуют явных concern'ов на каждый запрос и проверки тестами (это сюжет и для интервью: «как вы выбираете БД под заказы» — по декларируемым и проверенным гарантиям).

4.7 MySQL 8.0.34 (2023) — повтор 2014-го, теперь с Elle

Повтор Kleppmann's 2014 Hermitage: RR в MySQL позволяет G2-item, G-single и lost update, плюс новые для Elle находки: нарушения internal consistency и monotonic atomic view (транзакция видит часть эффектов другой, потом «забывает» остальные). Вывод: «RR несколько сильнее Read Committed, но слабее формального Repeatable Read» (по Adya/Berenson). Бонус: AWS RDS MySQL-кластеры при репликации нарушают serializability «рутинно».

4.8 Redis-Raft (2020) — 21 баг в Raft-рендезинге Redis

Первый полный Jepsen-анализ, где стеки транзакций проверял новый чекер Elle: в dev-сборках Redis-Raft (1b3fbf6…e0123a9) найдено 21 issue: полная потеря данных при любом failover (новый лидер выходил с пустым состоянием), бесконечные циклы на любой записи при follower-proxy (нет re-entrancy check — команда прогонялась через Raft-лог вечно), stale reads даже в здоровом кластере (лидер не публиковал no-op при вступлении в должность), split-brain с потерей обновлений, спазматические NOLEADER. К концу цикла всё, кроме одного крэша, исправлено. Заодно честная карта репликации Redis: Sentinel «eventually consistent, merge = last failover wins», Cluster «окна потери ack-записей», Enterprise CRDT «LWW для строк — subject to lost updates» — никакой из трёх не гарантирует отсутствие потерь. WAIT не делает систему строго консистентной.

4.9 Современные очереди (Redpanda 2022, NATS 2025) и контрпример TigerBeetle (2025)

  • Redpanda (Raft внутри, Kafka-протокол): 3 liveness + 7 safety — крэши, aborted reads, несогласованные offset'ы, circular information flow, lost/stale messages; 7 закрыто к публикации.
  • NATS JetStream (2025): at-least-once по документации — а потеря коммитов при повреждении файлов на меньшинстве нод; отключение питания в связке с сетевыми задержками → потеря подтверждённых записей и стойкий split-brain. Причина названа прямо: fsync раз в 2 минуты, а не перед ack. Показательный случай разрыва «ack ≠ durable».
  • TigerBeetle (2025): редкий контрпример: strong serializability устояла на всех партициях/крэшах/дисковых повреждениях (вплоть до порчи файлов всех реплик) — при стоимости дизайна (двойная бухгалтерия, LSM, ручной операционный контракт). Найдено «всего» 7 крэшей, бесконечные ретраи по дизайну, отсутствие сценария полной потери ноды.

5. Системные уроки Jepsen (что обобщается поверх кейсов)

  1. Асинхронная репликация + failover = окно потери подтверждённых записей (Redis 56%, Kafka ISR→1, Mongo слабые дефолты, NATS fsync 2 мин, ES устаревшие шарды). Гарантия клиенту даётся только когда запись долговечна (кворум/ISR/fsync перед ack).
  2. Документация врёт сильнее, чем код. Почти каждый анализ начинается с цитаты из доков и заканчивается несовпадением: Redis «CP», Kafka «f−1», etcd «sequential — сильнейшая», MongoDB «full ACID», Cassandra «QUORUM = safety».
  3. Локи в лучшем случае — вероятностное ограничение конкурентности: etcd «locks aren't real», RabbitMQ-мьютекс. Безопасность даёт fencing-токен на защищаемом ресурсе — никогда локи как таковые (KB §7).
  4. Имена ANSI-уровней ≠ формальные определения: PG RR — это SI; MySQL RR слабее формального RR; PG «serializable» с поломанной конфликт-детекцией. Надо называть феномены.
  5. fsync — отдельный и последний рубеж: ack раньше fsync = потеря при питании (NATS). Мастер-репликация решает доступность, не долговечность.
  6. Свойство проверяется только ломкой: ручные сценарии (isolationtester, Hermitage) ловят известное; генеративные чекеры (Elle) находят новое. Отсюда — краеугольный камень для ответа про тестирование распределённых систем.

6. Три факта для интервью (и одна запасная)

  1. Redis + Sentinel (Jepsen, 2013): 56% подтверждённых записей потеряно при партиции — «при партиции старый мастер продолжал ack'ить в своём меньшинстве, Sentinel выбрал нового, и его записи выбросили; так работает любой failover поверх асинхронной репликации». Подача: в ответ «почему не Redis как БД/итог для ленты» или «как не терять данные» — «ack только после долговечности: кворум или запись в лог с fsync, плюс идемпотентность клиента».
  2. Postgres 12.3 и MySQL 8.0.34: имена isolation-уровней не равны формальным — «Jepsen показал: PG repeatable read — это snapshot isolation (write skew разрешён), а serializable нарушал G2-item; MySQL RR слабее формального RR». Подача: «на чём храните заказы» — «выбираем уровень по феноменам: serializable/SSI, если важны unique-констрейнты и счётчики; считаем SI недостаточным для денег».
  3. etcd-lоки небезопасны (Jepsen, 2020) — «KV-операции в etcd строго сериализуемы, но два процесса могут держать лок одновременно; поэтому безопасность строится на fencing-токенах (revision), а не на лочах». Прямо стыкуется с KB §7.
  4. (запасная, если спросят про очереди/брокеры) NATS JetStream (2025): «ack до fsync (раз в 2 минуты) = потеря подтверждённых сообщений при отключении питания; для очередей долговечность подтверждается только после устойчивой записи».

7. Как пользоваться под слоты

  • Режим: не читать подряд; 20–30 минут: таблица §4 + §6 (факты для интервью) + §5 (уроки). Детали 4.1–4.9 — по мере надобности, когда тема всплывёт в бриф.
  • Стыковка с базой: KB §7 (консистентность/консенсус — все модели и fencing-токены), KB §5 (репликация: кворумы, read repair), KB §10 (эксплуатация: fsync, долговечность), articles/09-consistency.md; число латентностей/сроков тут не нужно — Jepsen про гарантии, а не про размеры.
  • Соседи по теме: lsd-kleppmann-lectures.md (45.36, кворумы ABD «не линейзуемы», 2PC, полный Raft — теория, которую Jepsen опровергает/подтверждает на практике), lsd-awesome-distributed-systems.md (45.32, Dynamo AP-рамка — «почему AP»), lsd-polomodov-guide.md (45.20, в Q&A стоит «Jepsen-тесты — как проверять заявленные гарантии под нагрузкой/дрифтом часов»).
  • Аналогия для интервью «как вы тестируете распределёнку»: Jepsen-подход = property-based + отказы (partition/pause/crash/skew) + чекер, ищущий контрпример к заявленной модели; в лайт-версии это же делают Chaos Monkey/chaos engineering. Названный «Elle» или «Knossos» в нужном контексте — признак глубины, но без воды: «проверяем гарантии на слом, а не на сценарии из доков».

8. Прочитанные материалы (что именно легло в конспект)

  • jepsen.io: / (о проекте, методика, новости), /analyses (индекс 60+ систем с датами и версиями), /consistency + /consistency/models, /consistency/phenomena, /consistency/dependencies (справочник моделей/феноменов по Adya, ANSI/Berenson, Viotti & Vukolic).
  • Анализы (jepsen.io, полные тексты): Redis-Raft 1b3fbf6 (2020), PostgreSQL 12.3 (2020), etcd 3.4.3 (2020), MongoDB 4.2.6 (2020), MySQL 8.0.34 (2023), TigerBeetle 0.16.11–0.16.30 (2025), NATS JetStream 2.12.1 (2025), Redpanda 21.10.1 (2022).
  • Классика (aphyr.com): «Call Me Maybe» Redis (283), ZooKeeper (291), Kafka (293), Cassandra (294), Redis redux (307), «Strong consistency models» (313), RabbitMQ (315), etcd & Consul (316), Elasticsearch (317), MariaDB Galera (327), «How to stop data loss»-тема в Redis-redux; страницы частично обрезаются для curl — там, где текст обрывался, использованы цитаты, попавшие в выжимку, и абстракты.
  • Примечание: «анализа Redis 2018» на jepsen.io/aphyr нет (в индексе Redis — только 2013/2013-WAIT и Redis-Raft 2020); сюжет «Redis теряет подтверждённые записи» закрыт проверенными фактами 2013 + Redis-Raft 2020 (цитаты доков Sentinel/Cluster/Enterprise).