Hace unos días, en el canal de Administración del servidor de Discord de Café Database, alguien preguntó cómo se podría implementar una cadena de ejecución de jobs con DBMS_SCHEDULER.

La idea es que juando una tarea termine, espere dos minuto y se vuelva a ejecutar.

El problema es que la duración de la tarea es variable y la replanificación debe hacerse en cuanto termine. Sea la hora que sea, que se ejecute dos minutos después.

Para ello, manejar el intérvalo de ejecución para calcular el momento siguiente no es buena idea, porque el valor de la siguiente ejecución se determina en el momento en el que el job empieza a ejecutarse, por lo que para crear esta cadena de ejecuciones en serie debemos modificar el START_DATE.

Voy a crear un procedimiento que se llame «DUERME_TIEMPO», en el que el proceso dormirá una cantidad de segundos variable entre cero y tres minutos. Para registrar el inicio y el fin del procedimiento, registraré el timestamp en una tabla EJECUCION para los valores de EMPEZAR A DORMIR (se ejecuta el sleep) y SE DESPIERTA (finaliza el sleep).

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

create or replace procedure duerme_tiempo is
begin
  -- registramos el inicio
  insert into ejecucion values ('EMPIEZA A DORMIR',systimestamp);
  commit;
  
  -- el proceso duerme por un tiempo random entre cero y 3 minutos
  dbms_session.sleep(mod(abs(dbms_random.random),180));
  
  -- registramos cuando termina la ejecución
  insert into ejecucion values ('SE DESPIERTA',systimestamp);
  commit;
       
end;
/

De modo que cada ejecución deje este log registrado.

SQL> exec duerme_tiempo;

Procedimiento PL/SQL terminado correctamente.

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

ESTADO
--------------------
TIEMPO
---------------------------------------------------------------------------
EMPIEZA A DORMIR
28/11/24 07:56:23,964952

SE DESPIERTA
28/11/24 07:57:42,979512

Para planificar su ejecución, voy a crear un job llamado DUERME_Y_DESPIERTA para que empiece en cuanto se habilite e inmediatamente después ejecute DUERME_TIEMPO en el inicio de minuto.

La frecuencia de intérvalos es, por tanto, ejecuciones de a cada minuto, cuando coincida el segundo cero. Esto es algo adicional, para hacer que los procesos empiecen siempre después de dos minutos del fin de su ejecución pero al inicio del minuto siguiente. Lo hago solamente para ver los instantes precisos de inicio del job que coincidan con un minuto exacto. Si queremos ser fieles a la ejecución exacta tras dos minutos de terminar la ejecución anterior, quitaríamos el parámetro «bysecond=0;».

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

PL/SQL procedure successfully completed.

Ahora el job está creado, pero no está activo. Esto es porque antes de activarlo, debemos modificar el procedure para que, al finalizar la ejecución, modifique el job DUERME_Y_DESPIERTA para que inicie dos minutos después.

Exactamente dos minutos después, cuando llegue al segundo «00». Es decir, cuando inicie el siguiente minuto.

create or replace procedure duerme_tiempo is
begin
  -- registramos el inicio
  insert into ejecucion values ('EMPIEZA A DORMIR',systimestamp);
  commit;
  
  -- el proceso duerme por un tiempo random entre cero y 3 minutos
  dbms_session.sleep(mod(abs(dbms_random.random),180));
  
  -- registramos cuando termina la ejecución
  insert into ejecucion values ('SE DESPIERTA',systimestamp);
  commit;
  
  -- Se programa el job para ejecutarse 2 minutos despues
  dbms_scheduler.set_attribute (
    name      => 'duerme_y_despierta',
    attribute => 'start_date',
    value     => sysdate+(2/24/60));
     
end;
/

En este momento ya podemos habilitar el job, y éste se ejecutará instantáneamente, y cada ejecución posterior se replanificará al vuelo independiente del tiempo que tarde.

SQL> select * from ejecucion;

ESTADO		     TIEMPO
-------------------- ---------------------------------------------------------------------------
EMPIEZA A DORMIR     28-NOV-24 11.15.01.001800 AM
SE DESPIERTA	     28-NOV-24 11.16.39.064239 AM   --> 1 minuto y 38 segundos después
EMPIEZA A DORMIR     28-NOV-24 11.19.00.185823 AM   --> 2 minutos y 21 segundos después (11:19 exacta)
SE DESPIERTA	     28-NOV-24 11.19.41.334749 AM   --> 41 segundos después
EMPIEZA A DORMIR     28-NOV-24 11.22.00.129578 AM   --> 2 minutos y 19 segundos después (11:22 exacta)
SE DESPIERTA	     28-NOV-24 11.22.10.134432 AM
EMPIEZA A DORMIR     28-NOV-24 11.25.00.075948 AM
SE DESPIERTA	     28-NOV-24 11.25.45.366549 AM
EMPIEZA A DORMIR     28-NOV-24 11.28.00.024666 AM
SE DESPIERTA	     28-NOV-24 11.30.57.174717 AM
EMPIEZA A DORMIR     28-NOV-24 11.33.00.029588 AM
SE DESPIERTA	     28-NOV-24 11.33.29.240348 AM
EMPIEZA A DORMIR     28-NOV-24 11.36.00.199082 AM
SE DESPIERTA	     28-NOV-24 11.37.53.430395 AM
Share This