Multi-master replication for
Postgres
K. Knizhnik, S. Kelvich
Коротко про мульти-мастер
Что такое транзакция и уровень изоляции транзакций
Как делают транзакции в версионных БД (MVCC)
Как делают транзакции в распределенных БД
Как мы сделали распределенные транзакции в Postgres.
Как мы сделали мульти-мастер репликацию поверх таких
транзакций
О чем расскажем
2
Мультимастер:
Репликация. Одинаковые данные (*)
Распределенные транзакции
OLTP нагрузка
Кластер доступен пока работает большинство нод
Ноду можно добавлять на лету
Совместимо с постгресом
Мульти-мастер, быстро
3
Мультимастер не предназначен для:
Шардинга
Масштабирования по записи
Мульти-мастер, быстро
4
Мульти-мастер, быстро.
Восстановление реплики
5
Мульти-мастер, быстро
6
Обычно в книгах пишут два определения:
Атомарное действие над несколькими объектами
Атомарность + Сериализуемость – результат выполнения
нескольких параллельных транзакций равносилен
некоторому последовательному (серийному) их
выполнению.
Концепция транзакций
7
// Пишем программу, которая во много процессов/потоков
делает такие действия
// x – переменная в общей памяти, my_id – локальный номер
процесса/потока
while(true){
x = my_id
if (x != my_id) print("Wow!")
}
Пример: race condition
8
x += 1 – ?
x = 4294967297 – ?
Еще примеры race condition
9
x += 1 – может быть атомарно если процессор умеет
делать инструкции память-память. И если он один.
x = 4294967297 – две инструкции на x32
Еще примеры race condition
10
Atomicity – Visibility, CLOG
Consistency – просто нужна была буква
Isolation – locks, MVCC
Durability – WAL
ACID
11
SQL-92
Уровни изоляции
12
(Peter Bailis, VLDB, 2013)
Уровни изоляции
13
Consistency (Linearizability = Serializability)
Availability
Partition tolerance
Нюансы:
Внутри стойки P – не проблема (Stonebraker)
Consistency в БД это не Linearizability
CAP теорема
14
Изоляция транзакций
15
Хотим посчитать отчет.
T1
ra(500)
rb(500)
wa(600)
wb(400)
Comm
T2
select sum(v) from t;
Получим 1100 =/
Изоляция: dirty read
16
Хотим присвоить двум объектам одинаковое значение.
T1
wa(12)
wb(12)
Comm
T2
wa(10)
wb(10)
Comm
−→
T1
wa(12)
wb(12)
Comm
T2
↓
↓
↓
↓
wa(10)
wb(10)
Comma = 10, b = 12 a = 10, b = 10
Кто видит проблему? =)
Изоляция: dirty write
17
T1
wa(10)
wb(10)
↓
↓
T2
wb(20)
wa(20)
↓
↓
↓
Deadlock detection: если процесс проспал время T на локе,
то строим граф блокировок и ищем циклы. Нашли циклы
– принудительно завершаем одну из транзакций в цикле.
Борьба: упорядочить изменения внутри транзакций.
Выбрать более строгий уровень изоляции.
Изоляция: dead lock
18
Делаем интерфейс для просмотра состояния счетов.
uid
1
1
acc
1
2
amount
500
500
T1
wa(600)
wb(400)
Comm
T2
ra(500)
rb(400)
Comm
Бэкапы
Аналитика
Изоляция: non-repeatable read
19
acc1
{5, 0, 1, 500}
{5, 42, 1, 500}
{42, 0, 1, 600}
T42
r1(500)
w1(600)
r2(500)
Comm
T43
r2(500)
w2(400)
Comm
acc2
{7, 0, 2, 500}
{7, 43, 2, 500}
{43, 0, 2, 400}
MVCC или snapshot isolation
20
STM чаще всего делают так же.
Нужен vacuum (сборка мусора).
MVCC или snapshot isolation,
замечания
21
Repeateble read:
Запомнить список активных транзакций на момент старта
новой
Видим строки с xmax <= xid
Если строка видима в нашей транзакции, то еще проверяем
в CLOG (для игнорирования абортированных транзакций)
Замечание №1: если брать список активных на каждое
выражение в транзакции, то получим RC. Замечание №2: такие
правила видимости специфичны для постгреса.
Правила видимости
22
Кроме последовательности стартов транзакций можно
вести еще последовательность завершений (CSN)
Тогда список активных не нужен
Правила видимости, альтернативно
23
Со списками активных:
Xid-ы получаем сквозные на кластер
Списки активных объединяем со всех нод
Коммит по 2PC (Как на свадьбе)
Новое состояние в CLOG: in doubt. Ставим между Prepare
и Commit.
С CSN:
CSN-ы получаем сквозные на кластер
Коммит по 2PC
Новое состояние в CLOG: in doubt.
+: Eсть теория децентрализации такого подхода
+: для CSN можно использовать физическое время
(георепликация)
Распределенные транзакции
24
Slide by Sameh Elnikety
Snapshot isolation, традиционно
25
Slide by Sameh Elnikety
Snapshot isolation, начало
транзакции
26
Slide by Sameh Elnikety
Snapshot isolation, выполнение
транзакции
27
Slide by Sameh Elnikety
Snapshot isolation, коммит
транзакции
28
Slide by Sameh Elnikety
Clock-SI
29
Slide by Sameh Elnikety
Clock-SI, начало транзакции
30
Slide by Sameh Elnikety
Clock-SI, выполнение транзакции
31
Slide by Sameh Elnikety
Clock-SI, коммит транзакции
32
Slide by Sameh Elnikety
Clock-SI, рассинхронизация часов
33
Slide by Sameh Elnikety
Clock-SI, рассинхронизация часов
34
Slide by Sameh Elnikety
Clock-SI, рассинхронизация часов
35
Slide by Sameh Elnikety
Clock-SI, рассинхронизация часов
36
Slide by Sameh Elnikety
Clock-SI, продолжительный коммит
37
Slide by Sameh Elnikety
Clock-SI, продолжительный коммит
38
Slide by Sameh Elnikety
Clock-SI, продолжительный коммит
39
Slide by Sameh Elnikety
Clock-SI, продолжительный коммит
40
Design objectives: OLAP vs. OLTP
41
Replication:
Identical replicated data on all nodes
Possibility to have local tables
Writes allowed to any node
=> Easy to use
=> We need to take care about proper isolation
Design objectives: replication
42
Transaction manager. We want:
Avoid single point of failure.
+: Spanner, Cockroach, Clock-SI
—: Snapshot sharing, GTM, ...
Avoid network communication for Read-Only transactions
+: Snapshot sharing (HANA), Spanner, Cockroach, Clock-SI
—: GTM, ...
Design objectives: transaction manager
43
Fault tolerance.
Paxos. Distributed consensus, low level.
Raft. Complete state-machine replication solution with failure
detector on timeouts and autorecovery. But all writes are
proxied to one node.
2PC. Blocks in case of node and coordinator failure. Postgres
already support 2pc.
3PC-like. Extra message between "P"and "C". 3PC, Paxos
commit, E3PC.
Design objectives: liveness
44
Summary.
No or small performance penalty for reads.
Tx can be issued to any node.
No special actions required in case of failure.
Design objectives
45
github.com/postgrespro/postgres_cluster
Patched version of Postgres 9.6
Transaction Manager API + Deadlock detection API.
Logical decoding of 2PC transactions.
Mmts extension.
Transaction Manager implementation (Clock-SI)
Logical replication protocol/client
Hooks on transaction commit and transforms it into 2PC.
Bunch of bgworkers.
Implementation
46
Mmts uses logical replication/decoding.
In-core support and extension by 2ndQuadrant.
Very flexible:
Can skip tables
Replication between different versions
Logical messages
Implementation
47
BE – backend, WS – Walsender, Arb – Arbiter, WR – Walreceiver
Implementation
48
Transaction Manager.
Clock-SI algorithm (MS research)
Make use of CSN instead of running lists. (we track xid-csn
correspondence in extension, but there is ongoing work to have
CSN in-core by Heikki and Alexander)
Implementation
49
DDL replication.
Statement-based.
Happily, postgres support 2PC for almost all DDL (alter enum
already fixed in -master)
CREATE TABLE AS, CREATE MATVIEW, etc – tricky, mixes
DDL and DML.
Temp tables are tricky – shouldn’t be replicted.
Depends on environment (search_path, auth, etc.)
Implementation
50
Postgres compatibility.
almost FULLY compatible with pg.
162 of 166 regressions tests pass as is.
1 test is using prepared statement inside CREATE TABLE AS
(CTA).
3 tests are using CTA(CTA(TEMP TABLE)).
Some obvious way to abuse statement based replication, e.g.
write function that create table with name based on current
timestamp.
Also sequences can add pain.
Implementation
51
Automatic recovery: normal work
Implementation
52
Automatic recovery: network split
Implementation
53
Automatic recovery: recovery process
Implementation
54
Automatic recovery: normal work again
Implementation
55
Not that hard:
Install mmts extension
Postgres:
max_prepared_transactions
wal_level = logical
max_worker_processes, max_replication_slots,
max_wal_senders
shared_preload_libraries = ’multimaster’
Multimaster extension:
multimaster.node_id = ...
multimaster.conn_strings = ’...’
Configuration
56
We want:
Test cluster liveness against network problems, restarts,
timeshifts, etc.
Sound like Jepsen. But unfortunately it uses ssh on precreated
vm’s/servers. That’s okay for single test, but painful for CI.
No sane way of testing network split with processes, i.e.
postgres TAP test framework is not helpful with that.
Testing
57
So we are using python unittest with docker.
3-5 containers is _way_ faster to start than vm’s.
takes 10 seconds to compile mmts extension, init and start
cluster.
failure injection via docker.exec (iptables, shift time, etc).
compatible with Travis-CI.
Testing
58
Testing itself: attach clients to each node of cluster and start
abusing nodes.
Client: bank-like test case. Transfer money between accounts
with concurrent total balance calculation.
Testing
59
Failures injected:
Node stop-start
Node kill-start
Node in network partition
Edge network split (a.k.a. majority rings)
Shift time
Change clock speed on nodes with libfaketime *
* – not yet implemented.
Testing
60
Performance.
Read-only tx speed is the same as in standalone postgres.
Commit takes more time (two net roundtrips).
Logical decoding slows down big transactions – but that
should be fixed, patch on commitfest.
Testing
61

Multimaster2

  • 1.
  • 2.
    Коротко про мульти-мастер Чтотакое транзакция и уровень изоляции транзакций Как делают транзакции в версионных БД (MVCC) Как делают транзакции в распределенных БД Как мы сделали распределенные транзакции в Postgres. Как мы сделали мульти-мастер репликацию поверх таких транзакций О чем расскажем 2
  • 3.
    Мультимастер: Репликация. Одинаковые данные(*) Распределенные транзакции OLTP нагрузка Кластер доступен пока работает большинство нод Ноду можно добавлять на лету Совместимо с постгресом Мульти-мастер, быстро 3
  • 4.
    Мультимастер не предназначендля: Шардинга Масштабирования по записи Мульти-мастер, быстро 4
  • 5.
  • 6.
  • 7.
    Обычно в книгахпишут два определения: Атомарное действие над несколькими объектами Атомарность + Сериализуемость – результат выполнения нескольких параллельных транзакций равносилен некоторому последовательному (серийному) их выполнению. Концепция транзакций 7
  • 8.
    // Пишем программу,которая во много процессов/потоков делает такие действия // x – переменная в общей памяти, my_id – локальный номер процесса/потока while(true){ x = my_id if (x != my_id) print("Wow!") } Пример: race condition 8
  • 9.
    x += 1– ? x = 4294967297 – ? Еще примеры race condition 9
  • 10.
    x += 1– может быть атомарно если процессор умеет делать инструкции память-память. И если он один. x = 4294967297 – две инструкции на x32 Еще примеры race condition 10
  • 11.
    Atomicity – Visibility,CLOG Consistency – просто нужна была буква Isolation – locks, MVCC Durability – WAL ACID 11
  • 12.
  • 13.
    (Peter Bailis, VLDB,2013) Уровни изоляции 13
  • 14.
    Consistency (Linearizability =Serializability) Availability Partition tolerance Нюансы: Внутри стойки P – не проблема (Stonebraker) Consistency в БД это не Linearizability CAP теорема 14
  • 15.
  • 16.
    Хотим посчитать отчет. T1 ra(500) rb(500) wa(600) wb(400) Comm T2 selectsum(v) from t; Получим 1100 =/ Изоляция: dirty read 16
  • 17.
    Хотим присвоить двумобъектам одинаковое значение. T1 wa(12) wb(12) Comm T2 wa(10) wb(10) Comm −→ T1 wa(12) wb(12) Comm T2 ↓ ↓ ↓ ↓ wa(10) wb(10) Comma = 10, b = 12 a = 10, b = 10 Кто видит проблему? =) Изоляция: dirty write 17
  • 18.
    T1 wa(10) wb(10) ↓ ↓ T2 wb(20) wa(20) ↓ ↓ ↓ Deadlock detection: еслипроцесс проспал время T на локе, то строим граф блокировок и ищем циклы. Нашли циклы – принудительно завершаем одну из транзакций в цикле. Борьба: упорядочить изменения внутри транзакций. Выбрать более строгий уровень изоляции. Изоляция: dead lock 18
  • 19.
    Делаем интерфейс дляпросмотра состояния счетов. uid 1 1 acc 1 2 amount 500 500 T1 wa(600) wb(400) Comm T2 ra(500) rb(400) Comm Бэкапы Аналитика Изоляция: non-repeatable read 19
  • 20.
    acc1 {5, 0, 1,500} {5, 42, 1, 500} {42, 0, 1, 600} T42 r1(500) w1(600) r2(500) Comm T43 r2(500) w2(400) Comm acc2 {7, 0, 2, 500} {7, 43, 2, 500} {43, 0, 2, 400} MVCC или snapshot isolation 20
  • 21.
    STM чаще всегоделают так же. Нужен vacuum (сборка мусора). MVCC или snapshot isolation, замечания 21
  • 22.
    Repeateble read: Запомнить списокактивных транзакций на момент старта новой Видим строки с xmax <= xid Если строка видима в нашей транзакции, то еще проверяем в CLOG (для игнорирования абортированных транзакций) Замечание №1: если брать список активных на каждое выражение в транзакции, то получим RC. Замечание №2: такие правила видимости специфичны для постгреса. Правила видимости 22
  • 23.
    Кроме последовательности стартовтранзакций можно вести еще последовательность завершений (CSN) Тогда список активных не нужен Правила видимости, альтернативно 23
  • 24.
    Со списками активных: Xid-ыполучаем сквозные на кластер Списки активных объединяем со всех нод Коммит по 2PC (Как на свадьбе) Новое состояние в CLOG: in doubt. Ставим между Prepare и Commit. С CSN: CSN-ы получаем сквозные на кластер Коммит по 2PC Новое состояние в CLOG: in doubt. +: Eсть теория децентрализации такого подхода +: для CSN можно использовать физическое время (георепликация) Распределенные транзакции 24
  • 25.
    Slide by SamehElnikety Snapshot isolation, традиционно 25
  • 26.
    Slide by SamehElnikety Snapshot isolation, начало транзакции 26
  • 27.
    Slide by SamehElnikety Snapshot isolation, выполнение транзакции 27
  • 28.
    Slide by SamehElnikety Snapshot isolation, коммит транзакции 28
  • 29.
    Slide by SamehElnikety Clock-SI 29
  • 30.
    Slide by SamehElnikety Clock-SI, начало транзакции 30
  • 31.
    Slide by SamehElnikety Clock-SI, выполнение транзакции 31
  • 32.
    Slide by SamehElnikety Clock-SI, коммит транзакции 32
  • 33.
    Slide by SamehElnikety Clock-SI, рассинхронизация часов 33
  • 34.
    Slide by SamehElnikety Clock-SI, рассинхронизация часов 34
  • 35.
    Slide by SamehElnikety Clock-SI, рассинхронизация часов 35
  • 36.
    Slide by SamehElnikety Clock-SI, рассинхронизация часов 36
  • 37.
    Slide by SamehElnikety Clock-SI, продолжительный коммит 37
  • 38.
    Slide by SamehElnikety Clock-SI, продолжительный коммит 38
  • 39.
    Slide by SamehElnikety Clock-SI, продолжительный коммит 39
  • 40.
    Slide by SamehElnikety Clock-SI, продолжительный коммит 40
  • 41.
  • 42.
    Replication: Identical replicated dataon all nodes Possibility to have local tables Writes allowed to any node => Easy to use => We need to take care about proper isolation Design objectives: replication 42
  • 43.
    Transaction manager. Wewant: Avoid single point of failure. +: Spanner, Cockroach, Clock-SI —: Snapshot sharing, GTM, ... Avoid network communication for Read-Only transactions +: Snapshot sharing (HANA), Spanner, Cockroach, Clock-SI —: GTM, ... Design objectives: transaction manager 43
  • 44.
    Fault tolerance. Paxos. Distributedconsensus, low level. Raft. Complete state-machine replication solution with failure detector on timeouts and autorecovery. But all writes are proxied to one node. 2PC. Blocks in case of node and coordinator failure. Postgres already support 2pc. 3PC-like. Extra message between "P"and "C". 3PC, Paxos commit, E3PC. Design objectives: liveness 44
  • 45.
    Summary. No or smallperformance penalty for reads. Tx can be issued to any node. No special actions required in case of failure. Design objectives 45
  • 46.
    github.com/postgrespro/postgres_cluster Patched version ofPostgres 9.6 Transaction Manager API + Deadlock detection API. Logical decoding of 2PC transactions. Mmts extension. Transaction Manager implementation (Clock-SI) Logical replication protocol/client Hooks on transaction commit and transforms it into 2PC. Bunch of bgworkers. Implementation 46
  • 47.
    Mmts uses logicalreplication/decoding. In-core support and extension by 2ndQuadrant. Very flexible: Can skip tables Replication between different versions Logical messages Implementation 47
  • 48.
    BE – backend,WS – Walsender, Arb – Arbiter, WR – Walreceiver Implementation 48
  • 49.
    Transaction Manager. Clock-SI algorithm(MS research) Make use of CSN instead of running lists. (we track xid-csn correspondence in extension, but there is ongoing work to have CSN in-core by Heikki and Alexander) Implementation 49
  • 50.
    DDL replication. Statement-based. Happily, postgressupport 2PC for almost all DDL (alter enum already fixed in -master) CREATE TABLE AS, CREATE MATVIEW, etc – tricky, mixes DDL and DML. Temp tables are tricky – shouldn’t be replicted. Depends on environment (search_path, auth, etc.) Implementation 50
  • 51.
    Postgres compatibility. almost FULLYcompatible with pg. 162 of 166 regressions tests pass as is. 1 test is using prepared statement inside CREATE TABLE AS (CTA). 3 tests are using CTA(CTA(TEMP TABLE)). Some obvious way to abuse statement based replication, e.g. write function that create table with name based on current timestamp. Also sequences can add pain. Implementation 51
  • 52.
    Automatic recovery: normalwork Implementation 52
  • 53.
    Automatic recovery: networksplit Implementation 53
  • 54.
    Automatic recovery: recoveryprocess Implementation 54
  • 55.
    Automatic recovery: normalwork again Implementation 55
  • 56.
    Not that hard: Installmmts extension Postgres: max_prepared_transactions wal_level = logical max_worker_processes, max_replication_slots, max_wal_senders shared_preload_libraries = ’multimaster’ Multimaster extension: multimaster.node_id = ... multimaster.conn_strings = ’...’ Configuration 56
  • 57.
    We want: Test clusterliveness against network problems, restarts, timeshifts, etc. Sound like Jepsen. But unfortunately it uses ssh on precreated vm’s/servers. That’s okay for single test, but painful for CI. No sane way of testing network split with processes, i.e. postgres TAP test framework is not helpful with that. Testing 57
  • 58.
    So we areusing python unittest with docker. 3-5 containers is _way_ faster to start than vm’s. takes 10 seconds to compile mmts extension, init and start cluster. failure injection via docker.exec (iptables, shift time, etc). compatible with Travis-CI. Testing 58
  • 59.
    Testing itself: attachclients to each node of cluster and start abusing nodes. Client: bank-like test case. Transfer money between accounts with concurrent total balance calculation. Testing 59
  • 60.
    Failures injected: Node stop-start Nodekill-start Node in network partition Edge network split (a.k.a. majority rings) Shift time Change clock speed on nodes with libfaketime * * – not yet implemented. Testing 60
  • 61.
    Performance. Read-only tx speedis the same as in standalone postgres. Commit takes more time (two net roundtrips). Logical decoding slows down big transactions – but that should be fixed, patch on commitfest. Testing 61