Hlavní navigace

Slony1 - Replikace pro PostgreSQL (3)

12. 8. 2004
Doba čtení: 6 minut

Sdílet

V minulém dílu jsem spustili agenty, dnes se podíváme na jejich vzájemnou komunikaci.

Propojení uzlů

Dalším krokem je vytvořit tabulku, která popisuje topologii fyzických síťových propojení mezi jednotlivými uzly. Spojení se navazují ve směru client → server a v tabulce propojení musí být záznamy pro oba směry, pokud je obousměrné spojení uzlů možné. Pokud toto spojení možné není, tak vhledem k tomu, že Slonik umí cascading slaves, lze použít spojení přes jiný uzel. Pokud není možmé ani to (dialup s dynamickou IP adresou), používají se pro tento účel ssh tunely, na nichž je mimořádně sympatická komprese přenášených dat.

Vložení toho záznamu lze přirovnat k provedení příkazu ifconfig. Nejdřív spojíme slave1 s masterem:

STORE PATH ( client = 2,server = 1,  conninfo = 'dbname=master host=localhost user=slony password=iehovah'); 

Slave agent na to ihned zareaguje zpracováním události STORE_PATH. Číslo 2 je číslo uzlu, který vygeneroval tuto zprávu.

DEBUG2 localListenThread: Received event 2,14 SYNC
DEBUG2 localListenThread: Received event 2,15 STORE_PATH
CONFIG storePath: pa_server=1 pa_client=2 pa_conninfo="dbname=master host=localhost user=slony password=iehovah" pa_connretry=10
DEBUG2 syncThread: new sl_action_seq 1 - SYNC 16 

a pokračuje dál jakoby nic, s master databází komunikovat nehodlá. V systémové databázi si slave1 vytvořil příslušný záznam:

slave1=# SELECT * from sl_path;
 pa_server | pa_client |                      pa_conninfo                         | pa_connretry
----------+-----------+ ------------------------- ----------------------- -------+--------------
        1 |         2 | dbname=master host=localhost user=slony password=iehovah |           10
(1 row) 

Podíváme se na master uzel:

master=# SELECT * from sl_path ;
 pa_server | pa_client | pa_conninfo | pa_connretry
-----------+-----------+ ------------+--------------
(0 rows) 

zatím žádný záznam. Provedeme stejnou operaci pro mastera:

STORE PATH ( client = 1,server = 2,  conninfo = 'dbname=slave1 host=localhost user=slony password=iehovah'); 

a i on si vytvoří ve své tabulce spojení příslušný záznam.

Routing zpráv

Servery spolu i nadále nekomunikují, ačkoliv máme nastavená vzájemná spojení. Dříve bylo nutné v UNIXech zadávat pro lokální síťová rozhraní kromě ifconfigu také route add. V konfiguračním jazyku slonik existuje pro toto příkaz STORE LISTEN, který má parametry ORIGIN, RECEIVER a PROVIDER. Tyto parametry lépe pochopíme, pokud si je převedeme do syntaxe příkazu route (použitá syntaxe je z OS Linux).

route add -host ORIGIN gw PROVIDER if RECEIVER 

Pokud jsou PROVIDER a ORIGIN shodné, nemusí se PROVIDER zadávat.

Spojíme slave1 s masterem:

STORE LISTEN ( origin=1, receiver=2); 

Slave na to zareaguje a spojí se s masterem.

DEBUG2 localListenThread: Received event 2,55 STORE_LISTEN
CONFIG storeListen: li_origin=1 li_receiver=2 li_provider=1
DEBUG1 remoteListenThread_1: thread starts
DEBUG2 remoteListenThread_1: start listening for event origin 1
DEBUG1 remoteListenThread_1: connected to 'dbname=master host=localhost user=slony password=iehovah' 

Zpracovávání událostí

slave1 si prohlédne frontu událostí, které má master připraveny k odeslání.

DEBUG2 remoteListenThread_1: queue event 1,104 STORE_NODE
DEBUG2 remoteListenThread_1: queue event 1,105 ENABLE_NODE
DEBUG2 remoteListenThread_1: queue event 1,106 SYNC
.
.
DEBUG2 remoteListenThread_1: queue event 1,176 SYNC
DEBUG2 remoteListenThread_1: queue event 1,177 STORE_PATH
.
.
DEBUG2 remoteListenThread_1: queue event 1,182 SYNC 

Fronta událostí masteru ovšem nezačíná u položky 104.

master=# SELECT min(ev_seqno) from sl_event;
 min
----
  1 

Počáteční události byly ignorovány, protože slave1 byl vytvořen na masteru v čase event_seqno=103, což vypsal agent při startu:

DEBUG2 setNodeLastEvent: no_id=1 event_seq=103 

První události od mastera jsou 104–105, což je právě vytvoření slave1 nodu.

DEBUG2 remoteWorkerThread_1: Received event 1,104 STORE_NODE
DEBUG2 remoteWorkerThread_1: Received event 1,105 ENABLE_NODE
DEBUG2 remoteWorkerThread_1: Received event 1,106 SYNC
DEBUG2 remoteWorkerThread_1: SYNC 176 processing 

Následující SYNC zprávy byly shrnuty do jedné a zpracována byla poslení s číslem 176.

DEBUG2 remoteWorkerThread_1: no sets need syncing for this event
DEBUG2 remoteWorkerThread_1: Received event 1,177 STORE_PATH
DEBUG2 remoteWorkerThread_1: Received event 1,178 SYNC 

Událostí 177 bylo vytvoření cesty master → slave1. Po jejím zpracování se v tabulce slave1 objevil další záznam:

slave1=# SELECT * from sl_path ;
 pa_server | pa_client |                       pa_conninfo                        | pa_connretry
-----------+-----------+ ------------------------ ------------------------- ------+--------------
         1 |         2 | dbname=master host=localhost user=slony password=iehovah |           10
         2 |         1 | dbname=slave1 host=localhost user=slony password=iehovah |           10
(2 rows) 

a dále již následuje zpracovávání SYNC událostí, jak jsou v jednominutových intervalech generovány masterem.

DEBUG2 remoteWorkerThread_1: SYNC 205 processing
DEBUG2 remoteWorkerThread_1: no sets need syncing for this event
DEBUG2 remoteListenThread_1: queue event 1,206 SYNC
DEBUG2 remoteWorkerThread_1: Received event 1,206 SYNC
DEBUG2 remoteWorkerThread_1: SYNC 206 processing
DEBUG2 remoteWorkerThread_1: no sets need syncing for this event 

Vzájemná komunikace uzlů

Podívejme se, jak probíhá vzájemná komunikace obou uzlů. K přenosu zpráv se využívá dvojice tabulek sl_confirm a sl_event. Do lokální tabulky sl_event jsou ukládány jak lokálně generované události určené k rozeslání na ostatní uzly, tak i události, které sesbírají remoteListenThready ze vzdálených sl_event tabulek a které budou přeposílány na další uzly. Zde je nutné poznamenat, že vzdálené tabulky sl_event i sl_confirm slouží pouze ke čtení. Uzel se taktéž neprobírá celou vzdálenou tabulkou sl_event, ale omezuje se na výběr dosud nezpracovaných zpráv od uzlů, o které je podle direktivy STORE LISTEN zájem. Vzdálená tabulka je kontrolována na přítomnost nových zpráv každých 10 sekund. Kromě toho je ještě použita funkce asynchroní notifikace, takže při malém zatížení serveru jsou zprávy zpracovávány okamžitě. Zprávy jsou po případném nakopírování do lokální tabulky sl_event zpracovány remoteWorkerThre­adem, který po jejich zpracování vytvoří v lokální tabulce sl_confirm záznam potvrzující jejich úspěšné zpracovávání. Zprávy určené i pro další uzly jsou z lokální tabulky sl_even smazány poté, co se objeví ve vzdálené tabulce sl_confirm potvrzení o zpracování.

Potvrzování došlých a zpracovaných zpráv

Potvrzení o úspěšném zpracování zpráv se ukládají do lokální tabulky sl_confirm. Potvrzení v tabulce sl_confirm vypadají takto:

slave1=# SELECT * from sl_confirm;
 con_origin | con_received | con_seqno |       con_timestamp
------------+------------- +-----------+---------- -----------------
          2 |            1 |         0 | 2004-06-26 20:04:41.164657
          1 |            2 |       299 | 2004-06-26 23:16:40.752149
          1 |            2 |       300 | 2004-06-26 23:17:40.896232
          1 |            2 |       301 | 2004-06-26 23:18:41.04357
          1 |            2 |       298 | 2004-06-26 23:15:40.608039 

V tabulce potvrzení vidíme, že uzel 2 (slave1) potvrzuje uzlu 1 (master) přijetí zprávy s pořadovým číslem 301 a všech předchozích. V současné době máme aktivovanou jen jednosměrnou komunikaci, a tak nemáme v tabulce žádné potvrzení o tom, že by uzel 1 zpracoval zprávy, které máme pro něj připravené v tabulce sl_event.

Protože je zbytečné skladovat potvrzení o přijetí předcházejících zpráv, jsou tato potvrzení v pětiminutových intervalech mazána. Vzdálená tabulka sl_confirm není na rozdíl od sl_event periodicky kontrolována po deseti sekundách, ale až po pěti minutách, což je právě v době, kdy se chystáme na úklid nashromážděných zpráv. I v tomto případě je využit mechanismus pro asynchronní notifikaci.

Zpracování vzdálené tabulky potvrzení je vidět po připojení slave1 agenta do master databáze. Vidíme, že nejvyšší zpráva potvrzená masterem je 0.

DEBUG2 remoteWorkerThread_1: forward confirm 2,0 received by 1 

Obousměrná komunikace uzlů

Zprovozníme obousměrnou komunikaci. Sloniku zadáme příkaz:

STORE LISTEN ( origin=1, receiver=2); 

a podíváme se, co na to slave1 agent:

DEBUG2 remoteWorkerThread_1: Received event 1,308 STORE_LISTEN 

Uzel master oznámil začátek naslouchání událostem generovaných uzlem slave1. Také začal zpracovávat zprávy z naší fronty, o čemž vydává pruběžně potvrzení:

skoleni

DEBUG2 remoteWorkerThread_1: forward confirm 2,6 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,14 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,15 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,21 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,27 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,33 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,45 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,51 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,54 received by 1
DEBUG2 remoteWorkerThread_1: forward confirm 2,55 received by 1 

Teoreticky bychom zde měli vidět všechna potvrzení. Master je ale generuje rychleji, než je slave1 stačí po obdržení notifikace selectem z tabulky vybírat. V praxi to v single-master replikačním systému vůbec nevadí. Pozdější přijetí potvrzení akorát způsobí v nejhorším případě to, že zprávy z lokální fronty odmažeme až v dalším pětiminutovém úlidovém intervalu.

Tím bychom zakončili část věnovanou předávání zpráv a příště se už podíváme na replikaci. Mechanismu předávání zpráv je dobré rozumět, protože v praxi čas od času řešíme situaci, kdy nám to přestalo replikovat, zprávy se hromadí a my nevíme proč.