Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Ambruzs.

Asked: February 14, 2001 - 2:16 am UTC

Last updated: February 17, 2004 - 10:09 am UTC

Version: 8.1.6.1

Viewed 10K+ times! This question is

You Asked

Hello Tom,

I just want to ask you about the Alerts in Oracle.
Especially the dbms_alert_info table.

When a session is registered for an alert a new record inserted into the dbms_alert_info table with its session Id and the registered alert name etc...
But when the session is logged out the record is still in the table if I have not removed by the dbms_alert.remove procedure for example.
If I forget to do that or the session is killed how will be deleted this record in the dbms_alert_info table?
Is it possible to delete by DELETE command from SQL, or there is something in the database what delete automatically all rows of all 'not alive' sessions?

Thank you very much!

Ambruzs

and Tom said...

If a session dies without removing the alert, that alert is eventually (but not immediately) cleaned up.

One way to clean up a dead alert is to in another session register for and then remove that alert. that'll get the last dead one out of there.



Rating

  (6 ratings)

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

Comments

what's SID column in dbms_alert_info

Robert Xuequn Xu, March 07, 2003 - 6:30 pm UTC

Sorry to wake up this old thread, but the data of SID in dbms_alert_info is a little cryptic. The doc says it's session ID - but what session ID? For example here's what I got:

15:24:32 SQL> select * from sys.dbms_alert_info;

NAME               SID                            C MESSAGE
------------------ ------------------------------ - ----------
LIMS$BACKGROUND    0045FEB10001                   Y


Which session is waiting for this alert? In other words, how can I find which session is "0045FEB10001"? Thanks! 

Tom Kyte
March 07, 2003 - 7:38 pm UTC

ops$tkyte@ORA920> select to_number( '0045', 'xxxx' ) sid, to_number( 'feb1', 'xxxx' ) serial#
  2  from dual;

       SID    SERIAL#
---------- ----------
        69      65201

1 row selected.


Now -- on some ports, you may have to swap bytes so that would be:

  1  select to_number( '4500', 'xxxx' ) sid, to_number( 'b1fe', 'xxxx' ) serial#
  2* from dual
ops$tkyte@ORA920> /

       SID    SERIAL#
---------- ----------
     17664      45566

1 row selected.


instead.

 

Deadlock on dbms_alert_info

Dilip, February 11, 2004 - 9:29 am UTC

Tom,
If this is not right place, I will put my question when you are taking new questions.

We are getting deadlock on dbms_alert_info

We need to refresh static data for an application when data is inserted/updated. Inserts/Updates takes place via single session only.

Here is what we have..

There are insert/update triggers on couple of tables.

CREATE OR REPLACE TRIGGER TRG_T1_REFRESH
AFTER INSERT OR UPDATE ON T1
BEGIN
DBMS_ALERT.SIGNAL('T1_ALERT','T1');
END;
/

CREATE OR REPLACE TRIGGER TRG_T2_REFRESH
AFTER INSERT OR UPDATE ON T2
BEGIN
DBMS_ALERT.SIGNAL('T2_ALERT','T2');
END;
/

There is a DBMS_JOB which register calls refresh_static_data procedure.

declare
l_job number;
begin
dbms_job.submit( l_job,
'begin
refresh_static_data;
end;',
sysdate,
'sysdate' );
commit;
end;
/


Here is the refresh_static_data procedure

CREATE OR REPLACE PROCEDURE refresh_cst_static_data
AS

v_http_data VARCHAR2(2000);
v_name VARCHAR2(60);
v_message VARCHAR2(200);
v_status NUMBER;
v_static_url VARCHAR2(200) := 'some_url_for_refresh';
v_refresh_url VARCHAR2(250);

BEGIN

DBMS_ALERT.REGISTER('T1_ALERT');
DBMS_ALERT.REGISTER('T2_ALERT');

LOOP

DBMS_ALERT.WAITANY(v_name, v_message, v_status, 60);

IF v_status = 0 THEN

v_refresh_url := v_static_url||v_message;

begin
v_http_data := utl_http.request(v_refresh_url);
exception when others then
null;
end;

ELSE
exit;
END IF;


END LOOP;

DBMS_ALERT.REMOVEALL();

END;
/


From time to time, we get deadlock error.

When we see trace file, it has following information.

...
...
DEADLOCK DETECTED
Current SQL statement for this session:
DELETE FROM DBMS_ALERT_INFO WHERE SID = :b1
----- PL/SQL Call Stack -----
object line object
handle number name
8dc57e28 468 package body SYS.DBMS_ALERT
8dc4e93c 64 procedure USZONE_MGR.REFRESH_CST_STATIC_DATA
8dc60d38 2 anonymous block
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:
....
....

Is this deadlock caused by transactions on DBMS_ALERT_INFO?
Not sure where to start looking to troubleshoot this one.

Thanks for you help,

Regards

Dilip


Tom Kyte
February 11, 2004 - 10:33 am UTC

what is line 64 of

8dc4e93c 64 procedure USZONE_MGR.REFRESH_CST_STATIC_DATA

? not clear from the example which line that could be.

Dilip, February 11, 2004 - 10:37 am UTC

Line 64 in the procedure is,

DBMS_ALERT.REMOVEALL();

Tom Kyte
February 11, 2004 - 12:29 pm UTC

I believe this is a true deadlock.

if one session "signals", the removeall will block. I believe the signalling session then tries to lock a resource the blocked one has. Deadlock in the conventional sense.

do this:

---------------- test1.sql ----------------------
set echo on

drop table t;
create table t ( x int );
insert into t values ( 1 );

variable nm varchar2(10)
variable msg varchar2(20)
variable stat number

exec dbms_alert.register( 'a' );
prompt goto other session, run test2 and come back and hit enter
pause
update t set x = x;
exec dbms_alert.removeall
commit;

--------------------------------------------------------------

---------------------- test2.sql -----------------------------

set echo on

exec dbms_alert.signal( 'a', 'msg' )
prompt go back to other session and hit enter
pause
update t set x = x;
commit;

---------------------------------------------------------------


run those two scripts and you'll have a small example of what I mean.


Look at the two real processes (you seem to have given a small snippet) and see if they do not go after the same resources. Perhaps you are missing a "commit" in the job before the removeall?


Dilip, February 16, 2004 - 2:55 pm UTC

Thank you Tom for your response.

I understand the example given by you and tried on my end resulting in deadlock.

But the original proc (which is executed by DBMS_JOB), does not lock any other resources (atleast I can not find it/understand it if it does). It registers for alerts, waits for them and if it finds any alert, makes call to utl_http.request function. If timeout happens in waiting for alert, then it calls dbms_alert.removeall.

Now I understand that the call to dbms_alert.removeall gets blocked by signalling session (if no commit or rollback in signalling session is issued before waitany times out) but there are no resources locked by the job issuing dbms_alert.removeall.

I am not able to reproduce this particular situation in test database but this deadlock happened again in production system.

For the time being, I am planning to increase time out period on dbms_alert.waitany to dbms_alert.maxwait so the job will just sit there, waiting for any alert.

Dilip

Tom Kyte
February 16, 2004 - 4:08 pm UTC

what does this running of a url do exactly then?

are we looking at the real code? how big is the real code.

Dilip, February 16, 2004 - 4:39 pm UTC

Tom,

I have put the actual code here.

1. Trigger

CREATE OR REPLACE TRIGGER TRG_SEGMENTATION_DATA_REFRESH
AFTER INSERT OR UPDATE ON SEGMENTATION
BEGIN
DBMS_ALERT.SIGNAL('SEGMENTATION_ALERT','SEGMENTATION');
END;
/

CREATE OR REPLACE TRIGGER TRG_SERVICE_CLASS_DATA_REFRESH
AFTER INSERT OR UPDATE ON SERVICE_CLASS
BEGIN
DBMS_ALERT.SIGNAL('SERVICE_CLASS_ALERT','SERVICE_CLASS');
END;
/

-- There are 26 more tables with similar trigger code

2. Procedure

CREATE OR REPLACE PROCEDURE refresh_cst_static_data
AS

v_http_data VARCHAR2(2000);
v_name VARCHAR2(60);
v_message VARCHAR2(200);
v_status NUMBER;
v_static_url VARCHAR2(200) := '</code> http://cst.zoneld.net/ecare/reloadStaticData/'; <code>
v_refresh_url VARCHAR2(250);

BEGIN

DBMS_ALERT.REGISTER('SEGMENTATION_ALERT');
DBMS_ALERT.REGISTER('SERVICE_CLASS_ALERT');

--This proc registers for 26 other alerts in the same way. I have removed those statements.

LOOP

DBMS_ALERT.WAITANY(v_name, v_message, v_status, 60);

IF v_status = 0 THEN

v_refresh_url := v_static_url||v_message;

begin
v_http_data := utl_http.request(v_refresh_url);
exception when others then
null;
end;

ELSE
exit;
END IF;


END LOOP;

DBMS_ALERT.REMOVEALL();

END;
/

3. This is the script for creating the job

declare
l_job number;
begin
dbms_job.submit( l_job,
'begin
refresh_cst_static_data;
end;',
sysdate,
'sysdate' );
commit;
end;
/


The URL points to a sevlet (on 9ias) which does the refresh of static data.

Servlet is selecting data for the table specified in the URL.

Tom Kyte
February 17, 2004 - 7:54 am UTC

ahh, got it.

When you call removeall -- it is removing the alerts in order of registration. Do this:


IN SESSION 1:

declare
v_name VARCHAR2(60);
v_message VARCHAR2(200);
v_status NUMBER;

begin
dbms_alert.register( 'A' );
dbms_alert.register( 'B' );
DBMS_ALERT.WAITANY(v_name, v_message, v_status,1);
end;
/



IN SESSION 2:

exec dbms_alert.signal( 'B', 'x' );



IN SESSION 1:

exec dbms_alert.removeall();

<that'll block -- but it'll have locked "alert A" in the removal and will be stuck on removing "alert B">


IN SESSION 2:

exec dbms_alert.signal( 'A', 'x' );

<that'll block -- and the other session #1 will get:

ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
ORA-06512: at "SYS.DBMS_ALERT", line 464
ORA-06512: at line 1



They are locking each others resources here.


Also, since ALERTS are totally "blocking city" sort of things (eg: only one session at a time can fire an alert of a given name!!!!) you have serialized all access to these 26 tables (surprised you do not have deadlocks all over the place actually).


A suggestion, create a table:

create table job_parameters( job_id number primary key, url varchar2(255) );
create index job_parameters_idx on job_parameters(url);

and use a trigger like this:


CREATE OR REPLACE TRIGGER TRG_SEGMENTATION_DATA_REFRESH
AFTER INSERT OR UPDATE ON SEGMENTATION
DECLARE
l_job number;
BEGIN
dbms_job.submit( l_job, 'run_url(JOB);' );
insert into job_parameters(job_id,url) values
(job_id,'http ://cst.zoneld.net/ecare/reloadStaticData/' );
END;
/



and code:

create or replace procedure run_url( p_job_id in number )
as
l_rec job_parameters%rowtype;
l_http_data long;
begin
select * into l_rec from job_parameters where job_id = p_job_id;
delete from job_parameters where URL = l_rec.url;
commit;
l_http_data := utl_http.request(l_rec.url);
exception
when no_data_found then NULL; -- just skip it, we didn't need to run again
end;
/


In that fashion -- N number of concurrent sessions can request that URL to be run at the same time.

The job will run -- pick up the URL, get rid of any duplicate URL's queued up, then run the URL. The other jobs will just run and exit -- until something appears in the job parameters table again.






Dilip, February 17, 2004 - 9:57 am UTC

Tom,
Thank you so much. Now I understand why we get deadlock in our particular situation.

I will implement your suggested solution. One question though, this solution will generate job id everytime. Is there any upper limit to job ids generated by oracle?

Regards,

Dilip

Tom Kyte
February 17, 2004 - 10:09 am UTC



999,999,999

but it'll cycle so it is rather "infinite"

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