postgresql/contrib/userlock
Tom Lane 6cc842abd3 Revise lock manager to support "session level" locks as well as "transaction
level" locks.  A session lock is not released at transaction commit (but it
is released on transaction abort, to ensure recovery after an elog(ERROR)).
In VACUUM, use a session lock to protect the master table while vacuuming a
TOAST table, so that the TOAST table can be done in an independent
transaction.

I also took this opportunity to do some cleanup and renaming in the lock
code.  The previously noted bug in ProcLockWakeup, that it couldn't wake up
any waiters beyond the first non-wakeable waiter, is now fixed.  Also found
a previously unknown bug of the same kind (failure to scan all members of
a lock queue in some cases) in DeadLockCheck.  This might have led to failure
to detect a deadlock condition, resulting in indefinite waits, but it's
difficult to characterize the conditions required to trigger a failure.
2000-12-22 00:51:54 +00:00
..
Makefile Add support for VPATH builds, that is, building somewhere else than in the 2000-10-20 21:04:27 +00:00
README.user_locks Add missing /contrib files 2000-06-19 14:02:16 +00:00
user_locks.c Revise lock manager to support "session level" locks as well as "transaction 2000-12-22 00:51:54 +00:00
user_locks.h Ye-old pgindent run. Same 4-space tabs. 2000-04-12 17:17:23 +00:00
user_locks.sql.in

User locks, by Massimo Dal Zotto <dz@cs.unitn.it>
Copyright (C) 1999, Massimo Dal Zotto <dz@cs.unitn.it>

This software is distributed under the GNU General Public License
either version 2, or (at your option) any later version.


This loadable module, together with my user-lock.patch applied to the
backend, provides support for user-level long-term cooperative locks.
For example one can write:

  select some_fields, user_write_lock_oid(oid) from table where id='key';

Now if the returned user_write_lock_oid field is 1 you have acquired an
user lock on the oid of the selected tuple and can now do some long operation
on it, like let the data being edited by the user.
If it is 0 it means that the lock has been already acquired by some other
process and you should not use that item until the other has finished.
Note that in this case the query returns 0 immediately without waiting on
the lock. This is good if the lock is held for long time.
After you have finished your work on that item you can do:

  update table set some_fields where id='key';
  select user_write_unlock_oid(oid) from table where id='key';

You can also ignore the failure and go ahead but this could produce conflicts
or inconsistent data in your application. User locks require a cooperative
behavior between users. User locks don't interfere with the normal locks
used by postgres for transaction processing.

This could also be done by setting a flag in the record itself but in
this case you have the overhead of the updates to the records and there
could be some locks not released if the backend or the application crashes
before resetting the lock flag.
It could also be done with a begin/end block but in this case the entire
table would be locked by postgres and it is not acceptable to do this for
a long period because other transactions would block completely.

The generic user locks use two values, group and id, to identify a lock,
which correspond to ip_posid and ip_blkid of an ItemPointerData.
Group is a 16 bit value while id is a 32 bit integer which could also be
an oid. The oid user lock functions, which take only an oid as argument,
use a group equal to 0.

The meaning of group and id is defined by the application. The user
lock code just takes two numbers and tells you if the corresponding
entity has been succesfully locked. What this mean is up to you.

My succestion is that you use the group to identify an area of your
application and the id to identify an object in this area.
Or you can just lock the oid of the tuples which are by definition unique.

Note also that a process can acquire more than one lock on the same entity
and it must release the lock the corresponding number of times. This can
be done calling the unlock funtion until it returns 0.