Thread: Logging conflicted queries on deadlocks

Logging conflicted queries on deadlocks

From
ITAGAKI Takahiro
Date:
Here is a patch to log conflicted queries on deadlocks. Queries are dumped
at CONTEXT in the same sorting order as DETAIL messages. Those queries are
picked from pg_stat_get_backend_activity, as same as pg_stat_activity,
so that users cannot see other user's queries. (It might be better to log
all queries in the server log and mask them in the client response, but
I'm not sure how to do it...)

| ERROR:  deadlock detected
| DETAIL:  Process 3088 waits for ShareLock on transaction 608; blocked by process 2928.
|         Process 2928 waits for ShareLock on transaction 609; blocked by process 2824.
|         Process 2824 waits for ShareLock on transaction 610; blocked by process 3088.
| CONTEXT:  Process 3088: UPDATE test SET i = i WHERE i = 1;
|         Process 2928: <insufficient privilege>
|         Process 2824: UPDATE test SET i = i WHERE i = 3;
| STATEMENT:  UPDATE test SET i = i WHERE i = 1;


Alvaro Herrera <alvherre@commandprompt.com> wrote:

> Perhaps it could be shown in CONTEXT, like so:
>
> I think it's useful to show the PID of each statement, for the case
> where there are more than two processes deadlocked.

Thanks for response. I bought your suggestion :-)

Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center


Attachment

Re: Logging conflicted queries on deadlocks

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

---------------------------------------------------------------------------


ITAGAKI Takahiro wrote:
>
> Here is a patch to log conflicted queries on deadlocks. Queries are dumped
> at CONTEXT in the same sorting order as DETAIL messages. Those queries are
> picked from pg_stat_get_backend_activity, as same as pg_stat_activity,
> so that users cannot see other user's queries. (It might be better to log
> all queries in the server log and mask them in the client response, but
> I'm not sure how to do it...)
>
> | ERROR:  deadlock detected
> | DETAIL:  Process 3088 waits for ShareLock on transaction 608; blocked by process 2928.
> |         Process 2928 waits for ShareLock on transaction 609; blocked by process 2824.
> |         Process 2824 waits for ShareLock on transaction 610; blocked by process 3088.
> | CONTEXT:  Process 3088: UPDATE test SET i = i WHERE i = 1;
> |         Process 2928: <insufficient privilege>
> |         Process 2824: UPDATE test SET i = i WHERE i = 3;
> | STATEMENT:  UPDATE test SET i = i WHERE i = 1;
>
>
> Alvaro Herrera <alvherre@commandprompt.com> wrote:
>
> > Perhaps it could be shown in CONTEXT, like so:
> >
> > I think it's useful to show the PID of each statement, for the case
> > where there are more than two processes deadlocked.
>
> Thanks for response. I bought your suggestion :-)
>
> Regards,
> ---
> ITAGAKI Takahiro
> NTT Open Source Software Center
>

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://postgres.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: Logging conflicted queries on deadlocks

From
Tom Lane
Date:
ITAGAKI Takahiro <itagaki.takahiro@oss.ntt.co.jp> writes:
> Here is a patch to log conflicted queries on deadlocks. Queries are dumped
> at CONTEXT in the same sorting order as DETAIL messages. Those queries are
> picked from pg_stat_get_backend_activity, as same as pg_stat_activity,
> so that users cannot see other user's queries.

Applied with revisions.  It's a cute idea --- I first thought it
couldn't work reliably because of race conditions, but actually we
haven't aborted our own transaction at this point, so everyone else
involved in the deadlock is still waiting and it should be safe to
grab their activity strings.

However there was still a big implementation problem, which is that
looking at pg_stat_activity could deliver very stale results, not only
about other backends' queries but even our own.  The data for that
comes from a snapshot that might have been taken much earlier in our
transaction.  I replaced the code you were using with a new pgstat.c
entry point that delivers up-to-date info directly from the shared
memory array.

> (It might be better to log all queries in the server log and mask them
> in the client response, but I'm not sure how to do it...)

Yeah, that would be cute, but we don't have any way to deliver a
different CONTEXT string to the client than we do to the log.  We could
generate duplicate messages that go only to the log but that seemed
pretty ugly.  In practice this definition is probably good enough.

One thing that I worried about for a little bit is that you can imagine
privilege-escalation scenarios.  Suppose that the user is allowed to
call some SECURITY DEFINER function that runs as superuser, and a
deadlock occurs inside that.  As the patch stands, every query involved
in the deadlock will be reported, which might be undesirable.  We could
make the test use the outermost session user's ID instead of current
ID, but that might only move the security risks someplace else.
Thoughts?

            regards, tom lane

Re: Logging conflicted queries on deadlocks

From
Gregory Stark
Date:
"Tom Lane" <tgl@sss.pgh.pa.us> writes:

> ITAGAKI Takahiro <itagaki.takahiro@oss.ntt.co.jp> writes:
>> Here is a patch to log conflicted queries on deadlocks. Queries are dumped
>> at CONTEXT in the same sorting order as DETAIL messages. Those queries are
>> picked from pg_stat_get_backend_activity, as same as pg_stat_activity,
>> so that users cannot see other user's queries.
>
> Applied with revisions.  It's a cute idea --- I first thought it
> couldn't work reliably because of race conditions, but actually we
> haven't aborted our own transaction at this point, so everyone else
> involved in the deadlock is still waiting and it should be safe to
> grab their activity strings.

There's no way the other transaction's timeout could fire while we're doing
this is there? Are we still holding the lw locks at this point which would
prevent that?

> One thing that I worried about for a little bit is that you can imagine
> privilege-escalation scenarios.  Suppose that the user is allowed to
> call some SECURITY DEFINER function that runs as superuser, and a
> deadlock occurs inside that.  As the patch stands, every query involved
> in the deadlock will be reported, which might be undesirable.  We could
> make the test use the outermost session user's ID instead of current
> ID, but that might only move the security risks someplace else.
> Thoughts?

Perhaps we should only do this if the current user's ID is the same as the
outermost session user's ID?

--
  Gregory Stark
  EnterpriseDB          http://www.enterprisedb.com
  Ask me about EnterpriseDB's 24x7 Postgres support!

Re: Logging conflicted queries on deadlocks

From
Gregory Stark
Date:
"Gregory Stark" <stark@enterprisedb.com> writes:

> There's no way the other transaction's timeout could fire while we're doing
> this is there? Are we still holding the lw locks at this point which would
> prevent that?

Ah, reading the patch I see comments indicating that yes that's possible and
no, we don't really care. So, ok. If the backend disappears entirely could the
string be empty? Perhaps it would be safer to copy out st_activity inside the
loop checking st_changecount?

It is a really nice feature though. Note that there was an unrelated demand
for just this info on one of the other lists just today. Thanks very much
Itagaki-san!

--
  Gregory Stark
  EnterpriseDB          http://www.enterprisedb.com
  Ask me about EnterpriseDB's On-Demand Production Tuning

Re: Logging conflicted queries on deadlocks

From
Tom Lane
Date:
Gregory Stark <stark@enterprisedb.com> writes:
> Ah, reading the patch I see comments indicating that yes that's possible and
> no, we don't really care. So, ok. If the backend disappears entirely could the
> string be empty?

Right, we'd be pointing at a BackendStatusArray entry that was now
unused, or even perhaps recycled by a new session.  That memory doesn't
move, so we don't need to worry about picking up something that's not
a status string at all, but worst case it could be not the string we
want.  I think the odds are pretty low though.

 Perhaps it would be safer to copy out st_activity inside the
> loop checking st_changecount?

Don't think it would really help any --- the other backend could've
aborted and changed its status string before we ever get to this code at
all.

> It is a really nice feature though. Note that there was an unrelated demand
> for just this info on one of the other lists just today. Thanks very much
> Itagaki-san!

That was my feeling --- the usefulness is high enough that a small
probability of a wrong display is a small price to pay.

            regards, tom lane

Re: Logging conflicted queries on deadlocks

From
Tom Lane
Date:
Gregory Stark <stark@enterprisedb.com> writes:
> "Tom Lane" <tgl@sss.pgh.pa.us> writes:
>> One thing that I worried about for a little bit is that you can imagine
>> privilege-escalation scenarios.

> Perhaps we should only do this if the current user's ID is the same as the
> outermost session user's ID?

A conservative approach would be to report the query texts *only* in the
server log and never to the client --- this would need a bit of klugery
but seems doable.

            regards, tom lane

Re: Logging conflicted queries on deadlocks

From
Tom Lane
Date:
I wrote:
>>>> One thing that I worried about for a little bit is that you can imagine
>>>> privilege-escalation scenarios.

> A conservative approach would be to report the query texts *only* in the
> server log and never to the client --- this would need a bit of klugery
> but seems doable.

Anybody have any opinions about changing this or sticking with the
behavior as-submitted?  It doesn't seem like an open-and-shut issue
to me.

If we report the query texts only to the server log, we could remove all
restrictions on which users' queries would be reported.  That would
clearly be helpful in some cases.  On the other hand, it would clearly
be less convenient to use than the existing approach that sends
information to the client.  I'm not real excited about adding still
another wart to ereport() to make this possible, either.

Comments welcome.

            regards, tom lane

Re: Logging conflicted queries on deadlocks

From
Alvaro Herrera
Date:
Tom Lane wrote:

> If we report the query texts only to the server log, we could remove all
> restrictions on which users' queries would be reported.  That would
> clearly be helpful in some cases.  On the other hand, it would clearly
> be less convenient to use than the existing approach that sends
> information to the client.  I'm not real excited about adding still
> another wart to ereport() to make this possible, either.

I think we should report to the client where it is not a security
breach, and to the server log otherwise.

I'm not sure about warts.  A separate server-only context field perhaps?
Or a separate flag in ErrorData elements saying whether each particular
one should go to server only.

If we add that, perhaps we could clean up the stuff in
checkSharedDependency with it, too.

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

Re: Logging conflicted queries on deadlocks

From
Tom Lane
Date:
Alvaro Herrera <alvherre@commandprompt.com> writes:
> I think we should report to the client where it is not a security
> breach, and to the server log otherwise.

Well, the point of my original comment is that it's not always so
obvious what might be a security breach or not.

> I'm not sure about warts.  A separate server-only context field perhaps?
> Or a separate flag in ErrorData elements saying whether each particular
> one should go to server only.

Yeah, one or the other.  I was actually thinking about a server-only
addition to the detail field, because using CONTEXT for this doesn't
really seem like quite the right thing --- if the deadlock occurs inside
a function or some other place where lines will get appended to CONTEXT,
it could be confusing.  If we're going to add features to elog.c to
support this specific case, what I'd rather have is

DETAIL: Process A waits for ...
Process B waits for ...
Process A: <query text>
Process B: <query text>

where the second group of lines only appears in the server log.

> If we add that, perhaps we could clean up the stuff in
> checkSharedDependency with it, too.

Hmm, hadn't realized there were other use-cases already.  That makes me
a little more willing to spend effort here.  Not sure that the above
design quite works for pg_shdepend though.  Do we need two independent
strings for client and log details?

            regards, tom lane

Re: Logging conflicted queries on deadlocks

From
"Guillaume Smet"
Date:
On Sun, Mar 23, 2008 at 3:21 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I wrote:
> > A conservative approach would be to report the query texts *only* in the
>  > server log and never to the client --- this would need a bit of klugery
>  > but seems doable.
>
>  Anybody have any opinions about changing this or sticking with the
>  behavior as-submitted?  It doesn't seem like an open-and-shut issue
>  to me.

From my experience, client apps usually don't report the information
about deadlock in a useful way (and they often don't report anything
at all except a standard error message). We usually dig into the
server log to find the information (and it's often useless because we
(currently) don't have information about the queries involved so we
log all queries for a while to have more debugging information).

So my point is that having all the information we can collect in the
server log is essential. I don't think the client app can do anything
useful with the query texts: they usually hide this sort of technical
details.

--
Guillaume

Re: Logging conflicted queries on deadlocks

From
Mario Weilguni
Date:
Tom Lane schrieb:
> ITAGAKI Takahiro <itagaki.takahiro@oss.ntt.co.jp> writes:
>
>> Here is a patch to log conflicted queries on deadlocks. Queries are dumped
>> at CONTEXT in the same sorting order as DETAIL messages. Those queries are
>> picked from pg_stat_get_backend_activity, as same as pg_stat_activity,
>> so that users cannot see other user's queries.
>>
>
> Applied with revisions.  It's a cute idea --- I first thought it
> couldn't work reliably because of race conditions, but actually we
> haven't aborted our own transaction at this point, so everyone else
> involved in the deadlock is still waiting and it should be safe to
> grab their activity strings.
>
> However there was still a big implementation problem, which is that
> looking at pg_stat_activity could deliver very stale results, not only
> about other backends' queries but even our own.  The data for that
> comes from a snapshot that might have been taken much earlier in our
> transaction.  I replaced the code you were using with a new pgstat.c
> entry point that delivers up-to-date info directly from the shared
> memory array.
>

Is there a chance that I might get this patch running in 8.1 and 8.2
releases? I modified the original patch to compile with 8.2.7, but it
did not work:
ERROR:  deadlock detected
DETAIL:  Process 26289 waits for ShareLock on transaction 57039190;
blocked by process 26277.
Process 26277 waits for ShareLock on transaction 57039191; blocked by
process 26289.
CONTEXT:  Process 26289: <backend information not available>
Process 26277: <backend information not available>