Re: Usage of epoch in txid_current

Поиск
Список
Период
Сортировка
От Andres Freund
Тема Re: Usage of epoch in txid_current
Дата
Msg-id 20190204074126.cqhjsgdtaafyrfaa@alap3.anarazel.de
обсуждение исходный текст
Ответ на Re: Usage of epoch in txid_current  (Thomas Munro <thomas.munro@enterprisedb.com>)
Ответы Re: Usage of epoch in txid_current  (Thomas Munro <thomas.munro@gmail.com>)
Список pgsql-hackers
Hi,

On 2018-09-19 13:58:36 +1200, Thomas Munro wrote:

> +/*
> + * Advance nextFullXid to the value after a given xid.  The epoch is inferred.
> + * If lock_free_check is true, then the caller must be sure that it's safe to
> + * read nextFullXid without holding XidGenLock (ie during recovery).
> + */
> +void
> +AdvanceNextFullTransactionIdPastXid(TransactionId xid, bool lock_free_check)
> +{
> +    TransactionId current_xid;
> +    uint32 epoch;
> +
> +    if (lock_free_check &&
> +        !TransactionIdFollowsOrEquals(xid,
> +                                      XidFromFullTransactionId(ShmemVariableCache->nextFullXid)))
> +        return;
> +
> +    LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
> +    current_xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
> +    if (TransactionIdFollowsOrEquals(xid, current_xid))
> +    {
> +        epoch = EpochFromFullTransactionId(ShmemVariableCache->nextFullXid);
> +        if (xid < current_xid)
> +            ++epoch; /* epoch wrapped */
> +        ShmemVariableCache->nextFullXid = MakeFullTransactionId(epoch, xid);
> +        FullTransactionIdAdvance(&ShmemVariableCache->nextFullXid);
> +    }
> +    LWLockRelease(XidGenLock);
>  }

Is this really a good idea? Seems like it's going to perpetuate the
problematic epoch logic we had before?

> --- a/src/bin/pg_resetwal/pg_resetwal.c
> +++ b/src/bin/pg_resetwal/pg_resetwal.c
> @@ -431,11 +431,15 @@ main(int argc, char *argv[])
>       * if any, includes these values.)
>       */
>      if (set_xid_epoch != -1)
> -        ControlFile.checkPointCopy.nextXidEpoch = set_xid_epoch;
> +        ControlFile.checkPointCopy.nextFullXid =
> +            MakeFullTransactionId(set_xid_epoch,
> +                                  XidFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid));
>  
>      if (set_xid != 0)
>      {
> -        ControlFile.checkPointCopy.nextXid = set_xid;
> +        ControlFile.checkPointCopy.nextFullXid =
> +            MakeFullTransactionId(EpochFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid),
> +                                  set_xid);
>  

>          /*
>           * For the moment, just set oldestXid to a value that will force
> @@ -705,8 +709,7 @@ GuessControlValues(void)
>      ControlFile.checkPointCopy.ThisTimeLineID = 1;
>      ControlFile.checkPointCopy.PrevTimeLineID = 1;
>      ControlFile.checkPointCopy.fullPageWrites = false;
> -    ControlFile.checkPointCopy.nextXidEpoch = 0;
> -    ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId;
> +    ControlFile.checkPointCopy.nextFullXid = MakeFullTransactionId(0, FirstNormalTransactionId);
>      ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
>      ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
>      ControlFile.checkPointCopy.nextMultiOffset = 0;
> @@ -786,8 +789,8 @@ PrintControlValues(bool guessed)
>      printf(_("Latest checkpoint's full_page_writes: %s\n"),
>             ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
>      printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
> -           ControlFile.checkPointCopy.nextXidEpoch,
> -           ControlFile.checkPointCopy.nextXid);
> +           EpochFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid),
> +           XidFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid));
>      printf(_("Latest checkpoint's NextOID:          %u\n"),
>             ControlFile.checkPointCopy.nextOid);
>      printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
> @@ -879,7 +882,7 @@ PrintNewControlValues(void)
>      if (set_xid != 0)
>      {
>          printf(_("NextXID:                              %u\n"),
> -               ControlFile.checkPointCopy.nextXid);
> +               XidFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid));
>          printf(_("OldestXID:                            %u\n"),
>                 ControlFile.checkPointCopy.oldestXid);
>          printf(_("OldestXID's DB:                       %u\n"),
> @@ -889,7 +892,7 @@ PrintNewControlValues(void)
>      if (set_xid_epoch != -1)
>      {
>          printf(_("NextXID epoch:                        %u\n"),
> -               ControlFile.checkPointCopy.nextXidEpoch);
> +               EpochFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid));
>      }

I still think it's a mistake to not display the full xid here, and
rather perpetuate the epoch stuff. I'm ok with splitting that into a
separate commit, but this ought to be fixed in the same release imo.


> diff --git a/src/include/access/transam.h b/src/include/access/transam.h
> index 83ec3f19797..814becf96d7 100644
> --- a/src/include/access/transam.h
> +++ b/src/include/access/transam.h
> @@ -44,6 +44,32 @@
>  #define TransactionIdStore(xid, dest)    (*(dest) = (xid))
>  #define StoreInvalidTransactionId(dest) (*(dest) = InvalidTransactionId)
>  
> +#define EpochFromFullTransactionId(x)    ((uint32) ((x).value >> 32))
> +#define XidFromFullTransactionId(x)        ((uint32) (x).value)
> +#define U64FromFullTransactionId(x)        ((x).value)
> +#define FullTransactionIdPrecedes(a, b)    ((a).value < (b).value)
> +#define FullTransactionIdPrecedesOrEquals(a, b)    ((a).value <= (b).value)
> +
> +/*
> + * A 64 bit value that contains an epoch and a TransactionId.  This is
> + * wrapped in a struct to prevent implicit conversion to/from TransactionId.
> + * Allowing such conversions seems likely to be error-prone.
> + */
> +typedef struct FullTransactionId
> +{
> +    uint64  value;
> +} FullTransactionId;

Probably good to note here that not all values are valid normal xids.


> +static inline FullTransactionId
> +MakeFullTransactionId(uint32 epoch, TransactionId xid)
> +{
> +    FullTransactionId result;
> +
> +    result.value = ((uint64) epoch) << 32 | xid;
> +
> +    return result;
> +}

Make sounds a bit like it's allocating...


>  /* advance a transaction ID variable, handling wraparound correctly */
>  #define TransactionIdAdvance(dest)    \
>      do { \
> @@ -52,6 +78,16 @@
>              (dest) = FirstNormalTransactionId; \
>      } while(0)
>  
> +/* advance a FullTransactionId variable, stepping over special XIDs */
> +static inline void
> +FullTransactionIdAdvance(FullTransactionId *dest)
> +{
> +    dest->value++;
> +    if (XidFromFullTransactionId(*dest) < FirstNormalTransactionId)
> +        *dest = MakeFullTransactionId(EpochFromFullTransactionId(*dest),
> +                                      FirstNormalTransactionId);
> +}

Hm, this seems pretty odd coding to me. Why not do something like

dest->value++;
while (XidFromFullTransactionId(*dest) < FirstNormalTransactionId)
    dest->value++;

That'd a) be a bit more readable, b) possible to do in a lockfree way at
a later point.


> diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
> index 1fcd8cf1b59..d1116454095 100644
> --- a/src/include/storage/standby.h
> +++ b/src/include/storage/standby.h
> @@ -72,7 +72,7 @@ typedef struct RunningTransactionsData
>      int            xcnt;            /* # of xact ids in xids[] */
>      int            subxcnt;        /* # of subxact ids in xids[] */
>      bool        subxid_overflow;    /* snapshot overflowed, subxids missing */
> -    TransactionId nextXid;        /* copy of ShmemVariableCache->nextXid */
> +    TransactionId nextXid;    /* xid from ShmemVariableCache->nextFullXid */

Probably worthwhile to pgindent partially.

Greetings,

Andres Freund


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Amit Kapila
Дата:
Сообщение: Re: WIP: Avoid creation of the free space map for small tables
Следующее
От: Michael Banck
Дата:
Сообщение: Re: Online verification of checksums