Blog do Seba

DBA, Consultor, Instrutor, [aprendiz de] Ninja e metido a Chef nas horas vagas!

Replicação lógica com o Londiste

Created in Apr 18, 2014

1300 Words. Read in about 6 Min.
Categories: PostgreSQL Londiste
Tags:

O objetivo desse post é implementar o londiste, que é uma ferramenta de replicação assincrona do tipo master/slave e faz parte do pacote de ferramentas SkyTools.

Pontos a validar dessa solução:

  • Tempo de replicação dos dados
  • Tipos de dados suportados, em especial:
    • Sequence
    • Timestamp
    • Bytea

Sobre o ambiente de testes

O ambiente de testes é composto por 2 servidores linux conforme abaixo:

Master Slave
Hostname pg-master pg-slave
SO CEntOS 6.5 CEntOS 6.5
PostgreSQL 9.3.4 9.3.4
Skytools 3.1.5 3.1.5
IP 192.168.152.149 192.168.152.150

Instalação do PostgreSQL

Processo necessário em ambos os servidores.

cd /tmp
wget -c http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm
yum localinstall pgdg-centos93-9.3-1.noarch.rpm
yum install postgresql93-server postgresql93-contrib
service postgresql-9.3 initdb
chkconfig postgresql-9.3 on

Ajustes no firewall

Executar apenas no servidor master:

iptables -I INPUT -p tcp --dport 5432 -s 192.168.152.149 -j ACCEPT
service iptables save

Nesse lab não foi necessário parar ou ajustar o SELinux.

Ajustes no postgresql.conf

listen_addresses = '*'

Ajustes na configuração do pg_hba.conf

Por questão de conveniência, libere acesso a todos os bancos dos servidores citados:

host    all   postgres    192.168.152.149/32  trust
host    all   postgres    192.168.152.150/32  trust

Agora inicie o postgresql:

service postgresql-9.3 start

Sobre o Londiste

O Londiste é uma ferramenta escrita em python, que utiliza o PgQ para o gerenciamento de eventos.

O PgQ que é um sistema de filas escrito em PL/pgSQL. Ele é divido em 3 principais funcionalidades:

  • Producers: coloca eventos em uma determinada fila
  • Ticker: é um daemon que separa as filas em pacotes de eventos
  • Consumers: lê eventos de uma determinada fila

Quanto à replicação do londiste, a mesma é controlada pelos nós (nodes), que são classicados como:

  • root: é o servidor master da replicação
  • branch: é o servidor slave da replicação e permite que outros nós o utilizem para replica de dados em cascata
  • leaf: é o servidor slave da replicação e não permite replicação apartir dele

Na prática, cada configuração do nó é um job para processar as filas do PgQ. Para manter as filas atualizadas, um daemon chamado de worker é executado. Varios workers podem ser executados simulâneamente.

O daemon do PgQ, diferente do worker, precisa ter acesso a todos os bancos, para procupar pelos jobs e manter as filas e é por isso que sua string de conexão é parcial (parâmetro base_connstr). Seu acesso padrão é realizado ao banco template1.

Recomendo que o daemon do PgQ seja executado do servidor que for o nó root (master) mas nada impede de o mesmo ficar em um servidor isolado.

É importante lembrar que a solução do Londiste é implementada através de triggers nas tabelas replicadas e isso gera 2 ações necessárias: obrigatoriedade de mesma estrutura de tabelas/colunas e criação de constraints tipo PK e FK nos objetos replicados.

Instalação do Londiste

yum install skytools-93 skytools-93-modules

Talvez seja necessário reiniciar o postgresql nesse ponto.

Configuração do londiste

Crie a estrutura de dados em ambos os servidores:

psql -U postgres -f schema_londiste.sql
-- criação do banco de dados
CREATE DATABASE teste_replica;

\c teste_replica

-- estrutura de dados
CREATE TABLE pessoa (
    id SERIAL PRIMARY KEY,
    nome TEXT NOT NULL,
    data_nascimento DATE NOT NULL,
    ultima_visita TIMESTAMP NOT NULL DEFAULT now(),
    foto BYTEA,
    biografia TEXT
);

-- funções de apoio
CREATE OR REPLACE FUNCTION bytea_import(p_path text, p_result out bytea) 
                   LANGUAGE plpgsql as $$
DECLARE
  l_oid oid;
  r record;
BEGIN
  p_result := '';
  select lo_import(p_path) into l_oid;
  for r in ( select data 
             from pg_largeobject 
             where loid = l_oid 
             order by pageno ) loop
    p_result = p_result || r.data;
  end loop;
  perform lo_unlink(l_oid);
END;
$$;

Servidor master

Crie os diretórios necessários:

mkdir {/etc,/var/log,/var/run}/londiste

Crie o arquivo /etc/londiste/teste_replica_root.ini, com o conteúdo abaixo:

[londiste3]
job_name = teste_replica_root
db = dbname=teste_replica host=192.168.152.149 user=postgres
queue_name = q_teste_replica
logfile = /var/log/londiste/teste_replica_root.log
pidfile = /var/run/londiste/teste_replica_root.pid

Quanto aos parâmetros utilizados:

Propriedade Descrição Sugestão de valores
job_name Nome do Job a ser executado
[NOME_BANCO]_[TIPO_NODE]
db Connection String com detalhes de conexão do banco replicado
host=[PGHOST] port=[PGPORT] dbname=[NOME_BANCO] user=[PGUSER]
queue_name Nome da fila
q_[NOME_BANCO]
logfile Caminho completo do arquivo de log
/var/log/londiste/[NOME_BANCO]_[TIPO_NODE].log
pidfile Caminho completo do arquivo pid do worker
/var/run/londiste/[NOME_BANCO]_[TIPO_NODE].pid

Caso seja necessário um arquivo de exemplo para ajudar a criar as configurações do job, utilize o comando londiste3 \-\-ini para gerar um arquivo de exemplo sanar as possíveis dúvidas ou ver as configurações disponíveis.

Após o ajuste das configurações é necessário criar o nó do londiste dentro do banco a ser replicado, assim, execute o comando abaixo:

londiste3 /etc/londiste/teste_replica_root.ini create-root node1 "dbname=teste_replica host=192.168.152.149 user=postgres"

Inicie o daemon para o nó criado:

londiste3 -d /etc/londiste/teste_replica_root.ini worker

É importante lembrar que para cada novo job é necessário criar um novo arquivo de configuração e a inicialização do job no banco replicado, conforme detalhado acima.

Configure o daemon do PgQ

Crie o arquivo /etc/londiste/pgqd.ini, com o conteúdo abaixo:

[pgqd]
base_connstr = host=192.168.152.149 user=postgres
logfile = /var/log/londiste/pgqd.log
pidfile = /var/run/londiste/pgqd.pid

Caso seja necessário um arquivo de exemplo para ajudar a criar as configurações do daemon, utilize o comando pgqd \-\-ini para gerar um arquivo de exemplo sanar as possíveis dúvidas ou ver as configurações disponíveis.

Inicie o daemon do PgQ:

pgqd /etc/londiste/pgqd.ini -d

Servidor slave

Crie o arquivo /etc/londiste/teste_replica_leaf.ini, com o conteúdo abaixo:

[londiste3]
job_name = teste_replica_leaf
db = dbname=teste_replica host=192.168.152.150 user=postgres
queue_name = q_teste_replica
logfile = /var/log/londiste/teste_replica_leaf.log
pidfile = /var/run/londiste/teste_replica_leaf.pid

Crie o nó do londiste:

londiste3 /etc/londiste/teste_replica_leaf.ini create-leaf node2 "dbname=teste_replica host=192.168.152.150 user=postgres" --provider="dbname=teste_replica host=192.168.152.149 user=postgres"

Inicie o worker:

londiste3 -d /etc/londiste/teste_replica_leaf.ini worker

Criando a carga de dados no servidor master

Note que não é necessário que as tabelas tenham os mesmos dados para iniciar a replicação, apenas a mesma estrutura.

Copiando os arquivos necessários para o servidor:

scp /Users/seba/Desktop/28337_1323532011934_4739236_n.jpg root@192.168.152.149:/tmp

Ajustando as permissões:

chown postgres:postgres /tmp/28337_1323532011934_4739236_n.jpg

Execute a carga inicial:

psql -U postgres -d teste_replica -f carga_inicial_londiste.sql 

\c teste_replica

-- carga de dados
INSERT INTO pessoa (nome, data_nascimento, foto, biografia)
WITH config AS (
    SELECT 
      1000 AS max_size
)
SELECT
  'Pessoa ' || new_id::text as nome,
  ('1987-04-16'::DATE + (round(CAST (random()*config.max_size AS NUMERIC),0)::TEXT || ' days')::INTERVAL)::DATE as data_nascimento,
  bytea_import('/tmp/28337_1323532011934_4739236_n.jpg') as foto,
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis tristique quam erat, in pellentesque diam feugiat in. In hac habitasse platea dictumst. Mauris malesuada faucibus diam, in tristique arcu dapibus at. Phasellus commodo nulla et orci adipiscing, quis iaculis turpis tincidunt. Proin placerat nisl non molestie porttitor. Curabitur scelerisque libero in lorem consequat consectetur ac a massa. Cras volutpat est ac lacinia ultricies. Donec quis faucibus ligula. Morbi luctus cursus lacus, a fermentum odio dignissim non. Mauris a tellus adipiscing, sodales magna sed, vehicula justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Etiam eu augue nec lectus tristique bibendum vitae nec elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.' as biografia
FROM
  config,
  generate_series(1,config.max_size) as new_id;

Adicione as tabelas a serem replicadas

No servidor master

londiste3 /etc/londiste/teste_replica_root.ini add-table pessoa

No servidor slave

londiste3 /etc/londiste/teste_replica_leaf.ini add-table pessoa

Testando a replicação

Apartir desse ponto, em alguns segundos, ambos os bancos têm os mesmos registros na tabela pessoa, veja:

[root@pg-slave ~]# psql -U postgres -d teste_replica -c 'SELECT COUNT(1) FROM pessoa;' -h 192.168.152.149
 count 
-------
  1000
(1 row)

[root@pg-slave ~]# psql -U postgres -d teste_replica -c 'SELECT COUNT(1) FROM pessoa;' -h 192.168.152.150
 count 
-------
  1000
(1 row)

Também, não é possível fazer alterações nos registros do servidor slave:

[root@pg-slave ~]# psql -U postgres -d teste_replica -h 192.168.152.150
psql (9.3.4)
Type "help" for help.

teste_replica=# delete from pessoa;
ERROR:  Table 'public.pessoa' to queue 'q_teste_replica': change not allowed (D)

Daqui pra frente, qualquer alteração será replicada (desde que a estrutura da tabela seja a mesma).

Referências:

Comentários

comments powered by Disqus