Banco de Dados

7 jul, 2016

Notifique eventos do PostgreSQL para listeners externos

Publicidade

Às vezes, precisamos chamar programas externos de nosso banco de dados PostgreSQL. Podemos enviar soquetes de instruções SQL. Eu escrevi sobre isso. O problema com essa abordagem é o que se segue. Se os rollbacks de usuários, as transações e os soquetes já foram emitidos, isso é um problema (ou não, dependendo de nossa aplicação). Ninguém também garante que o processo por trás do servidor de encaixe tem acesso aos dados da transação. Se formos muito rápidos, talvez a transação ainda não tenha sido comprometida. Podemos usar uma função sleep, mas funções sleep são sempre uma má ideia. PostgreSQL nos dá outra ferramenta para dissociar processos: LISTEN e NOTIFY.

Segue um exemplo. Primeiro criamos uma tabela:

CREATE TABLE PUBLIC.TBLEXAMPLE
(
  KEY1 CHARACTER VARYING(10) NOT NULL,
  KEY2 CHARACTER VARYING(14) NOT NULL,
 
  VALUE1 CHARACTER VARYING(20),
  VALUE2 CHARACTER VARYING(20) NOT NULL,
 
  CONSTRAINT TBLEXAMPLE_PKEY PRIMARY KEY (KEY1, KEY2)
)

Agora vamos adicionar um “after insert” trigger à nossa tabela:

CREATE TRIGGER TBLEXAMPLE_AFTER
AFTER INSERT
ON PUBLIC.TBLEXAMPLE
FOR EACH ROW
EXECUTE PROCEDURE PUBLIC.NOTIFY();

E agora, dentro da função trigger, enviamos um evento de notificação (‘MyEvent’, nesse caso) com as informações da linha. Temos que enviar texto simples no evento de notificação, por isso vamos usar JSON para codificar os dados da linha.

CREATE OR REPLACE FUNCTION PUBLIC.NOTIFY() RETURNS trigger AS
$BODY$
BEGIN
  PERFORM pg_notify('myEvent', row_to_json(NEW)::text);
  RETURN new;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100;

Agora vamos construir um exemplo do lado do servidor que se conecta ao nosso banco de dados PostgreSQL e ouvir o evento. Nesse caso, vamos usar nodejs para construir o protótipo. Esse exemplo também vai enfileirar eventos em um servidor gearman.

var pg = require('pg'),
    gearmanode = require('gearmanode'),
    gearmanClient,
    conString = 'tcp://username:password@localhost:5432/gonzalo',
    pgClient;
 
gearmanode.Client.logger.transports.console.level = 'error';
 
gearmanClient = gearmanode.client();
 
console.log('LISTEN myEvent');
pgClient = new pg.Client(conString);
pgClient.connect();
pgClient.query('LISTEN myEvent');
pgClient.on('notification', function (data) {
    console.log("\033[34m" + new Date + '-\033[0m payload', data.payload);
    gearmanClient.submitJob('sms.sender.one', data.payload);
});

E isso é tudo. Agora só precisamos executar uma instrução INSERT para a nossa tabela. Esse processo irá disparar o nosso evento, e os nossos nodejs vão enfileirar o processo em uma fila gearman.

INSERT INTO PUBLIC.TBLEXAMPLE(KEY1, KEY2, VALUE1, VALUE2) VALUES ('k1', 'k2', 'v1', 'v2');

É bom observar que, se a nossa instrução de inserção está dentro de uma transação e do nosso rollback, o notify não enviará qualquer mensagem.

***

Gonzalo Ayuso faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://gonzalo123.com/2016/07/04/notify-events-from-postgresql-to-external-listeners/