This is a followup to yesterdays post on DBMS_JOB and is critical if you’re upgrading to 19c soon. Mike Dietrich wrote a nice piece last week on the “under the covers” migration of the now deprecated DBMS_JOB package to the new Scheduler architecture. You should check it out before reading on here.

Mike’s post concerned mainly what would happen during upgrade (spoiler: the DBMS_JOB jobs become scheduler jobs but you can maintain them using the old API without drama), but immediately Twitter was a buzz with a couple of concerns that I wanted to address:

1) What about new jobs submitted via the old API after the upgrade?

For comparison, here’s what you see currently in 18c – DBMS_JOB is quite separate from the scheduler.


SQL> select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production

SQL> declare
  2    j int;
  3  begin
  4    dbms_job.submit(j,'begin dbms_session.sleep(60); end;',sysdate,'sysdate+1');
  5  end;
  6  /

PL/SQL procedure successfully completed.

SQL> select job, what from user_jobs;

       JOB WHAT
---------- --------------------------------------------------
       181 begin dbms_session.sleep(60); end;

SQL> select job_name, job_action from user_scheduler_jobs;

no rows selected


Now here’s the same in 19c


SQL> select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

SQL> declare
  2    j int;
  3  begin
  4    dbms_job.submit(j,'begin dbms_session.sleep(60); end;',sysdate,'sysdate+1');
  5  end;
  6  /

PL/SQL procedure successfully completed.

SQL> select job, what from user_jobs;

       JOB WHAT
---------- --------------------------------------------------
        22 begin dbms_session.sleep(60); end;

SQL> select job_name, job_action from user_scheduler_jobs;

JOB_NAME             JOB_ACTION
-------------------- ------------------------------------------------------------
DBMS_JOB$_22         begin dbms_session.sleep(60); end;

SQL> select * from user_scheduler_jobs
  2  @pr
==============================
JOB_NAME                      : DBMS_JOB$_22
JOB_SUBNAME                   :
JOB_STYLE                     : REGULAR
JOB_CREATOR                   : MCDONAC
CLIENT_ID                     :
GLOBAL_UID                    :
PROGRAM_OWNER                 :
PROGRAM_NAME                  :
JOB_TYPE                      : PLSQL_BLOCK
JOB_ACTION                    : begin dbms_session.sleep(60); end;
NUMBER_OF_ARGUMENTS           : 0
SCHEDULE_OWNER                :
SCHEDULE_NAME                 :
SCHEDULE_TYPE                 : PLSQL
START_DATE                    : 26-MAY-19 07.12.47.000000 PM +08:00
REPEAT_INTERVAL               : sysdate+1
EVENT_QUEUE_OWNER             :
EVENT_QUEUE_NAME              :
EVENT_QUEUE_AGENT             :
EVENT_CONDITION               :
EVENT_RULE                    :
FILE_WATCHER_OWNER            :
FILE_WATCHER_NAME             :
END_DATE                      :
JOB_CLASS                     : DEFAULT_JOB_CLASS
ENABLED                       : TRUE
AUTO_DROP                     : TRUE
RESTART_ON_RECOVERY           : FALSE
RESTART_ON_FAILURE            : FALSE
STATE                         : SCHEDULED
JOB_PRIORITY                  : 3
RUN_COUNT                     : 0
UPTIME_RUN_COUNT              :
MAX_RUNS                      :
FAILURE_COUNT                 : 0
UPTIME_FAILURE_COUNT          :
MAX_FAILURES                  :
RETRY_COUNT                   : 0
LAST_START_DATE               :
LAST_RUN_DURATION             :
NEXT_RUN_DATE                 : 27-MAY-19 07.12.47.000000 PM -07:00
SCHEDULE_LIMIT                :
MAX_RUN_DURATION              :
LOGGING_LEVEL                 : OFF
STORE_OUTPUT                  : TRUE
STOP_ON_WINDOW_CLOSE          : FALSE
INSTANCE_STICKINESS           : TRUE
RAISE_EVENTS                  :
SYSTEM                        : FALSE
JOB_WEIGHT                    : 1
NLS_ENV                       : NLS_LANGUAGE='AMERICAN' NLS_TERRITORY='AMERICA'...
SOURCE                        :
NUMBER_OF_DESTINATIONS        : 1
DESTINATION_OWNER             :
DESTINATION                   :
CREDENTIAL_OWNER              :
CREDENTIAL_NAME               :
INSTANCE_ID                   :
DEFERRED_DROP                 : FALSE
ALLOW_RUNS_IN_RESTRICTED_MODE : FALSE
COMMENTS                      :
FLAGS                         : 9007199254872624
RESTARTABLE                   : FALSE
HAS_CONSTRAINTS               : FALSE
CONNECT_CREDENTIAL_OWNER      :
CONNECT_CREDENTIAL_NAME       :
FAIL_ON_SCRIPT_ERROR          : FALSE

PL/SQL procedure successfully completed.

You can see that it will be enabled by default and is classed as a regular job. Even if you submit a one-off job, it will still be classed as regular not lightweight.


SQL> declare
  2    j int;
  3  begin
  4    dbms_job.submit(j,'begin dbms_session.sleep(60); end;',sysdate);
  5  end;
  6  /

PL/SQL procedure successfully completed.

SQL> select job, what from user_jobs;

       JOB WHAT
---------- --------------------------------------------------
        25 begin dbms_session.sleep(60); end;

SQL> select * from user_scheduler_jobs
  2  @pr
==============================
JOB_NAME                      : DBMS_JOB$_25
JOB_SUBNAME                   :
JOB_STYLE                     : REGULAR
JOB_CREATOR                   : MCDONAC
CLIENT_ID                     :
GLOBAL_UID                    :
PROGRAM_OWNER                 :
PROGRAM_NAME                  :
JOB_TYPE                      : PLSQL_BLOCK
JOB_ACTION                    : begin dbms_session.sleep(60); end;
NUMBER_OF_ARGUMENTS           : 0
SCHEDULE_OWNER                :
SCHEDULE_NAME                 :
SCHEDULE_TYPE                 : ONCE
START_DATE                    : 26-MAY-19 08.37.09.000000 PM +08:00
REPEAT_INTERVAL               :
EVENT_QUEUE_OWNER             :
EVENT_QUEUE_NAME              :
EVENT_QUEUE_AGENT             :
EVENT_CONDITION               :
EVENT_RULE                    :
FILE_WATCHER_OWNER            :
FILE_WATCHER_NAME             :
END_DATE                      :
JOB_CLASS                     : DEFAULT_JOB_CLASS
ENABLED                       : TRUE
AUTO_DROP                     : TRUE
RESTART_ON_RECOVERY           : FALSE
RESTART_ON_FAILURE            : FALSE
STATE                         : SCHEDULED
JOB_PRIORITY                  : 3
RUN_COUNT                     : 0
UPTIME_RUN_COUNT              :
MAX_RUNS                      :
FAILURE_COUNT                 : 0
UPTIME_FAILURE_COUNT          :
MAX_FAILURES                  :
RETRY_COUNT                   : 0
LAST_START_DATE               :
LAST_RUN_DURATION             :
NEXT_RUN_DATE                 : 27-MAY-19 11.37.09.268652 AM +08:00
SCHEDULE_LIMIT                :
MAX_RUN_DURATION              :
LOGGING_LEVEL                 : OFF
STORE_OUTPUT                  : TRUE
STOP_ON_WINDOW_CLOSE          : FALSE
INSTANCE_STICKINESS           : TRUE
RAISE_EVENTS                  :
SYSTEM                        : FALSE
JOB_WEIGHT                    : 1
NLS_ENV                       : NLS_LANGUAGE='AMERICAN' NLS_TERRITORY='AMERICA' NLS_CURRENC
NLS_NUMERIC_CHARACTERS='.,' NLS_CALENDAR='GREGORIAN' NLS_DATE_FORMAT='DD-MON-RR' NLS_DATE_L
NLS_TIME_FORMAT='HH.MI.SSXFF AM' NLS_TIMESTAMP_FORMAT='DD-MON-RR HH.MI.SSXFF AM' NLS_TIME_T
NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH.MI.SSXFF AM TZR' NLS_DUAL_CURRENCY='$' NLS_COMP='BINA
NLS_NCHAR_CONV_EXCP='FALSE'
SOURCE                        :
NUMBER_OF_DESTINATIONS        : 1
DESTINATION_OWNER             :
DESTINATION                   :
CREDENTIAL_OWNER              :
CREDENTIAL_NAME               :
INSTANCE_ID                   :
DEFERRED_DROP                 : FALSE
ALLOW_RUNS_IN_RESTRICTED_MODE : FALSE
COMMENTS                      :
FLAGS                         : 9007199254880304
RESTARTABLE                   : FALSE
HAS_CONSTRAINTS               : FALSE
CONNECT_CREDENTIAL_OWNER      :
CONNECT_CREDENTIAL_NAME       :
FAIL_ON_SCRIPT_ERROR          : FALSE

PL/SQL procedure successfully completed.

Important Note: There is one critical thing you need to be aware of with this change. DBMS_JOB is an “old-school” public API, hence anyone and everyone pretty much had access to it. Anyone familiar with DBMS_SCHEDULER will know that the components within it are true database objects, which can be protected with privileges. So when you upgrade to 19c, to ensure that you do not get nasty surprises, users that are using DBMS_JOB will need the CREATE JOB privilege otherwise their previous ability to submit jobs will disappear. For example:


SQL> conn scott/tiger@db192_pdb1
Connected.

SQL> declare
  2    j int;
  3  begin
  4    dbms_job.submit(j,'begin dbms_session.sleep(60); end;',sysdate);
  5  end;
  6  /
declare
*
ERROR at line 1:
ORA-27486: insufficient privileges
ORA-06512: at "SYS.DBMS_ISCHED", line 9387
ORA-06512: at "SYS.DBMS_ISCHED", line 9376
ORA-06512: at "SYS.DBMS_ISCHED", line 175
ORA-06512: at "SYS.DBMS_ISCHED", line 9302
ORA-06512: at "SYS.DBMS_IJOB", line 196
ORA-06512: at "SYS.DBMS_JOB", line 168
ORA-06512: at line 4

2) What about the cherished transactional nature of DBMS_JOB?

If the old style jobs are now scheduler jobs, do we lose the transactional element of DBMS_JOB? Nope. Even though we will create a paired scheduler entry, DBMS_JOB is still transactional (which I love!).


SQL> declare
  2    j int;
  3  begin
  4    dbms_job.submit(j,'begin dbms_session.sleep(60); end;',sysdate,'sysdate+1');
  5  end;
  6  /

PL/SQL procedure successfully completed.

SQL> select job, what from user_jobs;

       JOB WHAT
---------- --------------------------------------------------
        21 begin dbms_session.sleep(60); end;

SQL> select job_name, job_action from user_scheduler_jobs;

JOB_NAME             JOB_ACTION
-------------------- ------------------------------------------------------------
DBMS_JOB$_21         begin dbms_session.sleep(60); end;

SQL> roll;
Rollback complete.
SQL> select job, what from user_jobs;

no rows selected

SQL> select job_name, job_action from user_scheduler_jobs;

no rows selected

SQL>

If you love that transactional capability too, then make sure to visit the database ideas page to vote up this idea. I have no issue with DBMS_SCHEDULER doing commits by default, but it would be cool if (say) for lightweight jobs we had an option to choose whether we commit or not.

27 responses to “DBMS_JOB – the joy of transactions”

  1. I’m confused!

    You write that “the DBMS_JOB jobs become scheduler jobs but you can maintain them using the old API without drama”, but also that “even though we will create a paired scheduler entry, DBMS_JOB is still transactional”, and then ask us to vote up the idea that dbms_scheduler should (optionally) handle transactions as well.

    The first statement would seem to imply that dbms_scheduler already has the ability to be transactional (otherwise how could “dbms_job jobs become scheduler jobs”?), unless dbms_scheduler is still using dbms_job behind the scenes? Or has dbms_scheduler already been modified “behind the scenes” to support both scenarios (ie “no commit” and “always commit”), but the only thing that is missing is the option for us as developers to specify what we want?

    1. Because DBMS_JOB is **deprecated**. Which means one day, it will probably/possibly disappear…..along with your ability to schedule something as part of a transaction.

  2. So… in other words, dbms_job is still used internally for these jobs, dbms_scheduler has not learned any new tricks, and sometime in the future when dbms_job is really removed, the old transactional functionality is gone?

    Too bad nobody has fixed this some 13 years after the issue was first raised, see https://asktom.oracle.com/pls/apex/asktom.search?tag=dbms-scheduler-emailing-after-inserting-into-database-tables#75168079004183

    1. “dbms_job is still used internally for these jobs”

      No, if you trace it, you’ll see dbms_job calls are doing DML on scheduler tables.

      But … that is why I said – GO AND VOTE ON THE IDEA…. Even if look at the idea, you’ll see that even members of community are dubious on the value of transactional. I don’t agree – I think transactional is super important, but thats why I said … GO AND VOTE.

  3. Iudith Mentzel Avatar
    Iudith Mentzel

    Hello Connor,

    I joined in and voted for it 🙂

    I can only wonder how come that at Oracle they do not yet realize how important this feature is
    for developers and what possibilities it did open up for elegant development solutions.

    After Tom Kyte has retired, his excellent ideas should still be followed,
    and exploiting DBMS_JOB transactionality in various ways was one of his top ones.

    Cheers & Best Regards,
    Iudith Mentzel

  4. […] Connor McDonald’s blog post: DBMS_JOB – the joy of transactions (May 28, 2019) […]

  5. How to realize the execution?

    SQL> select * from user_scheduler_jobs
    2 @pr

    1. Here is the pr.sql script. It came from Tanel Poder via an AskTOM original

      =================
      .

      set termout off
      def _pr_tmpfile=c:\tmp\pr.out

      store set &_pr_tmpfile.set replace
      set termout on

      set serverout on size 1000000 termout off echo off
      save &_pr_tmpfile replace
      set termout on

      0 c clob := q’\
      0 declare

      999999 \’;;
      999999 l_theCursor integer default dbms_sql.open_cursor;;
      999999 l_columnValue varchar2(4000);;
      999999 l_status integer;;
      999999 l_descTbl dbms_sql.desc_tab2;;
      999999 l_colCnt number;;
      999999 begin
      999999 dbms_sql.parse( l_theCursor, c, dbms_sql.native );;
      999999 dbms_sql.describe_columns2( l_theCursor, l_colCnt, l_descTbl );;
      999999 for i in 1 .. l_colCnt loop
      999999 dbms_sql.define_column( l_theCursor, i,
      999999 l_columnValue, 4000 );;
      999999 end loop;;
      999999 l_status := dbms_sql.execute(l_theCursor);;
      999999 while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
      999999 dbms_output.put_line( ‘==============================’ );;
      999999 for i in 1 .. l_colCnt loop
      999999 dbms_sql.column_value( l_theCursor, i,
      999999 l_columnValue );;
      999999 dbms_output.put_line
      999999 ( rpad( l_descTbl(i).col_name,
      999999 30 ) || ‘: ‘ || l_columnValue );;
      999999 end loop;;
      999999 end loop;;
      999999 exception
      999999 when others then
      999999 dbms_output.put_line(dbms_utility.format_error_backtrace);;
      999999 raise;;
      999999 end;;
      /

      set termout off
      @&_pr_tmpfile.set

      get &_pr_tmpfile nolist
      host del &_pr_tmpfile
      set termout on
      =================

  6. Hi,
    i already voted for the transactionallity of dbms_schedule. What it’s not clear to me is that I understood that in Oracle 19c the call to DBMS_JOB is implemented internally using DBMS_SCHEDULLE. If that is true, is there a form to call DBMS_SCHEDULE in a transaction?

    1. Unfortunately not a public one. I’m just hoping the votes will mean that dbms_job will be retained heading well into the future

      1. A shame…. As we use jobs to improve performance, and parallelize some processes, unfortunatelly, that forces us to wait to see if Oracle decides to make that public, or some other transactional solution, before we change our calls to DBMS_JOB… 😦

        Thank you very much, Connor

        1. I’m hoping we never have to change away from dbms_job. Now that it (internally) calls the more mature dbms_scheduler code, I would hope that we dont need to desupport it.

  7. One more question. If I use DBMS_SCHEDULE instead DBMS_JOB, there is an implicit COMMIT that will finish my on-fly transactions? Or DBMS_SCHEDULE uses an autonomous transaction that does not interfere with mine?
    Thanks in advance

  8. Hello Connor,

    what means the parameter BG_JOB_ID if I submit job via DBMS_JOB?
    This parameter is still documented in19c as: “Job ID of the current session if it was established by an Oracle Database background process. ”

    dbms_job.submit (jobid, q'[begin dbms_output.put_line (sys_context (‘USERENV’,’BG_JOB_ID’)); end;]’);

    The vlues of user_jobs.job [1,2,3] were converted into user_scheduler_jobs.job_name [DBMS_JOB$_1,DBMS_JOB$_2,DBMS_JOB$_3].
    The output in the column user_scheduler_job_run_details.output is [73451,73452,73453].

  9. […] a “gotcha” brought to my attention by one of our AskTOM readers. I’ve mentioned in the past that DBMS_JOB, having been deprecated in favour of DBMS_SCHEDULER, got a new lease of […]

  10. Gediminas Reimeris Avatar
    Gediminas Reimeris

    Hi, thanks for the post.
    One backward-compatibility issue I’ve come over: variable JOB that can be used to pass JOB ID to application module: in 19c it passes DBA_OBJECTS.OBJECT_ID of the corresponding Scheduler job object instead of the expected DBA_JOBS.JOB.

    var jobid number
    exec dbms_job.submit(:jobid, q'{begin raise_application_error(-20000, ‘Parameter JOB=’|| job); end;}’)
    commit;
    print jobid
    select additional_info from user_scheduler_job_run_details where job_name = ‘DBMS_JOB$_’ || :jobid;

  11. There is approach (undocumented) to make DBMS_SCHEDULER.CREATE_JOB transactional http://mvelikikh.blogspot.com/2021/06/making-dbmsschedulercreatejob.html

    1. True. But undocumented and therefore unsupported

  12. […] submitted via the older DBMS_JOB mechanism, then as long as you are on 19c or above, then these are also scheduler […]

  13. We’ve use AutoUpgrade and it migrated JOB to SCHEDULE. Can you just do a remove of the job after? A bit hesitant about this as there appears to be a correlation of some sort between the two as if we disable/enable on scheduler it set the broken=true/false behind the scene and vice versa. So if we do remove from dba_jobs, does it remove from scheduler too? Also, does having both showing in DBA_JOBS and DBA_SCHEDULER_JOBS mean they are both going to be run twice at the same time or not necessarily, some smarts in the background will figure it out somehow that only one will run 😦 Am lost 🙂

    1. My understanding is that they are still (DBMS) JOBs, not scheduler jobs. The linkage is because the scheduler now takes cares of running them itself. “Complete” migration to the scheduler is left to you if you want to go that way.

  14. Sreedhar Medipally Avatar
    Sreedhar Medipally

    Hello Cinnor,

    Nice article!!! We have a unique problem. We don’t have the credentials set for running scripts using the scheduler jobs. Most of the times, the job is run with oracle user by default. But at times it runs with grid user.
    Could you please help figuring it out how to make it run with oracle user all the time?

    Thanks & Regards,
    Sreedhar

  15. Sreedhar Medipally Avatar
    Sreedhar Medipally

    It is set to these values…

    run_user = nobody
    run_group = nobody

    Now changing it to

    run_user = oracle
    run_group = oinstall

Got some thoughts? Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Trending

Blog at WordPress.com.