Colas Multiple Consumer

Que son las colas Multiple_Consumer

En el apartado anterior hemos creado y trabajado con colas Single_Consumer.

En este apartado vamos a trabajar con colas Multiple_Consumer.

Cuando hablamos de colas Single o Multiple Consumer, nos estamos refiriendo, siempre, a cuantos destinatarios distintos tiene un mensaje.

En el apartado anterior, hemos utilizado colas Single_Consumer y, aunque trabajábamos con dos sesiones, un mensaje se desencolaba una sola vez (salvo los ROLLBACKS, que devolvian el mensaje a la cola).

En el caso de colas Multiple Consumers:

  • Cada mensaje se desencola una y solo una vez por cada destinatario.
  • En caso de ROLLBACK, el mensaje se devuelve a la cola para aquel destinatario.
  • Cada destinatario puede desencolar los mensajes de forma independiente de los otros destinatarios: el sistema lleva un control de que destinatarios has desencolado cada mensaje y cuantos faltan por desencolar.

Creación de la cola Multiple_consumers

La creación de la cola Multiple_consumers es similar a la cola anterior. Solo se debe especificar la opción Multiple_consumers en la creación de la queue table. Por tanto, una misma queue table no puede tener colas Multiple_consumer y Single_Consumer.

La creación de la queue table y de la cola quedaria asi:

-- Creacion de la queue table indicando multiple_consumers
BEGIN 
dbms_aqadm.create_queue_table ( 
      queue_table=> 'tm_factura',         -- nombre de la Queue table
      queue_payload_type=> 'factura',     -- Tipo de datos que soporta entre comillas
      multiple_consumers=> TRUE,          -- si es multiple consumers
      message_grouping=> DBMS_AQADM.NONE  -- si agrupa los mensajes por transacciones
  );
COMMIT;        --> no olvidar el commit
END;
/
-- Creacion de la cola
BEGIN 
  dbms_aqadm.create_queue(
        queue_name=> 'cm_factura',              -- Nombre de la cola
        queue_table=> 'tm_factura',             -- Queue table en la que se crea
        queue_type=> DBMS_AQADM.NORMAL_QUEUE    -- Hay colas normales y de error
       ); 
COMMIT;
END;
/
BEGIN 
   dbms_aqadm.start_queue('cm_factura', TRUE, TRUE); -- permitir encolar y desencolar
COMMIT;
END;
/

Primer intento de encolar

Para encolar en una cola Multiple Consumers, podemos utilizar la rutina creada para el test de las colas Single_Consumers:

SQL> exec encolar_mensaje(3,'cm_factura');
BEGIN encolar_mensaje(3,'cm_factura'); END;

*
ERROR en la linea 1:
ORA-24033: no hay ningún receptor del mensage
ORA-06512: en "SYS.DBMS_AQ", linea 169
ORA-06512: en "ESQUEMA.ENCOLA", linea 6
ORA-06512: en "ESQUEMA.ENCOLAR_MENSAJE", linea 16
ORA-06512: en linea 1

Para encolar en una cola Multiple Consumers, AQ debe conocer los receptores del mensaje.

Que son los Receptores y Subscriptores de mensajes

En la práctica, esto significa que, para encolar en una cola Multiple Consumers:

  • Se deben especificar los receptores en el enqueue del mensage.
  • O bien, se deben definir subscriptores de los mensajes antes de encolar los mensajes.

Los subscriptores de una cola son los destinatarios por defecto. En efecto: por defecto, un mensaje encolado a una cola Multiple Consumers va destinado a todos y cada uno de los subscriptores de la cola.

Si un mensaje va destinado a otros destinatarios distintos (incluido un subconjunto de los subscriptores), se debe especificar la lista de receptores en el momento de enviar el mensaje.

Creación de un subscriptor para la cola y test de encolar

Vamos a crear un subscriptor, de nombre subscriptor_1, para la cola cm_factura. De esta forma, podemos encolar sin especificar subscriptores. Para definir subscriptores, se utiliza la estructura agent.

El agente está definido como:

TYPE SYS.AQ$_AGENT IS OBJECT (
   name       VARCHAR2(30),
   address    VARCHAR2(1024),
   protocol   NUMBER  DEFAULT 0);
BEGIN
   dbms_aqadm.add_subscriber(
       queue_name => 'cm_factura',
       subscriber => sys.aq$_agent('subscriptor_1', NULL ,0)
      );
   commit;
END;
/
-- ahora podemos encolar sin especificar destinatarios
SQL> exec encolar_mensaje(3,'cm_factura');
SQL> COMMIT;

En estos momentos, tenemos en la cola la factura num 3 destinada al subscriptor subscriptor_1.

Vamos a crear otro subscriptor, de nombre subscriptor_2, para la cola cm_factura.

BEGIN
   dbms_aqadm.add_subscriber(
       queue_name => 'cm_factura',
       subscriber => sys.aq$_agent('subscriptor_2', NULL, 0)
      );
   commit;
END;
/
-- ahora podemos encolar sin especificar destinatarios
SQL> exec encolar_mensaje(4,'cm_factura');
SQL> COMMIT;

En estos momentos, tenemos en la cola la factura num 4 destinada a los subscriptores subscriptor_1 y subscriptor_2.

Ver el contenido de las colas MultipleConsumer

Cuando se crean las queue tables y las colas Multiple Consumers, se cean una serie de tablas y vistas que podemos consultar (pero no modificar su contenido). Todas ellas se forman a partir del nombre de la queue table.

Vamos a ver algunas de ellas con su contenido actual (2 subscriptore y los mensajes encolados):

Subscriptores de una cola

Se pueden ver consultando la vista AQ$<queuetable>_S

SQL> select * from AQ$TM_FACTURA_S;

QUEUE        NAME           ADDRESS  PROTOCOL  TRANSFORMATION
------------ -------------- -------- --------- --------------
CM_FACTURA   SUBSCRIPTOR_1                  0
CM_FACTURA   SUBSCRIPTOR_2                  0

Mensajes que hay en una cola

Se pueden ver los mensajes que hay en una cola consultando la vista AQ$<queuetable>

Vamos a ver algunos campos:

SQL> select queue, msg_id, to_char(enq_time,'yyyy-mm-dd hh24:mi:ss') enq_time,  msg_state, enq_txn_id,
     consumer_name
     from aq$tm_factura;

QUEUE       MSG_ID                            ENQ_TIME             MSG_STATE  ENQ_TXN_ID  CONSUMER_NAME
----------- --------------------------------- -------------------- ---------- ----------- -------------
CM_FACTURA  73ECB6703FEE0382E040007F01017DFB  2009-09-19 23:26:28  READY      2.17.4623   SUBSCRIPTOR_1
CM_FACTURA  73ECB6703FEF0382E040007F01017DFB  2009-09-19 23:30:24  READY      3.15.4651   SUBSCRIPTOR_1
CM_FACTURA  73ECB6703FEF0382E040007F01017DFB  2009-09-19 23:30:24  READY      3.15.4651   SUBSCRIPTOR_2

Podemos ver el identificador interno del mensaje (MSG_ID), hora que se encolado, su estado (READY indica disponible para desencolar), la transacción y el subscriptor o Consumidor.

Los MSG_ID son siempre distintospara distintos mensajes, pero, en este caso, los MSG_ID se diferencian solo en un carácter E/F después de 73ECB6703FE

Podemos ver que el primer mensaje tiene un único subscriptor y el segundo tiene 2.

Ver el contenido de los mensajes

Se puede ver el contenido de los mensajes en el campo user_data de la tabla <queueyable>. El nombre del campo, user_data, tiene el mismo nombre en todas las queue tables.

Veamos el contenido de un registro:

SQL> select user_data
     from tm_factura
     where msgid = '73ECB6703FEE0382E040007F01017DFB';

USER_DATA(NUM_FACTURA, FECHA, OBSERVACIONES, CLIENTE, PRODUCTOS(CANTIDAD, DESCRIPCION, PRECIO))
-----------------------------------------------------------------------------------------------
FACTURA(3, '19-SEP-09', 'observaciones factura', 'cliente 24', LINEAS(LINEA(2, 'desc1', 25), LI
NEA
(4, 'desc2', NULL), LINEA(1, 'desc3', 1))

Desde sqlplus, el contenido se ve en formato objeto y con sus constructores.

Es de destacar que, exceptuando sqlplus, hay pocos programas que nos permitan ver directamente el contenido de los mensajes de las colas.

Encolar especificando Receptores

Si no nos interesa que un cierto mensaje vaya destinado a  todos los subscriptores, podemos utilizar el envio a Receptores concretos (o lista de Receptores).

Veamos un ejemplo:

declare
  enq_opt dbms_aq.enqueue_options_t;
  msg_opt dbms_aq.message_properties_t;
  msg_id raw(16);
  lista dbms_aq.aq$_recipient_list_t;  --> lista de receptores
begin
      -- llenamos la lista de receptores
    lista(1) := sys.aq$_agent('subscriptor_1', NULL, 0);
    lista(2) := sys.aq$_agent('otro_subscriptor', NULL, 0);
      -- hay que asignar la lista a la variable msg_opt
    msg_opt.recipient_list := lista;
      -- finalmente, encolamos el mensaje
    dbms_aq.enqueue (
       'cm_factura',
       enq_opt,
       msg_opt,
       factura(25,sysdate,'especificando receptores', NULL,NULL),
       msg_id);
    commit;
end;
/

Obsérvese:

  • Hemos enviado a dos receptores: uno es subscriptor y el otro no
    • Cuando se envia a una lista explícita de receptores, no tienen por que ser subscriptores
      • Los subscriptores se utilizan como destinatarios implícitos y por defecto
      • Los receptores son destinatarios explícitos
    • Cada mensaje puede tener unos Receptores distintos o bien los subscriptores de defecto.
  • En este caso, la variable lista, que es un array, no lo dimensionamos previaente y lo vamos llenando directamente
    • Esto es debido a que dbms_aq.aq$_recipient_list_t está definido como un type PL/SQL (y no un Object Type SQL conmo es el Object Type factura)
TYPE SYS.AQ$_RECIPIENT_LIST_T IS TABLE OF SYS.AQ$_AGENT
INDEX BY BINARY_INTEGER;

Desencolar en colas Multiple Consumer

Para desencolar en colas multiple consumer, se debe especificar el consumidor o subscriptor.

Veámosmo con un ejemplo:

SQL> set serveroutput on
SQL> declare
  deq_opt dbms_aq.dequeue_options_t;
  msg_opt dbms_aq.message_properties_t;
  msg_id raw(16);
  datos factura;
begin
    deq_opt.consumer_name := 'otro_subscriptor';  --> indicar el subscriptor
    dbms_aq.dequeue (
       'cm_factura',
       deq_opt,
       msg_opt,
       datos,
       msg_id);
    dbms_output.put_line(datos.num_factura || ' ' || datos.observaciones);
end;
/
25 especificando receptores
SQL> commit;

La única diferencia con las solas Single Consumer es que hay que especificar el subscriptor.