Advanced Queuing Internals: Julian Dyke Independent Consultant
Advanced Queuing Internals: Julian Dyke Independent Consultant
Advanced Queuing Internals: Julian Dyke Independent Consultant
Internals
Julian Dyke
Independent Consultant
Introduction
Single Consumer Queues
Multiple Consumer Queues
Recipients
Subscribers
Exception Queues
Array Payloads
Buffered Messages
Spilled Messages
Performance
Buffered messages
Stored in SGA
Can be spilled to queue table
Lost during instance restart
Subscribers can:
Specify rules to control which messages they dequene
Specify transformations to be performed against dequeued data
DBMS_AQADM.CREATE_QUEUE_TABLE ('QT1','RAW');
DBMS_AQADM.CREATE_QUEUE_TABLE ('QT1','TYPE1');
The following objects will be created (object IDs and constraint IDs will vary):
The following objects will be created (object IDs and constraint IDs will vary):
70591 is used
LOB column AQ$_QT1_E
for USER_PROP columnQUEUE
The following indexes will be created by default (constraint IDs will vary):
QT<object_id>_BUFFER
e.g. QT70581_BUFFER
based on X$BUFFER2
AQ$_<queue_table_name>_F
e,g. AQ$_QT1_F
based on QT1 and ALL_DEQUEUE_QUEUES
SELECT
"ADDR", "INDX", "INST_ID", "OBJNO", "QUEUE_ID", "MSGID", "CORRID",
"SEQUENCE_NUM", "MSG_NUM", "STATE", "PRIORITY", "EXPIRATION",
"ENQ_TIME", "ENQ_UID", "ENQ_USER_NAME", "RETRY_COUNT",
"SENDER_NAME", "SENDER_ADDRESS", "SENDER_PROTOCOL",
"DEQUEUE_MSGID", "SRCSEQUENCE_NUM", "SUBSCRIBER_ID",
"EXCEPTIONQ_SCHEMA", "EXCEPTIONQ_NAME"
FROM X$BUFFER2
WHERE objno = 70581;
SELECT
qt.q_name Q_NAME, qt.rowid ROW_ID, qt.msgid MSGID, qt.corrid CORRID,
qt.priority PRIORITY, qt.state STATE, qt.delay DELAY, qt.expiration EXPIRATION,
qt.enq_time ENQ_TIME, qt.enq_uid ENQ_UID, qt.enq_tid ENQ_TID,
qt.deq_time DEQ_TIME, qt.deq_uid DEQ_UID, qt.deq_tid DEQ_TID,
qt.retry_count RETRY_COUNT, qt.exception_qschema EXCEPTION_QSCHEMA,
qt.exception_queue EXCEPTION_QUEUE, qt.cscn CSCN, qt.dscn DSCN,
qt.chain_no CHAIN_NO, qt.local_order_no LOCAL_ORDER_NO,
qt.time_manager_info TIME_MANAGER_INFO, qt.step_no STEP_NO,
qt.user_data USER_DATA , qt.sender_name SENDER_NAME,
qt.sender_address SENDER_ADDRESS, qt.sender_protocol SENDER_PROTOCOL,
qt.dequeue_msgid DEQUEUE_MSGID, 'PERSISTENT' DELIVERY_MODE,
0 SEQUENCE_NUM, 0 MSG_NUM, qo.qid QUEUE_ID,
qt.user_prop USER_PROP
FROM
"QT1" qt,
ALL_DEQUEUE_QUEUES qo
WHERE qt.q_name = qo.name
AND qo.owner = 'US01'
WITH READ ONLY;
DBMS_AQADM.CREATE_QUEUE
(queue_name => 'Q1',queue_table => 'QT1');
DECLARE
message TYPE1;
msgprop dbms_aq.message_properties_t;
enqopt dbms_aq.enqueue_options_t;
enq_msgid RAW(16);
BEGIN
message := new TYPE1 (10001,20001,30001);
msgprop.expiration :=DBMS_AQ.NEVER
dbms_aq.enqueue
(
queue_name => 'Q1',
enqueue_options => enqopt,
message_properties => msgprop,
payload => message,
msgid => enq_msgid
);
END;
insert into "US01"."QT1" (q_name, msgid, corrid, priority, state, delay, expiration,
time_manager_info, local_order_no, chain_no, enq_time, step_no, enq_uid, enq_tid,
retry_count, exception_qschema, exception_queue, recipient_key, dequeue_msgid,
user_data, sender_name, sender_address, sender_protocol, user_prop, cscn, dscn)
values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, 0, :15,:16, :17, :18, :19, :20, :21,
:22, :23, :24, :25)
In Oracle 11.1 this statement uses the LOAD TABLE CONVENTIONAL operation
STAT #3 id=1 cnt=0 pid=0 pos=1 obj=0 op='LOAD TABLE CONVENTIONAL (cr=1 pr=5 pw=5
time=0 us)')
The statement selects all rows in the queue specified by :1 with a state of :2
The FIRST_ROWS(1) hint is used to optimize the plan
The statement locks any rows to be deleted
This will generate undo/redo
The statement uses the FOR UPDATE SKIP LOCKED clause to skip any rows
still locked by ongoing transactions
STAT #3 id=1 cnt=1 pid=0 pos=1 obj=0 op='FOR UPDATE (cr=7 pr=2 pw=2 time=0 us)'
STAT #3 id=2 cnt=1 pid=1 pos=1 obj=0 op='SORT ORDER BY (cr=7 pr=0 pw=0 time=0 us
cost=4 size=2759 card=1)'
STAT #3 id=3 cnt=1 pid=2 pos=1 obj=70581 op='TABLE ACCESS FULL QT1 (cr=7 pr=0
pw=0 time=0 us cost=3 size=2759 card=1)'
DBMS_AQADM.CREATE_QUEUE
(queue_name => 'Q1',queue_table => 'QT1');
DBMS_AQADM.CREATE_QUEUE
(queue_name => 'Q1E',queue_table => 'QT1'
queue_type => DBMS_AQADM.EXCEPTION_QUEUE);
BEGIN
dbms_aqadm.create_queue_table
('QT3','TYPE1',multiple_consumers=>TRUE)
dbms_aqadm.create_queue ('Q3','QT3');
dbms_aqadm.start_queue ('Q3');
END;
/
The following objects will be created (object IDs and constraint IDs will vary):
AQ$_<queue_table_name>_I
IOT that maintains state for dequeue operations
One row per message per recipient/subscriber
AQ$_<queue_table_name>_S
Heap table containing information about subscribers
AQ$_<queue_table_name>_H
IOT used to store dequeue history
One row per message per recipient/subscriber
AQ$_<queue_table_name>_G
IOT correlating messages to subscriber signatures
SELECT
<column_list>
FROM
"QT8" qt,
"AQ$_QT8_H" h,
"AQ$_QT8_S" s
WHERE qt.msgid = h.msgid
AND ((h.subscriber# != 0 AND h.subscriber# = s.subscriber_id)
OR (h.subscriber# = 0 AND h.address# = s.subscriber_id))
AND (qt.state != 7 OR qt.state != 9)
WITH READ ONLY;
DECLARE
l_payload TYPE1;
l_msgprop dbms_aq.message_properties_t;
l_deqopt dbms_aq.dequeue_options_t;
l_deq_msgid RAW(16);
BEGIN
l_deqopt.consumer_name := 'CONSUMER2';
dbms_aq.dequeue
(
queue_name => 'Q3',
dequeue_options => l_deqopt,
message_properties => l_msgprop,
payload => l_payload,
msgid => l_deq_msgid
);
END;
Notes
A consumer name MUST be specified
The message must have been enqueued specifically for that consumer
The queue monitor (QMNC) process asynchronously checks the timer table
(AQ$_QT3_T) for actions
If any actions are found these are sent to the queue monitor slaves (Q001,
Q002 etc)
When last recipient has dequeued message, queue monitor slaves perform
the following actions
Delete all rows for message in queue history table (AQ$_QT3_H)
Delete row in queue table (QT3) for message
DECLARE
l_subscriber sys.aq$_agent;
BEGIN
l_subscriber := sys.aq$_agent ('SUBSCRIBER1',NULL,NULL);
DBMS_AQADM.ADD_SUBSCRIBER
(
queue_name => 'Q3',
subscriber => l_subscriber
);
l_subscriber := sys.aq$_agent ('SUBSCRIBER2',NULL,NULL);
DBMS_AQADM.ADD_SUBSCRIBER
(
queue_name => 'Q3',
subscriber => l_subscriber
);
END;
DECLARE
l_payload TYPE1;
l_msgprop dbms_aq.message_properties_t;
l_enqopt dbms_aq.enqueue_options_t;
l_enq_msgid RAW(16);
BEGIN
FOR f IN 1..10
LOOP
l_payload := new TYPE1 (10000 + f,20000 + f,30000 + f);
l_msgprop.expiration := DBMS_AQ.NEVER;
dbms_aq.enqueue
(
queue_name => 'Q3',
enqueue_options => l_enqopt,
message_properties => l_msgprop,
payload => l_payload,
msgid => l_enq_msgid
);
END LOOP;
END;
SET SERVEROUTPUT ON
DECLARE
l_payload TYPE1;
l_msgprop dbms_aq.message_properties_t;
l_deqopt dbms_aq.dequeue_options_t;
l_deq_msgid RAW(16);
BEGIN
l_deqopt.consumer_name := 'SUBSCRIBER1';
dbms_aq.dequeue
(
queue_name => 'Q3',
dequeue_options => l_deqopt,
message_properties => l_msgprop,
payload => l_payload,
msgid => l_deq_msgid
);
DBMS_OUTPUT.PUT_LINE ('C1 = '||TO_CHAR (l_payload.c1));
DBMS_OUTPUT.PUT_LINE ('C2 = '||TO_CHAR (l_payload.c2));
DBMS_OUTPUT.PUT_LINE ('C3 = '||TO_CHAR (l_payload.c3));
END;
DECLARE
l_subscriber sys.aq$_agent;
BEGIN
l_subscriber := sys.aq$_agent ('SUBSCRIBER3',NULL,NULL);
DBMS_AQADM.ADD_SUBSCRIBER
(
queue_name => 'Q3',
subscriber => l_subscriber
);
DBMS_AQADM.REMOVE_SUBSCRIBER
(
queue_name => 'Q3',
subscriber => l_subscriber
);
END;
New subscribers will only be allowed to dequeue messages that have been enqueued after the subscriber was added
DBMS_AQADM.CREATE_QUEUE_TABLE ('QT3','TYPE3');
DBMS_AQADM.CREATE_QUEUE ('Q3','QT3');
DBMS_AQADM.START_QUEUE ('Q3');
Buffered messages
Are much faster than persistent queues
Do not guarantee reliability
Cannot form part of a transaction
Do not support (Oracle 11.1)
Message retention / delay
Transaction grouping
Array enqueue / dequeue
Message export / import
The following definitions are used with the examples in this section
BEGIN
dbms_aqadm.create_queue_table ('QT1','TYPE1')
dbms_aqadm.create_queue ('Q1','QT1');
dbms_aqadm.start_queue ('Q1');
END;
DECLARE
l_payload TYPE1;
l_msgprop dbms_aq.message_properties_t;
l_enqopt dbms_aq.enqueue_options_t;
l_enq_msgid RAW(16);
BEGIN
l_payload := new TYPE1 (10001,20001,30001);
l_msgprop.expiration := DBMS_AQ.NEVER;
l_enqopt.visibility := DBMS_AQ.IMMEDIATE;
l_enqopt.delivery_mode := DBMS_AQ.BUFFERED;
dbms_aq.enqueue
(
queue_name => 'Q1',
enqueue_options => l_enqopt,
message_properties => l_msgprop,
payload => l_payload,
msgid => l_enq_msgid
);
END;
Two view definitions are also updated when the first buffered message is
enqueued:
AQ$<queue_table_name>
e.g. AQ$QT3
reports all messages in persistent and buffered queues
AQ$_<queue_table_name>_F
e.g. AQ$_QT3_F
reports all messages that have not yet been dequeued in both
persistent and buffered queues
Limited to
5000 buffered messages
15000 captured messages