Navigation Bar

Wednesday, January 1, 2025

DBMS_SCHEDULER : To run a process asynchronously

DBMS_SCHEDULER is used to schedule the running of jobs/processes at regular intervals.
Suppose I have a process PR_PROCESS_JOB that takes a certain amount of time to complete, say 25 seconds, and I create a scheduler to run this process every 15 seconds. The oracle scheduler runs in such a way that is wont start the next scheduler process till the previous job has run and completed.
This can lead to a scenario where the number of records to be processed can keep building up.

To optimize this we can run the process in the background using DBMS_JOB.SUBMIT.
Now the job will run in the background and the scheduler is ready to start a new thread at the next interval of 15 seconds thus improving the processing speed and ensuring that the records in queue to be processed are in a manageable limit.

Below are the scripts for running the job.
 
Below is a sample process which we need to run as a scheduler. To simulate a process run we put a sleep of 25 seconds for this process.
CREATE OR REPLACE PROCEDURE PR_PROCESS_JOB AS 

  v_program_name   VARCHAR2(100) := 'PR_PROCESS_JOB';
  v_table_name     VARCHAR2(100) := NULL;
  ARCHIVE_EXCEPTION   EXCEPTION;
  v_err_desc       VARCHAR2(500);
  v_error_code     NUMBER       := PKG_ARCHIVE.SUCCESS;
  v_archv_btn  VARCHAR2(10) := 'STOP';
  v_loop_cntr NUMBER := 0;
BEGIN

      DBMS_LOCK.SLEEP(25);
      
      v_err_desc := 'Calling scheduler every 25 seconds ' || v_loop_cntr;
      PR_ARCHIVE_ERROR_LOG(v_program_name, v_table_name ,v_err_desc, SQLCODE , SQLERRM , SYSDATE , USER, v_error_code);
      
      
     commit;

END PR_PROCESS_JOB;
We now make a wrapper procedure which will call this PR_PROCESS_JOB in the background using DBMS_JOB.SUBMIT
create or replace PROCEDURE PR_TEST_SCHEDULER AS 

    i_jobno binary_integer;

BEGIN

    DBMS_JOB.SUBMIT(i_jobno, 'BEGIN PR_PROCESS_JOB; END;',SYSDATE );
    COMMIT;
          
END PR_TEST_SCHEDULER;

We give the following grants to HR schema to create a scheduler process.
grant create any job to HR;
GRANT EXECUTE ON DBMS_SCHEDULER TO HR;


We now call this procedure in a schedulre using DBMS_SCHEDULER.create_job as follows.
BEGIN
DBMS_SCHEDULER.create_job (job_name     => 'TEST_SCHEDULER_JOB',
                               job_type     => 'STORED_PROCEDURE',
                               job_action   => 'PR_TEST_SCHEDULER',
                               start_date   => TO_dATE('31-dec-24 10:19:00','DD-MON-YY HH24:MI:SS'),
							   repeat_interval    =>  'freq=secondly; interval=15;',
                               enabled      => TRUE);
END;
Below are the oracle data dictionary views from which you can monitor the job run status. The output below shows the main scheduler process TEST_SCHEDULER_JOB and 2 DBMS_JOB processes
select OWNER, JOB_NAME, JOB_SUBNAME, JOB_STYLE, JOB_CREATOR, JOB_TYPE, JOB_ACTION,START_DATE, REPEAT_INTERVAL, STATE, LAST_START_DATE, LAST_RUN_DURATION, NEXT_RUN_DATE from all_scheduler_jobs where owner = 'HR_ARCHIVE';
OWNER JOB_NAME JOB_SUBNAME JOB_STYLE JOB_CREATOR JOB_TYPE JOB_ACTION START_DATE REPEAT_INTERVAL STATE LAST_START_DATE LAST_RUN_DURATION NEXT_RUN_DATE
HR_ARCHIVE DBMS_JOB$_201   REGULAR HR_ARCHIVE PLSQL_BLOCK BEGIN PR_PROCESS_JOB; END; 01-JAN-25 10.32.00.000000000 AM +05:30   RUNNING 01-JAN-25 10.32.00.201000000 AM +05:30   01-JAN-25 10.32.00.144000000 AM +05:30
HR_ARCHIVE DBMS_JOB$_202   REGULAR HR_ARCHIVE PLSQL_BLOCK BEGIN PR_PROCESS_JOB; END; 01-JAN-25 10.32.15.000000000 AM +05:30   RUNNING 01-JAN-25 10.32.15.270000000 AM +05:30   01-JAN-25 10.32.15.259000000 AM +05:30
HR_ARCHIVE TEST_SCHEDULER_JOB   REGULAR HR_ARCHIVE STORED_PROCEDURE PR_TEST_SCHEDULER 31-DEC-24 10.19.00.000000000 AM ASIA/CALCUTTA freq=secondly; interval=15; SCHEDULED 01-JAN-25 10.32.15.120000000 AM ASIA/CALCUTTA +00 00:00:00.179000 01-JAN-25 10.32.30.000000000 AM ASIA/CALCUTTA
Other oracle views from which you can monitor the running job are
select * from all_scheduler_job_run_details where owner = 'HR_ARCHIVE' and LOG_DATE > to_date('01-JAN-25 10:16:00','DD-MON-YY HH24:MI:SS') ORDER BY LOG_DATE DESC;

SELECT * FROM ALL_SCHEDULER_JOB_LOG WHERE OWNER = 'HR_ARCHIVE' and LOG_DATE > to_date('01-JAN-25 10:16:00','DD-MON-YY HH24:MI:SS') ORDER BY LOG_DATE DESC;

select * from all_scheduler_running_jobs;
If we log the timings of the process which is running in a table it will show an entry at a frequency of every 15 seconds, as ,if one process is not completed the scheduler spawns another job which will maintain the frequency of job processing to every 15 seconds.
SELECT TXT_PROGRAM_NAME, TXT_REMARKS, CRTD_BY, CRTD_DT FROM ARCHIVE_ERROR_LOG ORDER BY CRTD_DT DESC;
TXT_PROGRAM_NAMETXT_REMARKSCRTD_BYCRTD_DT
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 10:47:25
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 10:47:10
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 10:46:55
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 10:46:40
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 10:46:25
We now change the PR_TEST_SCHEDULER wrapper to call the PR_PROCESS_JOB directly without running it in the background. and recreate the scheduler to run every 15 seconds.
create or replace PROCEDURE PR_TEST_SCHEDULER AS 

    i_jobno binary_integer;

BEGIN

--    DBMS_JOB.SUBMIT(i_jobno, 'BEGIN PR_PROCESS_JOB; END;',SYSDATE );
    PR_PROCESS_JOB;
    COMMIT;
          
END PR_TEST_SCHEDULER;
The LOG_DATE, ACTUAL_START_DATE columns in all_scheduler_job_run_details, ALL_SCHEDULER_JOB_LOG now shows the scheduler being called at intervals of every 25 seconds though the scheduler has been set to run every 15 seconds. 
If we check the timings of the process which is running in the log table, it will show an entry at a frequency of every 25 seconds and now the scheduler frequency of every 15 seconds.
SELECT TXT_PROGRAM_NAME, TXT_REMARKS, CRTD_BY, CRTD_DT FROM ARCHIVE_ERROR_LOG ORDER BY CRTD_DT DESC;
TXT_PROGRAM_NAMETXT_REMARKSCRTD_BYCRTD_DT
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 11:03:06
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 11:02:41
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 11:02:16
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 11:01:51
PR_PROCESS_JOBCalling scheduler every 25 seconds 0HR_ARCHIVE01-JAN-25 11:01:26
Thus using DBMS_JOB.SUBMIT we can run the process asynchronously at regular scheduler intervals. The drawback of this is that if that if each process in PR_PROCESS_JOB takes a long time to complete the number of DBMS_JOB threads can increase and it can cause a performance bollleneck. To control the number of this background processes we can check the count of these processes in PR_TEST_SCHEDULER. If this count is within a fixed number say 5, only then we call the next DBMS_JOB.SUBMIT process.
SELECT COUNT(1) FROM 
all_scheduler_jobs
where JOB_ACTION = 'BEGIN PR_PROCESS_JOB; END;'
and STATE = 'RUNNING';
Now PR_TEST_SCHEDULER will check if the number of processes running is more than 5. If so it will simply exit and not call PR_PROCESS_JOB till one job gets completed. Thus at any point we can check and make sure that the number of jobs submitted is not > 'x' count and the scheduler processes are not increasing continuously and causing a CPU and performance bottleneck. Below is the code for the same
create or replace PROCEDURE PR_TEST_SCHEDULER AS 

    i_jobno binary_integer;
    v_job_cntr INTEGER;
BEGIN

    SELECT COUNT(1)
    INTO v_job_cntr
    FROM all_scheduler_jobs
    where JOB_ACTION = 'BEGIN PR_PROCESS_JOB; END;'
    and STATE = 'RUNNING';

    IF v_job_cntr <= 5
    THEN
      DBMS_JOB.SUBMIT(i_jobno, 'BEGIN PR_PROCESS_JOB; END;',SYSDATE );

    COMMIT;
    END IF;
          
END PR_TEST_SCHEDULER;

Thought for the day
Wisdom teaches her children and gives help to those who seek her 
   Whoever loves her loves life, and those who seek her from early morning are filled with joy.
   (Wisdom beckons us to start the day with prayer and praises to our Creator) 
Whoever holds her fast inherits glory, 
  and the Lord blesses the place she enters. 
Those who serve her minister to the Holy One; 
  the Lord loves those who lover her.
Sirach 4:11-14

No comments:

Post a Comment