Tuesday, November 23, 2010

Hot Standby e Streaming Replication

A partir da versão 9.0 do PostgreSQL, é possível combinar hot standby e envio de registros (streaming replication) para compor uma solução de replicação. Os benefícios dessa solução são aqueles citados no artigo anterior (disponibilidade, confiabilidade, desempenho e/ou balanceamento de carga). Neste caso, teremos o envio assíncrono das transações realizadas no servidor principal para o servidor secundário e, além disso, o servidor secundário aceitará consultas somente leitura (consultas que não modificam dados, ou seja, não escrevem no log de transação tais como SELECT, COPY TO, BEGIN, END, ROLLBACK); comandos como INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, nextval() não são permitidos.  A figura abaixo ilustra esse cenário:



 A implementação de tal cenário está descrita abaixo. Vale lembrar que os pré-requisitos apresentados no artigo anterior são utilizados aqui.

No servidor principal, editamos o arquivo /bd/primario/postgresql.conf:

listen_addresses = '*'
wal_level = hot_standby
max_wal_senders = 1
wal_keep_segments = 20

O parâmetro listen_addresses permite que o servidor secundário se conecte ao servidor principal para receber os logs de transação. O parâmetro wal_level indica o nível de log de transação armazenado no servidor principal; minimal (padrão) não permite que este tipo de replicação seja realizada. O parâmetro max_wal_senders indica o número máximo de servidores secundários permitidos. O parâmetro wal_keep_segments só é necessário se (i) for realizar a cópia dos arquivos com o servidor principal em atividade e (ii) se houverem paradas programadas do servidor secundário; este valor deve ser maior do que o número de arquivos de log de transação gerados durante a cópia dos arquivos. Neste caso, 20 é igual a 320 MB (20 * 16 MB).

A modificação desses parâmetros exige que o PostgreSQL do servidor principal seja reiniciado. Assim se a sua janela de manutenção não é flexível, programe-se. A aplicação dessas modificações não implica ter que iniciar a replicação imediatamente.

postgres@principal:~$ pg_ctl restart -D /bd/primario
waiting for server to shut down.... done
server stopped
server starting


No servidor principal, crie uma role (usuário) para replicar os dados. O PostgreSQL exige que o usuário que realiza a replicação seja um super-usuário e possa se conectar ao servidor primário.

postgres@principal:~$ psql
psql (9.0.1)
Type "help" for help.
postgres=# CREATE ROLE usuario SUPERUSER LOGIN;
CREATE ROLE
postgres=# \q


Dependendo da sua política de segurança, você pode precisar definir uma senha para este usuário. Neste caso, a senha deve ser informada no arquivo de senhas (~postgres/.pgpass ou %APPDATA%\postgresql\pgpass.conf no Windows) ou na string de conexão no arquivo /bd/secundario/recovery.conf.

postgres@principal:~$ psql
psql (9.0.1)
Type "help" for help.
postgres=# \password usuario
Enter new password:
Enter it again:
postgres=# \q


No servidor principal, edite o arquivo /bd/primario/pg_hba.conf. O método de autenticação vai depender da sua política de segurança. Se você decidiu colocar senha para o usuário que realiza a replicação, utilize o método de autenticação md5; caso contrário, utilize o método de autenticação trust.

host    replication        usuario        10.1.1.2/32        md5


A adição de uma nova regra de autenticação, exige a recarga das regras de autenticação.

postgres@principal:~$ pg_ctl reload -D /bd/primario
server signaled


No servidor secundário, edite o arquivo /bd/secundario/postgresql.conf:

hot_standby = on


O parâmetro hot_standby indica se o servidor secundário aceita conexões.

No servidor secundário, crie o arquivo /bd/secundario/recovery.conf. Este arquivo conterá informações sobre a aplicação das modificações realizadas no servidor principal e transmitidas para o servidor secundário.

standby_mode = 'on'
primary_conninfo = 'host=10.1.1.1 port=5432 user=usuario password=minhasenha'
trigger_file = '/bd/secundario/failover.trg'


O parâmetro standby_mode especifica se o servidor PostgreSQL é um servidor secundário (standby). O parâmetro primary_conninfo especifica como se conectar ao servidor principal. A senha (especificado em password) pode ser omitida ali e especificada no arquivo de senhas (~postgres/.pgpass ou %APPDATA%\postgresql\pgpass.conf no Windows). Se o parâmetro trigger_file for utilizado, indica que a presença do arquivo citado (/bd/secundario/failover.trg), termina a recuperação, ou seja, o servidor passa a ser um servidor principal (que aceita quaisquer tipos de comandos; não somente aqueles comandos somente leitura).

No servidor principal, teremos que realizar a cópia física dos arquivos para o servidor secundário. Há duas maneiras de realizar esta cópia dependendo da disponibilidade do servidor principal:
  • com o servidor principal parado;
  • com o servidor principal em atividade.
Se o servidor principal puder ficar parado durante a cópia, podemos fazer:

postgres@principal:~$ pg_ctl stop -D /bd/primario
waiting for server to shut down.... done
server stopped
postgres@principal:~$ rsync -av --exclude postgresql.conf \
--exclude pg_hba.conf --exclude pg_xlog/* --exclude pg_log/* \
/bd/primario/ postgres@10.1.1.2:/bd/secundario
postgres@principal:~$ pg_ctl start -D /bd/primario
server starting


Caso o servidor principal não possa parar, podemos fazer todo processo online. Neste caso precisamos executar os seguintes comandos no servidor principal:

postgres@principal:~$ psql
psql (9.0.1)
Type "help" for help.
postgres=# select pg_start_backup('replicacao', true);
pg_start_backup
-----------------
0/5044CB4
(1 row)

postgres=# \q
postgres@principal:~$ rsync -av --exclude postmaster.pid \
--exclude postgresql.conf --exclude pg_hba.conf \
--exclude backup_label --exclude pg_xlog/* --exclude pg_log/* \
/bd/primario/ postgres@10.1.1.2:/bd/secundario
postgres@principal:~$ psql
psql (9.0.1)
Type "help" for help.
postgres=# select pg_stop_backup();
NOTICE:  WAL archiving is not enabled; you must ensure that all required
WAL segments are copied through other means to complete the backup
pg_stop_backup
----------------
0/90D7950
(1 row)

postgres=# \q


A primeira consulta (select pg_start_backup()) prepara o servidor para iniciar a cópia dos arquivos. O próximo passo é copiar os arquivos (utilizamos o rsync mas pode ser qualquer outro aplicativo) e, neste caso, temos que excluir alguns arquivos (postmaster.pid, postgresql.conf, pg_hba.conf, backup_label, pg_xlog/* e pg_log/*). Indicamos que a cópia já foi concluída executando a função (pg_stop_backup()). Vale lembrar que o parâmetro wal_keep_segments no servidor principal deve ser maior do que o número de arquivos de log de transação gerados durante a cópia. Isto porque o servidor secundário precisará destes arquivos (eles não podem ser reciclados) para "acompanhar" o servidor principal. Caso o parâmetro wal_keep_segments não seja suficiente, (i) você terá que aumentar o valor e realizar todo este passo novamente ou (ii) caso você esteja arquivando os arquivos de log de transação basta copiá-los para o diretório pg_xlog do servidor secundário.

Neste momento você pode iniciar o servidor secundário. Não se esqueça de criar os arquivos postgresql.conf e pg_hba.conf no servidor secundário.

postgres@secundario:~$ pg_ctl start -D /bd/secundario
server starting


Caso os logs estejam habilitados (logging_collector = on) no servidor secundário, as seguintes mensagens indicam que a replicação está acontecendo com sucesso:

LOG:  database system was interrupted; last known up at 2010-11-14 14:08:19 BRT
LOG:  entering standby mode
LOG:  redo starts at 0/5044CB4
LOG:  restartpoint starting: xlog
LOG:  consistent recovery state reached at 0/A000000
LOG:  database system is ready to accept read only connections
LOG:  streaming replication successfully connected to primary

Neste momento, espera-se que o servidor secundário esteja "acompanhando" o servidor principal mas sempre em um estado consistente. Assim, a mesma consulta em ambos servidores podem retornar resultados diferentes (lembre-se que a replicação é assíncrona).

Se você tiver perguntas, as listas de discussão pgbr-geral (português) e pgsql-general (inglês) são bons lugares para perguntar.