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
- Preparar o Publisher para publicar dados para replicação (banco de origem);
- Preparar o Subscriptor para receber os dados publicados (banco de destino);
- Codificar os componente que monitoram as alterações do Publisher;
- 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;