How to Use User Locks with DBMS_LOCK

Make sure only one session runs a process at a time with named user locks, and read the result codes REQUEST and RELEASE return.

Row locks protect rows, but sometimes the thing to protect is a process: only one session should run the nightly repricing at a time, however many try to start it. DBMS_LOCK provides named user locks for this. Sessions request a lock by name, and only one can hold it in exclusive mode.

Code for This Guide

The main examples are in the examples/pkg-utilities folder of the Oracle Database 26ai code repository on GitHub, each with its output. They use NIMBUS, the sample schema of a fictional airline, which you can install with the scripts in the setup/nimbus folder.

They come from Oracle Database 26ai SQL and PL/SQL Book.

Syntax

dbms_lock.allocate_unique(lockname, lockhandle_out);

result := dbms_lock.request(lockhandle, lockmode => dbms_lock.x_mode,
                            timeout => seconds, release_on_commit => boolean);

result := dbms_lock.release(lockhandle);

REQUEST returns 0 for success, 1 for a timeout, 2 for a deadlock, 3 for a parameter error, 4 if the session already holds the lock, and 5 for an illegal handle. RELEASE returns 0, 3, 4 (not owned), or 5.

Request and Release a Lock

Example:

declare
  v_handle varchar2(128);
  v_result integer;
begin
  dbms_lock.allocate_unique('NIMBUS_NIGHTLY_REPRICING', v_handle);
  v_result := dbms_lock.request(v_handle, dbms_lock.x_mode, timeout => 0,
                                release_on_commit => false);
  dbms_output.put_line('request: ' || v_result || ' (0 = success)');
  v_result := dbms_lock.request(v_handle, dbms_lock.x_mode, timeout => 0);
  dbms_output.put_line('again:   ' || v_result || ' (4 = already own it)');
  v_result := dbms_lock.release(v_handle);
  dbms_output.put_line('release: ' || v_result);
end;
/

Output:

request: 0 (0 = success)
again:   4 (4 = already own it)
release: 0

PL/SQL procedure successfully completed.

ALLOCATE_UNIQUE turns the name into a handle. The first request succeeds; the second returns 4, since this session already holds the lock. A second session asking with timeout 0 would get 1 and know the job is already running.

Release on Commit

By default a user lock is held until it is released or the session ends. With RELEASE_ON_COMMIT => TRUE, the lock ends with the transaction.

Example:

declare
  v_handle varchar2(128);
  v_result integer;
begin
  dbms_lock.allocate_unique('NIMBUS_FARE_IMPORT', v_handle);
  v_result := dbms_lock.request(v_handle, dbms_lock.x_mode, timeout => 0,
                                release_on_commit => true);
  dbms_output.put_line('request: ' || v_result);
  commit;                                                    -- releases the lock
  v_result := dbms_lock.release(v_handle);
  dbms_output.put_line('release after commit: ' || v_result || ' (4 = not owned)');
end;
/

Output:

request: 0
release after commit: 4 (4 = not owned)

PL/SQL procedure successfully completed.

After the COMMIT, the session no longer holds the lock, so RELEASE returns 4.

Things to Know

  • Using DBMS_LOCK needs EXECUTE on the package, granted by a DBA.
  • ALLOCATE_UNIQUE commits the current transaction, so call it before starting DML, or use ALLOCATE_UNIQUE_AUTONOMOUS, which does not.
  • Choose names with an application prefix; lock names are shared across the whole database. Names starting with ORA$ are reserved.

Related Guides

Conclusion

DBMS_LOCK gives PL/SQL named locks for protecting processes rather than rows. Allocate a handle from a unique name, request it with a timeout and check the result code, and release it when the work is done or tie it to the transaction with RELEASE_ON_COMMIT.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted