wiki:Phase1_Scaling_Replication

Скалирање и репликација на базите на податоци

Во овој дел ќе демонстрираме имплементација на физичка streaming репликација на PostgreSQL база на податоци помеѓу два компјутери во локална мрежа. Streaming репликацијата претставува механизам за континуирано пренесување на WAL записи од примарниот сервер до репликата во реално време преку WAL (Write-Ahead Log) записи. PostgreSQL користи Write-Ahead Logging механизам, при што секоја промена во базата (INSERT, UPDATE, DELETE) најпрво се запишува во WAL лог датотеки. Овие WAL записи потоа се испраќаат до репликата, која ги репродуцира (replay) истите операции и на тој начин ја одржува синхронизацијата со примарниот сервер.

Типови на репликација во PostgreSQL

PostgreSQL поддржува физичка и логичка репликација.

  • Физичката репликација создава точна, byte-for-byte копија на примарниот сервер на еден или повеќе standby сервери. Ова се постигнува со континуирано читање и праќање на WAL записи од примарниот сервер до standby серверите, кои ги применуваат промените локално, што е возможно бидејќи WAL содржи детална историја за сите промени направени на базата.
  • Логичката репликација во PostgreSQL функционира на ниво на SQL операции, при што се реплицираат логички промени како INSERT, UPDATE и DELETE. Таа користи publish–subscribe модел, каде примарниот сервер дефинира publication, а репликата креира subscription за одредени табели. Овој пристап овозможува селективна репликација и поголема флексибилност во споредба со физичката репликација.

Асинхрона и синхрона репликација

Streaming репликацијата може да функционира во асинхрон или синхрон режим.

  • Кај асинхроната репликација, примарниот сервер не чека потврда од репликата за запишаните WAL записи. Трансакцијата се смета за успешно завршена веднаш по запишување во WAL логот на примарниот сервер. Овој пристап овозможува подобри перформанси и помало доцнење, но постои ризик од губење на податоци доколку примарниот сервер падне пред репликата да ги прими сите записи.
  • Кај синхроната репликација, примарниот сервер чека потврда од репликата пред да ја потврди трансакцијата. Овој режим гарантира целосна конзистентност и нема ризик од губење на податоци, но резултира со поголемо доцнење и намалени перформанси.

Во нашиот пример ќе кориситме streaming асинхрона репликација.

Конфигурација на Primary серверот

services:
  postgres-primary:
    image: postgres:18
    container_name: postgres-primary
    networks:
      - anb_pgnet
    restart: always
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin
      POSTGRES_DB: film_rental
    ports:
      - "5432:5432"
    volumes:
      - ./init-schema.sql:/docker-entrypoint-initdb.d/01-schema.sql
      - ./insert-data.sql:/docker-entrypoint-initdb.d/02-data.sql
      - pg_primary_data:/var/lib/postgresql
      - ./pg_hba.conf:/etc/postgresql/pg_hba.conf
    command: >
      postgres
      -c wal_level=replica
      -c max_wal_senders=10
      -c max_replication_slots=10
      -c listen_addresses='*'
      -c hba_file=/etc/postgresql/pg_hba.conf

volumes:
  pg_primary_data:

networks:
  anb_pgnet:
    driver: bridge
    ipam:
      config:
        - subnet: 172.19.0.0/16

За примарниот сервер користиме Docker контејнер со PostgreSQL 18. За потребите на репликацијата, најважни се следните параметри:

  • wal_level=replica - овозможува генерирање на WAL записи потребни за физичка репликација.
  • max_wal_senders=10 - дозволува до 10 паралелни процеси за испраќање WAL записи кон реплики.
  • max_replication_slots=10 - овозможува користење replication slots.
  • listen_addresses='*' - дозволува примање конекции од други машини.
  • hba_file - дефинира custom pg_hba.conf конфигурација за контролирање на пристапот.

За да се обезбеди детерминистичко IP адресирање, дефинирана е фиксна Docker bridge мрежа со subnet 172.19.0.0/16.

Конфигурација на pg_hba.conf (hba_file)

За да може standby серверот да се поврзе кон примарниот сервер и да прима WAL записи, потребно е да се дозволи replication конекција во pg_hba.conf:

# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    replication     replicator      172.19.0.1/32       scram-sha-256
local   all   all   trust
  • host - правилото се однесува на TCP/IP конекции.
  • replication - правилото важи за replication конекции, а не за нормален пристап до база.
  • replicator - корисникот кој има REPLICATION привилегија.
  • 172.19.0.1/32 - ја претставува Docker gateway адресата преку која пристигнува replication конекцијата. Маската /32 означува дека е дозволена точно една IP адреса.
  • scram-sha-256 - означува дека конекцијата се автентицира со лозинка користејќи SCRAM (Salted Challenge Response Authentication Mechanism) базиран на SHA-256 алгоритам.

Бидејќи примарниот сервер работи во Docker контејнер, standby серверот не се појавува со својата реална LAN IP адреса (192.168.1.103). Docker користи bridge networking и NAT механизам, при што конекцијата кон контејнерот се појавува од Docker gateway адресата 172.19.0.1.

Поради тоа, replication пристапот е ограничен исклучиво на gateway адресата (172.19.0.1/32), со што се обезбедува минимален и контролиран пристап до replication функционалноста.

Дополнително, за локални Unix socket конекции во контејнерот потребно е:

local all all trust

За да може standby серверот да се поврзе и да прима WAL записи од примарниот сервер, потребно е да се креира корисник со REPLICATION привилегија.

film_rental=# CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'replica_pass';
CREATE ROLE 

Иницијализација на standby серверот со pg_basebackup

За да може standby серверот да започне со streaming репликација, потребно е да се направи иницијална конзистентна копија од примарната база. Ова се постигнува со алатката pg_basebackup, која креира физичка копија од целиот PostgreSQL кластер додека серверот е активен.

Командата што се користи е:

pg_basebackup -h 192.168.1.219 -U replicator -D ./data/18/docker -P -v -R

  • -h – IP адреса на примарниот сервер
  • -U – replication корисник
  • -D – директориум каде ќе се зачува копијата
  • -P – прикажува прогрес
  • -v – verbose режим
  • -R – автоматски креира standby.signal и конфигурација за поврзување со primary серверот

Со ова standby серверот добива конзистентна копија од состојбата на базата во моментот на backup и е подготвен да започне со streaming на WAL записи.

pg_basebackup -h 192.168.1.219 -U replicator -D ./data/18/docker -P -v -R 
Password: 
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/7000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_284"
34297/34297 kB (100%), 1/1 tablespace                                         
pg_basebackup: write-ahead log end point: 0/7000120
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed

По иницијализацијата со pg_basebackup, standby серверот се стартува преку Docker контејнер кој го користи директориумот ./data како PostgreSQL кластер.

services:
  postgres-replica:
    image: postgres:18
    container_name: postgres-replica
    restart: always
    ports:
      - "5433:5432"
    volumes:
      - ./data:/var/lib/postgresql
    command: >
      postgres
      -c hot_standby=on

  • hot_standby=on - дозволува read-only операции на репликата.

Бидејќи во директориумот веќе постои standby.signal датотека, PostgreSQL автоматски стартува во recovery режим и воспоставува streaming конекција кон примарниот сервер.

Верификација на репликацијата

На standby серверот може да се провери дали серверот е во recovery режим со следната команда:

film_rental=# SELECT pg_is_in_recovery();
 pg_is_in_recovery 
-------------------
 t
(1 row)

Вредноста t (true) означува дека серверот работи во recovery режим, односно дека е standby сервер. На примарниот сервер може да се провери дали постои активна replication конекција:

film_rental=# SELECT client_addr, state, sync_state FROM pg_stat_replication;
 client_addr |   state   | sync_state
-------------+-----------+------------
 172.19.0.1  | streaming | async
(1 row)
  • streaming означува дека WAL записите се испраќаат кон standby серверот
  • async означува дека се користи асинхрона репликација

Тестирање на репликацијата со INSERT

На primary серверот внесуваме нов запис:

docker exec -it postgres-primary psql -U admin -d film_rental -c "INSERT INTO language(name) VALUES ('OvaETest');"
INSERT 0 1

Потоа на standby серверот проверуваме дали записот е реплициран:

docker exec -it postgres-replica psql -U admin -d film_rental -c "SELECT name FROM language WHERE name='OvaETest';"        
   name   
----------
 OvaETest
(1 row)

Ова потврдува дека streaming репликацијата функционира правилно и дека промените направени на примарниот сервер се пренесуваат и применуваат на standby серверот во реално време.

Promotion and failover

За да се демонстрира промена на лидерот во системот, се симулира пад на примарниот сервер и рачно се промовира standby серверот. Во оваа поставеност примарниот PostgreSQL сервер работи на PC, додека standby серверот работи на вториот компјутер во истата локална мрежа. Целта на овој тест е да се прикаже дека standby серверот може да ја преземе улогата на primary сервер, а стариот primary потоа да се врати како нов standby сервер.

Симулирање пад на primary серверот

На PC се запира примарниот PostgreSQL контејнер:

docker compose down

Со ова примарниот сервер повеќе не е достапен. Бидејќи standby серверот на вториот компјутер веќе ги примал WAL записите од примарниот сервер, тој може да се промовира во нов primary сервер. По запирањето на primary серверот, на standby серверот се појавуваат логови кои покажуваат дека streaming replication конекцијата е прекината:

postgres-replica  | 2026-05-10 22:35:01.071 UTC [33] LOG:  replication terminated by primary server
postgres-replica  | 2026-05-10 22:35:01.071 UTC [33] DETAIL:  End of WAL reached on timeline 1 at 0/4000710.
postgres-replica  | 2026-05-10 22:35:01.072 UTC [33] FATAL:  could not send end-of-streaming message to primary: server closed the connection unexpectedly
postgres-replica  |             This probably means the server terminated abnormally
postgres-replica  |             before or while processing the request.
postgres-replica  |     invalid socket
postgres-replica  |     no COPY in progress
postgres-replica  | 2026-05-10 22:35:01.072 UTC [32] LOG:  invalid record length at 0/4000710: expected at least 24, got 0
postgres-replica  | 2026-05-10 22:35:01.085 UTC [43] FATAL:  streaming replication receiver "walreceiver" could not connect to the primary server: connection to server at "192.168.1.219", port 5432 failed: FATAL:  the database system is shutting down
postgres-replica  | 2026-05-10 22:35:01.085 UTC [32] LOG:  waiting for WAL to become available at 0/4000728
postgres-replica  | 2026-05-10 22:35:06.503 UTC [30] LOG:  restartpoint starting: time
postgres-replica  | 2026-05-10 22:35:06.511 UTC [30] LOG:  restartpoint complete: wrote 0 buffers (0.0%), wrote 0 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=0.001 s, sync=0.001 s, total=0.008 s; sync files=0, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=13271 kB; lsn=0/4000698, redo lsn=0/4000698
postgres-replica  | 2026-05-10 22:35:06.511 UTC [30] LOG:  recovery restart point at 0/4000698
postgres-replica  | 2026-05-10 22:35:06.511 UTC [30] DETAIL:  Last completed transaction was at log time 2026-05-10 22:13:03.759754+00.

Овие логови покажуваат дека standby серверот ја изгубил конекцијата со primary серверот и чека нови WAL записи. Ова е очекувано однесување, бидејќи primary контејнерот е запрен.

Дополнително, може да се провери дека повеќе нема активен WAL receiver:

film_rental=# SELECT status, sender_host, sender_port, latest_end_lsn, latest_end_time FROM pg_stat_wal_receiver;
 status | sender_host | sender_port | latest_end_lsn | latest_end_time 
--------+-------------+-------------+----------------+-----------------
(0 rows)

Резултатот (0 rows) означува дека standby серверот во тој момент нема активна WAL receiver конекција кон primary серверот.

Promotion на standby серверот

Promotion се извршува на standby серверот со командата:

film_rental=# SELECT pg_promote();
 pg_promote 
------------
 t
(1 row)

По promotion се проверува дали серверот повеќе не е во recovery режим:

film_rental=# SELECT pg_is_in_recovery();
 pg_is_in_recovery 
-------------------
 f
(1 row)

Вредноста f означува дека серверот повеќе не работи како standby, туку е промовиран во normal/primary режим и може да прима write операции.

Враќање на стариот primary како standby

По promotion, стариот primary сервер на PC не смее само повторно да се стартува како primary. Во тој случај би постоеле два сервери кои можат да примаат write операции, што може да доведе до split-brain ситуација и неконзистентни податоци. Затоа стариот primary треба повторно да се иницијализира како standby сервер кој ќе го следи новиот primary сервер. На PC прво се отстранува старата локална PostgreSQL data содржина или Docker volume што го користел стариот primary. Потоа се прави нов pg_basebackup од промовираниот standby сервер, кој сега ја има улогата на primary:

pg_basebackup -h 192.168.1.103 -p 5433 -U replicator -D ./data/18/docker -P -v -R
Password:
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/5000028 on timeline 2
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_146"
34298/34298 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/5000158
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed

Опцијата -R автоматски креира standby.signal и primary_conninfo, со што PC серверот при следното стартување ќе работи како standby сервер и ќе се поврзе кон новиот primary. Потоа на PC се стартува PostgreSQL како replica:

docker compose up -d

На PC може да се провери дали серверот е standby:

film_rental=# SELECT pg_is_in_recovery();
 pg_is_in_recovery 
-------------------
 t
(1 row)

На новиот primary сервер може да се провери дали PC серверот е поврзан како replica:

film_rental=# SELECT client_addr, state, sync_state FROM pg_stat_replication;
  client_addr  |   state   | sync_state 
---------------+-----------+------------
 192.168.1.219 | streaming | async
(1 row)

Со ова улогите се заменети: standby серверот е промовиран во нов primary сервер, а стариот primary сервер на PC е повторно иницијализиран како standby сервер.

Забелешка

За стариот primary сервер на PC да може да стане replica, новиот primary сервер мора да дозволува replication конекции. Бидејќи по promotion standby серверот ја презема улогата на primary, неговиот pg_hba.conf станува активната конфигурација за контролирање на пристапот.

Поради тоа, во pg_hba.conf на новиот primary сервер се додава правило кое дозволува replication конекција од PC:

host    replication     replicator      192.168.1.219/32      scram-sha-256

Ова правило значи дека корисникот replicator може да се поврзе за replication од IP адресата на PC серверот 192.168.1.219, користејќи password authentication со scram-sha-256.

По измената на pg_hba.conf конфигурацијата се извршува reload:

SELECT pg_reload_conf();

Доколку ова правило не постои, pg_basebackup од PC кон новиот primary сервер не може да се изврши и PostgreSQL враќа грешка дека нема соодветен pg_hba.conf entry за replication конекцијата.

Last modified 2 weeks ago Last modified on 05/11/26 23:24:00
Note: See TracWiki for help on using the wiki.