Template for PostgreSQL HA with Patroni and HA test

ในการเริ่มต้นสร้าง PostgreSQL ด้วย patroni จะพบว่ามี config ค่อนข้างเยอะ จะมีวิธีไหนบ้างที่ง่ายๆ ที่จะ start database เพื่อทดลอง feature และ evaluate การทำงานใน case ต่างๆ เช่น กรณี fail over, read split-brain รวมถึงการ tuning เพื่อหา parameter ที่เหมาะสมไปใช้สำหรับงานจริง ในลักษณะของ sandbox ส่วนตัว แนะนำให้ใช้ template จาก Patroni: A Template for PostgreSQL HA with ZooKeeper, etcd or Consul โดยขั้นตอนตาม script ดังนี้

$ git clone https://github.com/zalando/patroni.git
$ cd patroni
$ docker build -t patroni .
$ docker-compose up -d

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4505a559a9e3 patroni "/bin/sh /entrypoint…" 43 seconds ago Up 42 seconds demo-etcd2
3b5183d6cee9 patroni "/bin/sh /entrypoint…" 43 seconds ago Up 42 seconds demo-etcd1
bd982fed4baa patroni "/bin/sh /entrypoint…" 43 seconds ago Up 42 seconds demo-patroni1
425d51032cb2 patroni "/bin/sh /entrypoint…" 43 seconds ago Up 42 seconds 0.0.0.0:5000-5001->5000-5001/tcp demo-haproxy
07cc2bfec72c patroni "/bin/sh /entrypoint…" 43 seconds ago Up 42 seconds demo-etcd3
cfa97b608d07 patroni "/bin/sh /entrypoint…" 43 seconds ago Up 42 seconds demo-patroni3
96a5b49cd787 patroni "/bin/sh /entrypoint…" 43 seconds ago Up 42 seconds demo-patroni2

จะเห็นว่า docker-compose.yml ได้ start patroni ขึ้นมา 3 node, etcd 3 node และ haproxy 1 node โดย expose port 5000 สำหรับ write และ 5001 สำหรับ read มาให้เลย

ทดสองเรียกใช้ cmd ของ patroni ใน cluster

$ docker exec -it demo-patroni1 patronictl list
+ Cluster: demo (7351235807499665431) --------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+------------+---------+-----------+----+-----------+
| patroni1 | 172.19.0.6 | Leader | running | 1 | |
| patroni2 | 172.19.0.7 | Replica | streaming | 1 | 0 |
| patroni3 | 172.19.0.5 | Replica | streaming | 1 | 0 |
+----------+------------+---------+-----------+----+-----------+

ทดสอบ edit config ของ postgresql instance

$ docker exec -i demo-patroni1 patronictl edit-config --apply - --force <<'JSON'
{
synchronous_mode: "on",
synchronous_mode_strict: "on",
"postgresql":
{
"parameters":{
"synchronous_commit": "on",
"synchronous_standby_names": "*"
}
}
}
JSON

$ docker exec -it demo-patroni2 patronictl show-config
loop_wait: 10
maximum_lag_on_failover: 1048576
postgresql:
parameters:
max_connections: 100
synchronous_commit: 'on'
synchronous_standby_names: '*'
pg_hba:
- local all all trust
- host replication replicator all md5
- host all all all md5
use_pg_rewind: true
retry_timeout: 10
synchronous_mode: 'on'
synchronous_mode_strict: 'on'
ttl: 30

ทดสอบ switch over primary node

$ docker exec -it demo-patroni1 patronictl list
+ Cluster: demo (7351235807499665431) -+-----------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+------------+--------------+-----------+----+-----------+
| patroni1 | 172.19.0.6 | Sync Standby | streaming | 3 | 0 |
| patroni2 | 172.19.0.7 | Leader | running | 3 | |
| patroni3 | 172.19.0.5 | Replica | streaming | 3 | 0 |
+----------+------------+--------------+-----------+----+-----------+

$ docker exec -it demo-patroni1 patronictl switchover --candidate patroni1 --force
Current cluster topology
+ Cluster: demo (7351235807499665431) -+-----------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+------------+--------------+-----------+----+-----------+
| patroni1 | 172.19.0.6 | Sync Standby | streaming | 3 | 0 |
| patroni2 | 172.19.0.7 | Leader | running | 3 | |
| patroni3 | 172.19.0.5 | Replica | streaming | 3 | 0 |
+----------+------------+--------------+-----------+----+-----------+
2024-03-28 03:11:15.87873 Successfully switched over to "patroni1"
+ Cluster: demo (7351235807499665431) ------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+------------+---------+---------+----+-----------+
| patroni1 | 172.19.0.6 | Leader | running | 3 | |
| patroni2 | 172.19.0.7 | Replica | stopped | | unknown |
| patroni3 | 172.19.0.5 | Replica | running | 3 | 0 |
+----------+------------+---------+---------+----+-----------+

ทดสอบใช้งาน postgresql

$ docker exec -i demo-patroni1 psql <<'SQL'
create table test(name varchar(50));
insert into test(name) values ('tao');
SQL
CREATE TABLE
INSERT 0 1

$ docker exec -i demo-patroni1 psql <<'SQL'
select * from test;
SQL
name
------
tao
(1 row)

ทดสอบ Postgresql HA ที่ setup ด้วย Patroni

Standby Server Tests

No.Test ScenarioObservation
1Kill Postgresql processPatroni จะ start process Postgresql เองโดยอัตโนมัติเมื่อเจอว่า postgresql process หายไป จากกระบวนการ health check
– เนื่องจากเป็น process ที่ standby server จะไม่ส่งผลต่อ application ขณะทำการ write data
2Stop Postgresql processPatroni ทำการ start process เช่นเดียวกับ case 1 โดยไม่ส่งผลต่อ application ขณะทำการ write data
3Reboot serverกรณีนี้จะต้อง set patroni service ให้เริ่มทำงานหลังจาก server boot เสร็จแล้ว ซึ่ง postgresql จะทำงานตามปกติหลังจาก service start
– ไม่ส่งผลต่อ application ขณะทำการ write data
4Stop patroni process – Postgresql process ยังคงทำงานปกติ
– ไม่สามารถใช้ cmd patronictl
– ไม่ส่งผลต่อ application ขณะทำการ write data

Primary Server Tests

NoTest ScenarioObservation
1Kill Postgresql processPatroni ทำการ start postgresql process จากกระบวนการ health check แต่เนื่องจากเป็น node primary ที่มี primary lock อยู่แล้ว จึงไม่เกิดการกระบวนการ switch over ไปยัง replica node
– ส่งผลต่อ application ไม่สามารถ write data ได้ในขณะ start process
2Reboot serverทำการ fail over ไปยัง standby server ด้วยกระบวนการเลือก standby server ที่อยู่ใน member หลังจากที่ server boot และ process postgresql ของ primary เดิมเริ่มทำงาน postgresql จะทำกระบวนการ pg_rewind เพื่อ update ข้อมูลล่าสุดและเปลี่ยนสถานะตัวเองเป็น standby server
– ส่งผลต่อ application ไม่สามารถ write data ได้
3Stop/kill patroni process – Standby Server จะ promote ตัวเองเป็น primary จากกระบวนการ acquired DCS Lock
– Primary server เดิมยังคงทำงาน รวมถึงการเขียนก็ยังเขียนที่ Primary server เดิม ซึ่งทำให้เกิด 2 Primary server ทำงานในเวลาเดียวกัน
– หลังจากที่ Patroni กลับมาทำงานอีกครั้งที่ Primary Server เดิม จะมีกระบวนการ pg_rewind เพื่อ update ข้อมูลล่าสุด และเปลี่ยนสถานะตัวเองเป็น Follower

* ในระหว่างที่เกิดเหตุการณ์ 2 primary server ทำงานพร้อมกันอาจจะมี data loss ได้ จะต้อง set replication parameter ให้เหมาะสม

Network Isolation Tests

No.Test ScenarioObservation
1Primary server ไม่สามารถ connect กับ Standby server ได้ – Postgresql หลุดจากการเป็น Primary server
– เกิดกระบวนการเลือก Primary server ใหม่จาก member ที่เหลืออยู่
– ส่งผลให้ application ไม่สามารถเขียนข้อมูลได้
2Standby server ไม่สามารถ connect กับ Server ใน member ได้ – Postgresql ยังคงทำงาน แต่จะไม่ถูกเลือกในกระบวนการเลือก Primary server ใหม่
– ไม่ส่งผลต่อการเขียนข้อมูลของ Applicaiton

Reference

Patroni for PostgreSQL

Patroni: A Template for PostgreSQL HA with ZooKeeper, etcd or Consul