A few days ago, in the Café Database Discord Server Administration channel, someone asked how a chain of job executions could be implemented with DBMS_SCHEDULER.

The idea is that when a task finishes, it waits two minutes and runs again.

The problem is that the duration of the task is variable and the rescheduling must be done as soon as it finishes. Whatever time it is, it will be executed two minutes later.

To do this, managing the execution interval to calculate the next time is not a good idea, because the value of the next execution is determined at the time the job starts running, so to create this chain of serial executions we must modify the START_DATE.

I am going to create a procedure called “SLEEP_TIME” (sleep_time), in which the process will sleep for a variable number of seconds between zero and three minutes. To record the start and end of the procedure, record the timestamp in an EXECUTION table for the values of START TO SLEEP (sleep runs) and WAKES UP (sleep ends).

create table execution_times (estado varchar2(20),tiempo timestamp);

create or replace procedure sleep_time is
begin
    -- log in execution_times table when it starts to sleep
    insert into execution_times values ('START TO SLEEP',systimestamp);
    commit;
    
    -- the process sleeps for a random time between 0 seconds and 3 minutes
    dbms_session.sleep(mod(abs(dbms_random.random),180));
    
    -- log in execution_times table when it finishes sleeping and wakes up
    insert into execution_times values ('WAKES UP',systimestamp);
    commit;
    
end;
/

So that each execution leaves this log recorded.

SQL> exec sleep_time;

Procedimiento PL/SQL terminado correctamente.

Transcurrido: 00:01:19.02
SQL> select * from execution_times;

ESTADO
--------------------
TIEMPO
---------------------------------------------------------------------------
START TO SLEEP
28/11/24 07:56:23,964952

WAKES UP
28/11/24 07:57:42,979512

To schedule its execution, I’m going to create a job called SLEEP_AND_WAKE to start as soon as it is enabled and immediately after that execute SLEEP_TIME at the start of the minute.

The interval frequency is therefore executions every minute, when the second zero coincides. This is something additional, to make the processes always start after two minutes of the end of their execution but at the beginning of the following minute. I do this only to see the precise moments of job start that coincide with an exact minute.

If we want to be faithful to the exact execution after two minutes of finishing the previous execution, we would then remove the parameter “bysecond=0;”.

begin
   dbms_scheduler.create_job (
        job_name => 'sleep_and_wake',
        job_type => 'plsql_block',
        job_action => 'begin sleep_time; end;',
        start_date => systimestamp,
        repeat_interval => 'freq=minutely; bysecond=0;',
        enabled => false);
end;
/

PL/SQL procedure successfully completed.

Now the job is created, but it is not active. This is because before activating it, we must modify the procedure so that, when the execution is finished, it modifies the SLEEP_AND_WAKE_UP job so that it starts two minutes later.

Exactly two minutes later, when it reaches the second “00”. That is, when the next minute starts.

create or replace procedure sleep_time is
begin
      -- log in execution_times table when it starts to sleep
      
      insert into execution_times values ('START TO SLEEP',systimestamp);
      commit;
      
      -- the process sleeps for a random time between 0 seconds and 3 minutes
      dbms_session.sleep(mod(abs(dbms_random.random),180));
      
      -- log in execution_times table when it finishes sleeping and wakes up
      insert into execution_times values ('WAKES UP',systimestamp);
      commit;
      
      -- NEW PART we set up again the start_time of the main job.
      dbms_scheduler.set_attribute (
           name => 'sleep_and_wake',
           attribute => 'start_date',
           value => sysdate+(2/24/60)); --> two minutes ahead
      
end;
/

At this point we can enable the job, and it will be executed instantly, and each subsequent execution will be rescheduled on the fly regardless of the time it takes.

Jobs are now chained and being executed serially with a delay of 2 minutes each, starting at second “00” of next minute.

SQL> select * from execution_times;

ESTADO               TIEMPO
-------------------- ---------------------------------------------------------------------------
START TO SLEEP       28-NOV-24 11.15.01.001800 AM
WAKES UP             28-NOV-24 11.16.39.064239 AM --> 1 minute and 38 seconds later
START TO SLEEP       28-NOV-24 11.19.00.185823 AM --> 2 minutes and 21 seconds later (11:19 exact time)
WAKES UP             28-NOV-24 11.19.41.334749 AM --> 41 seconds later
START TO SLEEP       28-NOV-24 11.22.00.129578 AM --> 2 minutes and 19 seconds later (11:22 exact time)
WAKES UP             28-NOV-24 11.22.10.134432 AM --> ...and so on.
START TO SLEEP       28-NOV-24 11.25.00.075948 AM
WAKES UP             28-NOV-24 11.25.45.366549 AM
START TO SLEEP       28-NOV-24 11.28.00.024666 AM
WAKES UP             28-NOV-24 11.30.57.174717 AM
START TO SLEEP       28-NOV-24 11.33.00.029588 AM
WAKES UP             28-NOV-24 11.33.29.240348 AM
START TO SLEEP       28-NOV-24 11.36.00.199082 AM
WAKES UP             28-NOV-24 11.37.53.430395 AM
Share This