= Скалирање и репликација на базите на податоци Во овој дел ќе демонстрираме имплементација на физичка 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 hot_standby=on -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 серверот во реално време.