Files
projeto-importado/docs/replicacao_slony_fiscal_s1.md
2025-11-28 00:29:42 -03:00

42 KiB
Raw Permalink Blame History

Roteiro Replicação Slony - Fiscal S1

Documentação do roteiro de replicação do sistema Fiscal S1 para o Atende.Net já considerando a versão 16 do PostgreSQL em produção nos clientes.

Esse documento é referente a configuração Inicial do Slony. Caso deseje apenas adicionar uma nova replicação ou remover uma já existente, siga a documentação listada abaixo nos processos adicionais!

Caso deseje ativar a replicação entre o Master 11.5 e Slave 16, deve-se utilizar esta documentação: Replicação Nativa - Fiscal S-1

Processos Adicionais

Os links abaixos são referentes a adição ou remoção de um node.

1 - Adicionando Node
2 - Remover Node
3 - Remover Estrutura Slony

Abaixo a representação do funcionamento da estrutura:

Diagrama_Funcionamento_Replicacao

Destino - Banco Fiscal Slony (00002_www.nfs-e.net_SGDB_1150_SLONY - 192.168.23.11)

Checar se o Slony esta instalado e executando:

rpm -qi slony1_11

Caso receba o retorno que a aplicação não está instalada , execute a instalação conforme os passos abaixo:

cd /root/
wget https://datacenter.atende.net/updates/slony1_11.zip
unzip slony1_11.zip
cd slony
rpm -Uvh slony1_11-2.2.10-1.rhel8.x86_64.rpm perl-Switch-2.17-10.el8.noarch.rpm perl-DBD-Pg-3.7.4-4.module_el8.3.0+426+0b4e9c0a.x86_64.rpm perl-Text-Balanced-2.03-395.el8.noarch.rpm perl-Filter-1.58-2.el8.x86_64.rpm perl-SelfLoader-1.23-416.el8.noarch.rpm

Verificar se o IP do banco Master do Fiscal S1 esta liberado no pg_hba.conf

vim /var/lib/pgsql/11/data/pg_hba.conf
Ex.: host    all             all             192.168.23.10/32        trust  (Libera Fiscal S1)
Ex:  host    all             all             172.16.XXX.XXX/32       trust (IP INT_RT do Cliente) 

systemctl reload postgresql-11.service

Conferir as configurações do postgresql.conf (/var/lib/pgsql/11/data/postgresql.conf)

wal_level = logical 
max_wal_senders = 3
max_replication_slots = 3
max_sync_workers_per_subscription = 2

Conferir se o usuário replicação existe no Banco de Dados (via pgAdmin):

login roles > replicacao

Caso não existe, crie o mesmo:

  CREATE ROLE replicacao WITH	LOGIN
	SUPERUSER
	INHERIT
	REPLICATION
	CONNECTION LIMIT -1
	PASSWORD '123456';

Efetue a criação do database conforme o cliente que você esta ativando a replicação via Slony

Exemplo: 00000_cliente

CREATE DATABASE "00000_cliente"
  WITH OWNER = dbaipm
       ENCODING = 'LATIN1'
       TABLESPACE = pg_default
       LC_COLLATE = 'pt_BR'
       LC_CTYPE = 'pt_BR'
       CONNECTION LIMIT = -1;

Setar os defaults privilégios para o Data Base (substituir o nome 00000_cliente pelo código e nome do cliente):


GRANT ALL ON DATABASE "00000_cliente" TO dbaipm;
GRANT ALL ON DATABASE "00000_cliente" TO administradores;
GRANT CONNECT ON DATABASE "00000_cliente" TO consultas;
GRANT ALL ON DATABASE "00000_cliente" TO migradores;
GRANT ALL ON DATABASE "00000_cliente" TO sistema;
REVOKE ALL ON DATABASE "00000_cliente" FROM public;

ALTER DEFAULT PRIVILEGES 
    GRANT INSERT, SELECT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON TABLES
    TO administradores WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT SELECT ON TABLES
    TO consultas WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT INSERT, SELECT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON TABLES
    TO migradores WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT INSERT, SELECT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON TABLES
    TO sistema WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT INSERT, SELECT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON TABLES
    TO atualizacao;

ALTER DEFAULT PRIVILEGES 
    GRANT SELECT, UPDATE, USAGE ON SEQUENCES
    TO administradores WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT USAGE ON SEQUENCES
    TO consultas WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT SELECT, UPDATE, USAGE ON SEQUENCES
    TO migradores WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT SELECT, UPDATE, USAGE ON SEQUENCES
    TO sistema WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT SELECT, UPDATE, USAGE ON SEQUENCES
    TO atendenetweb;

ALTER DEFAULT PRIVILEGES 
    GRANT EXECUTE ON FUNCTIONS
    TO public;

ALTER DEFAULT PRIVILEGES 
    GRANT EXECUTE ON FUNCTIONS
    TO administradores WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT EXECUTE ON FUNCTIONS
    TO consultas WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT EXECUTE ON FUNCTIONS
    TO migradores WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT EXECUTE ON FUNCTIONS
    TO sistema WITH GRANT OPTION;

ALTER DEFAULT PRIVILEGES 
    GRANT EXECUTE ON FUNCTIONS
    TO dbaipm;

Criar as extensões

CREATE EXTENSION pg_buffercache;
CREATE EXTENSION pg_freespacemap;
CREATE EXTENSION pg_stat_statements;
CREATE EXTENSION btree_gist;
CREATE EXTENSION postgis;
CREATE EXTENSION postgis_sfcgal;

Execute o SQL para criação da estrutura (schema replicacao) no SGBD de destino na database criada no passo anterior:

--drop SCHEMA replicacao cascade;
CREATE SCHEMA replicacao;

CREATE SEQUENCE replicacao.sqttblog
    INCREMENT 1
    START 1
    MINVALUE 0
    MAXVALUE 9223372036854775807
    CACHE 1;    
CREATE TABLE replicacao.tblog
(
  logcodigo bigint NOT NULL DEFAULT nextval('replicacao.sqttblog'::regclass),
  logdatahora timestamp not null default now(),
  logip varchar(30) not null default inet_client_addr(),
  logpid bigint not null default pg_backend_pid(),
  logacao text not null,
  logquery text not null default current_query()
);
COMMENT ON COLUMN replicacao.tblog.logcodigo IS 'Código do log';
COMMENT ON COLUMN replicacao.tblog.logdatahora IS 'Datahora da ocorrencia';
COMMENT ON COLUMN replicacao.tblog.logip IS 'Ip do client';
COMMENT ON COLUMN replicacao.tblog.logpid IS 'Pid da conexao';
COMMENT ON COLUMN replicacao.tblog.logacao IS 'Acao sendo executada';
COMMENT ON COLUMN replicacao.tblog.logquery IS 'Query';

ALTER TABLE replicacao.tblog
  ADD CONSTRAINT pk_tblog PRIMARY KEY(logcodigo);
  

CREATE SEQUENCE replicacao.sqttbddl
    INCREMENT 1
    START 1
    MINVALUE 0
    MAXVALUE 9223372036854775807
    CACHE 1;
    
CREATE TABLE replicacao.tbddl
(
    ddlcodigo bigint NOT NULL DEFAULT nextval('replicacao.sqttbddl'::regclass),
    ddlcomando text COLLATE pg_catalog."default" NOT NULL,
    ddldatahora timestamp default now(),
    CONSTRAINT pk_tbddl PRIMARY KEY (ddlcodigo)
);


--Altera o tablespace das tabelas do IPM Fiscal
create or replace function replicacao.fn_altera_tablespace()
  returns boolean as
$body$
declare
  r_objeto record;
begin
  --Tabelas
  for r_objeto in (select 'alter table "'||nspname||'"."'||pg_class.relname||'" set tablespace replicacao;' as cmd,
                          '"'||nspname||'"."'||pg_class.relname||'"' as nome
                     from pg_namespace
                     left join pg_class
                       on pg_class.relnamespace = pg_namespace.oid
                    where translate(pg_namespace.nspname, '1234567890', '') in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico')
                      and pg_class.relkind = 'r'
                      and pg_class.reltablespace <> (select oid from pg_tablespace where spcname = 'replicacao')
                    order by pg_namespace.nspname,
                          pg_class.relname)
  loop    
    insert into replicacao.tblog (logacao, logquery) values ('Alterando tablespace da tabela '||r_objeto.nome, r_objeto.cmd);
    raise notice 'Alterando tablespace da tabela %.', r_objeto.nome;
    execute r_objeto.cmd;
  end loop;
  
  --Índices
  for r_objeto in (select 'alter index "'||nspname||'"."'||pg_class.relname||'" set tablespace replicacao;' as cmd,
                          '"'||nspname||'"."'||pg_class.relname||'"' as nome
                     from pg_namespace
                     left join pg_class
                       on pg_class.relnamespace = pg_namespace.oid
                    where translate(pg_namespace.nspname, '1234567890', '') in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico')
                      and pg_class.relkind = 'i'
                      and pg_class.reltablespace <> (select oid from pg_tablespace where spcname = 'replicacao')
                    order by pg_namespace.nspname,
                          pg_class.relname)
  loop    
    insert into replicacao.tblog (logacao, logquery) values ('Alterando tablespace da tabela '||r_objeto.nome, r_objeto.cmd);
    raise notice 'Alterando tablespace do índice %.', r_objeto.nome;
    execute r_objeto.cmd;
  end loop;
  
  return true;
end;
$body$
  LANGUAGE plpgsql VOLATILE;

create or replace function replicacao.fn_remove_relacao_atendenet()
  returns boolean as
$body$
declare
  r_fk record;
begin
  for r_fk in (select *,
                      'alter table "'||schema_filha||'"."'||tabela_filha||'" drop constraint "'||conname||'";' as cmd
                 from (SELECT pg_constraint.conname,
                              schema_pai.nspname as schema_pai,
                              tabela_pai.relname as tabela_pai,
                              schema_filha.nspname as schema_filha,
                              tabela_filha.relname as tabela_filha
                         FROM pg_constraint
                        INNER JOIN pg_class tabela_filha
                           ON tabela_filha.oid = pg_constraint.conrelid
                        INNER JOIN pg_namespace schema_filha
                           ON schema_filha.oid = tabela_filha.relnamespace
                        INNER JOIN pg_class tabela_pai
                           ON tabela_pai.oid = pg_constraint.confrelid
                        INNER JOIN pg_namespace schema_pai
                           ON schema_pai.oid = tabela_pai.relnamespace
                        where pg_constraint.contype = 'f'
                          and (translate(substr(schema_pai.nspname,1,1),'1234567890','') = '' or 
						       translate(substr(schema_filha.nspname,1,1),'1234567890','') = '' or
							   schema_pai.nspname in ('saudeweb', 'saudewebglo', 'spunico') or
							   schema_filha.nspname in ('saudeweb', 'saudewebglo', 'spunico'))
                          and pg_constraint.confupdtype <> 'c') orig
                where translate(schema_pai, '1234567890', '') not in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico')
                   or translate(schema_filha, '1234567890', '') not in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico'))
  loop
    insert into replicacao.tblog (logacao, logquery) values ('Apagando a FK "'||r_fk.conname||'" da tabela "'||r_fk.schema_filha||'"."'||r_fk.tabela_filha||'".', r_fk.cmd);
    raise notice 'Apagando a FK "%" da tabela "%".', r_fk.conname, r_fk.schema_filha||'"."'||r_fk.tabela_filha;
    execute r_fk.cmd;
  end loop;
  return true;
end;
$body$
  LANGUAGE plpgsql VOLATILE;  

Verificar se o disco de 1,2 TB esta adicionado no Destino:

Disco: /var/lib/pgsql/11/tablespaces/replicacao

[root@00002-NFSE_SGDB_1150_SLONY ~]# df -h
Sist. Arq.                                    Tam. Usado Disp. Uso% Montado em
devtmpfs                                      8,8G     0  8,8G   0% /dev
tmpfs                                         8,8G   12K  8,8G   1% /dev/shm
tmpfs                                         8,8G  8,8M  8,8G   1% /run
tmpfs                                         8,8G     0  8,8G   0% /sys/fs/cgroup
/dev/mapper/cl_00000--clean--centos8-root      15G  4,9G   11G  33% /
/dev/mapper/cl_00000--clean--centos8-home     2,4G   50M  2,4G   3% /home
/dev/sda2                                     976M  179M  731M  20% /boot
/dev/mapper/sgdb04-dados                      119G  921M  119G   1% /var/lib/pgsql/11
/dev/sda1                                     599M  6,7M  593M   2% /boot/efi
/dev/mapper/sgdb01-pg_log                      19G  168M   19G   1% /var/lib/pgsql/11/data/log
/dev/mapper/sgdb03-indices                     89G  668M   89G   1% /var/lib/pgsql/11/tablespaces/indice
/dev/mapper/sgdb02-pg_wal                      29G  272M   29G   1% /var/lib/pgsql/11/data/pg_wal
/dev/mapper/cl_00000--clean--centos8-var_log   15G  347M   15G   3% /var/log
tmpfs                                         1,8G     0  1,8G   0% /run/user/0
/dev/mapper/sgdb10-replicacao                 1,3T  8,7G  1,2T   1% /var/lib/pgsql/11/tablespaces/replicacao
[root@00002-NFSE_SGDB_1150_SLONY ~]#

IMPORTANTE: EXECUTAR O PASSO ABAIXO APENAS SE O DISCO NÃO EXISTIR NO SERVIDOR LINUX E O TABLESPACE REPLICACAO NÃO EXISTA NO BANCO DE DADOS DE DESTINO!

Deve-se adicionar um HD temporário ao servidor SLAVE (destino) do banco de dados ao qual receberá o tablespace replicacao.
Como sugestão, adicionar o HD com tamanho de 1234GB, no formato Thin Provision com a controladora SCSI Controller 1, desta forma o HD crescerá somente o necessário e será fácil de identificá-lo para posterior remoção.

lsblk

fdisk /dev/sde

#Dispositivo Inicializar Início        Fim    Setores Tamanho Id Tipo
#/dev/sde1                 2048 2587885567 2587883520    1,2T 8e Linux LVM

vgcreate sgdb10 /dev/sde1
lvcreate -L +1233G -n replicacao sgdb10
mkfs.xfs /dev/mapper/sgdb10-replicacao
vim /etc/fstab
/dev/mapper/sgdb10-replicacao /var/lib/pgsql/11/tablespaces/replicacao xfs     defaults        0 1
mkdir -p /var/lib/pgsql/11/tablespaces/replicacao
mount /var/lib/pgsql/11/tablespaces/replicacao
chown postgres.postgres /var/lib/pgsql/11/tablespaces/replicacao/ -R
chmod 700 /var/lib/pgsql/11/tablespaces/replicacao/ -R  

Criar tablespace replicacao:

CREATE TABLESPACE replicacao
  OWNER postgres
  LOCATION '/var/lib/pgsql/11/tablespaces/replicacao';  

Destino - Banco Cliente Versão 16 (00000-clean-bdp-01-1600.atende.net)

No banco do cliente (PostgreSQL16), criar um disco para a replicação do Fiscal:

Deve-se adicionar um HD temporário ao servidor SLAVE (destino) do banco de dados ao qual receberá o tablespace replicacao.
Como sugestão, adicionar o HD com tamanho de 1234GB, no formato Thin Provision com a controladora SCSI Controller 1, desta forma o HD >crescerá somente o necessário e será fácil de identificá-lo para posterior remoção.

su - root 

Criando pasta:

mkdir -p /var/lib/postgresql/16/tablespaces/replicacao

Adicionando disco:

lsblk
pvcreate /dev/sde
vgcreate postgresql-vg10 /dev/sde
lvcreate -l +100%FREE -n lv-replicacao postgresql-vg10
mkfs.xfs /dev/mapper/postgresql--vg10-lv--replicacao
blkid | grep replicacao
blkid | grep replicacao | grep "0cbfc3a7-07d2-483e-885a-73936788b157"
ls -l /dev/disk/by-id/ | grep dm-7
#Retorno: dm-uuid-LVM-My295Ba6z7Vl5TX7eyue3b5bCCOtX1NfqddL0ZoIJLfOjD0N9deI8yySYvvRff3h -> ../../dm-7

vim /etc/fstab

# disco replicacao slony
/dev/disk/by-id/dm-uuid-LVM-My295Ba6z7Vl5TX7eyue3b5bCCOtX1NfqddL0ZoIJLfOjD0N9deI8yySYvvRff3h /var/lib/postgresql/16/tablespaces/replicacao xfs defaults,noatime,nodiratime 0 1  

Aplicando permissão para o diretório:  

chown postgres.postgres /var/lib/postgresql/16/tablespaces/replicacao

Verificar se o IP do banco Master do Fiscal S1 esta liberado no pg_hba.conf

vim /etc/postgresql/16/main/pg_hba.conf
Ex.: host    all             all             172.16.1.18/32        trust  (Libera Fiscal S1)

systemctl reload postgresql.service

Conferir configurações no arquivo postgresql.conf:

cd /etc/postgresql/16/main/
cp postgresql.conf postgresql.bkp-antesslony
vim postgresql.conf

wal_level = logical 
max_wal_senders = 3
max_replication_slots = 3
max_sync_workers_per_subscription = 2
wal_receiver_status_interval = 60s
wal_receiver_timeout = 300s

OBSERVAÇÃO: Se o parametro estiver max_worker_processes = 3, deverá ser alterado para max_worker_processes = 4

ATENÇÃO: O RESTART DO SERVIÇO DE BANCO DE DADOS DEVERÁ SER EXECUTADO APÓS AS 18H!
(systemctl restart postgresql.service)

Criar a tablespace replicacao para recebimento dos dados:

CREATE TABLESPACE replicacao
  OWNER postgres
  LOCATION '/var/lib/postgresql/16/tablespaces/replicacao';  
--drop SCHEMA replicacao cascade;
CREATE SCHEMA replicacao;

CREATE SEQUENCE replicacao.sqttblog
    INCREMENT 1
    START 1
    MINVALUE 0
    MAXVALUE 9223372036854775807
    CACHE 1;    
CREATE TABLE replicacao.tblog
(
  logcodigo bigint NOT NULL DEFAULT nextval('replicacao.sqttblog'::regclass),
  logdatahora timestamp not null default now(),
  logip varchar(30) not null default inet_client_addr(),
  logpid bigint not null default pg_backend_pid(),
  logacao text not null,
  logquery text not null default current_query()
);
COMMENT ON COLUMN replicacao.tblog.logcodigo IS 'Código do log';
COMMENT ON COLUMN replicacao.tblog.logdatahora IS 'Datahora da ocorrencia';
COMMENT ON COLUMN replicacao.tblog.logip IS 'Ip do client';
COMMENT ON COLUMN replicacao.tblog.logpid IS 'Pid da conexao';
COMMENT ON COLUMN replicacao.tblog.logacao IS 'Acao sendo executada';
COMMENT ON COLUMN replicacao.tblog.logquery IS 'Query';

ALTER TABLE replicacao.tblog
  ADD CONSTRAINT pk_tblog PRIMARY KEY(logcodigo);
  

CREATE SEQUENCE replicacao.sqttbddl
    INCREMENT 1
    START 1
    MINVALUE 0
    MAXVALUE 9223372036854775807
    CACHE 1;
    
CREATE TABLE replicacao.tbddl
(
    ddlcodigo bigint NOT NULL DEFAULT nextval('replicacao.sqttbddl'::regclass),
    ddlcomando text COLLATE pg_catalog."default" NOT NULL,
    ddldatahora timestamp default now(),
    CONSTRAINT pk_tbddl PRIMARY KEY (ddlcodigo)
);


--Altera o tablespace das tabelas do IPM Fiscal
create or replace function replicacao.fn_altera_tablespace()
  returns boolean as
$body$
declare
  r_objeto record;
begin
  --Tabelas
  for r_objeto in (select 'alter table "'||nspname||'"."'||pg_class.relname||'" set tablespace replicacao;' as cmd,
                          '"'||nspname||'"."'||pg_class.relname||'"' as nome
                     from pg_namespace
                     left join pg_class
                       on pg_class.relnamespace = pg_namespace.oid
                    where translate(pg_namespace.nspname, '1234567890', '') in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico')
                      and pg_class.relkind = 'r'
                      and pg_class.reltablespace <> (select oid from pg_tablespace where spcname = 'replicacao')
                    order by pg_namespace.nspname,
                          pg_class.relname)
  loop    
    insert into replicacao.tblog (logacao, logquery) values ('Alterando tablespace da tabela '||r_objeto.nome, r_objeto.cmd);
    raise notice 'Alterando tablespace da tabela %.', r_objeto.nome;
    execute r_objeto.cmd;
  end loop;
  
  --Índices
  for r_objeto in (select 'alter index "'||nspname||'"."'||pg_class.relname||'" set tablespace replicacao;' as cmd,
                          '"'||nspname||'"."'||pg_class.relname||'"' as nome
                     from pg_namespace
                     left join pg_class
                       on pg_class.relnamespace = pg_namespace.oid
                    where translate(pg_namespace.nspname, '1234567890', '') in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico')
                      and pg_class.relkind = 'i'
                      and pg_class.reltablespace <> (select oid from pg_tablespace where spcname = 'replicacao')
                    order by pg_namespace.nspname,
                          pg_class.relname)
  loop    
    insert into replicacao.tblog (logacao, logquery) values ('Alterando tablespace da tabela '||r_objeto.nome, r_objeto.cmd);
    raise notice 'Alterando tablespace do índice %.', r_objeto.nome;
    execute r_objeto.cmd;
  end loop;
  
  return true;
end;
$body$
  LANGUAGE plpgsql VOLATILE;

create or replace function replicacao.fn_remove_relacao_atendenet()
  returns boolean as
$body$
declare
  r_fk record;
begin
  for r_fk in (select *,
                      'alter table "'||schema_filha||'"."'||tabela_filha||'" drop constraint "'||conname||'";' as cmd
                 from (SELECT pg_constraint.conname,
                              schema_pai.nspname as schema_pai,
                              tabela_pai.relname as tabela_pai,
                              schema_filha.nspname as schema_filha,
                              tabela_filha.relname as tabela_filha
                         FROM pg_constraint
                        INNER JOIN pg_class tabela_filha
                           ON tabela_filha.oid = pg_constraint.conrelid
                        INNER JOIN pg_namespace schema_filha
                           ON schema_filha.oid = tabela_filha.relnamespace
                        INNER JOIN pg_class tabela_pai
                           ON tabela_pai.oid = pg_constraint.confrelid
                        INNER JOIN pg_namespace schema_pai
                           ON schema_pai.oid = tabela_pai.relnamespace
                        where pg_constraint.contype = 'f'
                          and (translate(substr(schema_pai.nspname,1,1),'1234567890','') = '' or 
						       translate(substr(schema_filha.nspname,1,1),'1234567890','') = '' or
							   schema_pai.nspname in ('saudeweb', 'saudewebglo', 'spunico') or
							   schema_filha.nspname in ('saudeweb', 'saudewebglo', 'spunico'))
                          and pg_constraint.confupdtype <> 'c') orig
                where translate(schema_pai, '1234567890', '') not in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico')
                   or translate(schema_filha, '1234567890', '') not in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico'))
  loop
    insert into replicacao.tblog (logacao, logquery) values ('Apagando a FK "'||r_fk.conname||'" da tabela "'||r_fk.schema_filha||'"."'||r_fk.tabela_filha||'".', r_fk.cmd);
    raise notice 'Apagando a FK "%" da tabela "%".', r_fk.conname, r_fk.schema_filha||'"."'||r_fk.tabela_filha;
    execute r_fk.cmd;
  end loop;
  return true;
end;
$body$
  LANGUAGE plpgsql VOLATILE;  

Origem - Banco Fiscal (00002_www.nfs-e.net_SGDB - 192.168.23.10)

Altere os dados conforme os do cliente que irá fazer a replicação:

vim /usr/local/slony1/etc/slon_tools.conf  

        $CLUSTER_NAME = 'replication_ipmfiscal';
        add_node(
                node => 1,
                host => '192.168.23.10',
                dbname => '00002_ipmfiscal',
                port => 5432,
                user => 'postgres',
                password => ''
        );

        add_node(
                node => 2,
                host => '192.168.23.11',
                dbname => '00000_teste',
                port => 5432,
                user => 'postgres',
                password => ''

        );  

Quando finalizada a configuração do Slave, deve-se prosseguir com o próximo passo:

/usr/local/slony1/bin/slonik_init_cluster | /usr/local/slony1/bin/slonik 

Após, inicie o cluster:

/usr/local/slony1/bin/slon_start 1
/usr/local/slony1/bin/slon_start 2

Atualize as funções no master:

CREATE SCHEMA replicacao;

CREATE TABLE replicacao.tbclientenode
(
  clicodigo integer not null,
  nodcodigo integer not null
);
ALTER TABLE replicacao.tbclientenode
  ADD CONSTRAINT pk_tbclientenode PRIMARY KEY(nodcodigo);
CREATE UNIQUE INDEX idx_unique_tbclientenode_cliente
   ON replicacao.tbclientenode (clicodigo);

CREATE SEQUENCE replicacao.sqttblog
    INCREMENT 1
    START 1
    MINVALUE 0
    MAXVALUE 9223372036854775807
    CACHE 1;    
CREATE TABLE replicacao.tblog
(
  logcodigo bigint NOT NULL DEFAULT nextval('replicacao.sqttblog'::regclass),
  logdatahora timestamp not null default now(),
  logip varchar(30) not null default inet_client_addr(),
  logpid bigint not null default pg_backend_pid(),
  logacao text not null,
  logquery text not null default current_query()
);
COMMENT ON COLUMN replicacao.tblog.logcodigo IS 'Código do log';
COMMENT ON COLUMN replicacao.tblog.logdatahora IS 'Datahora da ocorrencia';
COMMENT ON COLUMN replicacao.tblog.logip IS 'Ip do client';
COMMENT ON COLUMN replicacao.tblog.logpid IS 'Pid da conexao';
COMMENT ON COLUMN replicacao.tblog.logacao IS 'Acao sendo executada';
COMMENT ON COLUMN replicacao.tblog.logquery IS 'Query';

ALTER TABLE replicacao.tblog
  ADD CONSTRAINT pk_tblog PRIMARY KEY(logcodigo);
  

drop function if exists replicacao.fn_inicia_replicacao(s_contexto text, a_tabelas text[], i_clicodigo integer);

create or replace function replicacao.fn_get_schema_slony()
  returns text as
$body$
begin
  return (select schemaname
            from pg_tables
           where tablename = 'sl_node');
end;
$body$
  LANGUAGE plpgsql VOLATILE;

create or replace function replicacao.fn_adiciona_schema_slony_search_path()
  returns boolean as
$body$
declare
  s_search_path text;
  s_schema_name text;
begin
  s_schema_name := (select replicacao.fn_get_schema_slony());

  s_search_path := pg_catalog.current_setting('search_path');  
  s_search_path := s_search_path||', '||s_schema_name;
  
  PERFORM pg_catalog.set_config('search_path', s_search_path, true);
  return true;
end;
$body$
  LANGUAGE plpgsql VOLATILE;

create or replace function replicacao.fn_remove_schema_slony_search_path()
  returns boolean as
$body$
declare
  s_search_path text;
  s_schema_name text;
begin
  s_schema_name := (select replicacao.fn_get_schema_slony());

  s_search_path := pg_catalog.current_setting('search_path');
  s_search_path := replace(s_search_path, ', '||s_schema_name, '');
  
  PERFORM pg_catalog.set_config('search_path', s_search_path, true);
  return true;
end;
$body$
  LANGUAGE plpgsql VOLATILE;
  
create or replace function replicacao.status()
  returns table(cliente integer, lagtime interval, lastevent bigint, lastreceived bigint, sincronizado boolean) as
$body$
begin
  perform (select replicacao.fn_adiciona_schema_slony_search_path());

  drop table if exists tbreturn;
  create temp table tbreturn as
    select tbclientenode.clicodigo as cliente,
           sl_status.st_lag_time as lagtime,
           sl_status.st_last_event as lastevent,
           sl_status.st_last_received as lastreceived,
           sl_status.st_last_event = sl_status.st_last_received as sincronizado
      from sl_status
      left join replicacao.tbclientenode
        on tbclientenode.nodcodigo = sl_status.st_received;
        
  perform (select replicacao.fn_remove_schema_slony_search_path());
  
  return query(select * from tbreturn);  
end;
$body$
  LANGUAGE plpgsql VOLATILE;
  
  
CREATE OR REPLACE FUNCTION replicacao.fn_get_node_by_clicodigo(i_clicodigo integer)
  RETURNS integer AS
$BODY$
declare
  i_node integer;
begin  
  select nodcodigo
    into i_node
	from replicacao.tbclientenode
   where clicodigo = i_clicodigo;
  
  if (i_node is null) then
    raise exception 'Não foi possível encontrar um node de replicação para o cliente %.', i_clicodigo;
  end if;
  
  return i_node;
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE OR REPLACE FUNCTION replicacao.fn_prepara_contexto(
    s_contexto text,
    a_tabelas text[])
  RETURNS boolean AS
$BODY$
declare
  i_set integer;
  s_pk text;
  r_tabela record;
begin
  perform replicacao.fn_adiciona_schema_slony_search_path();
  insert into replicacao.tblog (logacao) values ('fn_prepara_contexto');
  
  lock table sl_event;
  
  if exists(select 1 from pg_event_trigger where evtname = 'tg_event_trigger_on_execute_ddl_replicacao_ipm') then
    ALTER EVENT TRIGGER tg_event_trigger_on_execute_ddl_replicacao_ipm DISABLE;
  end if;
  
  if exists(select 1 from sl_set where set_comment = s_contexto) then
    raise exception 'O contexto "%", já existe.', s_contexto;
  else
    select coalesce(max(set_id),0)+1 into i_set from sl_set;
    perform storeset(i_set, s_contexto);
  end if;
  
  for r_tabela in select unnest(a_tabelas) as nometabela
  loop
    if (position('webbased.' in lower(r_tabela.nometabela)) > 0) then
	  raise exception 'A tabela "%", está relacionada ao schema webbased, o qual não deve ser replicado.', r_tabela.nometabela;
    end if;
  
    if (exists(select 1
                 from sl_table
                where tab_nspname||'.'||tab_relname = r_tabela.nometabela)) then
      raise exception 'A tabela "%", já está sendo replicada.', r_tabela.nometabela;
    end if;
  
    select indice.relname
      into s_pk
      from pg_class tabela
      left join pg_namespace
        on pg_namespace.oid = tabela.relnamespace
      left join pg_index
        on pg_index.indrelid = tabela.oid
      left join pg_class indice
        on indice.oid = pg_index.indexrelid
     where pg_namespace.nspname||'.'||tabela.relname = r_tabela.nometabela
       and pg_index.indisprimary;

    if coalesce(s_pk,'') = '' then
      raise exception 'A tabela "%", não possui chave primária.', r_tabela.nometabela;
    end if;
       
    perform setaddtable(i_set, (select coalesce(max(tab_id),0)+1 from sl_table), r_tabela.nometabela, s_pk, '');
  end loop;

  if exists(select 1 from pg_event_trigger where evtname = 'tg_event_trigger_on_execute_ddl_replicacao_ipm') then
    ALTER EVENT TRIGGER tg_event_trigger_on_execute_ddl_replicacao_ipm ENABLE;
  end if;

  perform replicacao.fn_remove_schema_slony_search_path();
  
  return true;
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE OR REPLACE FUNCTION replicacao.fn_inicia_replicacao_contexto(
    s_contexto text,
    i_clicodigo integer)
  RETURNS boolean AS
$BODY$
begin
  perform replicacao.fn_adiciona_schema_slony_search_path();
  insert into replicacao.tblog (logacao) values ('fn_inicia_replicacao_contexto');
  
  if exists(select 1 from pg_event_trigger where evtname = 'tg_event_trigger_on_execute_ddl_replicacao_ipm') then
    ALTER EVENT TRIGGER tg_event_trigger_on_execute_ddl_replicacao_ipm DISABLE;
  end if;
  
  perform replicacao.fn_adiciona_slave_replicacao(s_contexto, i_clicodigo);

  if exists(select 1 from pg_event_trigger where evtname = 'tg_event_trigger_on_execute_ddl_replicacao_ipm') then
    ALTER EVENT TRIGGER tg_event_trigger_on_execute_ddl_replicacao_ipm ENABLE;
  end if;

  perform replicacao.fn_remove_schema_slony_search_path();
  
  return true;
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE OR REPLACE FUNCTION replicacao.fn_interrompe_replicacao(s_contexto text)
  RETURNS boolean AS
$BODY$
declare
  i_set integer;
begin
  perform replicacao.fn_adiciona_schema_slony_search_path();
  insert into replicacao.tblog (logacao) values ('fn_interrompe_replicacao('''||s_contexto||''')');
  
  if not exists(select 1 from sl_set where set_comment = s_contexto) then
    raise exception 'O contexto "%", não existe.', s_contexto;
  else
    select set_id into i_set from sl_set where set_comment = s_contexto;
  end if;

  perform dropset(i_set);

  perform replicacao.fn_remove_schema_slony_search_path();
  
  return true;
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE OR REPLACE FUNCTION replicacao.fn_interrompe_replicacao_tabela(s_tabela text)
  RETURNS boolean AS
$BODY$
begin
  perform replicacao.fn_adiciona_schema_slony_search_path();
  insert into replicacao.tblog (logacao) values ('fn_interrompe_replicacao_tabela('''||s_tabela||''')');
    
  if (not exists(select tab_id
                   from sl_table
                  where tab_nspname||'.'||tab_relname = s_tabela)) then
    raise exception 'A tabela "%", não está sendo replicada.', s_tabela;      
  end if;

  perform setdroptable((select tab_id
                          from sl_table
                         where tab_nspname||'.'||tab_relname = s_tabela));

  perform replicacao.fn_remove_schema_slony_search_path();
  
  return true;
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE OR REPLACE FUNCTION replicacao.fn_adiciona_slave_replicacao(
    s_contexto text,
    i_clicodigo integer)
  RETURNS boolean AS
$BODY$
declare
  i_set integer;  
  i_node_master integer;
  i_node_slave integer;
begin
  perform replicacao.fn_adiciona_schema_slony_search_path();
  i_node_master = 1;
  i_node_slave = replicacao.fn_get_node_by_clicodigo(i_clicodigo);

  insert into replicacao.tblog (logacao) values ('fn_adiciona_slave_replicacao');
  
  if not exists(select 1 from sl_set where set_comment = s_contexto) then
    raise exception 'O contexto "%", não existe.', s_contexto;
  else
    select set_id into i_set from sl_set where set_comment = s_contexto;
	
    if exists(select 1 from sl_set where set_comment = s_contexto and age(xmin) = 0) then
      raise exception 'O contexto "%", não deve ser preparado e iniciado na mesma execução.', s_contexto;
    end if;
  end if;

  if not exists(select 1 from sl_node where no_id = i_node_master) then
    raise exception 'O node de replicação "%", não existe.', i_node_master;
  end if;

  if not exists(select 1 from sl_node where no_id = i_node_slave) then
    raise exception 'O node de replicação "%", não existe.', i_node_slave;
  end if;

  perform subscribeset(i_set, i_node_master, i_node_slave, false, false);

  perform replicacao.fn_remove_schema_slony_search_path();
  
  return true;
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
  
CREATE OR REPLACE FUNCTION replicacao.fn_interrompe_replicacao_tabelas_sem_slave()
  RETURNS boolean AS
$BODY$
declare
  s_objetos text;
  r_record record;
begin
  perform replicacao.fn_adiciona_schema_slony_search_path();
  insert into replicacao.tblog (logacao) values ('fn_interrompe_replicacao_tabelas_sem_slave');

  lock table sl_set;

  for r_record in 
    select sl_table.tab_nspname||'.'||sl_table.tab_relname as table
      from sl_table
      left join sl_set
        on sl_set.set_id = sl_table.tab_set
      left join sl_subscribe
        on sl_subscribe.sub_set = sl_set.set_id
     where sl_subscribe.sub_receiver is null
   loop
     perform replicacao.fn_interrompe_replicacao_tabela(r_record.table);
   end loop;

   perform replicacao.fn_remove_schema_slony_search_path();
   perform replicacao.fn_adiciona_schema_slony_search_path();
   
   for r_record in
     select sl_set.set_comment as contexto
       from sl_set
       left join sl_subscribe
         on sl_subscribe.sub_set = sl_set.set_id
      where sl_subscribe.sub_receiver is null
  loop
    perform replicacao.fn_interrompe_replicacao(r_record.contexto);
  end loop;
   
  perform replicacao.fn_remove_schema_slony_search_path();
  
  return true;
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE OR REPLACE FUNCTION replicacao.fn_remove_slave_replicacao(
    s_contexto text,
    i_clicodigo integer)
  RETURNS text AS
$BODY$
declare
  i_set integer;  
  i_node_master integer;
  i_node_slave integer;
begin
  perform replicacao.fn_adiciona_schema_slony_search_path();
  i_node_master = 1;
  i_node_slave = replicacao.fn_get_node_by_clicodigo(i_clicodigo);

  insert into replicacao.tblog (logacao) values ('fn_remove_slave_replicacao');
  
  if not exists(select 1 from sl_set where set_comment = s_contexto) then
    raise exception 'O contexto "%", não existe.', s_contexto;
  else
    select set_id into i_set from sl_set where set_comment = s_contexto;
	
    if exists(select 1 from sl_set where set_comment = s_contexto and age(xmin) = 0) then
      raise exception 'O contexto "%", não deve ser preparado e iniciado na mesma execução.', s_contexto;
    end if;
  end if;

  if not exists(select 1 from sl_node where no_id = i_node_master) then
    raise exception 'O node de replicação "%", não existe.', i_node_master;
  end if;

  if not exists(select 1 from sl_node where no_id = i_node_slave) then
    raise exception 'O node de replicação "%", não existe.', i_node_slave;
  end if;

  raise notice 'Executar no slave: "select * from _replication_ipmfiscal.unsubscribeset(%, %, false);"', i_set, i_node_slave;
  
  /*perform unsubscribeset(i_set, i_node_slave, false);*/

  perform replicacao.fn_remove_schema_slony_search_path();
  
  return format('Executar no slave: "select * from _replication_ipmfiscal.unsubscribeset(%s, %s, false);"', i_set, i_node_slave);
end 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;


CREATE SEQUENCE replicacao.sqttbddl
    INCREMENT 1
    START 1
    MINVALUE 0
    MAXVALUE 9223372036854775807
    CACHE 1;
    
CREATE TABLE replicacao.tbddl
(
    ddlcodigo bigint NOT NULL DEFAULT nextval('replicacao.sqttbddl'::regclass),
    ddlcomando text COLLATE pg_catalog."default" NOT NULL,
    ddldatahora timestamp default now(),
    CONSTRAINT pk_tbddl PRIMARY KEY (ddlcodigo)
);

CREATE OR REPLACE FUNCTION replicacao.fn_event_trigger_on_execute_ddl()
    RETURNS event_trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF
AS $BODY$  
BEGIN
  if position((select replicacao.fn_get_schema_slony()) in current_query()) = 0 then
    insert into replicacao.tbddl (ddlcomando) values (current_query());
  end if;
END
$BODY$;

Acessar o banco do fiscal, schema replicação, tabela tbclientenode (alterar o código do cliente) não necessário o SQL abaixo:

insert into replicacao.tbclientenode (clicodigo, nodcodigo) values (55, 2);

clicodigo = (Codigo do sistema) do cliente no Atende.Net 
nodcodigo - Código do node criado no slon_tools.conf ou no arquivo relacionado a sua adição no master.

Obs: Para cada node SLAVE (Destino) adicional deve-se executar o procedimento descrito no chamado: 480647, anexo 9 (Migração Fiscal - Criando Nodes)

Ao final, deve-se executar o script para criar a estrutura no banco slave, para isto edite o script de backup que fica no servidor master(IPMFiscal)


su - postgres

vim backup_estrutura_slony.sh

# Altere
cod_fiscal="8043" -- Código do schema (Fiscal)

# Destino 11 Fiscal Slony
IP_Destino_11="192.168.23.11" (IP do Banco de Dados do Slony intermediário, não precisa ser alterado)
Porta_Destino_11="5432" (Portal default sem necessidade de alterar)
BD_Destino_11="00000_teste" (Informe o database do cliente que irá ativar a replicacao)

# Destino Cliente Atende
IP_Destino_16="172.16.XXX.XXX ou 172.24.XXX.XXX" (Informe a INT_RT do cliente Atende.Net - Banco de Dados PostgreSQL 16)
Porta_Destino_16="65432" (Porta default)
BD_Destino_16="00000_teste" (Informe o nome da database do cliente)


# Execute o script - O backup e o log serão criados na storage de backup "X:\STORE\Transferir\00002_ipmfiscal"
su - postgres
./backup_estrutura_slony.sh

# Ao final, confira os log´s.
cat 8043*.log | grep ERROR | uniq -u

# Conferir se os Schemas foram criados, o comando deve retornar os Schemas restaurados, caso não existe favor verificar
cat 8043*.log | grep "creating SCHEMA 8043" | uniq -u (Substituir pelo Código do Schema)

Validações (00002_www.nfs-e.net_SGDB_1150_SLONY - 192.168.23.11)

Validar Schema e Authorization no banco destino:

-- Schema: migracao_fiscal
-- DROP SCHEMA migracao_fiscal;
CREATE SCHEMA migracao_fiscal
  AUTHORIZATION dbaipm;

GRANT ALL ON SCHEMA migracao_fiscal TO dbaipm;
GRANT ALL ON SCHEMA migracao_fiscal TO administradores;
GRANT USAGE ON SCHEMA migracao_fiscal TO consultas;
GRANT ALL ON SCHEMA migracao_fiscal TO migradores;
GRANT ALL ON SCHEMA migracao_fiscal TO sistema;

Setar privilégios schemas fiscal:

do
$body$
declare
  r_objeto record;
begin
  for r_objeto in (select 'GRANT ALL ON SCHEMA "'||pg_namespace.nspname||'" TO dbaipm;
                           GRANT ALL ON SCHEMA "'||pg_namespace.nspname||'" TO administradores;
                           GRANT USAGE ON SCHEMA "'||pg_namespace.nspname||'" TO consultas;
                           GRANT ALL ON SCHEMA "'||pg_namespace.nspname||'" TO migradores;
                           GRANT ALL ON SCHEMA "'||pg_namespace.nspname||'" TO sistema;' as cmd,
						  '"'||pg_namespace.nspname||'"' as nome
                     from pg_namespace
                    where translate(pg_namespace.nspname, '1234567890', '') in ('dcarq', '_fiarq', '_gtarq', '_isarq', '_nfarq', '_spunico', '_wgt', '_wnp', '_wsn', 'saudeweb', 'saudewebglo', 'spunico', 'migracao_fiscal', 'migracao_saude')
				   )
  loop    
    insert into replicacao.tblog (logacao, logquery) values ('Alterando grants do schema '||r_objeto.nome, r_objeto.cmd);
    raise notice 'Alterando grants do schema %.', r_objeto.nome;
    execute r_objeto.cmd;
  end loop;
end;
$body$;

Liberar porta 65434 no Firewall do cliente, para a conexão entre PG11 x PG16:
Vá em: Firewall > Rules > LAN
Destination: 65434
Description: Bdp-1150-SLONY

Libera Porta Firewall

Script de teste

# Master (Origem)
create table replicacao.tbteste4(col1 integer primary key, col2 text);
insert into replicacao.tbteste4 values(1,'1');
select * from replicacao.tbteste4;

# Slave (11.5)
create table replicacao.tbteste4(col1 integer primary key, col2 text);  

# Destino (16)  
create table replicacao.tbteste4(col1 integer primary key, col2 text);

# Master (origem)
select replicacao.fn_prepara_contexto('Teste Inicial 4', string_to_array('replicacao.tbteste4',','));

select replicacao.fn_inicia_replicacao_contexto('Teste Inicial 4', 55); --Segundo parametro é o código do cliente (CÓDIGO DO SISTEMA NO GDT)

# Slave (11.5)
select * from replicacao.tbteste4; --Já deve ter 1 registro

CREATE PUBLICATION publication_mig_ipm_fiscal_00000_cliente;
ALTER PUBLICATION publication_mig_ipm_fiscal_00000_cliente ADD table replicacao.tbteste4;

# Destino (16)  
CREATE SUBSCRIPTION subscription_mig_ipm_fiscal_00000_cliente CONNECTION 'host=172.16.1.18 port=65434 user=replicacao dbname=00000_cliente' PUBLICATION publication_mig_ipm_fiscal_00000_cliente;  
select * from replicacao.tbteste4;  

# Master (origem)
insert into replicacao.tbteste4 values(2,'2');

# Slave (11.5)
select * from replicacao.tbteste4; --Deve ter 2 registro 
--Tudo deve estar funcionando. Pode apagar a tabela de teste  

# Destino (16)
select * from replicacao.tbteste4; --Deve ter 2 registro
--Tudo deve estar funcionando. Pode apagar a tabela de teste

# Master (origem)
select replicacao.fn_interrompe_replicacao('Teste Inicial 4');
drop table replicacao.tbteste4;

# Slave (11.5)
drop PUBLICATION publication_mig_ipm_fiscal_00000_cliente;
drop table replicacao.tbteste4;  

# Destino (16)
ALTER SUBSCRIPTION subscription_mig_ipm_fiscal_00000_cliente DISABLE;
DROP SUBSCRIPTION subscription_mig_ipm_fiscal_00000_cliente;
drop table replicacao.tbteste4;

Parar o serviço e iniciar novamente, após, verifique o Lag Time:

/usr/local/slony1/bin/slon_kill

/usr/local/slony1/bin/slon_start 1
/usr/local/slony1/bin/slon_start 2
select * from replicacao.status();