Às vezes fico assombrado pela quantidade de tabelas que temos no nosso banco de dados. E o pior de tudo é que a nomenclatura não ajuda. Temos um monte de sistemas legados operando.
Nosso sistema ‘master’ está rodando o postgres 8.3 em debian ‘lenny’.
Usamos o pgpool para prover o banco de dados, acessando duas bases que são replicadas com slony. E o problema eterno tem sido listar todas as tabelas para gerar o ‘set’ da replicação. os scripts alt_perl fazem a parte braçal, mas ainda não existe uma ferramenta que permita simplesmente comandar algo como ‘replicar este aqui naquele lá’.
Pensando nisso, resolvi criar uma ferramenta que automatize, em parte, esse processo. Criar o set contendo todas as tabelas e sequências e permitir a subida rápida disso tudo. Quero contar com a possibilidade de mudar rapidamente a configuração do sistema.
Meu set tem três itens: tabelas com chave primária, sequências e tabelas sem chave primária. Pensando na rapidez do processo criei um script que coloca todas as tabelas numa lista em PDL pronta para ser operada a partir do alt_perl:
-- Function: public.set_schema_pdl(character varying)
-- DROP FUNCTION public.set_schema_pdl(character varying);
CREATE OR REPLACE FUNCTION public.set_schema_pdl(s character varying)
RETURNS text AS
$BODY$
declare
t text;
ot integer; --origin
os integer; --origin
setid integer; -- id do set a ser gerado
sn varchar;
rn varchar;
r record;
begin
--ids atuais do set, tabela e sequencia
execute 'select max(set_id) from _'||s||'.sl_set' into setid;
if setid is null then
setid:=0;
end if;
setid:=setid+1;
execute 'select max(tab_id) from _'||s||'.sl_table' into ot;
if ot is null then
ot:=0;
end if;
ot:=ot+1;
execute 'select max(seq_id) from _'||s||'.sl_sequence' into os;
if os is null then
os:=0;
end if;
os:=os+1;
t:= '$SLONY_SETS = {
"set'||setid||'" =>{
"set_id"=>'||setid||',
"table_id"=>'||ot||',
"sequence_id"=>'||os||',
"pkeyedtables"=>[';
for r in execute 'select schemaname, relname from pg_catalog.pg_statio_user_tables where schemaname!~''^_''=''t'' and not exists (select * from _'||s||'.sl_table where tab_relname=relname and tab_nspname=schemaname) and exists(select * from pg_catalog.pg_index where indrelid=relid and indisprimary=''t'')' loop
t:=t||''''||r.schemaname||'.'||r.relname||''',
';
end loop;
t:=t||'],
"indexedtables"=>[';
for r in execute 'select schemaname, relname from pg_catalog.pg_statio_user_tables where schemaname!~''^_''=''t'' and not exists (select * from _'||s||'.sl_table where tab_relname=relname and tab_nspname=schemaname) and NOT exists(select * from pg_catalog.pg_index where indrelid=relid and indisprimary=''t'')' loop
t:=t||''''||r.schemaname||'.'||r.relname||''',
';
end loop;
t:=t||'],
"sequences"=>[';
for r in execute 'select schemaname, relname from pg_catalog.pg_statio_user_sequences where schemaname!~''^_''=''t'' and not exists (select * from _'||s||'.sl_sequence where seq_relname=relname and seq_nspname=schemaname)' loop
t:=t||''''||r.schemaname||'.'||r.relname||''',
';
end loop;
t:=t||'],
},};
';
return t;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;
ALTER FUNCTION public.set_schema_pdl(character varying) OWNER TO postgres;
Essa procedure essencialmente lista os objetos “interessantes” da base e escreve o PDL contendo as definições de replicação. Normalmente será alguma coisa assim:
tiago@urbanmap:~$ psql -U user -t -h localhost databasename -c "select set_schema_pdl('nome_do_cluster')" -o "slon_sets.conf"
…e pronto! você treá um arquivo contendo todos nomes de objetos replicáveis no banco. Ele se parece com isto:
$SLONY_SETS = {
"set1" =>{
"set_id"=>1,
"table_id"=>1,
"sequence_id"=>1,
"pkeyedtables"=>[minha_tabela,minha_outra_tabela,um_schema.outra_minha_tabela ...],
"sequences"=>[minha_sequencia, outra_sequencia,...,],
"indexedtables"=>[tabela_sem_chave, outra_tabela_sem_chave,...,],
],
},};
Fazer isso “manualmente” nas minhas bases, acredite, era terrível! esquecer uma tabela ou sequência compromete toda a replicação. Daí até descobrir onde estava o problema…
Agora, só falta colocar a replicação pra rodar!