IMO, ao fazer as principais atualizações de versão, deve haver um plano de fallback adequado, porque, caso o aplicativo apresente bugs ou não funcione bem na versão atualizada, poderemos reverter para a versão mais antiga imediatamente. O Slony-I fornece essa funcionalidade na forma de switchback. Esta postagem demonstra a atualização de tempo de inatividade mínimo, incluindo etapas de alternância/retorno.
Antes de ir para a demonstração, um passo importante a ser observado, anteriormente à versão PG 9.0.x, as colunas de tipo de dados bytea usam para armazenar dados no formato ESCAPE e a versão posterior no formato HEX. Ao executar o switchback (versão mais recente para a versão mais antiga), esse tipo de diferença de formato bytea não é suportado pelo Slony-I, portanto, o formato ESCAPE deve ser mantido durante toda a duração da atualização, caso contrário, você poderá encontrar um erro:
ERROR remoteWorkerThread_1_1: error at end of COPY IN: ERROR: invalid input syntax for type bytea
CONTEXT: COPY sl_log_1, line 1: "1 991380 1 100001 public foo I 0 {id,500003,name,"A ",b,"\\x41"}"
ERROR remoteWorkerThread_1: SYNC aborted
Para corrigir, nenhuma alteração necessária no PG 8.4.x, mas no PG 9.3.5, o parâmetro bytea_output deve ser definido de HEX para ESCAPE, conforme mostrado. Podemos configurá-lo no nível do cluster ($PGDATA/postgresql.conf) ou no nível do usuário (ALTER TABLE…SET), preferi ir com alterações no nível do usuário.
slavedb=# alter user postgres set bytea_output to escape;
ALTER ROLE
Vamos prosseguir com as etapas de atualização. Abaixo estão os detalhes do meu servidor de duas versões usados nesta demonstração, altere-os de acordo com a configuração do seu servidor, se você estiver tentando:
Origin Node (Master/Primary are called as Origin) Subscriber Node (Slave/Secondary are called as Subscriber)
------------------------------------------------- ----------------------------------------------------------
Host IP : 192.168.22.130 192.168.22.131
OS Version : RHEL 6.5 64 bit RHEL 6.5 64 bit
PG Version : 8.4.22 (5432 Port) 9.3.5 (5432 Port)
Slony Vers. : 2.2.2 2.2.2
PG Binaries : /usr/local/pg84/bin /opt/PostgreSQL/9.3/
Database : masterdb slavedb
PK Table : foo(id int primary key, name char(20), image bytea) ...restore PK tables structure from Origin...
Para compreensão simples e fácil implementação, dividi a demonstração em três seções
1. Compilando para binários Slony-I em versões do PostgreSQL
2. Criando scripts de replicação e executando
3. Testando a alternância/retorno.
1. Compilando para binários Slony-I contra a versão do PostgreSQL
Faça o download das fontes do Slony-I aqui e execute a instalação da fonte nos binários do PostgreSQL nos nós Origem e Assinante.
On Origin Node:
# tar -xvf slony1-2.2.2.tar.bz2
# cd slony1-2.2.2
./configure --with-pgbindir=/usr/local/pg84/bin
--with-pglibdir=/usr/local/pg84/lib
--with-pgincludedir=/usr/local/pg84/include
--with-pgpkglibdir=/usr/local/pg84/lib/postgresql
--with-pgincludeserverdir=/usr/local/pg84/include/postgresql/
make
make install
On Subscriber Node: (assuming PG 9.3.5 installed)
# tar -xvf slony1-2.2.2.tar.bz2
# cd slony1-2.2.2
./configure --with-pgconfigdir=/opt/PostgreSQL/9.3/bin
--with-pgbindir=/opt/PostgreSQL/9.3/bin
--with-pglibdir=/opt/PostgreSQL/9.3/lib
--with-pgincludedir=/opt/PostgreSQL/9.3/include
--with-pgpkglibdir=/opt/PostgreSQL/9.3/lib/postgresql
--with-pgincludeserverdir=/opt/PostgreSQL/9.3/include/postgresql/server/
--with-pgsharedir=/opt/PostgreSQL/9.3/share
make
make install
2. Criando scripts de replicação e executando
Para configurar a replicação, precisamos criar alguns scripts que cuidam da replicação, incluindo switchover/switchback.
1. initialize.slonik – Este script contém as informações de conexão dos nós Origem/Assinante.
2. create_set.slonik – Este script contém todas as Tabelas PK de Origem que replicam para o Nó do Assinante.
3. subscribe_set.slonik – Este script inicia a replicação de dados de conjuntos para o nó do assinante.
4. switchover.slonik – Este script ajuda a mover o controle da Origem para o Assinante.
5. switchback.slonik – Este script ajuda a reverter o controle do Assinante para a Origem.
Finalmente, mais dois scripts de inicialização “start_OriginNode.sh” e “start_SubscriberNode.sh” que inicia os processos slon de acordo com os binários compilados nos nós de origem/assinante.
Baixe todos os scripts aqui.
Aqui estão os dados de exemplo no Origin Node(8.4.22) na Foo Table com uma coluna de tipo de dados bytea, que iremos replicar para o Subscriber Node(9.3.5) com a ajuda dos scripts criados.
masterdb=# select * from foo;
id | name | image
----+----------------------+-------
1 | Raghav | test1
2 | Rao | test2
3 | Rags | test3
(3 rows)
Vamos chamar os scripts um por um para configurar a replicação. LEMBRE-SE QUE TODOS OS SCRIPT SLONIK DEVEM SER EXECUTADOS APENAS NO NÓ ORIGIN, EXCETO “start_OriginNode.sh” E “start_SubscriberNode.sh” QUE DEVEM SER EXECUTADOS INDIVIDUALMENTE.
-bash-4.1$ slonik initalize.slonik
-bash-4.1$ slonik create_set.slonik
create_set.slonik:13: Set 1 ...created
create_set.slonik:16: PKey table *** public.foo *** added.
-bash-4.1$ sh start_OriginNode.sh
-bash-4.1$ sh start_SubscriberNode.sh //ON SUBSCRIBER NODE
-bash-4.1$ slonik subscribe_set.slonik
Após a execução bem-sucedida do script acima, você pode notar que os dados em Origin(masterdb) foram replicados para Subscriber(slavedb). Também não permitindo nenhuma operação DML no nó do Assinante:
slavedb=# select * from foo;
id | name | image
----+----------------------+-------
1 | Raghav | test1
2 | Rao | test2
3 | Rags | test3
(3 rows)
slavedb=# insert into foo values (4,'PG-Experts','Image2');
ERROR: Slony-I: Table foo is replicated and cannot be modified on a subscriber node - role=0
Legal… Nós movemos os dados para a versão mais recente do PostgreSQL 9.3.5. Nesse estágio, se você sentir que todos os dados foram replicados para o nó do assinante, poderá fazer a alternância.
3. Testando a alternância/retorno.
Vamos alternar para a versão mais recente com o script e tentar inserir dados nos nós de assinante/origem.
-bash-4.1$ slonik switchover.slonik
switchover.slonik:8: Set 1 has been moved from Node 1 to Node 2
slavedb=# insert into foo values (4,'PG-Experts','Image2');
INSERT 0 1
masterdb=# select * from foo ;
id | name | image
----+----------------------+-------
1 | Raghav | test1
2 | Rao | test2
3 | Rags | test3
4 | PG-Experts | Image2
(4 rows)
masterdb=# insert into foo values (5,'PG-Experts','Image3');
ERROR: Slony-I: Table foo is replicated and cannot be modified on a subscriber node - role=0
Perfeito… Isto é o que estamos procurando, agora slavedb(Nó do Assinante) rodando a versão PG 9.3.5 aceitando dados e masterdb(Nó de Origem) recebendo os dados do slavedb. Também está rejeitando DMLs executados no masterdb.
O Slony-I Logs mostra os movimentos do ID do nó de origem/assinante no momento da alternância:
2014-12-12 04:55:06 PST CONFIG moveSet: set_id=1 old_origin=1 new_origin=2
2014-12-12 04:55:06 PST CONFIG storeListen: li_origin=1 li_receiver=2 li_provider=1
2014-12-12 04:55:06 PST CONFIG remoteWorkerThread_1: update provider configuration
2014-12-12 04:55:06 PST CONFIG remoteWorkerThread_1: helper thread for provider 1 terminated
2014-12-12 04:55:06 PST CONFIG remoteWorkerThread_1: disconnecting from data provider 1
...
...
2014-12-12 04:55:11 PST INFO start processing ACCEPT_SET
2014-12-12 04:55:11 PST INFO ACCEPT: set=1
2014-12-12 04:55:11 PST INFO ACCEPT: old origin=1
2014-12-12 04:55:11 PST INFO ACCEPT: new origin=2
2014-12-12 04:55:11 PST INFO ACCEPT: move set seq=5000006393
2014-12-12 04:55:11 PST INFO got parms ACCEPT_SET
Se você encontrar algum problema neste estágio, poderá voltar para a versão mais antiga. Após o retorno, você pode continuar com a versão mais antiga até que seu aplicativo ou outros problemas sejam corrigidos. Este é o plano de reversão perfeito sem perder muito tempo em caso de problemas após a transição.
-bash-4.1$ slonik switchback.slonik
switchback.slonik:8: Set 1 has been moved from Node 2 to Node 1
slavedb=# insert into foo values (5,'PG-Experts','Image3');
ERROR: Slony-I: Table foo is replicated and cannot be modified on a subscriber node - role=0
masterdb=# insert into foo values (5,'PG-Experts','Image3');
INSERT 0 1
slavedb=# select * from foo ;
id | name | image
----+----------------------+-------
1 | Raghav | test1
2 | Rao | test2
3 | Rags | test3
4 | PG-Experts | Image2
5 | PG-Experts | Image3
(5 rows)
Muito agradável…!!! Esta não é a reversão exata com tempo de inatividade mínimo? Sim, é uma troca perfeita entre nós sem perder uma transação.
Logs mostrando o retorno do Assinante para o Nó de Origem:
2014-12-12 04:58:45 PST CONFIG moveSet: set_id=1 old_origin=2 new_origin=1
2014-12-12 04:58:45 PST CONFIG storeListen: li_origin=2 li_receiver=1 li_provider=2
2014-12-12 04:58:45 PST CONFIG remoteWorkerThread_2: update provider configuration
2014-12-12 04:58:45 PST CONFIG remoteWorkerThread_2: helper thread for provider 2 terminated
2014-12-12 04:58:45 PST CONFIG remoteWorkerThread_2: disconnecting from data provider 2
2014-12-12 04:58:46 PST CONFIG storeListen: li_origin=2 li_receiver=1 li_provider=2
...
...
2014-12-12 04:58:47 PST INFO start processing ACCEPT_SET
2014-12-12 04:58:47 PST INFO ACCEPT: set=1
2014-12-12 04:58:47 PST INFO ACCEPT: old origin=2
2014-12-12 04:58:47 PST INFO ACCEPT: new origin=1
2014-12-12 04:58:47 PST INFO ACCEPT: move set seq=5000006403
2014-12-12 04:58:47 PST INFO got parms ACCEPT_SET
2014-12-12 04:58:48 PST CONFIG moveSet: set_id=1 old_origin=2 new_origin=1
A essa altura, você deve ter notado que nenhuma das transações é perdida durante a operação de alternância entre as versões do PostgreSQL. Apenas o tempo de inatividade pode ser seu aplicativo para iniciar/parar para conectar-se aos nós Origem e Assinante, mas enquanto os nós Origem/Assinante nunca são removidos, eles estão apenas em execução.
Lembre-se, o método mostrado aqui não é útil apenas para atualizações, mas é o mesmo método no Slony-I para mover entre nós.
Obrigado pela sua paciência :). Espero que este post ajude você a atualizar o PostgreSQL com o mínimo de tempo de inatividade usando o Slony-I, incluindo um plano de reversão adequado.