It sorta works, but I'm confused about locking

Started by Tom Laneover 27 years ago8 messages
#1Tom Lane
tgl@sss.pgh.pa.us

I've got this new notify code almost working, but...

What exactly is the protocol for locking a table that you intend to
modify? The old notify code just did RelationSetLockForWrite(),
but it's not clear to me that that works correctly --- for one thing,
it doesn't seem to offer any way of detecting failure to acquire the
lock. (RelationSetLockForWrite calls MultiLockReln, which *does*
return a boolean, but RelationSetLockForWrite ignores it...) Also,
it's not at all clear whether I should call RelationUnsetLockForWrite
at the end of the routine or not; some existing code does, some doesn't.

I'm concerned because interlocking of the specialized NOTIFY-related
statements seems to work fine, but they seem not to be interlocked
against user operations on the pg_listener table.

For example, this works as I'd expect:

psql#1 psql#2

begin;
listen z;

notify z;
(hangs up until #1 commits)

end;

because "listen" acquires a write lock on the pg_listener table, which
the notify has to wait for.

But this doesn't work as I'd expect:

psql#1 psql#2

begin;
select * from pg_listener;

notify z;
(completes immediately)

end;

Seems to me the "select" should acquire a read lock that would prevent
the #2 backend from writing pg_listener until the end of #1's transaction.

Is there a bug here, or is there some special definition of user access
to a system table that means the select isn't acquiring a read lock?
Selects and updates on ordinary user tables seem to interlock fine...

regards, tom lane

#2Massimo Dal Zotto
dz@cs.unitn.it
In reply to: Tom Lane (#1)
Re: [HACKERS] It sorta works, but I'm confused about locking

I've got this new notify code almost working, but...

Could you plase send it to me ?

What exactly is the protocol for locking a table that you intend to
modify? The old notify code just did RelationSetLockForWrite(),
but it's not clear to me that that works correctly --- for one thing,
it doesn't seem to offer any way of detecting failure to acquire the
lock. (RelationSetLockForWrite calls MultiLockReln, which *does*
return a boolean, but RelationSetLockForWrite ignores it...) Also,
it's not at all clear whether I should call RelationUnsetLockForWrite
at the end of the routine or not; some existing code does, some doesn't.

It is not done where there is an immediate CommitTransactionCommand which
already releases the locks.

I'm concerned because interlocking of the specialized NOTIFY-related
statements seems to work fine, but they seem not to be interlocked
against user operations on the pg_listener table.

For example, this works as I'd expect:

psql#1 psql#2

begin;
listen z;

notify z;
(hangs up until #1 commits)

end;

because "listen" acquires a write lock on the pg_listener table, which
the notify has to wait for.

But this doesn't work as I'd expect:

psql#1 psql#2

begin;
select * from pg_listener;

notify z;
(completes immediately)

end;

Seems to me the "select" should acquire a read lock that would prevent
the #2 backend from writing pg_listener until the end of #1's transaction.

Is there a bug here, or is there some special definition of user access
to a system table that means the select isn't acquiring a read lock?
Selects and updates on ordinary user tables seem to interlock fine...

Very strange, it seems a bug. But users should not know about pg_listener.

--
Massimo Dal Zotto

+----------------------------------------------------------------------+
|  Massimo Dal Zotto                email:  dz@cs.unitn.it             |
|  Via Marconi, 141                 phone:  ++39-461-534251            |
|  38057 Pergine Valsugana (TN)     www:  http://www.cs.unitn.it/~dz/  |
|  Italy                            pgp:  finger dz@tango.cs.unitn.it  |
+----------------------------------------------------------------------+
#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Massimo Dal Zotto (#2)
Re: [HACKERS] It sorta works, but I'm confused about locking

Massimo Dal Zotto <dz@cs.unitn.it> writes:

it's not at all clear whether I should call RelationUnsetLockForWrite
at the end of the routine or not; some existing code does, some doesn't.

It is not done where there is an immediate CommitTransactionCommand which
already releases the locks.

Hmm. OK, but I guess I don't really understand why it's ever valid to
release a lock before the end of the transaction --- and
CommitTransactionCommand isn't necessarily the end of the transaction
anyway, if you're inside a transaction block.

Basically: it seems to me it's a bug to call UnsetLock (any flavor)
from *anywhere* except transaction commit. If this is not so, why not?

regards, tom lane

#4Bruce Momjian
maillist@candle.pha.pa.us
In reply to: Tom Lane (#1)
Re: [HACKERS] It sorta works, but I'm confused about locking

I've got this new notify code almost working, but...

What exactly is the protocol for locking a table that you intend to
modify? The old notify code just did RelationSetLockForWrite(),
but it's not clear to me that that works correctly --- for one thing,
it doesn't seem to offer any way of detecting failure to acquire the
lock. (RelationSetLockForWrite calls MultiLockReln, which *does*
return a boolean, but RelationSetLockForWrite ignores it...) Also,
it's not at all clear whether I should call RelationUnsetLockForWrite
at the end of the routine or not; some existing code does, some doesn't.

Welcome to PostgreSQL. This is the type of thing I saw when doing the
mega-patch, were people were not doing things consistently, because some
coders do not understand what is happening inside the code, and just
write something that works, sort of.

I recommend you check out what is currently done properly, and fix the
ones that are incorrect.

I can imagine some cases where you would want to get a lock and keep it
until the end of the transaction, and other times when you would want to
release it before transaction end.

I'm concerned because interlocking of the specialized NOTIFY-related
statements seems to work fine, but they seem not to be interlocked
against user operations on the pg_listener table.

For example, this works as I'd expect:

psql#1 psql#2

begin;
listen z;

notify z;
(hangs up until #1 commits)

end;

because "listen" acquires a write lock on the pg_listener table, which
the notify has to wait for.

But this doesn't work as I'd expect:

psql#1 psql#2

begin;
select * from pg_listener;

notify z;
(completes immediately)

end;

Seems to me the "select" should acquire a read lock that would prevent
the #2 backend from writing pg_listener until the end of #1's transaction.

Is there a bug here, or is there some special definition of user access
to a system table that means the select isn't acquiring a read lock?
Selects and updates on ordinary user tables seem to interlock fine...

Select certainly should be locking. Something is wrong, but I am not
sure what. If you want me to check into it, let me know.

-- 
  Bruce Momjian                        |  http://www.op.net/~candle
  maillist@candle.pha.pa.us            |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#4)
Re: [HACKERS] It sorta works, but I'm confused about locking

Bruce Momjian <maillist@candle.pha.pa.us> writes:

I recommend you check out what is currently done properly, and fix the
ones that are incorrect.

Well, yes. The question was how to tell which is which :-)

I can imagine some cases where you would want to get a lock and keep it
until the end of the transaction, and other times when you would want to
release it before transaction end.

I guess I'm not understanding something. How can it ever be correct
practice to release a lock before transaction end? For example, if I
write some changes in a table, and then release the lock, wouldn't that
allow other backends to see the not-yet-committed changes? What if I
then abort my transaction? Now the other backends have acted on
information they should never have seen at all.

Releasing a read lock strikes me as just as dangerous but for more
subtle reasons --- once you have read a table, what you have read
ought to look the same until the end of your transaction.

Since there is an unset-write-lock function, I assume it must have
valid uses, but I don't see what they are.

Is there a bug here, or is there some special definition of user access
to a system table that means the select isn't acquiring a read lock?
Selects and updates on ordinary user tables seem to interlock fine...

Select certainly should be locking. Something is wrong, but I am not
sure what. If you want me to check into it, let me know.

Please. (Note that I saw this with my revised version of async.c;
I believe you will see the same behavior with the currently-checked-in
code, but do not have the time to rebuild that version to make sure.)

regards, tom lane

#6Vadim Mikheev
vadim@krs.ru
In reply to: Tom Lane (#5)
Re: [HACKERS] It sorta works, but I'm confused about locking

Tom Lane wrote:

I can imagine some cases where you would want to get a lock and keep it
until the end of the transaction, and other times when you would want to
release it before transaction end.

I guess I'm not understanding something. How can it ever be correct
practice to release a lock before transaction end? For example, if I

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Postgres does this for system tables only.

write some changes in a table, and then release the lock, wouldn't that
allow other backends to see the not-yet-committed changes? What if I

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
No. Backend uses xmin/xmax to know is tuple visible or not.

then abort my transaction? Now the other backends have acted on
information they should never have seen at all.

Backend will see that xmin(xmax) is not committed.

Vadim

#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: Vadim Mikheev (#6)
Re: [HACKERS] It sorta works, but I'm confused about locking

Vadim Mikheev <vadim@krs.ru> writes:

Tom Lane wrote:

I guess I'm not understanding something. How can it ever be correct
practice to release a lock before transaction end? For example, if I
write some changes in a table, and then release the lock, wouldn't that
allow other backends to see the not-yet-committed changes? What if I

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
No. Backend uses xmin/xmax to know is tuple visible or not.

Ah, some light dawns. Thanks for the clue!

Postgres does this for system tables only.

OK, so what is good coding practice? Always release write lock after
operating on a system table? Or is that only good some of the time,
and if so what's the consideration?

regards, tom lane

#8Bruce Momjian
maillist@candle.pha.pa.us
In reply to: Tom Lane (#5)
Re: [HACKERS] It sorta works, but I'm confused about locking

Bruce Momjian <maillist@candle.pha.pa.us> writes:

I recommend you check out what is currently done properly, and fix the
ones that are incorrect.

Well, yes. The question was how to tell which is which :-)

I didn't say it was easy. :-)

I can imagine some cases where you would want to get a lock and keep it
until the end of the transaction, and other times when you would want to
release it before transaction end.

I guess I'm not understanding something. How can it ever be correct
practice to release a lock before transaction end? For example, if I
write some changes in a table, and then release the lock, wouldn't that
allow other backends to see the not-yet-committed changes? What if I
then abort my transaction? Now the other backends have acted on
information they should never have seen at all.

Releasing a read lock strikes me as just as dangerous but for more
subtle reasons --- once you have read a table, what you have read
ought to look the same until the end of your transaction.

Since there is an unset-write-lock function, I assume it must have
valid uses, but I don't see what they are.

Suppose you want to update a table as part of internal query processing.
You lock it, update it, and unlock it. You just need the lock while
you are updating it.

Is there a bug here, or is there some special definition of user access
to a system table that means the select isn't acquiring a read lock?
Selects and updates on ordinary user tables seem to interlock fine...

Select certainly should be locking. Something is wrong, but I am not
sure what. If you want me to check into it, let me know.

Please. (Note that I saw this with my revised version of async.c;
I believe you will see the same behavior with the currently-checked-in
code, but do not have the time to rebuild that version to make sure.)

-- 
  Bruce Momjian                        |  http://www.op.net/~candle
  maillist@candle.pha.pa.us            |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026