Thread: deadlock on the same relation

deadlock on the same relation

From
"Francesco Formenti - TVBLOB S.r.l."
Date:
Hi all,
I have a problem about deadlock. I have several stored procedures; only
one of them uses ACCESS EXCLUSIVE LOCK on a table; however, all the
stored procedures can access to that table, using SELECT, INSERT or UPDATE.
The stored procedures are called by different processes of an external
application.

In a non-predictable way, I obtain error messages like this one:

2005-11-29 18:23:06 [12771] ERROR:  deadlock detected
DETAIL:  Process 12771 waits for AccessExclusiveLock on relation 26052
of database 17142; blocked by process 12773.
        Process 12773 waits for AccessExclusiveLock on relation 26052 of
database 17142; blocked by process 12771.
CONTEXT:  PL/pgSQL function "set_session_box_status" line 7 at SQL statement

I don't understand what's happening; is it possible that a deadlock is
detected on the same relation (that is, the stored procedure using the
ACCESS EXCLUSIVE LOCK mentioned before) ?

I'm using Postgres 7.4.7 on a Debian Sarge.

Thanks!
Francesco


--

Francesco Formenti - TVBLOB S.r.l.
Software Engineer

 Via G. Paisiello, 9 20131 Milano, Italia
 -----------------------------------------
 Phone +39 02 36562440
 Fax +39 02 20408347
 Web Site http://www.tvblob.com
 E-mail francesco.formenti@tvblob.com



Re: deadlock on the same relation

From
Tom Lane
Date:
"Francesco Formenti - TVBLOB S.r.l." <francesco.formenti@tvblob.com> writes:
> I have a problem about deadlock. I have several stored procedures; only
> one of them uses ACCESS EXCLUSIVE LOCK on a table; however, all the
> stored procedures can access to that table, using SELECT, INSERT or UPDATE.
> The stored procedures are called by different processes of an external
> application.

> In a non-predictable way, I obtain error messages like this one:

> 2005-11-29 18:23:06 [12771] ERROR:  deadlock detected
> DETAIL:  Process 12771 waits for AccessExclusiveLock on relation 26052
> of database 17142; blocked by process 12773.
>         Process 12773 waits for AccessExclusiveLock on relation 26052 of
> database 17142; blocked by process 12771.
> CONTEXT:  PL/pgSQL function "set_session_box_status" line 7 at SQL statement

Probably you have been careless about avoiding "lock upgrade"
situations.  If you are going to take an exclusive lock on a relation,
it is dangerous to already hold a non-exclusive lock on the same
relation, because that prevents anyone else from getting an exclusive
lock; thus if another process is doing the exact same thing you are in
a deadlock situation.

Since SELECT/INSERT/UPDATE take non-exclusive locks, you can't do one of
those and later ask for exclusive lock within the same transaction.
The general rule is "get the strongest lock you will need first".

            regards, tom lane

Re: deadlock on the same relation

From
"Jim C. Nasby"
Date:
On Fri, Dec 02, 2005 at 10:15:04AM -0500, Tom Lane wrote:
> "Francesco Formenti - TVBLOB S.r.l." <francesco.formenti@tvblob.com> writes:
> > I have a problem about deadlock. I have several stored procedures; only
> > one of them uses ACCESS EXCLUSIVE LOCK on a table; however, all the
> > stored procedures can access to that table, using SELECT, INSERT or UPDATE.
> > The stored procedures are called by different processes of an external
> > application.
>
> > In a non-predictable way, I obtain error messages like this one:
>
> > 2005-11-29 18:23:06 [12771] ERROR:  deadlock detected
> > DETAIL:  Process 12771 waits for AccessExclusiveLock on relation 26052
> > of database 17142; blocked by process 12773.
> >         Process 12773 waits for AccessExclusiveLock on relation 26052 of
> > database 17142; blocked by process 12771.
> > CONTEXT:  PL/pgSQL function "set_session_box_status" line 7 at SQL statement
>
> Probably you have been careless about avoiding "lock upgrade"
> situations.  If you are going to take an exclusive lock on a relation,
> it is dangerous to already hold a non-exclusive lock on the same
> relation, because that prevents anyone else from getting an exclusive
> lock; thus if another process is doing the exact same thing you are in
> a deadlock situation.
>
> Since SELECT/INSERT/UPDATE take non-exclusive locks, you can't do one of
> those and later ask for exclusive lock within the same transaction.
> The general rule is "get the strongest lock you will need first".

And better yet, don't grab an exclusive lock...

I'm curious; what are you doing that requires one?
--
Jim C. Nasby, Sr. Engineering Consultant      jnasby@pervasive.com
Pervasive Software      http://pervasive.com    work: 512-231-6117
vcard: http://jim.nasby.net/pervasive.vcf       cell: 512-569-9461

Re: deadlock on the same relation

From
"Jim C. Nasby"
Date:
On Mon, Jan 16, 2006 at 11:57:35AM +0100, Francesco Formenti - TVBLOB S.r.l. wrote:
> Jim C. Nasby wrote:
>
> >On Thu, Jan 12, 2006 at 06:11:32PM +0100, Francesco Formenti - TVBLOB
> >S.r.l. wrote:
> >
> >
> >>I put an ACCESS EXCLUSIVE LOCK on the table of objects at the beginning
> >>of the stored procedure, just to ensure that only one process at a time
> >>can check and set the status of the object, so the status is always
> >>consistent. Is there another way to do that?
> >>
> >>
> >
> >Take a look at SELECT ... FOR UPDATE. It will allow you to block access
> >to a single object without locking the entire table.
> >
> >
>
>
> OK!, thank you. Based on your precious suggestions and reading some more
> documentation, I think I've found the proper way to resolve the problem
> without affecting the performance.
>
> Given an object that can be in status A, B or C, and given the map of
> allowed transitions, such as:
> A--->B
> A--->C
> B--->C
> I can resolve the concurrency problem using a technique based on both
> optimistic lock and Postgres'MVCC strategy.
> Setting the new status, the function set_status(.) will check for both
> starting and ending status, so it will look like:
>
> UPDATE set status=newstatus WHERE (obj_id= $1 AND curr_status= $2)
>
> The MVCC model checks if the WHERE clause is valid before applying the
> changes; since the check is made for object id and for the current
> status, if the status has changed due to the commit of a concurrent
> transaction, the update will fail, and the object status will remain
> consistent.
> I think this should work, I didn't tried it iet. Of course, the proper
> WHERE clause could be placed in the stored procedure, maybe reproducing
> the finite state machine of the object (e.g., if I want to put the
> object in status C, I will check WHERE obj_id= $1 AND curr_status <>
> new_status, rather than curr_status = $2).

Yes, simply checking for a valid current status will allow this to work.
You could also put the state transition information into a check
constraint to enforce the state machine. At a former job I actually had
the states named (in an external table), and did the check with a
trigger that read the valid transition information from another table.

> So, I will not use explicit locks here. However, I'm still curious about
> the model of locking, because I don't understand the reason of the
> deadlock on the same relation. How does it works under the hood? I
> assumed a model like non-recursive mutex locking: I lock the mutex
> accessing to a protected area, and other processes call acquire(.) and
> wait for the mutex to be freed; so, there's a queue. As a test, I tried
> to put an access exclusive lock at the beginning of all my stored
> procedures, and to increase the deadlock_timeout, trying to enlarge the
> waiting buffer of the queue, but the result has been awful! There were
> deadlock on ALL of the stored procedures! :-(
> Is the queue model valid for locks? Or something like try_acquire(.)
> rather than acquire(.) happens, so, if the mutex is locked, an error is
> raised and no queue is made?

I'm not a C coder, so I can't help you there. What I can tell you is
that anytime you're updating multiple things in the same transaction
you're at risk for a deadlock. In your case it was probably due to
either updating multiple rows in the same table in a different order
(ie: transaction 1 updates ID 1, 3, 5 while transaction 2 updates 5, 7,
1 - risk of deadlock), or there were triggers involved that could
operate against other objects (remember that RI is done as triggers).
--
Jim C. Nasby, Database Architect                   jim@nasby.net
512.569.9461 (cell)                         http://jim.nasby.net