Skip to Main Content
  • Questions
  • Please help understand expiration_secs in sys.dbms_lock.allocate_unique

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Raja.

Asked: February 08, 2019 - 4:45 pm UTC

Last updated: February 12, 2019 - 11:26 am UTC

Version: 11.2g

Viewed 1000+ times

You Asked

Please help understand the meaning of the param expiration_secs in
sys.dbms_lock.allocate_unique


Does it represent the time till which the named DB lock stays on, if not unlocked/commit/rollback by the same session?

https://docs.oracle.com/cd/E24693_01/appdev.11203/e23448/d_lock.htm#i1002533

My assumption in basic english, was it should have meant the allocated lock_handle auto release time. But it seems to be intended for some other purpose not sufficiently explained in the document.

I humbly accept my lack of understanding.

and Chris said...

When you create a user lock, the database stores it in sys.dbms_lock_allocated.

The expiration_secs for sys.dbms_lock.allocate_unique relates to when the database will purge rows from this table.

By default the expiry date is 10 days in the future:

select sysdate from dual; 

SYSDATE                
12-FEB-2019 03:19:13   

declare
  handle varchar2(100);
begin
  dbms_lock.allocate_unique (
    lockname        => 'test_lock',
    lockhandle      => handle
  );
end;
/

select * from sys.dbms_lock_allocated
where  name = 'test_lock';

NAME        LOCKID       EXPIRATION             
test_lock     1073742910 22-FEB-2019 03:19:14   


So when does the database clean these out?

As MOS note 840840.1 says:

The DBMS_LOCK_ALLOCATED table is periodically cleared out by calling the allocate_unique()
procedure when the modulus of the lockid is 0 and the expiration column is less than the current
date.
On every 100th attempt to create a new entry, allocate_unique purges expired entries from the
table. If the entry already exists, allocate_unique updates the expiry time.


The lock I just created doesn't expire for another ten days. So even if I create a bunch more, it doesn't expire:

declare 
  handle varchar2(100);
begin
  for i in 1 .. 100 loop
    dbms_lock.allocate_unique (
      lockname        => 'test_lock_' || i,
      lockhandle      => handle,
      expiration_secs => 30
    );
  end loop;
end;
/

select case
         when regexp_like ( name, '[0-9]+' ) then 'new'
         else 'orig'
       end lck,
       min ( to_number ( regexp_substr ( name, '[0-9]+' ) ) ) mn_num,
       max ( to_number ( regexp_substr ( name, '[0-9]+' ) ) ) mx_num,
       min ( expiration ), max ( expiration ), count(*)
from   sys.dbms_lock_allocated
where  name like 'test_lock%'
group  by case
         when regexp_like ( name, '[0-9]+' ) then 'new'
         else 'orig'
       end;

LCK    MN_NUM   MX_NUM   MIN(EXPIRATION)        MAX(EXPIRATION)        COUNT(*)   
orig     <null>   <null> 22-FEB-2019 03:19:14   22-FEB-2019 03:19:14            1 
new           1      100 12-FEB-2019 03:19:45   12-FEB-2019 03:19:45          100 


But I created all these with a 30s timeout.

So if we wait a while and create a bunch more locks, these are all purged:

exec dbms_lock.sleep ( 35 );

declare 
  handle varchar2(100);
begin
  for i in 101 .. 200 loop
    dbms_lock.allocate_unique (
      lockname        => 'test_lock_' || i,
      lockhandle      => handle,
      expiration_secs => 30
    );
  end loop;
end;
/

select case
         when regexp_like ( name, '[0-9]+' ) then 'new'
         else 'orig'
       end lck,
       min ( to_number ( regexp_substr ( name, '[0-9]+' ) ) ) mn_num,
       max ( to_number ( regexp_substr ( name, '[0-9]+' ) ) ) mx_num,
       min ( expiration ), max ( expiration ), count(*)
from   sys.dbms_lock_allocated
where  name like 'test_lock%'
group  by case
         when regexp_like ( name, '[0-9]+' ) then 'new'
         else 'orig'
       end;

LCK    MN_NUM   MX_NUM   MIN(EXPIRATION)        MAX(EXPIRATION)        COUNT(*)   
orig     <null>   <null> 22-FEB-2019 03:19:14   22-FEB-2019 03:19:14            1 
new         101      200 12-FEB-2019 03:20:22   12-FEB-2019 03:20:22          100 


But the original remains, because we still have nearly 10 days to wait until it expires.

If you allocate_unique for an existing entry, this will update it's expiry time:

select * from sys.dbms_lock_allocated
where  name = 'test_lock';

NAME        LOCKID       EXPIRATION             
test_lock     1073742910 22-FEB-2019 03:19:14   

declare
  handle varchar2(100);
begin
  dbms_lock.allocate_unique (
    lockname        => 'test_lock',
    lockhandle      => handle,
    expiration_secs => 100000
  );
end;
/

select * from sys.dbms_lock_allocated
where  name = 'test_lock';

NAME        LOCKID       EXPIRATION             
test_lock     1073742910 13-FEB-2019 07:12:09   


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

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