Thread: Foreign Key Deadlocking

Foreign Key Deadlocking

From
"Steven Flatt"
Date:
Hi, we're using Postgres 8.1.4.
 
We've been seeing deadlock errors of this form, sometimes as often as several times per hour:
 
Apr 17 13:39:50 postgres[53643]: [4-1] ERROR:  deadlock detected
Apr 17 13:39:50 postgres[53643]: [4-2] DETAIL:  Process 53643 waits for ShareLock on transaction 111283280; blocked by process 53447.
Apr 17 13:39:50 postgres[53643]: [4-3]     Process 53447 waits for ShareLock on transaction 111282124; blocked by process 53242.
Apr 17 13:39:50 postgres[53643]: [4-4]     Process 53242 waits for ShareLock on transaction 111282970; blocked by process 53240.
Apr 17 13:39:50 postgres[53643]: [4-5]     Process 53240 waits for ShareLock on transaction 111282935; blocked by process 53168.
Apr 17 13:39:50 postgres[53643]: [4-6]     Process 53168 waits for ShareLock on transaction 111282707; blocked by process 53643.
 
The deadlocks almost always seem to involve 4 or 5 processes.
 
After observing the behaviour of the locks table, and searching the newsgroup and elsewhere, I'm fairly certain I know what the problem is.  There is extremely high update activity by a dozen or more processes on a table which has FK references into two other tables.  Each process may update 10s or 100s of rows and there is really no predictable access pattern.
 
This blurb is from a previous discussion I found:
-----
postgres performs a lock (share lock) on the tuples to which the foreign keys point, apparently to prevent other transactions from modifying the foreign key before this transaction commits. it is practically impossible to cause the references to be always in the same order, so a deadlock can occur.
-----
 
I also see claims that this problem is fixed in 8.2, and if the fix is what I think it is, it's also in 8.1.6.
 
Release 8.1.6
Changes
* Fix bug causing needless deadlock errors on row-level locks (Tom)
 
Upgrading to 8.2 is not realistic at this point of our project cycle, but if the fix is indeed in 8.1.6, I can push to upgrade to 8.1.latest.
 
Can someone confirm that I've identified the right fix?
 
Thanks,
Steve
 

Re: Foreign Key Deadlocking

From
Csaba Nagy
Date:
> Can someone confirm that I've identified the right fix?

I'm pretty sure that won't help you... see:

http://archives.postgresql.org/pgsql-general/2006-12/msg00029.php

The deadlock will be there if you update/insert the child table and
update/insert the parent table in the same transaction (even if you
update some other field on the parent table than the key referenced by
the child table). If your transactions always update/insert only one of
those tables, it won't deadlock (assuming you order the inserts/updates
properly per PK).

Cheers,
Csaba.



Re: Foreign Key Deadlocking

From
Tom Lane
Date:
"Steven Flatt" <steven.flatt@gmail.com> writes:
> Hi, we're using Postgres 8.1.4.
> We've been seeing deadlock errors of this form, sometimes as often as
> several times per hour:
> ...
> I also see claims that this problem is fixed in 8.2, and if the fix is what
> I think it is, it's also in 8.1.6.
> Can someone confirm that I've identified the right fix?

Hard to say without a lot more data, but as a general rule you should be
on 8.1.latest in any case.  See
http://www.postgresql.org/support/versioning
You might also find ammunition in the release notes:
http://developer.postgresql.org/pgdocs/postgres/release.html

            regards, tom lane

Re: Foreign Key Deadlocking

From
"Steven Flatt"
Date:
Thanks for your answers and feedback.
 
All things considered, it is easiest (and acceptable) in this case to remove RI between the tables where the deadlocks were occurring.
 
We are still looking to upgrade to 8.1.latest but that is another matter...
 
Steve
 

Re: Foreign Key Deadlocking

From
Dave Cramer
Date:
Hi Csaba,

I have a similar problem.

In an attempt to avoid the overhead of select count(*) from mailbox
where uid = somuid I've implemented triggers on insert and delete.

So there is a

user table which refers to to an inbox table,

so when people insert into the inbox there is an RI trigger grabbing
the shared lock, then the count triggers try to grab an exclusive
lock resulting in a deadlock.

Can we safely remove the shared locks ?

Is there a right way to implement the count triggers. I've tried
before triggers, and after triggers, both result in different kinds
of deadlocks.

Dave
On 18-Apr-07, at 11:36 AM, Csaba Nagy wrote:

>> Can someone confirm that I've identified the right fix?
>
> I'm pretty sure that won't help you... see:
>
> http://archives.postgresql.org/pgsql-general/2006-12/msg00029.php
>
> The deadlock will be there if you update/insert the child table and
> update/insert the parent table in the same transaction (even if you
> update some other field on the parent table than the key referenced by
> the child table). If your transactions always update/insert only
> one of
> those tables, it won't deadlock (assuming you order the inserts/
> updates
> properly per PK).
>
> Cheers,
> Csaba.
>
>
>
> ---------------------------(end of
> broadcast)---------------------------
> TIP 5: don't forget to increase your free space map settings


Re: Foreign Key Deadlocking

From
Erik Jones
Date:
On Apr 19, 2007, at 9:00 AM, Dave Cramer wrote:
>
> On 18-Apr-07, at 11:36 AM, Csaba Nagy wrote:
>
>>> Can someone confirm that I've identified the right fix?
>>
>> I'm pretty sure that won't help you... see:
>>
>> http://archives.postgresql.org/pgsql-general/2006-12/msg00029.php
>>
>> The deadlock will be there if you update/insert the child table and
>> update/insert the parent table in the same transaction (even if you
>> update some other field on the parent table than the key
>> referenced by
>> the child table). If your transactions always update/insert only
>> one of
>> those tables, it won't deadlock (assuming you order the inserts/
>> updates
>> properly per PK).
>>
>> Cheers,
>> Csaba.

> Hi Csaba,
>
> I have a similar problem.
>
> In an attempt to avoid the overhead of select count(*) from mailbox
> where uid = somuid I've implemented triggers on insert and delete.
>
> So there is a
>
> user table which refers to to an inbox table,
>
> so when people insert into the inbox there is an RI trigger
> grabbing the shared lock, then the count triggers try to grab an
> exclusive lock resulting in a deadlock.
>
> Can we safely remove the shared locks ?
>
> Is there a right way to implement the count triggers. I've tried
> before triggers, and after triggers, both result in different kinds
> of deadlocks.
>
> Dave

The ways I've done this in the past is to have the count triggers
make inserts into some interim table rather than try to update the
actual count field and have another process that continually sweeps
what's in the interim table and makes aggregated updates to the count
table.  Even if there isn't much to aggregate on any given sweep,
this gives you a sequential pattern as your inserts/deletes on the
main table don't depend on any locking in another table (well,
strictly speaking, your inserts into the interim table would be
blocked by any exclusive locks on it but you shouldn't need to ever
do that anyway).


erik jones <erik@myemma.com>
software developer
615-296-0838
emma(r)




Re: Foreign Key Deadlocking

From
Alvaro Herrera
Date:
Dave Cramer escribió:
> Hi Csaba,
>
> I have a similar problem.
>
> In an attempt to avoid the overhead of select count(*) from mailbox
> where uid = somuid I've implemented triggers on insert and delete.
>
> So there is a
>
> user table which refers to to an inbox table,
>
> so when people insert into the inbox there is an RI trigger grabbing
> the shared lock, then the count triggers try to grab an exclusive
> lock resulting in a deadlock.
>
> Can we safely remove the shared locks ?
>
> Is there a right way to implement the count triggers. I've tried
> before triggers, and after triggers, both result in different kinds
> of deadlocks.

Would it be possible for the triggers to lock the records, before
starting the actual operation, in well known orders, to avoid the
deadlocks?

A frequently mentioned approach to avoid the point of contention is to
have a "totals" record and have the triggers insert "deltas" records; to
get the sum, add them all.  Periodically, take the deltas and apply them
to the totals.

--
Alvaro Herrera                                http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Re: Foreign Key Deadlocking

From
Csaba Nagy
Date:
> A frequently mentioned approach to avoid the point of contention is to
> have a "totals" record and have the triggers insert "deltas" records; to
> get the sum, add them all.  Periodically, take the deltas and apply them
> to the totals.

This is what we do here too. There is only one exception to this rule,
in one case we actually need to have the inserted records and the
updated parent in one transaction for data consistency, in that case the
delta approach won't work... we didn't find any other solution to that
except patching postgres not to lock the parent keys at all, which has
it's own problems too (occasional breakage of the foreign key
relationship when the parent is deleted and a child still slips in, but
this is very rare in our case not to cause problems which cannot be
cleaned up with relative ease - not to mention that there could be other
problems we didn't discover yet or our usage patterns are avoiding).

Cheers,
Csaba.