Skip to Main Content
  • Questions
  • Can I do mutlithreaded programming in PLSQL

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, KK .

Asked: May 02, 2000 - 1:21 pm UTC

Last updated: January 18, 2010 - 5:20 pm UTC

Version:

Viewed 10K+ times! This question is

You Asked

Can I do multithread programming in
Oracle proceudres?

By the way, I think dbms_pipe has similar
functions as dbms_alert's signal
functions, what's the difference?
Which one is much better if I want
to use them to write a program which
get stock price from the server?



and Tom said...



No, a stored procedure is single threaded. Using dbms_jobs, you can have a single procedure initiate many procedures in the background but this is more like a unix "fork/exec" then a thread.


dbms_alert is used to send a signal to ALL interested parties. It is transactional (the signal is not sent until you commit). It "loses" signals -- if I signal the same event 5 times and commit -- only one event might get broadcast. It is like a unix signal in this way. Dbms_alert is asyncronous -- the sender never gets anything back from the reciever.


dbms_pipe is used to send a message to a SINGLE interested party (although >1 person can be reading the pipe, only ONE person will get the message). The message is sent immediately, regardless of your transactions state. It is like a unix pipe. Dbms_pipe can be syncronous -- i can send a message and get a message back.


If you are getting the stock price from a webserver -- I would use utl_http (i would probably use utl_http in any case). Added in 7.3.3 of the database, utl_http lets me grab a web page. I can write a cgi-bin or some other sort of dynamic process activated by a webserver to return data to my stored procedure.

Short of that -- dbms_pipe would be the logical second choice as dbms_alert isn't for a 2 way conversation as you would need in a "request" "answer" situation as you have. See </code> http://asktom.oracle.com/~tkyte/plex/index.html <code>for something that might help you there.


Rating

  (23 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Notification to the client : DBMS_ALERT

Ramakrishnan.A, January 25, 2005 - 8:26 am UTC

Dear TOM,

We are developing Fault Management Application for Telecom Switch
Fault raised by switch has to be immediately shown in user via UI & and also need to store it in DB.

Our requirement is 24 X 7 shop and 200 to 300 fault per minute will be raised that has to be shown to the client immediately.

My question is will dbms_alert fit for our requirement?
When ever we insert data can send Notification to the client.

"In short its like stock ticker application any change to any stock we want send Notification to the client Application."

Please tell us which is best way to implement this requirement ?


Best regards
Ramakrishnan.A

Tom Kyte
January 25, 2005 - 9:40 am UTC

You would need a client on that desktop that is always waiting in dbms_alert.wait (eg: blocked in that call).


it would work, but the only thing that client would do is

a) block in wait
b) put up a message box when it got a message


I'd probably use AQ instead of dbms_alert to scale this up a bit though. The client would block (if you wrote it in C, it could actually get notified via a callback and would not have to block with AQ) waiting for a message.

Old Ada Guy, January 25, 2005 - 4:05 pm UTC

I haven't tried this, but how good a multi-threaded solution would the following be to, say, load a zillion-row file?

1. make the file an external table.
2. launch n PL/SQL autonomous transactions, each of which reads a different part of the file and does DML.
3. use a custom table to coordinate the threads and provide the final rendezvous.

Tom Kyte
January 25, 2005 - 6:58 pm UTC

or how's about:

alter table external_table parallel;
insert /*+ append parallel */ into real_table select * from external_table;



:) parallel query works on external tables (and it would be very hard -- no, make that impossible -- to slice up an external table without parallel query -- as there are no rowids or indexes to randomly access it by)


utl_http

P.Karthick, February 17, 2005 - 4:24 am UTC

Hai tom

After going through the above feedback i wanted to know more in detail about the package UTL_HTTP

i went through the document in the link

</code> http://download-west.oracle.com/docs/cd/A87860_01/doc/appdev.817/a76936/utl_http.htm <code>

but i would like to know what is the main purpous of having a package like this. when it is highly usefull.

i have a yahoo mail id. is it possible to access my mail box if i provide the user name and password and store the mails that i receive there into a table i locally create in my database.

thank you

Tom Kyte
February 17, 2005 - 9:18 am UTC

it is highly useful when plsql needs to run a url?


it would be "difficult" to do the yahoo thing, you would have to figure out all of the urls you needed to run in order to accomplish it.

technically feasible, yes. easy -- probably not.

multithreaded plsql

John Donaldson, March 02, 2005 - 6:16 pm UTC

Thank-you!

interested on DBMS_ALERT "loses" signals ...

Giovanni Azua, June 15, 2005 - 4:17 am UTC

Hi Tom,

You described in the first post how DBMS_ALERT works
in general:

**********
dbms_alert is used to send a signal to ALL interested parties. It is transactional (the signal is not sent until you commit). It "loses" signals -- if I signal the same event 5 times and commit -- only one event might get
broadcast. It is like a unix signal in this way.
Dbms_alert is asyncronous -- the sender never gets anything back from the reciever.
**********

But I still have an open question regarding it. if there is no process waiting (probably because is busy) when a signal arrives, is the signal lost as well?

Meaning are the signals persistent? enqueued?

I was trying to use dbms_alert as decoupled logging mechanism among different schemas in the same instance but if the signal is lost this way then it does not work.

Thanks in advance,
Best Regards,
Giovanni


Tom Kyte
June 15, 2005 - 9:44 am UTC

If you yell "next" into an empty room (no one is there to hear you), you have signalled, but no one will have heard it.

the thing you shouted isn't "lost" as much as "there was no one interested in hearing it"

Same with alerts. If you signal an alert and no on cares, that is just fine, it just happens and goes away.

You cannot use dbms_alert that way. Sounds like "autonomous transactions" to me (what you are trying to do)

interested in DBMS_ALERT "loses signals" ...

Giovanni Azua, June 15, 2005 - 10:33 am UTC

Hello Thomas,

I am using extensively DBMS_ALERT for two different purposes in a large application:

1-. Decoupled logging. The overall application consist of several database schemas (users). Each schema is kind of component optionally deployed and decoupled from other schemas. Problem is that schema(s) Yi need to listen and log loading progress from schema X in a decoupled manner.

2-. Synchronization mechanism. There is one overall process that *must* be sequential i.e.

a) Load data
b) Compute aggregations and Data Mining methods
c) Execute rules on b) results.

b) consist of many small subprocesses that are independent from each other (jobs). Running all of them sequentially takes very very long and does not scale in multiprocessor systems. Running all of them in parallel is extremelly fast
but need a way to synchronize them within the overall sequential process until all b) subprocesses (jobs using DBMS_JOB) have completed/or failed. For this purpose I use DBMS_ALERT but as you can imagine it is really critical to have 100% certainty there are no "signal loses".

********************************************************

I did the following DBMS_ALERT test that shows that the messages persist if the session has registered for a given "topic":

1-. Created the following procedures:

create or replace procedure signal_message(ipmessage IN VARCHAR2)
is
cALERT_TOPIC CONSTANT VARCHAR2(100) := 'some_topic';
PRAGMA AUTONOMOUS_TRANSACTION;
begin
dbms_alert.signal(cALERT_TOPIC, ipmessage);
--// does not interfere calling transaction.
commit;
end;
/

create or replace procedure receive_message
as
cALERT_TOPIC CONSTANT VARCHAR2(100) := 'some_topic';
pvreceipt_message VARCHAR2(1000);
pvstatus VARCHAR2(1000);
begin
DBMS_ALERT.REGISTER(cALERT_TOPIC);
DBMS_ALERT.WAITONE (cALERT_TOPIC
, pvreceipt_message
, pvstatus
, 300
);
dbms_output.put_line(pvreceipt_message);
end;
/

2-. Open two command prompts with sqlplus and:

a) exec receive_message; -- on sqlplus #1
b) exec signal_message('just signal test'); -- on sqlplus #2
c) sqlplus #1 shows message 'just signal test' ...
d) exec signal_message('very long time in the queue'); -- on sqlplus #2
e) went for luch, dessert, very long coffee ... 1.5 hours
f) exec receive_message; -- on sqlplus #1
g) sqlplus #1 shows message 'very long time in the queue' ...

Meaning that as long as the session is registered for the signal then the signal gets enqueued for it until the receiver wants to waitone/read it.

********************************************************

But still have a problem I can not find an answer for. I would like at least to have control on the situation where signals are still lost. To show the problem do the following:

1-. Create the following procedures (they use the previous ones):

create or replace procedure signal_message_loop
is
PRAGMA AUTONOMOUS_TRANSACTION;
begin
for i in 1 .. 200
loop
signal_message('Message test ' || i);
end loop;
end;
/

create or replace procedure receive_message_loop
as
cALERT_TOPIC CONSTANT VARCHAR2(100) := 'some_topic';
v_message VARCHAR2(1000);
v_status VARCHAR2(1000);
v_continue BOOLEAN;
begin
DBMS_ALERT.REGISTER(cALERT_TOPIC);
dbms_output.enable(99999);
v_continue := true;
while v_continue
loop
DBMS_ALERT.WAITONE ( cALERT_TOPIC
, v_message
, v_status
, 10
);
if v_status = 1 then
v_continue := false;
end if;
dbms_output.put_line(v_message);
end loop;
end;
/

2-. Opened two sqlplus sessions:

a) exec receive_message_loop; -- on sqlplus #1
b) exec signal_message_loop; -- on sqlplus #2

The result is that approx 10% of the messages are lost despite the fact that a commit follows each signal. I got messages in the range 129 to 152 lost.

My question is:

- Is there any buffer that got exhausted?
- Would such buffer be configurable? a parameter?
- This is not my specific usage of DBMS_ALERT for jobs synchronization; BUT, would this same issue happen if I had N jobs signaling the same "topic" concurrently lets say 100 jobs signal same topic at the same time for synchronization?

********************************************************

Your help will be most appreciated,

Many thanks in advance,
Best Regards,
Giovanni

Tom Kyte
June 15, 2005 - 3:20 pm UTC

I modified your procedure just a little to either register the session or receive a message:

ops$tkyte@ORA9IR2> create or replace procedure receive_message( p_register in boolean default true )
  2  as
  3       cALERT_TOPIC CONSTANT VARCHAR2(100) := 'some_topic';
  4       pvreceipt_message VARCHAR2(1000);
  5       pvstatus          VARCHAR2(1000);
  6  begin
  7          if (p_register)
  8          then
  9          DBMS_ALERT.REGISTER(cALERT_TOPIC);
 10      else
 11          DBMS_ALERT.WAITONE (cALERT_TOPIC
 12                         , pvreceipt_message
 13                         , pvstatus
 14                         , 300
 15                     );
 16          dbms_output.put_line(pvreceipt_message);
 17          end if;
 18  end;
 19  /
 
Procedure created.
 
ops$tkyte@ORA9IR2> exec signal_message( 'msg 0' );
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> exec receive_message;
PL/SQL procedure successfully completed.

<b>that shows that a message signaled BEFORE someone registered to listen for it just "goes away"</b>

 
ops$tkyte@ORA9IR2> exec signal_message( 'msg 1' );
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> exec signal_message( 'msg 2' );
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> exec receive_message(FALSE);
msg 2
 
PL/SQL procedure successfully completed.

ops$tkyte@ORA9IR2> exec receive_message(FALSE);
<b>that just hangs - the multiply queued messages all overwrite eachother, they are NOT queued, there is no msg 1 to be picked up</b>


 

That is what I mean by "signals get lost" 

If 15 people signal -- only ONE of them get through, they do not queue up.


This is the way ALERTS work by design and there is no "fix", they are working correctly.

If you need messages to be delivered AT LEAST ONCE and AT MOST ONCE, you used the wrong package

You should use DBMS_AQ

 

not the whole story

A reader, June 15, 2005 - 3:43 pm UTC

"No, a stored procedure is single threaded. " said tom

well, that is not the whole story
ever heard of java in the database ?
called from plsql ?
java supports threads ?

be careful: aurora / oracle jvm uses
so-called "non preemptive threading model"
see Oracle Java Developer's Guide Chapter 2 for example
(which is allowed by jva language specification by the way)

tried it once (receiving xml via http in one thread and
parsing it/inserting into db in another)

wasn't really fun - gave it up at last
(main problem were java deadlocks occuring)

but should work theoretically ...
quote from Chapter 2 "There is no need to use threads within the application logic because the Oracle server preemptively schedules the session JVMs"
don't agree with this quote

can you show me how to implement scenario given with this
threading model ?

hope sometimes preemptive threading model will be supported ...

tom: is there hope we'll sometimes (soon ?) will get such a jvm in the db ?

Tom Kyte
June 16, 2005 - 3:27 am UTC

no.

doesn't make sense in the context of a stored procedure.

Missing the point

Billy, June 16, 2005 - 3:10 am UTC

A reader said:

> well, that is not the whole story
> ever heard of java in the database ?
> called from plsql ?
> java supports threads ?

This thread is also about how to multithread in *PL/SQL* specifically. (large numbers of PL/SQL developers do not know Java either)

The Java argument is simply not applicable. Heck, I can make the same argument and say that EXTPROC can be used and that you can write your multi-threaded code in C/C++ in an external process which is accessible via a PL/SQL wrapper call. Just how sensible and feasible is that?

What you're also missing is that an Oracle process (dedicated or shared) is also inherantly serialised (which is why we have PQ slave processes!!).

Write a client process. Open a connection handle to Oracle. Now spawn two threads and have both create SQL statement handles at the same time and attempt to execute them. You get an Oracle error returned as there is a *single* process servicing your connection and it can only service *one* request (aka SQL statement handle) at a time.

You think that running that in Java instead inside the Oracle process will solve that problem..? I have my doubts. Unless you have those Java threads using local JDBC connections back to Oracle - which means that now you are creating a whole bunch of new sessions in Oracle.

I do not see this as a sensible or feasible way to address parallel processing in Oracle.

And nor is Java the magic wand that can solve all programming problems. For heaven's sake, it is just another friggen programming language. Period.



Tom Kyte
June 16, 2005 - 3:44 am UTC

<quote>
And nor is Java the magic wand that can solve all programming problems. For
heaven's sake, it is just another friggen programming language. Period.
</quote>

:)

can there be deduced ...

A reader, June 16, 2005 - 8:19 am UTC

<quote>
<quote>
And nor is Java the magic wand that can solve all programming problems. For
heaven's sake, it is just another friggen programming language. Period.
</quote>

:)
</quote>

so who stated that Java is magic and can solve all programming problems ????

furthermore:
from "Java is just another friggen programming language. :)"
and the fact that Oracle choose to support Java in the database,
can there be deduced that
Oracle is just another friggen db-engine ?

(seems logical to me)


Tom Kyte
June 16, 2005 - 9:59 am UTC

I just liked his comment.

That is all.




Using a hammer as a chissel

Billy, June 17, 2005 - 2:54 am UTC

A reader said:

> so who stated that Java is magic and can solve all
> programming problems ????

Well, the subject at hand is how to multi-thread in PL/SQL. Then there's suddenly this posting that say "do it Java instead".

I find it hard not to read that as a "Java can solve everything". It also reaffirms my experiences that many Java developers think that Java is the tool for everything and can do everything. Including turning a hammer into chissel.

> from "Java is just another friggen programming language.
> :)" and the fact that Oracle choose to support Java in the
> database, can there be deduced that Oracle is just another
> friggen db-engine ?

What you're missing is that you cannot use a chissel as a hammer either. PL/SQL does not claim to be abke to do everything (the SQL part in the language name clearly states the language's focus).

So you want to talk TCP from PL/SQL.. how? Java compliments PL/SQL inside the Oracle database. It allows the developer greater flexibility.

But make no mistake. Java will always play second fiddle to PL/SQL inside the Oracle database because [statement of fact] PL/SQL is more capable in processing data in Oracle than Java.

Therefore it makes no sense at all to attempt to use Java to multi-thread data processing.

You want to multi-thread data processing? Try pipelined table functions. Written in PL/SQL.


A reader, September 13, 2005 - 3:15 pm UTC

Hi Tom,

We are getting the following error when
the web application calls a Oracle stored procedure using
a XA transaction.

This stored procedure then calls another stored procedure
in a remote database using database link. This is when the
error occurs.

Error: ORA-24777 use of non-migratable database link not allowed

What are the possible solutions for this problem, apart from
configuring MTS and using non XA transaction?

Thank you.

Tom Kyte
September 13, 2005 - 4:23 pm UTC

as I understand it, if you use XA, you use XA -- XA must be the coordinator, Oracle isn't anymore.

A reader, September 13, 2005 - 5:00 pm UTC

Could you please elaborate your reply?
Does it mean we don't have to do anything on database side?
App is developed in Java.

Thanks.

Tom Kyte
September 13, 2005 - 5:12 pm UTC

if you are using XA, XA must be the coordinator of distributed transactions (meaning, we cannot be)

If a tree falls in the forest...

Aalbert, September 14, 2005 - 4:57 am UTC

> If you yell "next" into an empty room
> (no one is there to hear you),
> you have signalled,
> but no one will have heard it.

Yell in empty room
Though no one will have heard it
You will have signalled

Man, this is Zen and the Art of Database Maintenance! :)

threaded programming

A reader, February 27, 2006 - 1:09 pm UTC

Hi

Working in telecommunication environment. Oracle 9iR2 on HP-UX.

I have a PL/SQL stored procedure which 3 years ago it was a simple if then end if and a query. Now days it converted to a package with 3 functions and 6 procedures.

Due to business requirements this process needs to have a response time less than 5 milliseconds. 3 years ago this was easily achieved. Now the package runs in 4 milliseconds. In the reality 80% of code was added for some functionality. The only functionality which needs to accomplish the 5 milliseconds requirement is a simple query .

I just came from a meeting regarding adding new functionalities in some processes. One of process discussed was the one I mentioned above. Since this process is already taking quite a lot of time and we have a requirement of 5 milliseconds someone suggested or asked if it was possible to run the whole package but obtaining the response of the critical part first.

I thought of dbms_job, running the critical part then using dbms_job to run the rest. This process runs almost 1 million of times per day.

Is there any other better way?

Tom Kyte
February 27, 2006 - 2:39 pm UTC

two funtion calls seems obvious doesn't it? The package can maintain a state, take the existing routine and break it into two bits.

Not knowing more about what precisely this thing does, that is about all I can offer.

Multithreading in one transaction?

Loïc LEFEVRE, October 03, 2008 - 11:37 am UTC

Hi Tom,
I would like to know if it is possible in Oracle to run in parallel different queries (merge, insert, update, delete) in only one transaction.

I would also need intra process/threads communication ala dbms_pipe.

Some people mentionned Java previously, is it possible to manage multiple threads (with synchronisation points) in the Oracle JVM for one transaction?

Regards,
Loïc
Tom Kyte
October 06, 2008 - 2:27 pm UTC

You can run a statement in parallel

period.

You cannot run multiple statements in parallel in the same session (I would like you to consider, think about the read consistency issues one would encounter).

Instead of thinking "many statements - all of which should be able to see eachothers work but none of which could possibly be allowed to - since the work is not completed!" think "many paralllel statements, run one after the other"

The Oracle JVM is a non-premptive JVM, once you submit a sql statement, there you go - that session is going to wait for it to finish.

I cannot even imagine - remotely imagine - how this would be a good thing.

Thanks a lot but how sad...

Loïc, October 08, 2008 - 5:38 pm UTC

Yes you are right (consistent reads...).

Also I should have read the Java Developer's Guide:

"The Oracle9i JVM implements a non-preemptive threading model. With this model,
the JVM runs all Java threads on a single operating system thread. It schedules them
in a round-robin fashion and switches between them only when they block.
Blocking occurs when you, for example, invoke the Thread.yield() method or
wait on a network socket by invoking mySocket.read()."

Moreover, I wrote a testcase and no thread run in parallel :(

However knowing what I do, I'm sure it should be feasible (theoretically). Let me explain:

I'm the responsible of the historisation process of an application. Each night, numerous tables linked by FK are copied into an historical database through a db link. When I look at the dependencies (FK), I can say that some table inserts can (really can) be run in parallel (here not speaking of parallel query but queries run in parallel).

In fact my main problem is the obligation to copy these tables all or nothing i.e. one transaction (sequential parallel queries currently). If I could copy each tables in a dedicated transaction (in the right order of course), then I could multithread this process (10 cpus server) but since the graph must be fully copied or not...

Maybe you have some ideas? :)

Anyway thanks for your comment and your time!

Loïc
Tom Kyte
October 08, 2008 - 10:23 pm UTC

"how sad"

think about the ramifications if you could.

... Instead of thinking "many statements - all of which should be able to see eachothers work but none of which could possibly be allowed to - since the work is not completed!" think "many parallel statements, run one after the other" ...


if you can make use of any parallelism (parallel query) go for it, else a transaction is by definition a sequential set of statements...

and a killer argument

Loïc, October 08, 2008 - 5:40 pm UTC

Oh I forgot to say that when executing queries in JDBC, the code synchronize on the Connection (the session) => no multithreading possible at all given one Connection.

Loïc ;)

Giulio Dottorini, October 10, 2008 - 6:00 am UTC

Hi Tom,
what i would like to know is: suppose i have an external process that ask to the db to calculate a price on passenger travel.
Suppose i've 2 or 3 kind of price for the same trip. For example: full fare 100$, special discount 70$ and so on.
is there a way of calling the same procedure (changing params) and make is run in a sort of parallel execution?
What i would like to make is :
- time t0 starts the request
- time t1 procedure xxxx(full fares)
- time t1 procedure xxxx(special fares)
...
-- time t2 results from the procedures (maybe written in a file od a table)

Suppose that the call of the external procedure can be even made bby a pl/sql procedure.

Thanks for your attention and excuse me for my english.

Reagards,
Giulio
Tom Kyte
October 13, 2008 - 2:31 am UTC

you would use dbms_job, have the N procedures run in their own session and populate an 'answer' table with the results - perhaps using dbms_alert to signal back to the original process as each 'thread' finishes.

eg (PSUEDO CODE, THIS WILL NOT COMPILE, YOU NEED TO FLESH THIS OUT)

begin
dbms_job.submit( l_job, 'begin proc(JOB); dbms_alert.signal('Yo!'); end;' );
insert into job_params (jobno,parms) values (l_job,'full fare' );
dbms_job.submit( l_job, 'begin proc(JOB); dbms_alert.signal('Yo!'); end;' );
insert into job_params (jobno,parms) values (l_job,'special fare' );
commit; -- jobs run
loop
dbms_alert.wait for signal, when last job finished, exit
end loop
read results
end


it is probably not worth it. Probably better would be to have NOT external procedures but things reading off of a queue. You would use AQ to enqueue a message to N of these "processors", they would answer either over a queue or in a table - whatever works best for you.

Giulio Dottorini, October 14, 2008 - 8:51 am UTC

Hi Tom,
it works very fine.
Thanks a lot for you help.

Reagrds.

How to send variables as in parameter to DBMS_JOBS.SUBMIT

Ashok, May 06, 2009 - 2:10 am UTC

begin
dbms_job.submit( l_job, 'begin proc(JOB); dbms_alert.signal('Yo!'); end;' );
insert into job_params (jobno,parms) values (l_job,'full fare' );
dbms_job.submit( l_job, 'begin proc(JOB); dbms_alert.signal('Yo!'); end;' );
insert into job_params (jobno,parms) values (l_job,'special fare' );
commit; -- jobs run
loop
dbms_alert.wait for signal, when last job finished, exit
end loop
read results
end

Hi Tom,
In the above programme if I want to send a variable as a in parameter to dbms_jobs.submit then is it possible.so in the place of proc(JOB) in the above dbms_jobs.submit ..I would like to send proc(variable).

How can we run procedures in parallel in pl/sql?
Tom Kyte
May 11, 2009 - 9:16 am UTC

My approach to dbms_job and parameters is to ALWAYS use a table to pass parameters to make it "bind friendly" - you just run proc(JOB) - every time, and it will already be hard parsed - regardless of the inputs.

It would look like this:

ops$tkyte%ORA10GR2> create table params( job_id number primary key, p1 number, p2 timestamp, p3 varchar2(30) ) organization index;

Table created.

ops$tkyte%ORA10GR2> create table t as select params.*, systimestamp end_time from params;

Table created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create or replace procedure proc( p_job in number )
  2  as
  3      l_rec params%rowtype;
  4  begin
  5      select * into l_rec from params where job_id = p_job;
  6      insert into t (job_id,p1,p2,p3,end_time)
  7      values (l_rec.job_id,l_rec.p1,l_rec.p2,l_rec.p3,systimestamp);
  8      delete from params where job_id = p_job;
  9      commit;
 10  end;
 11  /

Procedure created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> declare
  2      l_job number;
  3      l_cnt number;
  4      l_msg long;
  5      l_status number;
  6  begin
  7      dbms_job.submit( l_job, 'proc(JOB); dbms_alert.signal(''MY_JOB'',null);' );
  8      insert into params (job_id,p1,p2,p3) values (l_job, 42, systimestamp, 'hello' );
  9
 10      dbms_job.submit( l_job, 'proc(JOB); dbms_alert.signal(''MY_JOB'',null);' );
 11      insert into params (job_id,p1,p2,p3) values (l_job, 55, systimestamp, 'world' );
 12
 13      dbms_alert.register( 'MY_JOB' );
 14      commit;
 15
 16      loop
 17          select count(*) into l_cnt from params where rownum=1;
 18          exit when l_cnt = 0;
 19          dbms_alert.waitone( 'MY_JOB', l_msg, l_status, 600 );
 20      end loop;
 21  end;
 22  /

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> select * from params;

no rows selected

ops$tkyte%ORA10GR2> select * from t;

    JOB_ID  P1 P2                           P3    END_TIME
---------- --- ---------------------------- ----- -----------------------------------
       349  42 11-MAY-09 08.55.53.167872 AM hello 11-MAY-09 08.55.56.018915 AM -04:00
       350  55 11-MAY-09 08.55.53.175672 AM world 11-MAY-09 08.55.56.030595 AM -04:00




make sure you have execute on DBMS_ALERT granted directly to you - else you will find:

Errors in file /home/ora10gr2/rdbms/log/ora10gr2_ora_5074.trc:
ORA-12012: error on auto execute of job 345
ORA-06550: line 1, column 107:
PLS-00201: identifier 'DBMS_ALERT' must be declared
ORA-06550: line 1, column 107:
PL/SQL: Statement ignored


in your alert log.

Missing multithreaded capability in PL/SQL

CJ, November 19, 2009 - 5:45 am UTC

I am writing a custom stats gathering package, and I have a requirement to improve performance by
gathering stats on multiple tables at the same time.

The ideal logic for me would be:

-- Accept schema name.
-- Create 4 batches of tables (based on sizes).
-- Run gather stats for each batch in a separate thread.
-- Finish, when all the threads finish.

Right now the only option I have is to use DBMS_JOB/
DBMS_SCHEDULER to spawn 4 jobs.
But wouldn't it be so much simpler if PL/SQL provided multithreaded syntax, so that I could do it all in a single block of code?

Almost similar is the requirement for rebuilding indexes. After ETL on a partition I need to rebuild the disabled bitmap indexes.
If only I could do it in multiple threads rather than DBMS_JOB...

Or am I missing a trick here?
Tom Kyte
November 23, 2009 - 3:28 pm UTC

... But wouldn't it be so much simpler if PL/SQL provided multithreaded syntax, so
that I could do it all in a single block of code?
...

maybe yes, maybe no, but not meaningful anyway - because adding multithreaded semantics to plsql would not happen overnight

and even if it did, it would probably be done like the java way was done in the database. If you run a multithreaded java bit of code in the database - it really just single threads and non-premptively multitask. The threads would yield to each other.


You need a way to dispatch the various "threads" (dbms_job or scheduler) and then rendezvous with them (to borrow from Ada). That is relatively straight forward, this is a technique I've used more than once:


...
begin
    for i in 1 .. numberofJobs
    loop
        dbms_job.submit( l_job, 'do_sql(job);' );
        insert into job_table values ( l_job, <whatever their parameters are> );
    end loop;
    dbms_alert.register( 'MULTIUSER' );
    commit;  -- jobs start running
    dbms_alert.waitone( 'MULTIUSER', l_msg, l_status, 600 );
    loop
        select count(*) into l_cnt from job_table where rownum=1;
        exit when l_cnt = 0;
        dbms_alert.waitone( 'MULTIUSER', l_msg, l_status, 600 );
    end loop;
end;


and the code that runs in the background would look like:

create or replace procedure do_sql( p_job in number )
as
    l_cursor sys_refcursor;
    l_rec    job_table%rowtype;
begin
    select * into l_rec from job_table where job = p_job;
...whatever...
    delete from job_table where job = p_job;
    dbms_alert.signal( 'MULTIUSER', '' );
    commit;
end;



It is not semantically that much different from "create thread" and "rendezvous"


Using DBMS_JOB for multithreading

CJ, December 04, 2009 - 10:01 am UTC

Many thanks for the reply Tom, and my apologies for not responding sooner...

>> It is not semantically that much different from "create thread" and "rendezvous"

I agree, but the reliance on 'external' feature like dbms_job makes me a bit nervous, because this means more chances of things going wrong.

For example, before I deploy the code I will have to check that the job_queue_processes is set to be sufficiently high. Even then I might not achieve the desired multithreading if all the job queue processes are already busy with long running jobs.

For now I will use dbms_job (or dbms_scheduler?) as you have described.

However, I am still not sure what the design constraint is that prohibits Oracle from putting Java like multithreading in PL/SQL.
Tom Kyte
December 04, 2009 - 5:26 pm UTC

dbms_job is external? how so? I'm not sure what you mean. It does have 'setup', but most/many features do.


... For example, before I deploy the code I will have to check that the
job_queue_processes is set to be sufficiently high. ...



and if you used a language feature, you would have to make sure the client language actually supported pre-emptive multitasking - which is doesn't - not even java in the database does. Like I said above

and you could make run time checked as well

ops$tkyte%ORA10GR2> declare
  2          l_junk   number;
  3          l_number number;
  4          l_str    varchar2(4000);
  5  begin
  6          l_junk := dbms_utility.GET_PARAMETER_VALUE( 'job_queue_processes', l_number, l_str );
  7          if ( nvl(l_number,0) = 0 )
  8          then
  9                  raise_application_error( -20001, 'job queues must be setup for this to run' );
 10          end if;
 11          dbms_output.put_line( 'we will have ' || l_number || ' concurrent jobs max' );
 12  end;
 13  /
we will have 10 concurrent jobs max

PL/SQL procedure successfully completed.


as for ... However, I am still not sure what the design constraint is that prohibits
Oracle from putting Java like multithreading in PL/SQL. ..


... and even if it did, it would probably be done like the java way was done in the database. If you run a multithreaded java bit of code in the database - it really just single threads and non-premptively multitask. The threads would yield to each other.

remote procedure call and triggers

A reader, January 15, 2010 - 3:20 am UTC

Hi Tom

A question regarding pl/sql multithreading.

I see that the only option is using dbms_scheduler or dbms_job.

I have this situation, there is a trigger defined for a table, whenever insert runs on this table trigger will call a remote procedure and do execute some logic in the remote database which takes arounds a couple of seconds. This means that each insert will take the insert time and the remote procedure call time.

I wonder if there is anyway without using dbms_scheduler (because there is quite a few insert going on) say

1. insert into table
2. trigger fires
3. insert finishes without waiting the remote proceudre call

Thanks!

David
Tom Kyte
January 18, 2010 - 5:20 pm UTC

you use the schedule or job queues to do that, that is about it. It is called loosely coupled processing, the insert creates a message (a job) that is processed later - giving you the appearance of fast performance.

and it'll make your system a lot more reliable as you won't be dependent on that 2nd system at runtime.

As long as you can do it as two independent transactions - it is absolutely the way to go.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library