Hlavní navigace

Slony1 - Replikace pro PostgreSQL (7)

16. 9. 2004
Doba čtení: 8 minut

Sdílet

Dnes započneme další éru - replikaci máme již zvládnutou, a tak se pustíme do administrace replikacniho clusteru.

V předchozích dílech jsme se naučili vytvářet databáze, přidávat je do replikačního clusteru, spouštět replikační agenty, nastavovat vzájemné spojení jednotlivých databází a provádět replikaci dat mezi libovolnými uzly. Naučili jsme se také řešit možné problémy, se kterými se můžeme při těchto operacích setkat. Umíme už dost na to, abychom si bez problémů nakonfigurovali svůj vlastní replikační cluster tak, jak nám to vyhovuje, a nasadili ho do produkce.

Ačkoliv předchozí operace se také dají nazývat administrací, já bych je nazval spíše instalací. Pod pojmem administrace si představuji údržbu již pracujícího clusteru. Touto údržbou se budeme společně dále zabývat.

Odpojení databáze z clusteru

Náš demonstrační cluster má strukturu, kterou vidíte na začátku šestého dílu. Rozhodli jsme se uzel slave2 z clusteru odpojit. Tuto akci nelze provést přímo prostředky PostgreSQL, tj. DROP DATABASE. Ostatním uzlům je nutné tuto změnu předem oznámit, aby si poupravili routing zpráv. Také je možné, že si tuto databázi chceme ponechat, protože se v ní kromě replikovaných dat nachází i něco jiného. Můžeme si navíc chtít ponechat i zreplikované tabulky.

Databázi není možné odpojit z clusteru tak, že k ní pouze zrušíme spojení příkazy DROP PATH a DROP LISTEN. Pokud to provedeme, začnou se nám hromadit na ostatních uzlech zprávy, protože v clusteru musí být každý s každým propojen.

Správný postup pro odpojení databáze z clusteru je použití příkazu DROP NODE. Tento příkaz kompletně odstraní konfiguraci daného uzlu z konfigurace clusteru a stopne replikačního agenta, pokud běží.

DROP NODE (ID = 3);

> slonik < setup.slonik
<stdin>:7: PGRES_FATAL_ERROR select "_dharma".dropNode(3);  - ERROR:  Slony-I: N
ode 3 is still configured as data provider 

Tímto nám administrační program slonik oznamuje, že některé uzly odebírají od slave2 data, a proto není možné tento uzel odpojit. Uzel slave2 je použit jako data provider poskytující set Godnames uzlu master a set pgbench tables uzlu c1slave2.

Tento problém je možné řešit dvěma způsoby:

  1. ručně předrátovat spojení jednotlivých uzlů
  2. označit uzel jako failed a nechat jiný uzel převzít jeho funkci

Pokud máte dostatek času, doporučuji používat metodu č. 1. Pokud hodně spěcháte a máte navíc komplikovanější strukturu clusteru, je nejrychlejší uzel odbouchnout pomocí FAILOVER a následným DROP NODE na něj zapomenout. Metodu č. 2 není možné použít v případě, kdy je daný uzel masterem pro daný set, jelikož bychom ztratili doposud nezreplikovaná data. V našem případě tomu tak není – žádný ze dvou zmíněných setů není na slave2 odemknut pro zápis.

Metoda č. 1 není náročná na čas počítače, ten to zvládne za pár sekund, ale na čas člověka, který tyto příkazy vydává. Pokud máte složitější stukturu clusteru čítající více uzlů a větší databáze, je nutné u všech akcí alespoň dvakrát měřit a pak teprve řezat. Pokud říznete vedle, tak vzhledem k řekněme netypickému konfiguračnímu systému Slony1 se tyto chyby obtížně vyhledávají, protože pro získání aktuálního stavu replikačního clusteru je nutné prolézat systémový replikační katalog na jednotlivých uzlech pomocí psql. Abychom to měli ještě zajímavější, je Slony1 naprogramován tak, že se všechny konfigurační eventy generují lokálně na cílových uzlech a nikoliv na masteru, a tak je kromě obsahu katalogu nutné překontrolovat i jeho synchronizaci s ostatnímy uzly.

V rámci výuky použijeme metodu č. 1, dvojku si necháme až na probírání failoveru. Zahajíme tedy operaci s využitím methody č. 1.

Fáze 1 – propojit přímo a obousměrně c1slave2 a master

STORE PATH ( client = 4,server = 1,  conninfo = 'dbname=master host=localhost us
er=slony password=iehovah');
STORE PATH ( client = 1,server = 4,  conninfo = 'dbname=c1slave2 host=localhost
user=slony password=iehovah');
STORE LISTEN ( origin = 4, receiver = 1);
STORE LISTEN ( origin = 1, receiver = 4); 

Po provedení těchto příkazů dojde k zajímavé situaci. Uzly c1slave2 a master budou propojeny dvakrát. Jednou přímo a podruhé prostřednictvím uzlu slave2. Master db si to přebere takto:

DEBUG2 remoteListenThread_3: queue event 4,455 STORE_PATH
DEBUG2 remoteWorkerThread_4: Received event 4,455 STORE_PATH
DEBUG2 remoteWorkerThread_3: forward confirm 4,455 received by 3
DEBUG2 localListenThread: Received event 1,2854 STORE_PATH
CONFIG storePath: pa_server=4 pa_client=1 pa_conninfo="dbname=c1slave2 host=localhost user=slony password=iehovah" pa_connretry=10
DEBUG2 remoteWorkerThread_3: forward confirm 1,2854 received by 3
DEBUG2 remoteWorkerThread_3: forward confirm 1,2854 received by 4
DEBUG2 localListenThread: Received event 1,2855 STORE_LISTEN
CONFIG storeListen: li_origin=4 li_receiver=1 li_provider=4
DEBUG1 remoteListenThread_4: thread starts
DEBUG2 remoteListenThread_4: start listening for event origin 4
DEBUG1 remoteListenThread_4: connected to 'dbname=c1slave2 host=localhost user=slony password=iehovah'
DEBUG2 remoteListenThread_4: queue event 4,456 STORE_LISTEN
DEBUG2 remoteWorkerThread_4: Received event 4,456 STORE_LISTEN
DEBUG2 remoteListenThread_3: queue event 4,456 STORE_LISTEN
DEBUG2 remoteWorker_event: event 4,456 ignored - duplicate
DEBUG2 remoteWorkerThread_3: forward confirm 1,2855 received by 3
DEBUG2 remoteWorkerThread_3: forward confirm 4,456 received by 3
DEBUG2 remoteWorkerThread_4: forward confirm 1,2855 received by 4
DEBUG2 remoteListenThread_4: queue event 4,457 SYNC
DEBUG2 remoteWorkerThread_4: Received event 4,457 SYNC
DEBUG2 remoteWorkerThread_4: SYNC 457 processing
DEBUG1 remoteWorkerThread_4: data provider 3 only confirmed up to ev_seqno 456 for ev_origin 4
DEBUG2 remoteWorkerThread_3: forward confirm 4,457 received by 3
DEBUG2 syncThread: new sl_action_seq 505542 - SYNC 2856
DEBUG2 localListenThread: Received event 1,2856 SYNC
DEBUG2 remoteWorkerThread_3: forward confirm 1,2856 received by 3
DEBUG2 remoteWorkerThread_4: SYNC 457 processing
DEBUG2 remoteWorkerThread_4: data provider 3 confirmed up to ev_seqno 457 for ev_origin 4 - OK 

Srozumitelněji řečeno:

Slony1 nebere tyto dvě spojení jako alternativní, tj. když zpráva dorazí na cílový uzel jedním ze spojení, nestačí to k jejímu zrušení na straně odesilatele. Zpráva musí dorazit každým konfigurovaným spojením. Proto je současná situace chybná a v praxi se jí snažte vždy vyvarovat. Musíte si zapamatovat, že je bezpodmínečně nutné používat DROP LISTEN na odstraňování starých spojení.

.

Situace se navíc ještě zkomplikuje, až tuto konfiguraci zjistí replikační agent slave2 databáze, který se odpojí od c1slave2 s chybou Bad file descriptor, což zruší fukčnost jednoho z námi nakonfigurovaných spojení. Po restartu agenta se situace znormalizuje. Nadbytečná propojení odstraníme příkazem:

DROP LISTEN ( origin = 4, receiver = 4, provider = 3);
DROP LISTEN ( origin = 1, receiver = 1, provider = 3);

Uzel c1slave2 využívá uzel slave2, který chceme zrušit, ještě k předávání zpráv od slave1. Jelikož slave1 je přímo připojen k master a c1slave2 už také, je třeba přehodit i toto spojení.

DROP LISTEN  ( origin = 2, receiver = 4, provider = 3);
STORE LISTEN ( origin = 2, receiver = 4, provider = 1);

Fáze 2 – změnit replikaci pgbench setu

c1slave2 replikuje pgbench set z uzlu slave2. Vzhledem k tomu, že již máme funkční spojení do db master, je možné změnit zdroj replikace toho setu ze slave2 na master.

V tomto případě je situace odlišná. Nepoužívejte UNSUBSCRIBE SET, protože po jeho použití bude nutné opětovně provést full copy setu. Zdroj setu se přehodí pouhým příkazem SUBSCRIBE.

SUBSCRIBE SET ( ID = 1, PROVIDER = 1, RECEIVER = 4, FORWARD = NO ); 

Zde vidíme výsledek této akce

DEBUG2 localListenThread: Received event 4,533 SUBSCRIBE_SET
CONFIG storeSubscribe: sub_set=1 sub_provider=1 sub_forward='f'
DEBUG2 sched_wakeup_node(): no_id=3 (0 threads + worker signaled)
DEBUG2 sched_wakeup_node(): no_id=1 (0 threads + worker signaled)
DEBUG1 remoteWorkerThread_1: helper thread for provider 1 created
DEBUG1 remoteWorkerThread_1: helper thread for provider 3 terminated 

Fáze 3 – změnit replikaci Godnames setu

Podobné jako v předcházejícím případě. Master bude godnames set odebírat od c1slave2 přímo a nikoliv prostřednictvím slave2.

Jelikož od master odebírá set godnames uzel slave1, je nutné uvést parametr forward – yes. V případě, že ho neuvedeme, tak si desynchronizujeme tento set napříč clusterem.

SUBSCRIBE SET ( ID = 2, PROVIDER = 4, RECEIVER = 1, FORWARD = YES ); 

Fáze 4 – Díky za všechny SYNCy

Poděkujeme uzlu č. 3 za jeho členství v replikačním clusteru.

DROP NODE (ID = 3);

Tímto příkazem nejenže vymažeme uzel z konfigurace clusteru, ale dojte též ke zrušení replikačního katalogu na tomto uzlu. Agent se po zrušení katalogu odpojí od ostatních databází a ukončí se.

root_podpora

DEBUG2 remoteListenThread_1: queue event 1,2925 DROP_NODE
DEBUG2 remoteWorkerThread_1: Received event 1,2925 DROP_NODE
WARN   remoteWorkerThread_1: got DROP NODE for local node ID
DEBUG2 remoteWorkerThread_4: forward confirm 1,2925 received by 4
NOTICE:  Slony-I: Please drop schema "_dharma"
NOTICE:  drop cascades to function _dharma.tablehasserialkey(text)
...
INFO   remoteListenThread_1: disconnecting from 'dbname=master host=localhost user=slony password=iehovah'
DEBUG1 remoteListenThread_1: thread done
INFO   remoteListenThread_4: disconnecting from 'dbname=c1slave2 host=localhost user=slony password=iehovah'
DEBUG1 remoteListenThread_4: thread done
DEBUG1 syncThread: thread done
DEBUG1 localListenThread: thread done
DEBUG1 cleanupThread: thread done
DEBUG1 main: scheduler mainloop returned
DEBUG2 sched_wakeup_node(): no_id=1 (0 threads + worker signaled)
DEBUG1 remoteWorkerThread_2: thread done
DEBUG2 sched_wakeup_node(): no_id=4 (0 threads + worker signaled)
DEBUG1 remoteWorkerThread_4: helper thread for provider 4 terminated
DEBUG1 remoteWorkerThread_4: disconnecting from data provider 4
DEBUG1 remoteWorkerThread_4: thread done
DEBUG1 main: done 

Na všech ostatních uzlech dojde k restartu replikačního agenta, přesněji řečeno mělo by dojít. Je nutné tento údaj ručně překontrolovat. Restarty agentů jsou v replikačních systémech vždy rizikové, Slony1, nyní ve verzi 1.0.2, spadne.

INFO   localListenThread: got restart notification - signal scheduler
DEBUG2 localListenThread: Received event 1,2926 SYNC
DEBUG1 localListenThread: thread done
DEBUG1 syncThread: thread done
INFO   remoteListenThread_4: disconnecting from 'dbname=c1slave2 host=localhost user=slony password=iehovah'
DEBUG1 remoteListenThread_4: thread done
INFO   remoteListenThread_2: disconnecting from 'dbname=slave1 host=localhost user=slony password=iehovah'
DEBUG1 cleanupThread: thread done
DEBUG1 main: scheduler mainloop returned
DEBUG2 sched_wakeup_node(): no_id=2 (0 threads + worker signaled)
DEBUG1 remoteListenThread_2: thread done
DEBUG1 remoteWorkerThread_2: thread done
DEBUG2 sched_wakeup_node(): no_id=3 (0 threads + worker signaled)
DEBUG1 remoteWorkerThread_3: thread done
DEBUG2 sched_wakeup_node(): no_id=4 (0 threads + worker signaled)
DEBUG1 remoteWorkerThread_4: helper thread for provider 4 terminated
DEBUG1 remoteWorkerThread_4: disconnecting from data provider 4
DEBUG1 remoteWorkerThread_4: thread done
DEBUG1 main: restart requested
CONFIG main: local node id = 1
Illegal instruction (core dumped) 

Pokud jste po přečtení tohoto dílu dospěli k názoru, že administrace replikačního systému Slony1 vyžaduje kvalifikovanou osobu, máte pravdu.

Byl pro vás článek přínosný?