We are initiating the rebuilding of many materialized views by using dbms_job.submit to execute a stored procedure that perform the rebuilding. However, I am having trouble trying to figure out how to determine if a submitted job failed. The issue that I am having is that the job is failing but I cannot identify what the issue is. So, I am trying to start out with a simple example, which is probably failing on a permission issue but I don't know where to look for the error message.
I have the following test procedure that I want to initiate using dbms_job.submit.
CREATE OR REPLACE PROCEDURE MYLANID.JUNKPROC
AS
lv_msg varchar2(3000);
BEGIN
INSERT INTO MYLANID.junk_log ( msg ) VALUES ('Hello World' );
commit;
EXCEPTION
WHEN OTHERS THEN
lv_msg := SUBSTR(sqlerrm, 1, 3000);
INSERT INTO MYLANID.junk_log ( msg ) VALUES (lv_msg);
END;
/
Note that this table is used above:
CREATE TABLE MYLANID.JUNK_LOG (
EVENT_TIME TIMESTAMP(6) DEFAULT systimestamp,
MSG VARCHAR2(3000 BYTE))
To submit the above procedure as a job, I execute the following anonymous block.
declare l_jobid binary_integer;
BEGIN
dbms_job.submit(job => l_jobid, what => 'BEGIN MYLANID.JUNKPROC; END;');
DBMS_OUTPUT.PUT_LINE('l_jobid:' || l_jobid);
commit;
END;
I then execute the following SQL...
select * from all_jobs;
...to see one record that represents my submitted job. When I re-query the all_jobs view, I see that this record quickly disappears from the view within a few seconds, presumably when the job completes. All is happy so far. I would like to use the presence of a record in the all_jobs view to determine whether a submitted job is running or has failed. I expect to be able to tell if it failed by looking at the ALL_JOBS.FAILURES column having a non null value > 0.
The problem, probably a permission issue, begins when I switch to another schema and I switch all of the occurrences of the above SQL and replace "MYSCHEMA" with "ANOTHERSCHEMA" that I also have access to. For example, I create the following
Table: ANOTHERSCHEMA.JUNK_LOG
Procedure: ANOTHERSCHEMA.JUNKPROC
I am even able to execute the stored procedure successfully in a query window while logged in as MYSCHEMA:
EXEC ANOTHERSCHEMA.JUNKPROC
However, if I execute the following code to submit a job that involves running the same ANOTHERSCHEMA procedure but by submitting it as a JOB...
declare l_jobid binary_integer;
BEGIN
dbms_job.submit(job => l_jobid, what => 'BEGIN ANOTHERSCHEMA.JUNKPROC; END;');
DBMS_OUTPUT.PUT_LINE('l_jobid:' || l_jobid);
commit;
END;
...then, when I query the jobs ALL_JOBS view...
select * from all_jobs;
...I see that the job has a positive value for the column FAILURE and I have no record of what the error was. This FAILURE count value continues to gradually increment over time as Oracle presumably retries up to 16? times until the job is marked BROKEN in the ALL_JOBS view.
But this is just a simple example and I don't know where to look for the error message that would tell me why the job using ANOTEHRSCHEMA references failed.
Where Do I look for the error log of failed jobs? I'm wondering if this will be somewhere only the DBA can see...
Update:
The above is just a simple test example. In my actual real world situation, my log shows that the job was submitted but I never see anything in USER_JOBS or even DBA_JOBS, which should show everything. I don't understand why the dbms_job.submit procedure would return the job number of the submitted job indicating that it was submitted but no job is visible in the DBA_JOBS view! The job that I did submit should have taken a long time to run, so I don't expect that it completed faster than I could notice.
First off, you probably shouldn't be using dbms_job. That package has been superseded for some time by the dbms_scheduler package which is significantly more powerful and flexible. If you are using Oracle 19c or later, Oracle automatically migrates dbms_job jobs to dbms_scheduler.
If you are using an Oracle version prior to 19c and a dbms_job job fails, the error information is written to the database alert log. That tends to be a bit of a pain to query from SQL particularly if you're not a DBA. You can define an external table that reads the alert log to make it queryable. Assuming you're on 11g, there is a view, x$dbgalertext, that presents the alert log information in a way that you can query it but DBAs generally aren't going to rush to give users permission on x$ tables.
If you use dbms_scheduler instead (or if you are on 19c or later and your dbms_job jobs get converted to dbms_scheduler jobs), errors are written to dba_scheduler_job_run_details. dbms_scheduler in general gives you a lot more logging information than dbms_job does so you can see things like the history of successful runs without needing to add a bunch of instrumentation code to your procedures.
I have a webpage (generated via PL/SQL) that allows someone to toggle a remote device on or off. They are presented with a list of devices and they use checkboxes to select the ones to toggle. UTL_HTTP is used to communicate with the devices. Currently, the devices are toggled serially. Once all have been toggled, an email is sent to the user. Depending on how many devices are selected, doing this serially has the potential to take too long. So I'm looking at using DBMS_SCHEDULER to execute the toggling in parallel.
The problem is that the toggling process returns a status, either 'OK' or the reason it failed. I need that result to include in the email to the user. So, I need the 'main' procedure to create the SCHEDULER jobs and then wait for them to finish (and somehow get their statuses) before sending an email to the user.
Is this possible, short of having each job write it's status to a table which is polled by the 'main' process? I've read references to DBMS_PIPE for inter-process communication, but haven't found a good example (ie, one that makes sense to me) showing how to do it.
If there's a way to do it, I couldn't figure it out. I ended up having each job write it's status to a table. The main process knows how many individual jobs were created and polls the table to tell when all jobs are finished (or it timesout after a specified amount of time has passed, in case one of the jobs dies for some reason).
An Alternate Solution to Watching Parallel Processes Run Through DBMS_SCHEDULER Jobs
New Edit: An Abbreviated Discussion of the Core Problem
(Edit: 03/10/2014) I added this discussion after some helpful feedback from one of the posters watching this thread.
Opening Comments: Other discussion threads here mention the use of some output value from the function call itself. This is not natively possible with the existing DBMS_SCHEDULER features.
There are no OUT type parameter values or function outputs related to notifying a condition encountered by the procedure invoked. The most immediate problem at hand is: How do we run a series of related tasks via PL/SQL stored procedure in parallel? (i.e., without waiting for one another to finish before each start themselves.)
Whatever invokes the procedure tasks should not wait around for a status output. The response time is likely to be widely variable and whatever calls the procedure in this way would also hang. Associated processes would be waiting for the completion of the procedure or the return of a specified output.
A Suggested Approach: Other comments on this problem are on the right track. Have the custom output written to a table where it can be queried later when a response is ready. If you really want to make this a hands-off task, try putting a trigger on the output table. Every time a message of a specific value (representing a completed state of the request) is populated, invoke a procedure with Oracle's Mail package that sends out your notification email.
How to Track Your Invoked Jobs By Understanding DBMS_SCHEDULER Features
Using a scheduled job is a good way of initiating and watching a set of procedure calls that are not sequentially dependent on one another. I have been able to accomplish a similar approach using the original DBMS_JOB functionality of previous versions of the Oracle database.
A Case Study: Using a web-based application interface (Oracle Application Express) I had a project which allowed the user to initiate a resource-intensive series of database operations. Initiating the request was all that was needed.
The Actual Use Scenario: The user didn't need to wait around for its completion. The problem was that wiring the web request form directly to a call to this database package and its procedures also tied down control of the form and its session making the user "wait" for the procedure itself to complete.
Firing off a scheduled job that invoked this process separated the interaction with the web page from the wait for the actual process to complete. Scheduling a job task was almost instantaneous, so the wait between the submission of the request and return of control back to the web page also had a negligible wait.
Using Oracle DBMS_SCHEDULER: An Introduction to the Approach
The Current Problem and Solution Under Discussion: Use the native DBMS_SCHEDULER status views to monitor the progress of your process. There are many, but ALL_SCHEDULER_JOB_LOG is the simpler of the collection and is a good start to what we're trying to accomplish.
Use an easily identifiable name for each job... and also each job that may be related to one another.
Initiate an additional job to watch all the parallel tasks until the last one is completed. Alter this "watching" job to end once this condition has been met.
The Basic Syntax to Initiate a Database Job on Scheduler
The procedure DBMS_SCHEDULER.CREATE_JOB creates a job in a single call without using an existing program or schedule:
DBMS_SCHEDULER.CREATE_JOB (
job_name IN VARCHAR2,
job_type IN VARCHAR2,
job_action IN VARCHAR2,
number_of_arguments IN PLS_INTEGER DEFAULT 0,
start_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,
repeat_interval IN VARCHAR2 DEFAULT NULL,
end_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,
job_class IN VARCHAR2 DEFAULT 'DEFAULT_JOB_CLASS',
enabled IN BOOLEAN DEFAULT FALSE,
auto_drop IN BOOLEAN DEFAULT TRUE,
comments IN VARCHAR2 DEFAULT NULL);
These are the input parameters that you should pay close attention to:
job_name You can leave it to default, or you can help organize your job requests by using a consistent naming convention. JOB_NAME is a special parameter because it has a helper function called *GENERATE_JOB_NAME*, where you can specify a naming prefix to combine with the internal name assignment. This isn't absolutely necessary, but it helps.
DBMS_SCHEDULER.GENERATE_JOB_NAME
(prefix IN VARCHAR2 DEFAULT 'JOB$_') RETURN VARCHAR2;
An example call within the create_job definition:
job_name => dbms_scheduler.generate_job_name( prefix => 'MY_EXAMPLE_JOB_')
So in the example above, we could have a series of jobs with names like: MY_EXAMPLE_JOB_0001, MY_EXAMPLE_JOB_0002, MY_EXAMPLE_JOB_0003...
job_type This one is straight from the Oracle documentation. Most likely it will be the type: *plsql_block* (The value may also be case sensitive).
repeat_interval Do not set this value for your one-time parallel tasks. The job will identify itself as completed once the referenced stored procedure reaches completion, or errors out.
end_date Leave this null or unassigned as well. This value does not apply for a one-time execution of the procedure it is watching.
start_date Leave this null or unassigned as well. No value means to initiate the specified job as soon as the job is ENABLED.
enabled Defaulted to FALSE you will need to set this to TRUE as soon as you create the job, or when you're ready to initiate the process "thread".
auto_drop This is an important one. The rest of this method depends on the meta data of each job remaining in the DBMS_SCHEDULER's log tables even after they have hit an exception or reached completion. Set this to FALSE.
job_action This will vary depending on the number of parallel processes you initiate. First, you should initiate the first of your parallel processes... and also the related "Monitoring" process that will be active for a specific request. Job actions for a plsql_block type job looks something like this:
Example PL/SQL Block:
BEGIN my_procedure(a, b, c); END;
Creating a Job Monitoring Process
Part of the problem you encountered is that DBMS_SCHEDULER may watch a process of varying execution time, but it's not very good at letting you know when it's done or if it encountered an exception.
Your "watcher" process just needs to be another scheduled job that queries the ALL_SCHEDULER_JOB_LOG table for the procedures it's responsible for, and figures out if all of them have reached the desired closing status.
Assumption: For a given request, you will know the number of parallel processes (remotely initiated switching events) required to complete this type of request... all the processes do not have to start exactly at the same time, but the watcher will need to know it still has to wait, even if all the related processes it can see have matched its criteria as "completed".
The kinds of tasks your "watching" procedure will need to include:
WATCHING SQL Criteria Example:
WITH MONITOR_QUERY AS (
SELECT COUNT(LOG_ID) AS COMPLETED_PROCESS_COUNT
FROM ALL_SCHEDULER_JOB_LOG
WHERE JOB_NAME LIKE '001-REQUEST%')
SELECT CASE WHEN COMPLETED_PROCESS_COUNT = <TOTAL_PROCESSES>
THEN 'DONE' ELSE 'IN-PROGRESS' END as REQUEST_STATUS
FROM MONITOR_QUERY
Also note that when you call the job that runs the monitoring process, you may find it useful to generate a unique job name ahead of time before kicking off its repeating job (should be done only once per request or set of parallel jobs):
DECLARE
who_am_i VARCHAR2(65);
BEGIN
SELECT dbms_scheduler.generate_job_name
INTO who_am_i
FROM DUAL;
--
DBMS_SCHEDULER.CREATE_JOB (job_name => who_am_i,
job_type => 'plsql_block',
job_action => 'BEGIN my_monitoring_task(p_1, p_2, who_am_i); END',
repeat_interval => 300,
comments => 'Interval time units are defaulted to SECONDS';
...);
END;
This job is most effective if it is created at the same time or shortly after the first parallel job in the series is launched.
When The Monitored Request is Completed
When the selection criteria is met (i.e., all related processes are closed in some way) then it's time to fire off your notification and also to stop the watcher for this request.
Stopping the Monitoring Job
DBMS_SCHEDULER.STOP_JOB (
job_name IN VARCHAR2
force IN BOOLEAN DEFAULT FALSE);
job_name If you used a custom naming scheme for each job you initiate, you could also store this value as an input parameter into your watcher procedure call. The watcher would then know how to shut itself off when it's done. Remember, if you use the GENERATE_JOB_NAME function call, you are only specifying the prefix to the entire job_name used in the scheduler.
force Set this one to FALSE (The default) or leave it unspecified. It is better to let Oracle find a way to gracefully put your watcher job to a halt.
Closing Thoughts and Comments
If the outcome or completion of several of your procedures are related, an additional scheduled job can be repeated as a monitoring "heartbeat" to check if all the dependencies for a discrete process have been met.
A comment on clean-up: This design requires the *auto_drop* parameter set as FALSE. A daily or weekly process could also be scheduled to issue a *drop_job* command that will clean up the scheduler's logs of records related to completed and reported requests.
You can also see, by including the invoking *job_name* within the scheduled job itself, you can provide the procedure(s) contained within it the ability to turn itself off once the right conditions have been met.
Advanced Queues to the rescue.
Slave sessions (jobs), when they are ready, put their return values into an AQ (practically any data structure is allowed).
The coordinator session, which initiated the slaves, listens on the queue and gathers the return values.
Actually, AQs are the recommended way for intersession communication in Oracle anyway.
In Oracle 12c the column ALL_SCHEDULER_JOB_RUN_DETAILS.OUTPUT can be used to return values from a job.
For example, create a job and write output using DBMS_OUTPUT:
begin
dbms_scheduler.create_job(
job_name => 'TEST_JOB',
job_type => 'PLSQL_BLOCK',
job_action => q'[begin dbms_output.put_line('Test output'); end; ]',
enabled => true
);
end;
/
Now read the output:
select job_name, to_char(log_date, 'YYYY-MM-DD') log_date, output
from all_scheduler_job_run_details
where owner = user
and job_name = 'TEST_JOB'
order by log_date desc;
JOB_NAME LOG_DATE OUTPUT
-------- -------- -------
TEST_JOB 2017-12-26 Test output
If you are able to use oracle version 11, you might use the DBMS_PARALLEL_EXECUTE pl/sql package, which does what you want. If you cannot upgrade, then you can implement some c callouts from pl/sql which provide similar functionality.
If you decide to use dbms_pipe and you are using the RAC database option be aware that using DBMS_PIPE has its limitations for failover.
I have defined a function that when it is called it will define an oracle job with
dbms_scheduler.create_job
that runs a store_procedure with arguments
my Function
Begin
job created and executed from here
end;
My problem is that when an instance of my job is executing I can not execute another instance of that job.
as I said my jobs executes a store_procedure that have arguments so I want to execute that job with different values of arguments in the same time but I can not.
is there any property or way that can help me to do this?
Give the jobs you create random names.
I have a scheduled job that runs every day. In that job I have called some stored procedures from various packages. I would like to know is there a sequence that is followed in Oracle i.e the sequence in which I have called them or the stored procedure are executed in any random order.
if you're saying you have a single job (vs one job for each SP) that calls packages like
begin
pkgA.procA;
pkgB.procB;
pkgC.procC;
...
end;
then they are executed in the order you have them in the pl/sql anonymous block.
if you have 1 job for each API call though, then they will either execute in parallel or in any random order.
I'm using dbms_scheduler to execute a PL/SQL stored procedure. I would like to be able to have that code create some text logging output and associate it with the run to verify it's working, but I can't find anything to do that in the docs. Is there a facility to do this that I'm missing? This is an 11g database running under Unix. It would be real nice if I could use dbms_output so I could also run it from sqlplus and get output.
There are a bunch of Oracle Scheduler data dictionary views that will help you monitor jobs. Here are two documentation pages related to that:
Monitoring Jobs
Monitoring and Managing the Scheduler
Moreover, Oracle Scheduler declares some internal Scheduler variables that you can use like any other PL/SQL identifier in your PL/SQL stored procedure. Here is the list of these variables.
If you want to log application specific information, I suggest you create your own log table. You can then insert into this table from within your stored procedure. You can even insert any of the Scheduler's internal variables there, like job_name and job_scheduled_start.
i make a table JOB_LOG
insert into that table from inside your procedure...
I agree with what the others have said. Here's the actual nuts and bolts, but with a nice interface, too. The way I typically do this:
Make a logging table:
CREATE TABLE job_log (
ts TIMESTAMP DEFAULT SYSTIMESTAMP PRIMARY KEY
, message VARCHAR2(255)
);
Make a stored proc that easily writes into your log table:
CREATE OR REPLACE PROCEDURE job_logger (v_message VARCHAR2)
IS
BEGIN
INSERT INTO job_log(message) VALUES (v_message);
COMMIT;
END;
/
Then within your job, you are probably running a stored procedure. Within your own stored procedure, simply add lines that call the job_logger() procedure to write to your log. This keeps the ugly INSERT ... COMMIT clutter out of your interesting stored proc code.
CREATE OR REPLACE PROCEDURE foo
IS
BEGIN
job_logger('Starting job foo.');
...
{your code here}
...
job_logger('Another message that will be logged.');
...
job_logger('Completed running job foo.');
EXCEPTION
...
job_logger('Oops, something bad happened!');
...
END;
/
Your log table is automatically timestamped and indexed by the primary key. To view the log, you might run this
SELECT * FROM job_log ORDER BY ts DESC;
Now if would rather not use the Oracle scheduler, and want instead to use the DBMS_OUTPUT way of writing output, and want to run this under a Unix shell, that is possible also.
You would make a script that calls sqlplus, somewhat like this. If your user is SCOTT and the stored proc is called FOO,
#!/bin/sh
. /whatever/script/that/sets/your/oracle/environment
echo "
set serveroutput on feedback off
exec foo
" | sqlplus -s -l scott/tiger#orcl
Note, the -s flag suppresses the Oracle SQL Plus banner for cleaner output. The -l flag makes it so that sqlplus will abort if the password is bad or something else wrong, rather than try to prompt for username. Feedback off suppresses the PL/SQL "Anonymous block completed" message.
If you want to schedule this, you can call it from cron like this:
00 00 * * * /path/to/the/above/script.sh > /where/you/want/your/output/saved.log 2>&1