Re: pgsql: Fix a couple of bugs in MultiXactId freezing

From: Andres Freund <andres(at)2ndquadrant(dot)com>
To: Alvaro Herrera <alvherre(at)2ndquadrant(dot)com>
Cc: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Noah Misch <noah(at)leadboat(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: pgsql: Fix a couple of bugs in MultiXactId freezing
Date: 2013-12-10 00:53:56
Message-ID: 20131210005356.GD27840@awork2.anarazel.de
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-committers pgsql-hackers

On 2013-12-09 19:14:58 -0300, Alvaro Herrera wrote:
> Here's a revamped version of this patch. One thing I didn't do here is
> revert the exporting of CreateMultiXactId, but I don't see any way to
> avoid that.

I don't so much have a problem with exporting CreateMultiXactId(), just
with exporting it under its current name. It's already quirky to have
both MultiXactIdCreate and CreateMultiXactId() in multixact.c but
exporting it imo goes to far.

> Andres mentioned the idea of sharing some code between
> heap_prepare_freeze_tuple and heap_tuple_needs_freeze, but I haven't
> explored that.

My idea would just be to have heap_tuple_needs_freeze() call
heap_prepare_freeze_tuple() and check whether it returns true. Yes,
that's slightly more expensive than the current
heap_tuple_needs_freeze(), but it's only called when we couldn't get a
cleanup lock on a page, so that seems ok.

> ! static TransactionId
> ! FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
> ! TransactionId cutoff_xid, MultiXactId cutoff_multi,
> ! uint16 *flags)
> ! {

> ! if (!MultiXactIdIsValid(multi))
> ! {
> ! /* Ensure infomask bits are appropriately set/reset */
> ! *flags |= FRM_INVALIDATE_XMAX;
> ! return InvalidTransactionId;
> ! }

Maybe comment that we're sure to be only called when IS_MULTI is set,
which will be unset by FRM_INVALIDATE_XMAX? I wondered twice whether we
wouldn't just continually mark the buffer dirty this way.

> ! else if (MultiXactIdPrecedes(multi, cutoff_multi))
> ! {
> ! /*
> ! * This old multi cannot possibly have members still running. If it
> ! * was a locker only, it can be removed without any further
> ! * consideration; but if it contained an update, we might need to
> ! * preserve it.
> ! */
> ! if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
> ! {
> ! *flags |= FRM_INVALIDATE_XMAX;
> ! return InvalidTransactionId;

Cna you place an Assert(!MultiXactIdIsRunning(multi)) here?

> ! if (ISUPDATE_from_mxstatus(members[i].status) &&
> ! !TransactionIdDidAbort(members[i].xid))#

It makes me wary to see a DidAbort() without a previous InProgress()
call.
Also, after we crashed, doesn't DidAbort() possibly return false for
transactions that were in progress before we crashed? At least that's
how I always understood it, and how tqual.c is written.

> ! /* We only keep lockers if they are still running */
> ! if (TransactionIdIsCurrentTransactionId(members[i].xid) ||

I don't think there's a need to check for
TransactionIdIsCurrentTransactionId() - vacuum can explicitly *not* be
run inside a transaction.

> ***************
> *** 5443,5458 **** heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
> * xvac transaction succeeded.
> */
> if (tuple->t_infomask & HEAP_MOVED_OFF)
> ! HeapTupleHeaderSetXvac(tuple, InvalidTransactionId);
> else
> ! HeapTupleHeaderSetXvac(tuple, FrozenTransactionId);
>
> /*
> * Might as well fix the hint bits too; usually XMIN_COMMITTED
> * will already be set here, but there's a small chance not.
> */
> Assert(!(tuple->t_infomask & HEAP_XMIN_INVALID));
> ! tuple->t_infomask |= HEAP_XMIN_COMMITTED;
> changed = true;
> }
> }
> --- 5571,5586 ----
> * xvac transaction succeeded.
> */
> if (tuple->t_infomask & HEAP_MOVED_OFF)
> ! frz->frzflags |= XLH_FREEZE_XVAC;
> else
> ! frz->frzflags |= XLH_INVALID_XVAC;
>

Hm. Isn't this case inverted? I.e. shouldn't you set XLH_FREEZE_XVAC and
XLH_INVALID_XVAC exactly the other way round? I really don't understand
the moved in/off, since the code has been gone longer than I've followed
the code...

*** a/src/backend/access/rmgrdesc/mxactdesc.c
> --- b/src/backend/access/rmgrdesc/mxactdesc.c
> ***************
> *** 41,47 **** out_member(StringInfo buf, MultiXactMember *member)
> appendStringInfoString(buf, "(upd) ");
> break;
> default:
> ! appendStringInfoString(buf, "(unk) ");
> break;
> }
> }
> --- 41,47 ----
> appendStringInfoString(buf, "(upd) ");
> break;
> default:
> ! appendStringInfo(buf, "(unk) ", member->status);
> break;
> }
> }

That change doesn't look like it will do anything?

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

In response to

Responses

Browse pgsql-committers by date

  From Date Subject
Next Message Noah Misch 2013-12-10 14:36:30 pgsql: Rename TABLE() to ROWS FROM().
Previous Message Alvaro Herrera 2013-12-09 22:14:58 Re: pgsql: Fix a couple of bugs in MultiXactId freezing

Browse pgsql-hackers by date

  From Date Subject
Next Message Craig Ringer 2013-12-10 01:00:40 Re: ANALYZE sampling is too good
Previous Message Craig Ringer 2013-12-10 00:53:49 Re: Re: [RFC] Shouldn't we remove annoying FATAL messages from server log?