PostgreSQL проти CockroachDB: висока доступність без драми — або з новими видами болю

Було корисно?

«Високу доступність» не купують за слайдами. Її запускають. О о 2:00 ночі. Коли відключається стояк, маршрутизатор перезавантажується,
розгортання іде не так, і ваш телефон для чергування починає робити кардіотренування.

PostgreSQL може бути нудно надійним у продакшені — якщо ви погоджуєтеся, що HA це здебільшого система, яку ви будуєте навколо нього.
CockroachDB пропонує HA як первинну функцію — якщо ви погоджуєтеся, що розподілені системи не знімають болю, вони його переміщують.

Справжнє питання: який тип відмов ви хочете?

Найпоширеніша помилка при виборі СУБД для HA — думати, що питання: «Яка СУБД більш доступна?»
Доступність — це не брендова риса. Це властивість «end-to-end», яка включає ваші клієнтські драйвери, DNS, балансувальники навантаження,
звички міграцій схем, дисципліну бекапів, телеметрію і тип людей, яких ви наймаєте.

Насправді питання таке: яку відмову ви готові діагностувати під стресом?

  • У випадку PostgreSQL історія HA зазвичай передбачає потокову реплікацію, менеджер відмов і чіткий поділ
    між «єдиним записуючим вузлом» і «репліками, які можуть відставати». Ваш біль зазвичай пов’язаний з оркестрацією фейловера,
    маршрутизацією читань/записів, відставанням реплікації та операційними гострими краями.
  • У випадку CockroachDB історія HA вбудована: консенсусна реплікація, автоматичне переміщення даних і SQL, що виглядає знайомо.
    Ваш біль переноситься в область розподіленого дебагу: конкуренція, розподіли діапазонів, хотспоти, підсилення затримки між регіонами
    та вивчення внутрішньої моделі станів кластера.

Оберіть біль, який ви готові оплачувати. Не той, що краще виглядає на діаграмі.

Коротке позиціонування: коли кожна СУБД — потрібний молоток

Вибирайте PostgreSQL HA, коли можна централізувати запис і контролювати складність

PostgreSQL — це робоча конячка. Ви отримуєте зрілу семантику SQL, величезну екосистему, передбачувані характеристики продуктивності
і модель відмов, яку більшість SRE-команд може осмислити. На практиці HA PostgreSQL зазвичай виглядає так:
один primary, одна або більше реплік, плюс оркестратор (Patroni, repmgr, Pacemaker/Corosync),
і шар маршрутизації (HAProxy, PgBouncer, хмарний лоадбалансер або логіка на рівні застосунку).

PostgreSQL HA — правильний вибір, коли:

  • Ваш записуваний трафік інтенсивний і чутливий до затримки, і ви можете тримати primary в одному регіоні/зоні.
  • Вам потрібна повнота можливостей Postgres (розширення, просунуті індекси, багате інструментування) без «майже сумісних» застережень.
  • Ви готові до того, що крос-регіональна HA зазвичай є відновленням після катастрофи (DR), а не «active-active записами».
  • У вас є команда, яка може трактувати фейловер як інженерну підсистему з вправами.

Вибирайте CockroachDB, коли вам потрібно, щоб записи переживали втрату вузла без спеціального HA-стеку

CockroachDB — це розподілений SQL: ключ-значення з шаром SQL зверху, що використовує реплікацію через консенсус. Продають його так:
«Вона продовжує працювати, коли машини вмирають». Часто це правда. Але ви платите новими режимами відмов і більшою кількістю рухомих частин на запит.
Ваша операційна позиція переходить від «захистити primary» до «тримати кластер збалансованим і охолоджувати хотспоти».

CockroachDB підходить, коли:

  • Вам потрібен автоматичний фейловер записів без промоції репліки.
  • У вас мультизональна інфраструктура і ви хочете, щоб база сама керувала розміщенням реплік і кворумами.
  • Організація готова вивчати профілювання розподіленої продуктивності і приймати вищу базову затримку заради надійності.
  • Ви можете свідомо спроектувати локалізацію даних (partitioning/geo), а не сподіватися на диво.

Сухий жарт №1: Будування HA на Postgres схоже на складання меблів IKEA — міцно й корисно, якщо не «фристайлити» без шурупа.

Історія та цікаві факти, які можна використати

Контекст важливий, бо дизайн баз даних — це здебільшого викопні рештки минулих катастроф.
Ось факти, які справді допомагають приймати рішення:

  1. PostgreSQL походить від POSTGRES (UC Berkeley, середина 1980-х), створеного тоді, коли правильність була важливішою за хмарні маркетингові фрази.
    Та ДНК проявляється в консервативній, передбачуваній поведінці.
  2. Write-ahead logging (WAL) старший за вашу CI-систему. Postgres покладається на WAL, щоб гарантувати довговічність і забезпечити реплікацію.
    Якщо ви не поважаєте WAL і контрольні точки, рано чи пізно у вас буде повільний інцидент.
  3. Streaming replication у Postgres з’явилася в 9.0 (2010), що зробило «реальну» фізичну реплікацію поширеною і швидкою.
    До того HA було громіздким і більш ручним.
  4. Postgres hot standby (читабельні репліки) з’явився у 9.0. Це дало стандартний патерн: масштабувати читання, захищати primary і планувати фейловери.
  5. CockroachDB була натхненна Google Spanner (описаним публічно у 2012). Теза Spanner: глобальний SQL з сильною узгодженістю
    за рахунок координації.
  6. CockroachDB використовує Raft-консенсус для реплікації. Raft спроектований, щоб бути зрозумілішим (порівняно з Paxos), але «зрозуміліший» все одно включає терміни як кворуми, ліцензі та передача лідерства.
  7. «Distributed SQL» став категорією, бо NoSQL не міг вдавати вічно. Багато команд захотіли транзакційний SQL назад
    після того, як зрозуміли: eventual consistency — це стиль життя, а не перемикач функції.
  8. CAP — не інструмент для порівняння продуктів. У реальних системах толерантність до розривів мережі не опційна, а «послідовність» має кілька значень.
    Корисне питання: які компроміси система робить під час розривів і наскільки це помітно для вашого застосунку?
  9. Розширення Postgres — суперсила і пастка. Вони дають можливість швидко будувати спеціалізовані системи (PostGIS, pgcrypto, інструменти типу Timescale),
    але вони прив’язують вас до семантики Postgres, яку «сумісні» системи можуть не відтворити.

Як насправді працює HA: реплікація Postgres проти консенсусу Cockroach

PostgreSQL HA: один записуючий вузол з репліками та оркестраційним клеєм

Ядро PostgreSQL — це модель з одним записуючим вузлом. Ви можете масштабувати читання репліками і виконувати фейловер, промотуючи репліку.
Але «HA Postgres» — це не одна функція; це хореографія:

  • Streaming replication копіює WAL з primary на репліки.
  • Synchronous replication може гарантувати, що коміт чекає підтвердження від репліки (менший RPO, вища затримка).
  • Failover management вирішує, який вузол стане primary і перев’язує клієнтів.
  • Запобігання split-brain — ваша задача. Потрібні fencing, кворум або надійний розподілений лок.

Перевага Postgres — ясність: є primary. Якщо записи падають, ви шукаєте primary або промотуєте один.
Ризик Postgres у тому, що ваш HA-стек — «батарейки не входять у комплект». Цей стек може бути відмінним, але його треба спроєктувати.

CockroachDB HA: консенсусна реплікація на всіх рівнях, і SQL поруч

CockroachDB розбиває дані на ranges (шарди). Кожен range реплікується (зазвичай 3 репліки за замовчуванням),
і одна репліка — це Raft-лідер, який обробляє записи для цього range. Транзакції координуються між ranges.
Фейловер внутрішній: якщо вузол помирає, інша репліка стає лідером для уражених ranges, за умови збереження кворуму.

Перевага Cockroach — оперативна: не потрібно вручну «промотувати» новий primary. Система самозцілюється в межах кворуму.
Ризик Cockroach — передбачуваність продуктивності: проста транзакція може зачіпати кілька ranges і кілька груп консенсусу,
особливо з ростом набору даних і еволюцією схеми.

RPO/RTO у реальних термінах

Якщо ви купуєте HA, ви купуєте RPO і RTO, а не враження.

  • Postgres асинхронна реплікація: RPO може бути ненульовим (ви можете втратити останні кілька секунд комітів при фейловері),
    RTO залежить від оркестрації та маршрутизації клієнтів.
  • Postgres синхронна реплікація: RPO може наближатися до нуля при правильній конфігурації, але час коміту зростає і записи можуть зупинитися,
    якщо синхронні стендаби недоступні.
  • CockroachDB: RPO зазвичай близький до нуля в межах кворуму; RTO часто швидкий при падінні вузла, але розриви мережі та
    перевантажені кластери можуть призводити до «доступно, але сумно»: тайм‑аути, повтори та деградований пропуск.

Цитата, щоб тримати вас в реальності, парафраз від Вернера Вогеляса: «Все ламається весь час». Прийміть це як ідею.

Затримка, хвостова затримка та чому «мульти-регіон» — пастка

Розмови про HA романтизують географію. «Active-active між регіонами». «Глобальні записи».
Потім з’являється фізика, без запрошення, як аудитор, якого ви забули вказати в копії листа.

У Postgres шлях запису локальний для primary. Якщо ви ставите primary в одному регіоні, а сервери застосунку — в іншому,
ваш p99‑латентність висміє ваш роадмап. Це не вина Postgres; це ваша помилка.
HA у Postgres зазвичай означає «тримайте primary поруч із тими, хто пише; тримайте репліки поруч із читачами; фейловуйте за потреби».

У CockroachDB мульти-регіон — частина архітектури, але це не магія. Сильно узгоджений запис потребує кворума.
Якщо ваші репліки розкидані по віддалених регіонах, час кругової поїздки кворуму стає базовою затримкою запису.
Система може розміщувати лідерів і репліки для оптимізації локальності, але вам все одно доведеться обирати компроміс:
затримка vs виживаність vs модель узгодженості.

Сухий жарт №2: Сильна узгодженість у мульти-регіоні — це єдиний спосіб перетворити «швидкість світла» на щомісячний платіж.

Хвостова затримка — там, де гинуть бази даних

Медіанна затримка — приємна для дашбордів. Хвостова затримка — те, що відчувають користувачі.
Розподілені системи схильні розширювати хвіст, бо більше компонентів бере участь у кожній операції.
CockroachDB може бути відмінною, але якщо ваша транзакція зачіпає кілька ranges на різних вузлах,
у вас більше шансів на черги, конкуренцію і повільні репліки.

У Postgres теж бувають хвостові проблеми — автовакавумні паузи, черги блокувань, сплески контрольних точок — але їхній масштаб частіше обмежений:
ресурсами одного primary і одним WAL-потоком.

Режими відмов: те, що вас будить

Режими відмов PostgreSQL (і як вони відчуваються)

  • Split brain: два вузли вірять, що вони primary. Симптоми: розбіжні лінії часу, конфліктні записи, «cannot connect» у циклі.
    Корені: погане fencing, неправильно налаштований менеджер фейловера, мережеві розриви, надто хитрі скрипти.
  • Відставання реплікації: репліки відстають. Симптоми: застарілі читання, несподіванки read-after-write, при фейловері можна втратити дані.
    Причини: повільний I/O, мережеве обмеження, довгі транзакції, сплески WAL під час пакетних задач, недостатні ресурси реплік.
  • Тиск на контрольні точки / WAL: стрибки затримки. Симптоми: періодичні зупинки запису, насичення I/O, зростання часу fsync.
    Причини: маленький shared_buffers при високому обігу, агресивні налаштування чекпоінтів, повільне сховище, занадто багато брудних буферів.
  • Заборгованість autovacuum: усе стає повільним, потім падає в прірву. Симптоми: блоут, зростання часу запитів, попередження про wraparound.
    Причини: порогові значення vacuum не налаштовані під високий обіг, довгі транзакції, недостатньо воркерів autovacuum.
  • Фейловер із несумісним станом: промотована репліка не має розширень, конфігів або інших параметрів.
    Симптоми: помилки застосунку після фейловера, несподівані плани виконання, відсутні ролі.
    Корінь: унікальні сервера та дрейф конфігурації.

Режими відмов CockroachDB (і як вони відчуваються)

  • Гарячі діапазони / хотспоти: частина ключ-простору стає магнітом трафіку. Симптоми: висока затримка для підмножини операцій, стрибки CPU на кількох вузлах.
    Причини: монотонно зростаючі ключі, конкуренція на одному рядку, поганий дизайн схеми для розподілу.
  • Повтори транзакцій: застосунок бачить помилки серіалізації або повторювані відмови. Симптоми: підвищена затримка, сплески таймаутів, зростання повторів.
    Причини: висока конкуренція, довгі транзакції, конфліктні записи, недостатні backoff або логіка повторів у клієнтах.
  • Недостатня реплікація / втрата кворуму: діапазон не може досягти кворуму. Симптоми: недоступність частини даних, зупинені записи.
    Причини: занадто багато відмов вузлів, неправильно розмірований кластер, техобслуговування як демонтаж.
  • Кворумна затримка між регіонами: записи повільні глобально. Симптоми: стрибки p95/p99, корельовані з RTT між регіонами.
    Причина: політика розміщення реплік, яка змушує кворуми через віддалені регіони.
  • Фоновий перерозподіл заважає: кластер «лікується», поки ви намагаєтеся обслуговувати трафік.
    Симптоми: тривалий диск‑ і мережевий трафік, вища затримка, більша варіативність.
    Причини: деактивація вузлів під навантаженням, раптові зміни топології, недостатній запас потужності.

Помітна тема: помилки Postgres часто пов’язані з оркестрацією та межами ресурсів одного вузла.
Помилки Cockroach частіше пов’язані з координацією та побічними ефектами розподілу.

Практичні завдання: команди, результати та рішення (12+)

Нижче — реальні операційні завдання з командами, які можна виконати, що означає вивід і яке рішення приймати.
Вони розділені між Postgres і CockroachDB, з кількома перевірками на рівні ОС. Жодної води; це те, що робить on-call.

PostgreSQL: визначити primary і стан реплікації

Завдання 1: Цей вузол — primary чи standby?

cr0x@server:~$ psql -XAtc "select pg_is_in_recovery();"
f

Значення: f означає, що цей вузол не в режимі recovery: він веде себе як primary. t означало б standby.
Рішення: Якщо ви очікували standby, але отримали f, зупиніться і перевірте стан фейловера; можливо, у вас split-brain.

Завдання 2: Перевірити відставання реплікації в байтах на primary

cr0x@server:~$ psql -Xc "select application_name, client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn,replay_lsn) as byte_lag from pg_stat_replication;"
 application_name | client_addr |  state  | sent_lsn  | write_lsn | flush_lsn | replay_lsn |  byte_lag
------------------+-------------+---------+-----------+-----------+-----------+------------+-----------
 replica1         | 10.0.1.21   | streaming | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4F2C8  |         0
 replica2         | 10.0.2.37   | streaming | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4E100  |     54024

Значення: byte_lag — наскільки кожна репліка відстає від того, що primary вже відправив.
Невелике відставання — нормально; постійне зростання означає, що репліка не витримує навантаження.
Рішення: Якщо відставання зростає, уникайте фейловеру на відстаючу репліку; перевірте I/O, CPU і мережу репліки.

Завдання 3: На standby виміряти затримку відтворення у часових термінах

cr0x@server:~$ psql -Xc "select now() - pg_last_xact_replay_timestamp() as replay_delay;"
 replay_delay
--------------
 00:00:02.184

Значення: Якщо це росте до хвилин під нормальним навантаженням, ваш «масштаб читань» дає застарілі дані.
Рішення: Перестаньте посилати критичні за затримкою читання сюди або виправте вузьке місце (часто диск).

Завдання 4: Перевірити довгі транзакції, що блокують vacuum і контроль блоуту

cr0x@server:~$ psql -Xc "select pid, usename, state, now()-xact_start as xact_age, left(query,80) as query from pg_stat_activity where xact_start is not null order by xact_age desc limit 5;"
 pid  | usename | state  |  xact_age  |                                      query
------+--------+--------+------------+--------------------------------------------------------------------------------
 8421 | app    | active | 00:42:11   | update orders set status='paid' where id=$1 returning id
 9173 | app    | idle in transaction | 01:13:02 | select * from customers where id=$1

Значення: «idle in transaction» годину — тривожний знак; це може перешкоджати прибиранню і викликати блоут.
Рішення: Виправте обробку з’єднань у застосунку; подумайте про idle_in_transaction_session_timeout і вбивайте порушників під час інциденту.

Завдання 5: Швидко знайти контенцію через блокування

cr0x@server:~$ psql -Xc "select a.pid, now()-a.query_start as age, a.state, left(a.query,70) as query, l.mode, l.granted from pg_locks l join pg_stat_activity a on a.pid=l.pid where a.datname=current_database() order by l.granted, age desc limit 12;"
 pid  |   age    | state  |                               query                               |        mode         | granted
------+----------+--------+-------------------------------------------------------------------+---------------------+---------
 5211 | 00:01:12 | active | alter table invoices add column tax_region text                   | AccessExclusiveLock | f
 4182 | 00:01:08 | active | select * from invoices where account_id=$1 order by created_at    | AccessShareLock     | t

Значення: Очікування AccessExclusiveLock блокує майже все на цій таблиці.
Рішення: Скасуйте DDL, якщо він небезпечний; перенесіть на CONCURRENTLY або використайте онлайн-міграції.

Завдання 6: Перевірити тиск чекпоінтів і поведінку буферів

cr0x@server:~$ psql -Xc "select checkpoints_timed, checkpoints_req, buffers_checkpoint, buffers_backend, maxwritten_clean, checkpoint_write_time, checkpoint_sync_time from pg_stat_bgwriter;"
 checkpoints_timed | checkpoints_req | buffers_checkpoint | buffers_backend | maxwritten_clean | checkpoint_write_time | checkpoint_sync_time
-------------------+-----------------+--------------------+-----------------+------------------+-----------------------+----------------------
               102 |              47 |            9231142 |          312991 |            25013 |               1882210 |                402113

Значення: Високе checkpoints_req відносно timed checkpoints вказує на тиск WAL, що примушує додаткові чекпоїнти.
Рішення: Налаштуйте чекпоїнти та оцініть пропускну здатність сховища; розгляньте розподіл записів або збільшення max_wal_size.

Завдання 7: Підтвердити, що архівація WAL насправді працює (для PITR)

cr0x@server:~$ psql -Xc "select archived_count, failed_count, last_archived_wal, last_archived_time, last_failed_wal, last_failed_time from pg_stat_archiver;"
 archived_count | failed_count | last_archived_wal |     last_archived_time     | last_failed_wal | last_failed_time
---------------+--------------+-------------------+----------------------------+-----------------+-----------------
         91822 |            0 | 00000001000000030000009A | 2025-12-30 02:18:12.104+00 |                 |

Значення: failed_count=0 — бажаний стан. Помилки архівації означають, що ваша історія відновлення — це вигадка.
Рішення: Якщо є помилки, припиніть вдавати, що у вас є PITR; виправте права, сховище або команду архівації до наступного інциденту.

PostgreSQL: позиція фейловеру і маршрутизація клієнтів

Завдання 8: Перевірити конфігурацію synchronous replication

cr0x@server:~$ psql -Xc "show synchronous_commit; show synchronous_standby_names;"
 synchronous_commit
--------------------
 on

 synchronous_standby_names
--------------------------
 FIRST 1 (replica1,replica2)

Значення: Коміти чекають одну стендабу. Якщо обидві стендаби нездорові, записи можуть застрягати.
Рішення: Якщо ви в аварії і треба продовжити записи, тимчасово послабте синхронні налаштування — але задокументуйте це і поверніть назад.

Завдання 9: Підтвердити, що застосунок спрямований на правильний endpoint (приклад PgBouncer)

cr0x@server:~$ psql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -Xc "show clients;"
 type | user | database | state  | addr       | port  | local_addr | local_port | connect_time
------+------|----------|--------|------------|-------|------------|------------|----------------------------
 C    | app  | prod     | active | 10.4.7.19  | 49212 | 10.4.2.10  | 6432       | 2025-12-30 02:18:55.911+00

Значення: Ви бачите активних клієнтів і їхні адреси. Корисно під час фейловера: чи клієнти взагалі доходять до проксі?
Рішення: Якщо клієнти не підключаються, база може бути в порядку, а шар маршрутизації зламаний (або блокований фаєрволом/DNS).

CockroachDB: стан кластера і санітарні перевірки розподілу

Завдання 10: Перевірити статус вузлів і їхню життєздатність

cr0x@server:~$ cockroach node status --host localhost:26257
  id |    address     |     build     |  started_at           | is_live | replicas |  cpu | mem |  ssd | version
-----+----------------+---------------+-----------------------+---------+----------+------+-----+------+---------
   1 | 10.0.1.10:26257| v24.1.3       | 2025-12-30 00:11:02   | true    |     2210 | 0.42 | 64G |  30% | 24.1
   2 | 10.0.2.10:26257| v24.1.3       | 2025-12-30 00:11:08   | true    |     2198 | 0.55 | 64G |  29% | 24.1
   3 | 10.0.3.10:26257| v24.1.3       | 2025-12-30 00:10:59   | false   |     2175 | 0.00 | 64G |  31% | 24.1

Значення: Вузол 3 не живий. При налаштуванні з 3 репліками втрата одного вузла зазвичай переживана, але ви вже один крок від проблеми.
Рішення: Призупиніть будь-яке техобслуговування; відновіть стан вузла або додайте потужність перед виконанням «хитрих» дій.

Завдання 11: Швидко перевірити стан реплікації

cr0x@server:~$ cockroach node status --ranges --host localhost:26257
  id | ranges | underreplicated | unavailable | leader_ranges
-----+--------+-----------------+-------------+--------------
   1 |   5600 |              12 |           0 |         1850
   2 |   5578 |              15 |           0 |         1902
   3 |   5489 |             301 |           2 |            0

Значення: Underreplicated і unavailable ranges вказують на проблеми з розміщенням даних і потенційну недоступність.
Рішення: Якщо unavailable ненульове, розглядайте це як інцидент, що впливає на користувачів. Відновіть кворум (поверніть вузол або перерозподіліть).

Завдання 12: Ідентифікувати дорогі запити і повтои (SQL-інтроспекція)

cr0x@server:~$ cockroach sql --host localhost:26257 -e "select app_name, query, count(*) as execs, sum(retries) as retries from crdb_internal.statement_statistics where aggregated_ts > now() - interval '10 minutes' group by app_name, query order by retries desc limit 5;"
  app_name |                    query                    | execs | retries
----------+---------------------------------------------+-------+---------
  api     | update accounts set balance = balance + $1  |  1200 |     340

Значення: Високі повтори для update вказують на конкуренцію або гарячі ключі.
Рішення: Зменшіть конкуренцію: змініть патерн записів (шардовані лічильники, уникати хотспотів по одиночному рядку), скоротіть транзакції, додайте backoff при повторах.

Завдання 13: Перевірити розподіл діапазонів і підозрілі хотспоти

cr0x@server:~$ cockroach sql --host localhost:26257 -e "select range_id, replicas, lease_holder, queries_per_second from crdb_internal.ranges_no_leases order by queries_per_second desc limit 5;"
  range_id | replicas | lease_holder | queries_per_second
----------+----------+--------------+-------------------
    90211 | {1,2}    |            1 |              892.2
    90197 | {1,2,3}  |            2 |              521.7

Значення: Діапазон із дуже високим QPS може стати вузьким місцем; зверніть увагу також на діапазони з лише двома репліками.
Рішення: Якщо хотспоти тривають, перегляньте схему і розподіл ключів; якщо кількість реплік низька, усуньте втрату вузла і перереплікуйте.

Перевірки ОС і сховища, що впливають на обидві системи

Завдання 14: Перевірити затримку диска та насичення (iostat)

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (db01)  12/30/2025  _x86_64_  (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.33    0.00    5.41    9.88    0.00   72.38

Device            r/s     w/s   rkB/s   wkB/s  await  svctm  %util
nvme0n1         120.0   980.0  6400.0 51200.0  18.40   0.78  92.10

Значення: %util близько 100% плюс високе await означає, що диск — вузьке місце.
Рішення: Припиніть звинувачувати «базу даних». Зменшіть тиск запису, додайте IOPS, перейдіть на швидше сховище або додайте вузли (Cockroach) / налаштуйте чекпоінти (Postgres).

Завдання 15: Підтвердити місце на файловій системі та іноді в inode

cr0x@server:~$ df -h /var/lib/postgresql /var/lib/cockroach
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  900G  812G   43G  95% /var/lib/postgresql
/dev/nvme1n1p1  900G  701G  153G  83% /var/lib/cockroach

Значення: 95% використання фліртує з катастрофою; Postgres особливо ненавидить закінчення диска під час WAL.
Рішення: Якщо стійке використання понад ~85–90%, сприймайте як термінове: розширте том, очистіть або перемістіть дані перед простоєм.

План швидкої діагностики

Коли база «повільна», у вас немає часу на філософію. Потрібна швидка, повторювана триаж‑послідовність, яка звужує вузьке місце.
Ось прагматична перша/друга/третя послідовність для обох систем.

Перше: Це база чи шлях до неї?

  • Перевірте помилки підключення/таймаути в логах застосунку. Якщо це DNS/LB/фаєрвол, база може бути невинною.
  • Переконайтеся, що endpoint резолвиться до очікуваних вузлів. Неправильна маршрутизація трафіку — класика після фейловеру.
  • Перевірте базове здоров’я ОС: CPU steal, повний диск, затримки диска.

Друге: Система заблокована (локи/контенція) чи насичена (I/O/CPU)?

  • Postgres: дивіться на локи, довгі транзакції і I/O wait; перевірте відставання реплікації, якщо читання застарілі.
  • CockroachDB: дивіться на повтори, хотспоти (ranges) і недореплікацію; перевірте життєздатність вузлів і доступність діапазонів.

Третє: Це топологічна/HA-подія?

  • Postgres: підтвердіть, що є рівно один primary, підтвердіть підключення реплік, підтвердіть стан менеджера фейловера.
  • CockroachDB: підтвердіть здоров’я кворуму, недореплікацію і чи не бореться ребалансування з вашим робочим навантаженням.

Дисципліна — не ганятися за планами виконання запитів, поки ви не підтвердили, що кластер не голодує по диску або не застряг за блокуванням.
Більшість інцидентів «продуктивності бази» — насправді «координаційні й сховищні інциденти в одежі SQL».

Три корпоративні міні-історії з передової

Міні-історія 1: Інцидент через неправильне припущення (фейловер Postgres)

Середня SaaS-компанія тримала Postgres з primary в одній зоні і streaming replica в іншій.
У них був менеджер фейловера і VIP. Всі почувалися в безпеці. Вони навіть написали «RPO близько нуля» в внутрішньому документі — і це спосіб спокусити долю.

Одного дня сховище primary почало таймаутитися. Менеджер фейловера промотував репліку. VIP перемістився.
Застосунок швидко відновився — поки служба підтримки не помітила «зниклі» нещодавні замовлення. Небагато, але достатньо, щоб було боляче.
Команда вважала, що синхронна реплікація увімкнена, бо «ми налаштували це місяці тому».

Виявилося, що sync‑налаштування застосовувалися на старому primary, але ніколи не були послідовно розгорнуті.
Гірше того, застосунок використовував репліки для деяких «екранів підтвердження», і вони тепер показували часову шкалу, яка не відповідала новому primary.
Сам фейловер був коректним. Припущення про RPO — ні.

Відновлення вимагало болісної перевірки: порівняння ID замовлень і часових міток, узгодження з upstream логами і пояснення бізнесу, чому «висока доступність»
не означає «відсутність втрати даних». Справжнє рішення — нудне: керування конфігом, забезпечення паритету параметрів
і чітка політика, які запити можуть йти на репліки.

Після інциденту вони змінили свій план дій: фейловер не вважається «завершеним», поки не перевірено режим реплікації, відставання реплік і маршрутизацію застосунків.
Вони також почали писати явні цілі RPO/RTO по фічах, а не по базі даних.

Міні-історія 2: Оптимізація, яка відбилася боком (hotspot у CockroachDB)

Команда e‑commerce мігрувала сервіс кошика з великою кількістю записів на CockroachDB для автоматичного фейловера між трьома зонами.
На початку результати були прийнятні. Затримки виглядали стабільно. Потім настала пік‑сезон і шлях оформлення замовлення почав давати повторы і таймаути.

Команда «оптимізувала» первинні ключі, зробивши їх послідовними для локальності індексів, виходячи з річного досвіду з Postgres.
У CockroachDB це створило хотспот: вставки вдаряли по невеликому набору ranges, бо ключі зростали монотонно.
Розщеплення діапазонів допомогло частково, але лідерство для найгарячіших діапазонів зібралося на кількох вузлах і вони стали CPU-завантаженими.

На чергуванні бачили, що вузли живі і реплікація здорова. Але затримки зростали, а застосунок агресивно намагався повторити операції,
перетворивши конкуренцію на самоіндукований DoS. Система була технічно доступна, але користувачі не могли надійно оформити замовлення.

Виправлення було контрінтуїтивним для мозку, звиклого до Postgres: змінити розподіл ключів (використати випадкові або hash‑sharded префікси),
і переробити «лічильники в одному рядку» у шардовані лічильники. Також ввели розумний backoff повторів і обмежили конкурентність у гарячих шляхах.

Вони залишили CockroachDB, але перестали намагатися змусити її поводитися як одновузлова база. Ось фактична міграція.

Міні-історія 3: Нудна, але правильна практика, що врятувала день (PITR у Postgres)

B2B платформа тримала Postgres з потоковою реплікацією і суворою процедурою PITR: щоденні базові бекапи,
архівація WAL і щоденна перевірка архівації, а також квартальні відпрацювання відновлень. Ніхто не любив вправи. І саме в цьому їхня сила.

Інженер запустив міграцію і видалив колонку в неправильній схемі. Застосунок не впав миттєво; він поступово відмовлявся при виконанні певних робіт.
Репліка вірно повторила помилку, бо репліки слухняні.

Команда стояла перед вибором: намагатися швидко запатчити вперед під тиском або відкотитися до відомої хорошої точки.
Оскільки у них були надійні архіви WAL і відпрацьовані кроки, вони відновили стан за timestamp за кілька хвилин до міграції і відтворили безпечні зміни.

Аварія тривала десятки хвилин, а не години. Постинцидент був милостивим.
Герої — не ті, хто писав хитрі скрипти; це люди, які тестували відновлення, коли нічого не горіло.

HA зменшила час простою через відмови вузлів. PITR врятував від людських помилок. Різні інструменти. Обидва обов’язкові.

Типові помилки: симптом → корінь → виправлення

1) Симптом: після фейловера записи працюють, але читання показують старі дані

Корінь: застосунок все ще читає з відстаючої репліки або маршрутизація читань/записів не оновилася чисто.

Виправлення: забезпечити маршрутизацію через єдиний endpoint з розумінням ролей; додати правила read-after-write; моніторити затримку відтворення реплік.

2) Симптом: primary Postgres «повільний кожні кілька хвилин»

Корінь: стрибки чекпоінтів або тиск на flush WAL через обмеження сховища та агресивні налаштування чекпоінтів.

Виправлення: збільшити max_wal_size, налаштувати чекпоінти і перенести WAL/дані на швидше сховище; перевірити латентності fsync.

3) Симптом: репліки Postgres відстають під час пакетних задач

Корінь: сплески WAL + I/O‑вузьке місце реплік, часто ускладнене довгими транзакціями, що затримують очищення.

Виправлення: гальмувати пакетні записи, збільшити ресурси реплік і усунути довгі транзакції; розглянути логічне розбиття або запускати задачі поза піком.

4) Симптом: CockroachDB показує «здорова» вузли, але застосунок таймаутить

Корінь: конкуренція транзакцій і повтори, часто через гарячі ключі або надто багатомовні транзакції.

Виправлення: переробити хотспоти (шардовані ключі, рандомізація), скоротити транзакції, реалізувати обмежені повтори з backoff і зменшити шаблони блокувань.

5) Симптом: CockroachDB нестабільна під час техобслуговування вузлів

Корінь: недостатній запас потужності; ребаланс і перереплікація конкурують з продакшен‑навант.

Виправлення: додати ресурси, евакуювати/деактивувати повільно, уникати одночасного зняття кількох вузлів; планувати техобслуговування з урахуванням навантаження.

6) Симптом: Postgres HA «працює», але іноді ви маєте два primary

Корінь: split brain через ненадійну вибірку лідера, відсутність fencing або не змодельовану поведінку мережевих розривів.

Виправлення: впровадити коректний кворум/розподілений лок (etcd/consul), надійне fencing (STONITH де потрібно) і тестувати сценарії розривів мережі.

7) Симптом: «Не можемо швидко відновитись», незважаючи на бекапи

Корінь: бекапи ніколи не відновлювалися на практиці; архіви WAL відсутні; креденшали/права застаріли.

Виправлення: плануйте вправи з відновлення, перевіряйте архівацію щодня і автоматизуйте валідацію; ставте відновлення як продакшен‑фічу.

Чеклісти / покроковий план

Чекліст для рішення: Postgres HA vs CockroachDB

  1. Визначте RPO/RTO по сервісу. Якщо ви не можете це записати, ви здогадуєтеся.
  2. Виміряйте потреби локалізації записів. Де відбуваються записи? Якщо «усюди», підтвердіть трасуваннями.
  3. Класифікуйте робоче навантаження: висока конкуренція OLTP, append-heavy, змішане read/write, довгі транзакції, аналітика у фоні.
  4. Перелічіть обов’язкові можливості: розширення, специфічна ізоляція, великі об’єкти, спеціалізовані індекси, logical decoding тощо.
  5. Реальність кадрів: чи є у вас оператори, які можуть дебажити консенсус і конкуренцію, або команда, що володіє внутрішньою роботою Postgres?
  6. Вправи з відмов: чи можете ви відпрацювати втрату регіону і довести відновлення у стейджингу під навантаженням, схожим на продакшен?

Покроково: будуємо «нудну HA» на PostgreSQL

  1. Виберіть топологію: primary + 2 репліки по зонах; вирішіть, які будуть read-only endpoints.
  2. Впровадьте реплікацію: streaming replication; оберіть async vs sync залежно від бюджету затримки.
  3. Додайте оркестрацію: реальний менеджер фейловера; уникайте саморобних скриптів, якщо не хочете археології.
  4. Додайте маршрутизацію: стабільні endpoint’и для застосунків; явне розділення read/write якщо використовуєте репліки.
  5. Запобігання split brain: вибірка лідера на кворумі; стратегія fencing; тестуйте розриви.
  6. Бекапи і PITR: базові бекапи + архівація WAL; перевіряйте щодня; відпрацьовуйте відновлення.
  7. Наблюваність: відставання реплікації, очікування блокувань, швидкість WAL, час чекпоінтів, затримка диска.
  8. Runbooks: promote, demote, rewind, rebuild replica; документуйте «чого не робити» під час інциденту.

Покроково: експлуатація CockroachDB без самопошкодження

  1. Почніть з 3+ вузлів по 3 зони (або більше для запасу). Не запускайте «достатньо» кластер у продакшені.
  2. Визначте локальність і обмеження до того, як вони знадобляться. Вирішіть, де повинні жити ліси/лідери для критичних таблиць.
  3. Моделюйте конкуренцію раніше: знайдіть лічильники, оновлення одного рядка, послідовні ключі і патерни «останній запис».
  4. Реалізуйте повтори правильно у застосунку з обмеженим backoff. Необмежені повтори — це DDoS‑фіча розподілених систем.
  5. Слідкуйте за здоров’ям ranges: недореплікація, недоступні ranges, концентрація leaseholder’ів.
  6. Плануйте техобслуговування з урахуванням ребалансування: деактивуйте повільно, тримайте запас потужності, уникайте зняття кількох вузлів під навантаженням.
  7. Бенчмаркуйте з реалістичною конкуренцією, а не з ввічливими синтетичними скриптами, що ніколи не конкурують.

Поширені питання

1) Чи може Postgres бути «active-active» для записів?

Не в базовій моделі з одним primary. Ви можете наближати це шардингом, маршрутизацією на рівні застосунку або спеціалізованими multi-master рішеннями,
але ви підписуєтесь на управління конфліктами і операційну складність. Якщо вам справді потрібен multi-writer з сильною узгодженістю,
система на базі консенсусу може підійти краще — якщо ви готові до компромісів.

2) Чи гарантує CockroachDB нульовий час простою?

Вона може пережити багато відмов вузлів без ручної промоції, але «нульовий час простою» залежить від кворуму, запасу потужності
і чи ваше навантаження не викликає конкуренцію/повторів. Вона стійка, але не непереможна.

3) Яка найпростіша конфігурація HA Postgres, яка не буде ненавидіти вас пізніше?

Primary + дві репліки по зонах, перевірений менеджер фейловера (не cron‑скрипти), єдиний стабільний endpoint для записів
і протестований PITR. Тримайте це нудним, автоматизуйте паритет конфігів і відпрацьовуйте фейловери.

4) Чи завжди краща синхронна реплікація в Postgres?

Вона зменшує RPO, але підвищує затримку і може зупиняти записи, якщо синхронні стендаби недоступні.
Використовуйте її, коли можете дозволити собі затримку і маєте надійні з’єднання до стендабів. Інакше — асинхрон + сильний PITR.

5) Чому додатки під CockroachDB потребують логіки повторів?

Тому що серіалізовані транзакції під конкуренцією можуть бути змушені повторитися, щоб зберегти коректність.
Якщо ви не обробляєте повтори належно, перетворите транзитну конкуренцію на помилки, видимі користувачам.

6) Що легше дебажити о 2:00 ночі?

Postgres зазвичай простіший, якщо інцидент — «один вузол повільний» або «відставання репліки», бо менше рухомих частин.
CockroachDB може бути простішою при банальних відмовах вузлів, але складнішою при патологіях продуктивності та конкуренції.

7) Чи можна просто перенести схему Postgres у CockroachDB?

Багато SQL можна мігрувати, але не припускайте ідентичної поведінки щодо локів, крайових випадків ізоляції, послідовностей/serial патернів
та доступності розширень. Основний ризик — не синтаксис, а характеристика навантаження: конкуренція і розподіл ключів.

8) Що з бекапами — чи CockroachDB знімає потребу думати про PITR?

Ні. HA вирішує відмови вузлів; бекапи вирішують людські помилки і логічну корупцію. Вам все ще потрібні відпрацьовані процедури відновлення.
Інша СУБД, та сама реальність.

9) Чи варто використовувати репліки для читання в Postgres, якщо ви дбаєте про коректність?

Так, але робіть це явно. Маршрузуйте лише безпечні читання на репліки або реалізуйте правила read-after-write.
Якщо ви вважатимете репліки повністю консистентними, рано чи пізно випустите баг як фічу.

10) Чи завжди CockroachDB повільніша через те, що вона розподілена?

Не завжди, але розподілена координація додає базову вартість, і хвостова затримка може розширюватися під конкуренцією.
Для деяких навантажень вона відмінна; для одно-регіонального низькозатримкового OLTP з інтенсивними записами Postgres часто виграє по «сирій» швидкості.

Висновок: наступні кроки, які можна виконати цього тижня

PostgreSQL HA — це система, яку ви будуєте; CockroachDB HA — це система, до якої ви приєднуєтесь. Обидві можуть надійно працювати в продакшені.
Обидві можуть зіпсувати ваш вікенд, якщо ви сприймаєте їх як магію.

Ось що робити далі, у порядку:

  1. Запишіть RPO/RTO по сервісу і отримайте бізнес‑погодження. Ви хочете менше сюрпризів, ніж аудитори.
  2. Проведіть вправу з відмови: вбити вузол, розірвати мережевий шлях і виміряти час відновлення «end-to-end» (включаючи клієнти).
  3. Впровадьте план швидкої діагностики як runbook і відпрацюйте його. Швидкість приходить від повторення, а не генія.
  4. Видаліть одну біль, яку хочете усунути першою: запобігання split‑brain у Postgres або зменшення конкуренції/hotspot у Cockroach.
  5. Протестуйте відновлення. Не «у нас є бекапи», а «ми відновили вчорашній бекап у чистому середовищі і перевірили коректність».

Якщо ви хочете HA без драми — обирайте архітектуру, яка відповідає звичкам вашої організації.
Якщо ви хочете HA з новими видами болю — обирайте ту, що відповідає цікавості вашої організації.
У будь‑якому разі тримайте це нудним там, де це важливо: бекапи, вправи і чітка відповідальність.

← Попередня
Вибір планувальника IO для ZFS: mq-deadline чи none для HDD, SSD і NVMe
Наступна →
Чіплети AMD: трюк, який воскресив Ryzen

Залишити коментар