How to Add Queue Subscribers in Oracle

Deliver each queue message to several subscribers, filter what each receives with rules, and dequeue by consumer name.

On a normal queue, each message goes to one consumer. A multiple-consumer queue works like publish and subscribe: every subscriber gets its own copy of each message, and a rule can limit a subscriber to the messages it cares about. DBMS_AQADM.ADD_SUBSCRIBER registers the subscribers.

Code for This Guide

The main examples are in the examples/pkg-messaging folder of the Oracle Database 26ai code repository on GitHub, each with its output. They use NIMBUS, the sample schema of a fictional airline, which you can install with the scripts in the setup/nimbus folder.

They come from Oracle Database 26ai SQL and PL/SQL Book.

Syntax

dbms_aqadm.create_queue_table(queue_table, payload_type, multiple_consumers => true);
dbms_aqadm.add_subscriber(queue_name, sys.aq$_agent('NAME', null, null),
                          rule => 'condition on tab.user_data');
-- dequeue as a subscriber
v_options.consumer_name := 'NAME';

The example uses the GATE_CHANGE_T type from How to Create Queues with DBMS_AQADM.

Two Subscribers, One with a Rule

Example:

begin
  dbms_aqadm.create_queue_table('ALERT_QT', 'GATE_CHANGE_T', multiple_consumers => true);
  dbms_aqadm.create_queue('ALERT_Q', 'ALERT_QT');
  dbms_aqadm.start_queue('ALERT_Q');
  dbms_aqadm.add_subscriber('ALERT_Q', sys.aq$_agent('CREW_APP', null, null));
  dbms_aqadm.add_subscriber('ALERT_Q', sys.aq$_agent('PAX_APP', null, null),
                            rule => q'[tab.user_data.flight_no like 'NM1%']');
end;
/
declare
  v_enq   dbms_aq.enqueue_options_t;
  v_deq   dbms_aq.dequeue_options_t;
  v_props dbms_aq.message_properties_t;
  v_msg   gate_change_t;
  v_msgid raw(16);
begin
  dbms_aq.enqueue('ALERT_Q', v_enq, v_props, gate_change_t('NM150', 'A1', 'B12'), v_msgid);
  dbms_aq.enqueue('ALERT_Q', v_enq, v_props, gate_change_t('NM205', 'D2', 'D4'), v_msgid);
  commit;
  v_deq.wait := dbms_aq.no_wait;
  for c in (select column_value as consumer
            from   sys.odcivarchar2list('CREW_APP', 'PAX_APP')) loop
    v_deq.consumer_name := c.consumer;
    v_deq.navigation    := dbms_aq.first_message;       -- start again for each consumer
    begin
      loop
        dbms_aq.dequeue('ALERT_Q', v_deq, v_props, v_msg, v_msgid);
        dbms_output.put_line(rpad(c.consumer, 10) || v_msg.flight_no || ' -> '
                             || v_msg.new_gate);
      end loop;
    exception
      when others then if sqlcode != -25228 then raise; end if;
    end;
  end loop;
  commit;
end;
/

Output:

PL/SQL procedure successfully completed.

CREW_APP  NM150 -> B12
CREW_APP  NM205 -> D4
PAX_APP   NM150 -> B12

PL/SQL procedure successfully completed.
  • CREW_APP subscribes to everything, so it receives both gate changes.
  • PAX_APP has a rule on the payload, flight numbers starting with NM1, so it receives only NM150.
  • Each consumer dequeues with its own CONSUMER_NAME and starts from FIRST_MESSAGE.

A message stays in the queue until every subscriber it was meant for has dequeued it.

Things to Know

  • Subscribers added later receive only messages enqueued after they subscribed.
  • USER_QUEUE_SUBSCRIBERS lists the subscribers and their rules; REMOVE_SUBSCRIBER deletes one.
  • A message can also name its own recipients in the RECIPIENT_LIST property, overriding the subscriber list.

Cleanup:

exec dbms_aqadm.drop_queue_table('ALERT_QT', force => true)

Related Guides

Conclusion

Multiple-consumer queues deliver each message to every subscriber, and subscriber rules filter what each one receives. Add subscribers with ADD_SUBSCRIBER and dequeue with each subscriber's CONSUMER_NAME.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted