CockroachDB / NewSQL — distributed SQL database. Postgres wire-compatible, Raft per range, HLC instead of TrueTime, multi-region survival/locality, range splits/rebalancing. Inspired by Spanner (PC/EC) but no atomic clocks needed. 4 scenarios: cross-range distributed transaction (HLC + Raft + 2PC), range rebalance after node add (auto-split, auto-rebalance), multi-region locality via REGIONAL BY ROW (GDPR data residency), region failure (REGION survival mode). 2 ADRs: CockroachDB vs Postgres+Citus vs Spanner choice; HLC vs TrueTime trade-off.
Single-region Postgres упирается в стену по трём осям одновременно: write-throughput на single primary (десятки тысяч txn/s — и всё), failover между регионами (10-30 минут с риском split-brain), и data residency (GDPR/CCPA требуют чтобы EU users физически жили в EU). Стандартные ответы — read-replicas (не помогают writes), Postgres+Citus (sharding есть, но ACID только per-shard и multi-region writes требуют app-level routing), full migration на NoSQL (теряем SQL, теряем ACID, переписываем приложение). NewSQL — это попытка получить distributed scale как у NoSQL + ACID и SQL как у RDBMS + strong consistency, не выбирая между ними.
CockroachDB конкретно делает это так: distributed KV (multi-Raft per range) + SQL parser/optimizer на верхнем уровне + 2PC поверх для cross-range транзакций + HLC вместо atomic clocks. Postgres wire-compatible — драйверы и ORM работают без изменений (psycopg, pg, JDBC). Multi-region first-class — REGIONAL BY ROW пинит каждую строку к home региону через служебную колонку crdb_region. Survival modes — REGION survival выдерживает потерю целого региона без manual failover. Open-source (BSL), self-hosted или managed (Cockroach Cloud).
Цена этого: каждая cross-range транзакция = consensus round-trip. Intra-region ~5ms, cross-region 50-150ms. Optimizer слабее Postgres на сложных аналитических запросах. Хранение 3× (replication factor 3 по умолчанию). Для single-DC small workload это overkill и медленнее обычного Postgres. NewSQL — это решение конкретной проблемы (multi-region ACID at scale), а не drop-in замена Postgres "потому что современнее".
NewSQL = распределённый KV (Raft per range) + SQL parser/optimizer + 2PC поверх; ACID и SQL гарантированы, но каждая cross-range транзакция = consensus round-trip (десятки ms), поэтому это не drop-in замена Postgres для latency-sensitive OLTP.
Ключевые объекты, которые надо держать в голове:
(physical_part_ms, logical_counter). Update rule: pt = max(local_phys, recv.pt); если pt не изменился — l++, иначе l = 0. Заменяет TrueTime: вместо commit-wait на uncertainty interval, CockroachDB делает uncertainty restart — если read попадает в [read_ts, read_ts + max_offset] существующего intent, транзакция перезапускается с обновлённым ts.Distributed transaction = write intents на ranges (provisional values + tx_id), Raft replication intra-range, 2PC для commit (с 19.2 — Parallel Commits: staged tx record + intents async, на чтении intent проверяется tx status), async cleanup intents → committed values.
Три региона по 3 ноды (типичный 3-region REGION-survival setup):
app-us (Django+psycopg), app-eu (Rails+pg) — обычные Postgres-клиенты.Edges делятся на три класса: pgwire (клиент → gateway-нода), Raft intra-region (тяжёлый поток репликации), gossip + cross-region Raft (для ranges с replicas в нескольких регионах — это и есть survival).
Range R3 нарисован специально cross-region (E2 leaseholder, replicas W3 и EU2) чтобы показать что survival = consensus quorum через WAN. Range R-eu — наоборот, полностью в eu-west (REGIONAL BY ROW для EU users).
ADR-001 (на ноде E1) — выбор CockroachDB против Citus и Spanner. ADR-002 — почему HLC + uncertainty restart, а не TrueTime commit-wait (commodity hardware vs Google's atomic clocks).
1. Cross-range distributed txn. BEGIN; UPDATE acct_A; UPDATE acct_B; COMMIT где acct_A на R1 (us-east) и acct_B на R2 (us-west). Coordinator E1 присваивает HLC tx_ts, пишет intent на R1 (Raft replicate intra-east), форвардит на W1 для R2, W1 forward'ит свой HLC до tx_ts, пишет intent на R2 (Raft replicate intra-west), на COMMIT — Parallel Commits с PREPARE vote от обоих ranges, async cleanup. Latency ~70ms (доминирует WAN consensus). Intra-region аналогичная транзакция — ~5ms.
2. Range rebalance после добавления ноды. R1 растёт под нагрузкой sequential writes до 480MB, при 512MB или 100K writes/s — auto-split на R1a (keys < K) и R1b (keys >= K). Операторы добавляют W2 в кластер — gossip пропагирует capacity. Allocator на каждой ноде сравнивает scores (загрузка E1=85%, W2=10%) и принимает decision: replica R1b с E3 → W2. Raft snapshot transfer (~30s для 256MB), catch-up log entries, joint consensus для membership change (add W2, remove E3), GC данных на E3.
3. Multi-region locality (REGIONAL BY ROW). EU user (id=42, crdb_region='eu-west') делает SELECT через app-eu → gateway EU1 → local lookup (range R-eu, leaseholder EU1) → local read без consensus → ~3ms ответ внутри EU. Тот же запрос от app-us через east-1 — east-1 видит crdb_region=eu-west и форвардит на EU1 — корректно, но медленно (~80ms WAN). Урок: routing должен быть на app-уровне тоже. WRITE на EU user — intent на R-eu, Raft replicate EU2/EU3 — local quorum, ~5ms, данные не покидают EU (GDPR happy).
4. REGION failure (survival mode). us-east полностью отваливается (DC outage / network partition). Range R3 имел leaseholder E2, replicas W3 + EU2 — кворум 2/3 выживает. Heartbeat timeout → Raft election на R3 → W3 кандидат, EU2 голосует за → W3 новый leader → W3 acquires lease → writes resume. In-flight транзакция на E1 abort, клиент retries через us-west gateway — следующий write идёт на W3 → Raft replicate на EU2 → majority 2/2 survivors → ack. Никакого manual failover, никакой потери данных. При возвращении us-east — re-replication и catch-up Raft log.
ADR-001 — CockroachDB vs Postgres+Citus vs Spanner. SaaS платформа с tenants в US/EU/APAC. Citus rejected: ACID только per-shard, multi-region writes требуют app coordination, manual rebalance. Spanner rejected: GCP-only (vendor lock-in), нет on-prem option, enterprise pricing 2-3× для small workloads, требует atomic clocks. CockroachDB выбран потому что: Postgres wire compatibility (миграция ORM без переписывания), multi-Raft даёт авто-sharding без app-level routing, REGIONAL BY ROW = GDPR без app changes, REGION survival выдерживает потерю DC, open-source (self-hosted option), HLC не требует special hardware. Trade-offs accepted: cross-range txns дороже (consensus RTT), optimizer слабее Postgres для аналитики (DWH = ClickHouse отдельно), ops cost 3× single Postgres.
ADR-002 — HLC + uncertainty restart, а не TrueTime commit-wait. Spanner использует TrueTime (GPS+atomic clocks в каждом DC), TT.now() → [earliest, latest] interval с uncertainty bound ε ~7ms, commit-wait блокирует write на ε для external consistency. Работает потому что Google имеет atomic clocks. CockroachDB на commodity hardware: NTP даёт 100-250ms точность в лучшем случае → commit-wait убивает latency (250ms на каждую транзакцию). Решение: HLC (physical_ms, logical_uint16), uncertainty interval [read_ts, read_ts + max_offset] (default 500ms). Если read попадает в uncertainty zone существующего intent — transaction restarts с обновлённым ts. Single-key linearizability без atomic clocks. Cost: extra restart latency для overlapping reads (~5-10ms). NOT external consistency между независимыми transactions across regions — для большинства OLTP acceptable. max_offset configurable; нода suicide при clock drift > max_offset (защита от lost-write).
ADR-003 (implicit) — REGION survival vs ZONE survival. REGION survival = 3 replicas, по одной в каждом регионе → выдерживает потерю целого региона, но cross-region quorum на каждый write (~70ms WAN). ZONE survival = replicas в одной region, разные AZ → выдерживает AZ failure, local quorum (~5ms), но потеря региона = downtime. Выбор по SLA и cost.
SELECT FOR UPDATE в multi-region дорог (cross-region lock). Используйте FOR SHARE где можно или дизайните без блокировок (CRDT-like подходы).