Replicação no Firebird com Delphi

Sim…. replicação para firebird… não tem ? tem sim… só trabalhar um pouco – nem tudo é como visão de brigadeiro..

A replicação consistem manter DUAS ou mais tabelas com conteúdo iguais – de tal forma que se obtém o mesmo resultado em qualquer uma das duas bases de dados.

Existe dois lados na replicação – um que tem os dados que precisam ser levados para o outro (publisher-aquele que publica) – o outro que recebe os dados publicados pelo servidor (subscriptor-aquele que se candidata a receber os dados). Por último a combinação de ambos em um só – ele é tanto publisher como subscriptor da mesma tabela;


Fase do desenvolvimento

  1. Preparar o Publisher para publicar dados para replicação (banco de origem);
  2. Preparar o Subscriptor para receber os dados publicados (banco de destino);
  3. Codificar os componente que monitoram as alterações do Publisher;
  4. Codificar a transferências dos dados da origem para o destino;

 

A título ilustrativo, vou adotar a tabela de PEDIDOs como base

Considere a seguinte tabela:

[code lang=”SQL”]

create table PEDIDOS (dcto varchar(10),
data Date,
cliente integer,
total numeric(15,4));

[/code]


Preparando o banco de origem em firebird

Como base para a replicação, vou criar uma coluna nova na tabela de pedidos na origem para indicar a chave de referência para a replicação – para fugir dos tipos auto-incrementos (já escrevi como fazer isto aqui no blog) vou usar uma coluna para identificar a chave de replicação que receberá uma representação texto para um GUID

alter table PEDIDOS add GID varchar(38);

[OFF] antes que alguém questione… não dá para usar auto-incremento em razão de atender a pré-condição de que o dado dever ser o mesmo nos dois banco de dados (traduzindo – não podem ser diferentes)

A coluna GID vai receber um valor por “trigger” toda vez que for incluída na tabela

[code lang=”SQL”]
SET TERM ^ ;
ALTER TRIGGER REPL_PEDIDOS_GID ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
begin
/* Trigger Utilizado para Replicacao */
if ((new.gid is null)) then
new.gid = UUID_TO_CHAR( gen_uuid() );
end^
SET TERM ; ^
[/code]

[OFF] como se nota inclui na trigger o UPDATE – fiz isto para pegar linhas antigas que ainda não foram replicadas em nenhum processo anterior – caso ocorre uma replicação parcial;

Até aqui já temos a marcação de chave na tabela de origem para localizar as linhas que serão publicadas para replicação. Agora vamos criar uma tabela de controle de publicação das replicações

[code lang=”SQL”]
CREATE TABLE REPL_ITENS
(
TABELA Varchar(128), // recebe o nome da tabela alvo
GID Varchar(38), // recebe o GID da tabela alvo
TIPO Char(1), // I-insert U-update D-delete
DATA Date, // data e hora da alteração
ID integer, // sequencial interna
SESSION_ID integer // sessão da transação que criou – um luxo
);
CREATE INDEX REPL_ITENSDATA ON REPL_ITENS (DATA);
CREATE INDEX REPL_ITENSGID ON REPL_ITENS (GID);
CREATE INDEX REPL_ITENSID ON REPL_ITENS (ID);
CREATE INDEX REPL_ITENSTABELA ON REPL_ITENS (TABELA);
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON REPL_ITENS TO SYSDBA WITH GRANT OPTION;
[/code]

Vamos precisar de um GENERATOR para popular a coluna ID da tabela e uma trigger para popular com o valor do GENERATOR. ver também

[code lang=”SQL”]
CREATE GENERATOR REPL_ITENS_GEN_ID;
SET TERM ^ ;
CREATE TRIGGER REPL_ITENS_ID FOR REPL_ITENS ACTIVE
BEFORE INSERT POSITION 0
AS
begin /* Replicacao Storeware */
new.id = gen_id(REPL_ITENS_GEN_ID,1);
new.session_id = rdb$get_context(‘SYSTEM’,’SESSION_ID’);
new.data = cast(‘now’ as date);
end^
SET TERM ; ^
[/code]

[OFF] para este caso não usei o GUID – vamos precisar garantir uma sequência na tabela que seja em ordem crescente – isto vai facilitar a codificação a frente.

 

Para fechar, vamos ensinar a tabela PEDIDOS como publicar as suas alterações:

[code lang=”SQL”]
SET TERM ^ ;
CREATE TRIGGER REPL_PEDIDOS_REG FOR PEDIDOS ACTIVE
AFTER INSERT OR UPDATE OR DELETE POSITION 0
AS
begin
/* Replicacao Storeware */
in autonomous transaction do
begin
if (inserting) then
insert into repl_itens ( tabela,gid,tipo)
values(‘PEDIDOS’,new.gid,’I’);
if (updating) then
insert into repl_itens ( tabela,gid,tipo)
values(‘PEDIDOS’,new.gid,’U’);
if (deleting) then
insert into repl_itens ( tabela,gid,tipo)
values(‘PEDIDOS’,old.gid,’D’);
end
end
^
SET TERM ;
[/code]


 

Preparando o banco de dados de destino

No banco de dados destino temos a mesma tabela de pedidos (o ideal que possua a mesma estrutura).

Considerando que durante a replicação dos dados ocorrerão momentos em que haverá UPDATEs e DELETEs para fazer – será necessário ter uma chave correspondente para a alteração do registro. Esta chave não pode ser controlado localmente pelo banco, ela precisa ser uma chave que tanto o banco de origem como o banco de destino sejam cooperativos. A este requisito a chave GID que recebe um GUID atente perfeitamente.

alter table PEDIDOS add GID varchar(38);

A “trigger” para gerenciar a coluna GID segue o mesmo comportamento do banco de origem.

[code lang=”SQL”]
SET TERM ^ ;
ALTER TRIGGER REPL_PEDIDOS_GID ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
begin
/* Trigger Utilizado para Replicacao */
if ((new.gid is null)) then
new.gid = UUID_TO_CHAR( gen_uuid() );
end^
SET TERM ; ^
[/code]

[OFF] Note que a trigger checa se o GID é null – isto é importante pois quem vai gerar a chave para o GID é o banco que criou o registro (onde ele nasceu) e durante toda a sua vida precisa receber o mesmo valor nos dois banco de dados.

Agora já temos tudo que precisamos no banco de dados para controlar o que será replicado. Passemos a construir os códigos que vão fazer a replicação em si.


Codificando o select para monitorar as alterações na Origem

O select na origem, ou no publisher, será feito considerando que queremos somente as linhas alteradas para serem transferidas para o banco de destino.

Para isto precisamos fazer controle de quais linhas foram e quais linhas ainda não foram para o destino. Para isto foi que usei um GENERATOR para a tabela de controle   REPL_ITEMS->id

Uma vez executado a replicação precisamos guardar qual foi o ultimo ID utilizado para ser utilizado na próxima chamada:

[code lang=”SQL”]
select a.*, b.tipo repl_tipo from PEDIDOS a, REPL_ITEMS b
where a.gid = b.gid and b.id>:id
order by b.id
[/code]

Para tabelas com muitas linhas, pode ser interessante usar um derivação incluindo o número de linhas a sincronizar a cada chamada:

[code lang=”SQL”]
select first 1000 a.*, b.tipo repl_tipo from PEDIDOS a, REPL_ITEMS b
where a.gid = b.gid and b.id>:id
order by b.id
[/code]


Codificando a transferência dos dados para o destino

Chegou o momento de enviar o dados da Origem para o Destino….
Dependendo da coluna REPL_TIPO que vem do Select na Origem:
I – Faz um INSERT na tabela de destino;
U – Faz um UPDATE;
D – Faz um DELETE;

Esteja preparado para tratar algumas exceções:

  • quando for fazer um UPDATE e o registro ainda não se encontra no destino, precisando fazer o INSERT para iniciar o registro;
  • quando for fazer DELETE de uma registro que não existe;
  • fazer INSERT de registro que já tem chave primária idêntica no destino;
  • se esta inciando a sincronização de uma tabela que já existe, sinalizar a tabela de eventos com os dados já existentes;

 


 

 

Projeto no Git

 

 

 

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *