Unable to understand how APEX_MAIL's job runs - oracle

I'm currently trying to implement a similar version of oracle's APEX_MAIL package. I have everything working, but I can't make the job work unless I modify it.
The job APEX_MAIL uses is called ORACLE_APEX_MAIL_QUEUE
BEGIN
DBMS_SCHEDULER.set_attribute( name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"', attribute => 'job_action', value => 'APEX_040000.WWV_FLOW_MAIL.PUSH_QUEUE');
DBMS_SCHEDULER.set_attribute( name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"', attribute => 'number_of_arguments', value => '2');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"',
argument_position => 1,
argument_value => '');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"',
argument_position => 2,
argument_value => '');
END;
/
So I go to the package to see what the code does. I'm was assuming push queue would send out emails in the queue. Instead, it calls the same job again!
PROCEDURE PUSH_QUEUE( P_SMTP_HOSTNAME IN VARCHAR2 DEFAULT NULL,
P_SMTP_PORTNO IN VARCHAR2 DEFAULT NULL )
IS
BEGIN
PUSH_QUEUE_BACKGROUND;
END PUSH_QUEUE;
PROCEDURE PUSH_QUEUE_BACKGROUND
IS
BEGIN
SYS.DBMS_SCHEDULER.RUN_JOB( JOB_NAME => 'ORACLE_APEX_MAIL_QUEUE', USE_CURRENT_SESSION => FALSE );
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE <> -27478 THEN
RAISE;
END IF;
END PUSH_QUEUE_BACKGROUND;
So basically this job does nothing, but I switch it to call PUSH_QUEUE_IMMEDIATE which does what I think it should do.
PROCEDURE PUSH_QUEUE_IMMEDIATE( P_FORCE_YN IN VARCHAR2 DEFAULT 'N')
IS
L_STATUS NUMBER;
L_LOCK_HDL VARCHAR2(128);
E_DB_SHUTDOWN EXCEPTION;
PRAGMA EXCEPTION_INIT(E_DB_SHUTDOWN, -1089);
BEGIN
WWV_FLOW_DEBUG.ENABLE_DBMS_OUTPUT;
SYS.DBMS_LOCK.ALLOCATE_UNIQUE( LOCKNAME => 'APEX_MAIL_QUEUE_LOCK', LOCKHANDLE => L_LOCK_HDL);
L_STATUS := SYS.DBMS_LOCK.REQUEST( LOCKHANDLE => L_LOCK_HDL,
LOCKMODE => SYS.DBMS_LOCK.X_MODE,
TIMEOUT => 0,
RELEASE_ON_COMMIT => FALSE );
WWV_FLOW_DEBUG.INFO('APEX Mail Lock status: ' || L_STATUS );
IF L_STATUS = 0 THEN
FOR C1 IN ( SELECT ID, MAIL_SEND_COUNT, LAST_UPDATED_ON
FROM WWV_FLOW_MAIL_QUEUE
ORDER BY MAIL_SEND_COUNT, LAST_UPDATED_ON) LOOP
BEGIN
WWV_FLOW_DEBUG.INFO( 'Pushing email: ' || C1.ID );
IF (C1.MAIL_SEND_COUNT = 0) OR (NVL(P_FORCE_YN,'N') = 'Y') OR
(C1.MAIL_SEND_COUNT > 0 AND (POWER(2,C1.MAIL_SEND_COUNT)/(60*24) + C1.LAST_UPDATED_ON) < SYSDATE) THEN
BACKGROUND( P_ID => C1.ID );
END IF;
WWV_FLOW_DEBUG.INFO( 'Pushed email: ' || C1.ID );
EXCEPTION
WHEN OTHERS THEN
WWV_FLOW_DEBUG.LOG_EXCEPTION;
IF L_LOCK_HDL IS NOT NULL THEN
L_STATUS := SYS.DBMS_LOCK.RELEASE( L_LOCK_HDL );
WWV_FLOW_DEBUG.INFO('APEX Mail released lock' );
END IF;
END;
END LOOP;
END IF;
IF L_LOCK_HDL IS NOT NULL THEN
L_STATUS := SYS.DBMS_LOCK.RELEASE( L_LOCK_HDL );
WWV_FLOW_DEBUG.INFO('APEX Mail released lock' );
END IF;
EXCEPTION WHEN E_DB_SHUTDOWN THEN
NULL;
END PUSH_QUEUE_IMMEDIATE;
I'm trying to copy APEX_MAIL to a point, but if I do, I won't have a working job. Can anyone point out if APEX_MAIL changes what the job does after an application setting change or any other change?
Thanks in advance!

APEX_MAIL.PUSH_QUEUE is usable in your own code to send your mail (in the queue) out immediate. The job normally calls PUSH_QUEUE_IMMEDIATE. I don't know if your setting ever was a bug in the installation or something wrong on your site.
Thus fact, it calls PUSH_QUEUE_IMMEDIATE in a separate session as APEX_040000 job.
Since everyone can request an immediate send of all the jobs in the queue, it makes sure via SYS.DBMS_LOCK.REQUEST only one session will actually do the transmit.

Related

Oracle AQ between 2 databases wont propagate

I am trying to write a proof of concept to test sending/receiving messages between 2 Oracle database using AQ.
Source database : 12.1.0.2 SRCDB
Destination database: 19.14.0.0, DESTDB
We want to send a message from SRCDB to DESTDB, such that when DESTDB gets the message it automatically invokes a PLSQL stored procedure to dequeue and process.
So, start on the SRCDB I do the following:
CREATE OR REPLACE TYPE the_mesg AS OBJECT ( mesg VARCHAR2(20) );
/
`
BEGIN
DBMS_AQADM.CREATE_QUEUE_TABLE(
queue_table => 'the_que_tab',
queue_payload_type => 'the_mesg',
multiple_consumers => TRUE -- Mesg only consumed once ???
);
END;
/
-- create queue
BEGIN
DBMS_AQADM.CREATE_QUEUE(
queue_name => 'the_que',
queue_table => 'the_que_tab' );
END;
/
-- Add remote subscriber
DECLARE
reginfo SYS.AQ$_REG_INFO;
reginfolist SYS.AQ$_REG_INFO_LIST;
BEGIN
DBMS_AQADM.ADD_SUBSCRIBER(
queue_name => 'the_que',
subscriber => SYS.AQ$_AGENT( name => 'the)_sub',
address => 'the_remuser.the_que#destdb',
protocol => 0 ),
queue_to_queue => FALSE );
END;
/
-- Add propogation schedule
BEGIN
DBMS_AQADM.SCHEDULE_PROPAGATION(
queue_name => 'the_que',
latency => 0,
start_time => SYSDATE,
next_time => 'SYSDATE',
destination => 'destdb',
destination_queue => 'the_remuser.the_que' );
DBMS_LOCK.SLEEP(10);
END;
/
-- start queue
BEGIN
DBMS_AQADM.START_QUEUE(
queue_name => 'the_que',
enqueue => TRUE,
dequeue => TRUE );
DBMS_LOCK.SLEEP(10);
END;
/
-- propagation schedule
BEGIN
DBMS_AQADM.ENABLE_PROPAGATION_SCHEDULE(
queue_name => 'the_que',
destination => 'destdb',
destination_queue => 'the_remuser.the_que');
END;
/
-- Create send mesg wrapper procedure
CREATE OR REPLACE PROCEDURE send_mesg ( p_msg IN VARCHAR2 ) AS
l_enq_options SYS.DBMS_AQ.enqueue_options_t;
l_mesg_props SYS.DBMS_AQ.message_properties_t;
l_mesg_handle RAW(16);
l_mesg the_mesg;
BEGIN
l_mesg := the_mesg( p_msg );
DBMS_AQ.enqueue (
queue_name => 'the_que',
enqueue_options => l_enq_options,
message_properties => l_mesg_props,
payload => l_mesg,
msgid => l_mesg_handle );
COMMIT;
END;
/
-- Send mesg
EXEC send_mesg ( 'hello' );
Here, the queue's enqueue/dequeue settings are enabled, and there is an entry for the message in the queue table.
Then on the destdb:
CREATE OR REPLACE TYPE the_mesg AS OBJECT (
mesg VARCHAR2(20)
);
/
BEGIN
DBMS_AQADM.CREATE_QUEUE_TABLE(
queue_table => 'the_que',
queue_payload_type => 'the_mesg' );
END;
/
BEGIN
DBMS_AQADM.CREATE_QUEUE(
queue_name => 'the_que',
queue_table => 'the_que_tab' );
END;
/
DECLARE
reginfo SYS.AQ$_REG_INFO;
reginfolist SYS.AQ$_REG_INFO_LIST;
BEGIN
reginfo := SYS.AQ$_REG_INFO( 'the_remuser.the_que',
1,
'PLSQL://recv_mesg',
HEXTORAW('FF') );
reginfolist := SYS.AQ$_REG_INFO_LIST ( reginfo );
DBMS_AQ.REGISTER ( reg_list => reginfolist,
reg_count => 1 );
END;
/
BEGIN
DBMS_AQADM.START_QUEUE( queue_name => 'the_que',
dequeue => TRUE );
END;
/
CREATE TABLE mesg_table_aq_demo
(
cdate DATE DEFAULT SYSDATE,
mesg VARCHAR2(32) NOT NULL
);
CREATE OR REPLACE PROCEDURE recv_mesg
(
context IN RAW,
reginfo IN SYS.AQ$_REG_INFO,
descr IN SYS.AQ$_DESCRIPTOR,
payload IN RAW,
payload1 IN NUMBER
)
IS
l_deq_options DBMS_AQ.dequeue_options_t;
l_mesg_props DBMS_AQ.message_properties_t;
l_mesg_handle RAW(16);
l_mesg the_mesg;
no_messages EXCEPTION;
PRAGMA EXCEPTION_INIT ( no_messages, -25228 );
BEGIN
l_deq_options.msgid := descr.msg_id;
l_deq_options.consumer_name := descr.consumer_name;
LOOP
DBMS_AQ.dequeue ( queue_name => 'the_que',
dequeue_options => l_deq_options,
message_properties => l_mesg_props,
payload => l_mesg,
msgid => l_mesg_handle );
INSERT INTO mesg_table_aq_demo ( cdate, mesg )
VALUES ( SYSDATE, l_mesg.mesg );
COMMIT;
END LOOP;
EXCEPTION
WHEN no_messages THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
/
When I run this by enqueuing a message on the SRCDB the message is not propagated to the DESTDB.
No errors, although sometimes (as a result of desperate hacking) I see the following on the source user_queue_schedules:
ORA-25226: dequeue failed, queue SRCUSER.THE_QUE is not enabled for dequeue
Even though it looks like it is.
What am I doing wrong?

Oracle - Generate Package.Procedure call

Good morning guys,
do you know an easy way to automatically generate an Oracle package.procedure call?
After have defined the package
Create Or Replace Package PKG1
As
Procedure PRC1
(
P_VAL1_I In NUMBER,
P_Return_Set_O Out Sys_Refcursor,
);
End;
I'd like to generate via a script the call
DECLARE
var_P_RETURN_SET_O SYS_REFCURSOR;
BEGIN
PKG1.PRC1(P_VAL1_I => 0, P_Return_Set_O => var_P_RETURN_SET_O );
END;
Of Course I'll replace later the input parameter(s).
Any idea/suggestion?
Here the answer to myself .. was not that difficult .. simply run a pl/sql that build the procedure call .
Declare
l_Package varchar2(255) :=upper('xxxxxxxxx');
l_Proc varchar2(255) :=upper('xxxxxxxxxxxx');
Begin
Dbms_Output.Put_Line('DECLARE');
For Var_Out In (
select ObjArg.Argument_Name,Data_type
FROM sys.user_objects UsrObj
Inner Join sys.user_arguments ObjArg On UsrObj.object_id= ObjArg.object_id
Where UsrObj.object_name = l_Package
And ObjArg.ObjecT_Name= l_Proc
AND UsrObj.object_type = 'PACKAGE'
And ObjArg.In_Out='OUT'
)
Loop
Dbms_Output.Put_Line('L_'||Var_Out.Argument_Name ||' '||Var_Out.Data_type||';');
end Loop;
Dbms_Output.Put_Line('BEGIN');
Dbms_Output.Put_Line(l_Package||'.'||l_Proc);
Dbms_Output.Put_Line('(');
For PArams In (
select ObjArg.Argument_Name,Data_type,ObjArg.In_Out
FROM sys.user_objects UsrObj
Inner Join sys.user_arguments ObjArg On UsrObj.object_id= ObjArg.object_id
Where UsrObj.object_name = l_Package
And ObjArg.ObjecT_Name= l_Proc
AND UsrObj.object_type = 'PACKAGE'
)
Loop
IF PArams.In_Out='OUT' Then
Dbms_Output.Put_Line(PArams.Argument_Name || '=> L_'||PArams.Argument_Name);
ELSE
Dbms_Output.Put_Line(PArams.Argument_Name || '=> xxx');
End If;
end Loop;
Dbms_Output.Put_Line(');');
Dbms_Output.Put_Line('END');
End;
Result ..
DECLARE
L_P_RETURN_SET_O REF CURSOR;
L_P_EXECUTION_STATUS_O NUMBER;
BEGIN
PKG.PROC
(
P_PARAM_I=> xxx
P_RETURN_SET_O=> L_P_RETURN_SET_O
);
END;
The code is not perfect ..but it works ...
Did you try:
variable rc refcursor;
exec PKG1.PRC1(0, :rc);
print rc;
You can create job which will call your procedure on specified intervals.
For instance:
BEGIN
DBMS_SCHEDULER.CREATE_JOB
(
job_name => 'JOB_NAME'
,start_date => SYSDATE
,repeat_interval => 'FREQ=DAILY;BYHOUR=05;BYMINUTE=00;BYSECOND=00'
,end_date => NULL
,job_class => 'DEFAULT_JOB_CLASS'
,job_type => 'PLSQL_BLOCK'
,job_action => 'BEGIN
PKG1.PRC1(P_VAL1_I => 0, P_Return_Set_O => var_P_RETURN_SET_O );
END;
,comments => 'JOB_NAME'
);
DBMS_SCHEDULER.ENABLE(name => 'JOB_NAME');
END;
This job will run daily at 5 a.m .
I hope it will solve your problem!

How do I call a function from within a PL/SQL process within Oracle APEX 4.2

I've created a new function called GET_FILENAME inside my Oracle APEX database. I'd like to call this function from within a APEX PL/SQL code block and pass it the filename. Below is the function I created in my APEX DB with SQL Dev.
create or replace function get_filename
(p_path IN VARCHAR2)
RETURN varchar2
IS
v_file VARCHAR2(100);
BEGIN
-- Parse string for UNIX system
IF INSTR(p_path,'/') > 0 THEN
v_file := SUBSTR(p_path,(INSTR(p_path,'/',-1,1)+1),length(p_path));
-- Parse string for Windows system
ELSIF INSTR(p_path,'\') > 0 THEN
v_file := SUBSTR(p_path,(INSTR(p_path,'\',-1,1)+1),length(p_path));
-- If no slashes were found, return the original string
ELSE
v_file := p_path;
END IF;
RETURN v_file;
END;
APEX side of things....
Below is the code I'd like to put in when calling the process.
TO_DATE(SUBSTR(GET_FILENAME(file_date),21,8),'YYYY-MM-DD')
Below is the apex process code and the comments are where I'd like to put it in.
BEGIN
APEX_COLLECTION.ADD_MEMBER
(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'C031',
p_c002 => 'FILE_DATE');
FOR UPLOAD_ROW IN (SELECT SEQ_ID FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '31',
p_attr_value => :P25_FILE_NAME -- I want to call the process here
);
END LOOP;
END;
I don't normally answer my own question. But this was easier than I thought :). Below is the code I used to get this working....
BEGIN
APEX_COLLECTION.ADD_MEMBER
(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'C031',
p_c002 => 'FILE_DATE');
FOR UPLOAD_ROW IN (SELECT SEQ_ID FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '31',
p_attr_value => TO_DATE(SUBSTR(GET_FILENAME(:P25_FILE_NAME),21,8),'YYYY-MM-DD')
);
END LOOP;
END;

Oracle Advance Queue - Dequeue not working

I can't seem to find the solution to my problem, I've been stuck at this for hours.
I'm usings Oracle AQs:
Dbms_Aqadm.Create_Queue_Table(Queue_Table => 'ITEM_EVENT_QT',
Queue_Payload_Type => 'ITEM_EVENT',
Multiple_Consumers => TRUE);
Dbms_Aqadm.Create_Queue(Queue_Name => 'ITEM_EVENT_QUEUE',
Queue_Table => 'ITEM_EVENT_QT',
Max_Retries => 5,
Retry_Delay => 0,
Retention_Time => 432000, -- 5 DAYS
Dependency_Tracking => FALSE,
COMMENT => 'Item Event Queue');
-- START THE QUEUE
Dbms_Aqadm.Start_Queue('ITEM_EVENT_QUEUE');
-- GRANT QUEUE PRIVILEGES
Dbms_Aqadm.Grant_Queue_Privilege(Privilege => 'ALL',
Queue_Name => 'ITEM_EVENT_QUEUE',
Grantee => 'PUBLIC',
Grant_Option => FALSE);
END;
Here's one of my subscribers:
Dbms_Aqadm.Add_Subscriber(Queue_Name => 'ITEM_EVENT_QUEUE',
Subscriber => Sys.Aq$_Agent('ITEM_SUBSCRIBER_1',
NULL,
NULL),
rule => 'tab.user_data.header.thread_no = 1');
Dbms_Aq.Register(Sys.Aq$_Reg_Info_List(Sys.Aq$_Reg_Info('ITEM_EVENT_QUEUE:ITEM_SUBSCRIBER_1',
Dbms_Aq.Namespace_Aq,
'plsql://ITEM_API.GET_QUEUE_FROM_QUEUE',
HEXTORAW('FF'))),1);
The subscriber registration:
Whenever a certain event occurs on my DB, I'm using a trigger to add "the event" to my AQ by calling the following procedure from my ITEM_API package:
PROCEDURE ADD_EVENT_TO_QUEUE(I_EVENT IN ITEM_EVENT,
O_STATUS_CODE OUT VARCHAR2,
O_ERROR_MSG OUT VARCHAR2) IS
ENQUEUE_OPTIONS DBMS_AQ.ENQUEUE_OPTIONS_T;
MESSAGE_PROPERTIES DBMS_AQ.MESSAGE_PROPERTIES_T;
MESSAGE_HANDLE RAW(16);
EVENT ITEM_EVENT;
HEADER_PROP HEADER_PROPERTIES;
BEGIN
EVENT := I_EVENT;
EVENT.SEQ_NO := ITEM_EVENT_SEQ.NEXTVAL;
ENQUEUE_OPTIONS.VISIBILITY := DBMS_AQ.ON_COMMIT;
ENQUEUE_OPTIONS.SEQUENCE_DEVIATION := NULL;
MESSAGE_PROPERTIES.PRIORITY := 1;
MESSAGE_PROPERTIES.DELAY := DBMS_AQ.NO_DELAY;
MESSAGE_PROPERTIES.EXPIRATION := DBMS_AQ.NEVER;
HEADER_PROP := HEADER_PROPERTIES(1);
EVENT.HEADER := HEADER_PROP;
DBMS_AQ.ENQUEUE(QUEUE_NAME => 'ITEM_EVENT_QUEUE',
ENQUEUE_OPTIONS => ENQUEUE_OPTIONS,
MESSAGE_PROPERTIES => MESSAGE_PROPERTIES,
PAYLOAD => EVENT,
MSGID => MESSAGE_HANDLE);
EXCEPTION
WHEN OTHERS THEN
ERROR_HANDLER.LOG_ERROR(NULL,
EVENT.ITEM,
EVENT.SEQ_NO,
SQLCODE,
SQLERRM,
O_STATUS_CODE,
O_ERROR_MSG);
RAISE;
END ADD_EVENT_TO_QUEUE;
And it's working because when I check my AQ table, I can find "the event", however my dequeue method is not dequeing, as you can see in the image bellow, there's no DEQ_TIME.
Here's my dequeue method, also from my ITEM_API package:
PROCEDURE GET_QUEUE_FROM_QUEUE(CONTEXT RAW,
REGINFO SYS.AQ$_REG_INFO,
DESCR SYS.AQ$_DESCRIPTOR,
PAYLOAD RAW,
PAYLOADL NUMBER) IS
R_DEQUEUE_OPTIONS DBMS_AQ.DEQUEUE_OPTIONS_T;
R_MESSAGE_PROPERTIES DBMS_AQ.MESSAGE_PROPERTIES_T;
V_MESSAGE_HANDLE RAW(16);
I_PAYLOAD ITEM_EVENT;
L_PROC_EVENT BOOLEAN;
O_TARGETS CFG_EVENT_STAGE_TBL;
O_ERROR_MSG VARCHAR2(300);
O_STATUS_CODE VARCHAR2(100);
BEGIN
R_DEQUEUE_OPTIONS.MSGID := DESCR.MSG_ID;
R_DEQUEUE_OPTIONS.CONSUMER_NAME := DESCR.CONSUMER_NAME;
R_DEQUEUE_OPTIONS.DEQUEUE_MODE := DBMS_AQ.REMOVE;
--R_DEQUEUE_OPTIONS.WAIT := DBMS_AQ.NO_WAIT;
DBMS_AQ.DEQUEUE(QUEUE_NAME => DESCR.QUEUE_NAME,
DEQUEUE_OPTIONS => R_DEQUEUE_OPTIONS,
MESSAGE_PROPERTIES => R_MESSAGE_PROPERTIES,
PAYLOAD => I_PAYLOAD,
MSGID => V_MESSAGE_HANDLE);
IF I_PAYLOAD IS NOT NULL THEN
L_PROC_EVENT := PROCESS_EVENT(I_PAYLOAD,
O_TARGETS,
O_STATUS_CODE,
O_ERROR_MSG);
END IF;
EXCEPTION
WHEN OTHERS THEN
ERROR_HANDLER.LOG_ERROR(NULL,
NULL,
NULL,
SQLCODE,
SQLERRM,
O_STATUS_CODE,
O_ERROR_MSG);
RAISE;
END GET_QUEUE_FROM_QUEUE;
Am I doing something wrong? How can I fix this? I think there might be a problem with my subscriber registration, but I'm not sure.
EDIT: I've just figured out that if I remove the subscribers and the register, and then re-add them, they'll dequeue all messages. Howerver if another event gets enqueued, it stays there indefinetly (or until I remove and add the subscribers again):
The record with state 0 and no DEQ_TIME is the new one.
Do I need a scheduler or something like that?
EDIT: I've added a scheduler propagation to my AQ:
DBMS_AQADM.SCHEDULE_PROPAGATION('ITEM_EVENT_QUEUE');
and even added the next_time field:
DBMS_AQADM.SCHEDULE_PROPAGATION('ITEM_EVENT_QUEUE', SYSDATE + 30/86400);
Still doesn't work. Any suggestions? I guess the AQ Notifications aren't working, and my callback procedure is never called. How can I fix this?
EDIT: I've removed my procedure from the package just for testing purposes, so my team mates can compile the ITEM_API package (I don't know if recompiling the package, may or may not have impacts on the dequeue process).
Still doesn't work.
Create a code block and run the following:
DECLARE
dequeue_options DBMS_AQ.dequeue_options_t;
message_properties DBMS_AQ.message_properties_t;
message_handle RAW (16);
I_PAYLOAD ITEM_EVENT;
no_messages exception;
msg_content VARCHAR2 (4000);
PRAGMA EXCEPTION_INIT (no_messages, -25228);
BEGIN
dequeue_options.wait := DBMS_AQ.NO_WAIT;
dequeue_options.consumer_name := 'ITEM_SUBSCRIBER_1';
dequeue_options.navigation := DBMS_AQ.FIRST_MESSAGE;
LOOP
DBMS_AQ.DEQUEUE (queue_name => 'ITEM_EVENT_QUEUE',
dequeue_options => dequeue_options,
message_properties => message_properties,
payload => I_PAYLOAD,
msgid => message_handle
);
END LOOP;
EXCEPTION
WHEN no_messages
THEN
DBMS_OUTPUT.PUT_LINE ('No more messages left');
END;
Let me know what happens to your enqueued messages.
You should have a table where you're dequing the data.
Can you also try adding the enqueud table in the agent and then specify the agent to the dequeue table.
DECLARE
aSubscriber sys.aq$_agent;
BEGIN
aSubscriber := sys.aq$_agent('ITEM_SUBSCRIBER_1',
'ITEM_EVENT_QUEUE',
0);
dbms_aqadm.add_subscriber
( queue_name => 'ITEM_EVENT_QUEUE'
,subscriber => aSubscriber);
END;
/
We faced a related problem (at least related to the title), we couldn't dequeue messages with a delay. The messages in the queue stayed the state "WAITING". And were not changed to "READY".
The Oracle AQ monitoring process that is responsable for changing the state from "WAITING" to "READY" (after the delay is expired) wasn't working properly.
For us a database restart fixed this issue.
I faced the same problem, but it was solved after changing these 2 DB parameters:
job_queue_processes (must be > than 0)
aq_tm_processes (autotuning)
Hope it helps.

How to wait for dbms_scheduler jobs to finish

Using Oracle 11.2
Hi,
Here is what I want to do: I'm scheduling jobs using dbms_scheduler. The number of jobs to schedule is not fixed and a max of 4 jobs should run at the same time. The procedure scheduling the jobs should wait until all jobs are completed. If one job fails, the "schedule" procedure should also fail and all remaining scheduled jobs should be deleted from the scheduler.
Currently I have had to sleeping and polling the table user_scheduler_jobs in a loop.
I'm new to PL/SQL and rather inexperienced so please don't be too harsh on me ;)
Here is my code so far.
First the snippet for scheduling the jobs:
BEGIN
FOR r IN (SELECT p_values FROM some_table WHERE flag = 0 )
LOOP
-- count running jobs
SELECT count(*) INTO v_cnt
FROM user_scheduler_jobs
WHERE job_name LIKE 'something%';
/*
If max number of parallel jobs is reached, then wait before starting a new one.
*/
WHILE v_cnt >= l_max_parallel_jobs
LOOP
dbms_lock.sleep(10);
SELECT count(*) INTO v_cnt
FROM user_scheduler_jobs
WHERE job_name LIKE 'something%' AND state = 'RUNNING';
SELECT count(*) INTO v_cnt_failures
FROM user_scheduler_jobs
WHERE job_name LIKE 'something%' AND state = 'FAILED' OR state = 'BROKEN';
IF v_cnt_failures > 0 THEN RAISE some_exception; END IF;
END LOOP;
-- Start a new Job
v_job_name := 'something_someting_' || p_values;
v_job_action := 'begin user.some_procedure(''' || r.p_values || '''); end;';
dbms_scheduler.create_job(job_name => v_job_name,
job_type => 'PLSQL_BLOCK',
job_action => v_job_action,
comments => 'Some comment ' || v_job_name,
enabled => FALSE,
auto_drop => FALSE);
dbms_scheduler.set_attribute(NAME => v_job_name,
ATTRIBUTE => 'max_failures',
VALUE => '1');
dbms_scheduler.set_attribute(NAME => v_job_name,
ATTRIBUTE => 'max_runs',
VALUE => '1');
dbms_scheduler.enable(v_job_name);
v_job_count := v_job_count + 1;
-- array for all jobs
v_jobs_aat(v_job_count) := v_job_name;
END LOOP;
-- ... Wait till all jobs have finisched.
check_queue_completion(v_jobs_aat); -- see procedure below
END;
Procedure for waiting till last four jobs have finisched:
PROCEDURE check_queue_completion(p_jobs_aat IN OUT t_jobs_aat) AS
v_state user_scheduler_jobs.state%TYPE;
v_index PLS_INTEGER;
v_done BOOLEAN := TRUE;
-- Exceptions
e_job_failure EXCEPTION;
BEGIN
WHILE v_done
LOOP
v_done := FALSE;
FOR i IN p_jobs_aat.first..p_jobs_aat.last
LOOP
SELECT state INTO v_state FROM user_scheduler_jobs WHERE job_name = p_jobs_aat(i);
--dbms_output.put_line('Status: ' || v_state);
CASE
WHEN v_state = 'SUCCEEDED' OR v_state = 'COMPLETED' THEN
dbms_output.put_line(p_jobs_aat(i) || ' SUCCEEDED');
dbms_scheduler.drop_job(job_name => p_jobs_aat(i), force => TRUE);
p_jobs_aat.delete(i);
WHEN v_state = 'FAILED' OR v_state = 'BROKEN' THEN
--Exception auslösen
dbms_output.put_line(p_jobs_aat(i) || ' FAILED');
RAISE e_job_failure;
WHEN v_state = 'RUNNING' OR v_state = 'RETRY SCHEDULED' THEN
NULL;
dbms_output.put_line(p_jobs_aat(i) || ' RUNNING or RETRY SCHEDULED');
v_done := TRUE;
/*DISABLED, SCHEDULED, REMOTE, CHAIN_STALLED*/
ELSE
dbms_output.put_line(p_jobs_aat(i) || ' ELSE');
dbms_scheduler.drop_job(job_name => p_jobs_aat(i), force => TRUE);
p_jobs_aat.delete(i);
END CASE;
END LOOP;
hifa.gen_sleep(30);
END LOOP;
IF p_jobs_aat.count > 0 THEN delete_jobs_in_queue(p_jobs_aat); END IF;
EXCEPTION WHEN e_job_failure THEN
delete_jobs_in_queue(p_jobs_aat);
RAISE_APPLICATION_ERROR(-20500, 'some error message');
END check_queue_completion;
It does the trick but it seems like some awful hack.
Isn't there a better way to:
Wait until all jobs have finished.
Just run four jobs at a time and start a new one as soon as one of the running jobs has finished.
Throw an exception if one job fails or is broken.
DECLARE
cnt NUMBER:=1;
BEGIN
WHILE cnt>=1
LOOP
SELECT count(1) INTO cnt FROM dba_scheduler_running_jobs srj
WHERE srj.job_name IN ('TEST_JOB1','TEST_JOB2');
IF cnt>0 THEN
dbms_lock.sleep (5);
END IF;
END LOOP;
dbms_output.put_line('ASASA');
END;
Use dbms_alert or dbms_pipe to send/receive information about job start/finish. Query the jobs table only if you do not receive the information in expected time.
Oracle Scheduler uses Oracle Rersource Manager heavily, Just submit your jobs, defined with an end notification and have a task waiting for your event Q that counts the jobs that are submitted and the jobs that are finished.
You use Oracle Resource manager to control the maximum number of jobs to run concurrently. This will also be based on the total database load, protecting other users agains a system flooded by jobs.

Resources