diff options
author | Andres Freund <andres@anarazel.de> | 2016-04-23 19:18:00 -0700 |
---|---|---|
committer | Andres Freund <andres@anarazel.de> | 2016-04-26 20:21:54 -0700 |
commit | c6ff84b06a68b71719aa1aaa5f6704d8db1b51f8 (patch) | |
tree | bca1d7f785aa03f73b137f3182d028e16abdf519 /src/backend/access | |
parent | 2ac3be2e763d9b971352819f285dd51519e0aeb9 (diff) | |
download | postgresql-c6ff84b06a68b71719aa1aaa5f6704d8db1b51f8.tar.gz postgresql-c6ff84b06a68b71719aa1aaa5f6704d8db1b51f8.zip |
Emit invalidations to standby for transactions without xid.
So far, when a transaction with pending invalidations, but without an
assigned xid, committed, we simply ignored those invalidation
messages. That's problematic, because those are actually sent for a
reason.
Known symptoms of this include that existing sessions on a hot-standby
replica sometimes fail to notice new concurrently built indexes and
visibility map updates.
The solution is to WAL log such invalidations in transactions without an
xid. We considered to alternatively force-assign an xid, but that'd be
problematic for vacuum, which might be run in systems with few xids.
Important: This adds a new WAL record, but as the patch has to be
back-patched, we can't bump the WAL page magic. This means that standbys
have to be updated before primaries; otherwise
"PANIC: standby_redo: unknown op code 32" errors can be encountered.
XXX:
Reported-By: Васильев Дмитрий, Masahiko Sawada
Discussion:
CAB-SwXY6oH=9twBkXJtgR4UC1NqT-vpYAtxCseME62ADwyK5OA@mail.gmail.com
CAD21AoDpZ6Xjg=gFrGPnSn4oTRRcwK1EBrWCq9OqOHuAcMMC=w@mail.gmail.com
Diffstat (limited to 'src/backend/access')
-rw-r--r-- | src/backend/access/rmgrdesc/standbydesc.c | 54 | ||||
-rw-r--r-- | src/backend/access/rmgrdesc/xactdesc.c | 30 | ||||
-rw-r--r-- | src/backend/access/transam/xact.c | 18 |
3 files changed, 76 insertions, 26 deletions
diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c index 4872cfb2d96..e6172ccdf73 100644 --- a/src/backend/access/rmgrdesc/standbydesc.c +++ b/src/backend/access/rmgrdesc/standbydesc.c @@ -58,6 +58,14 @@ standby_desc(StringInfo buf, XLogReaderState *record) standby_desc_running_xacts(buf, xlrec); } + else if (info == XLOG_INVALIDATIONS) + { + xl_invalidations *xlrec = (xl_invalidations *) rec; + + standby_desc_invalidations(buf, xlrec->nmsgs, xlrec->msgs, + xlrec->dbId, xlrec->tsId, + xlrec->relcacheInitFileInval); + } } const char * @@ -73,7 +81,53 @@ standby_identify(uint8 info) case XLOG_RUNNING_XACTS: id = "RUNNING_XACTS"; break; + case XLOG_INVALIDATIONS: + id = "INVALIDATIONS"; + break; } return id; } + +/* + * This routine is used by both standby_desc and xact_desc, because + * transaction commits and XLOG_INVALIDATIONS messages contain invalidations; + * it seems pointless to duplicate the code. + */ +void +standby_desc_invalidations(StringInfo buf, + int nmsgs, SharedInvalidationMessage *msgs, + Oid dbId, Oid tsId, + bool relcacheInitFileInval) +{ + int i; + + if (relcacheInitFileInval) + appendStringInfo(buf, "; relcache init file inval dbid %u tsid %u", + dbId, tsId); + + appendStringInfoString(buf, "; inval msgs:"); + for (i = 0; i < nmsgs; i++) + { + SharedInvalidationMessage *msg = &msgs[i]; + + if (msg->id >= 0) + appendStringInfo(buf, " catcache %d", msg->id); + else if (msg->id == SHAREDINVALCATALOG_ID) + appendStringInfo(buf, " catalog %u", msg->cat.catId); + else if (msg->id == SHAREDINVALRELCACHE_ID) + appendStringInfo(buf, " relcache %u", msg->rc.relId); + /* not expected, but print something anyway */ + else if (msg->id == SHAREDINVALSMGR_ID) + appendStringInfoString(buf, " smgr"); + /* not expected, but print something anyway */ + else if (msg->id == SHAREDINVALRELMAP_ID) + appendStringInfoString(buf, " relmap"); + else if (msg->id == SHAREDINVALRELMAP_ID) + appendStringInfo(buf, " relmap db %u", msg->rm.dbId); + else if (msg->id == SHAREDINVALSNAPSHOT_ID) + appendStringInfo(buf, " snapshot %u", msg->sn.relId); + else + appendStringInfo(buf, " unknown id %d", msg->id); + } +} diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c index e8a334c17db..6f07c5cfaac 100644 --- a/src/backend/access/rmgrdesc/xactdesc.c +++ b/src/backend/access/rmgrdesc/xactdesc.c @@ -18,6 +18,7 @@ #include "access/xact.h" #include "catalog/catalog.h" #include "storage/sinval.h" +#include "storage/standbydefs.h" #include "utils/timestamp.h" /* @@ -203,32 +204,9 @@ xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId } if (parsed.nmsgs > 0) { - if (XactCompletionRelcacheInitFileInval(parsed.xinfo)) - appendStringInfo(buf, "; relcache init file inval dbid %u tsid %u", - parsed.dbId, parsed.tsId); - - appendStringInfoString(buf, "; inval msgs:"); - for (i = 0; i < parsed.nmsgs; i++) - { - SharedInvalidationMessage *msg = &parsed.msgs[i]; - - if (msg->id >= 0) - appendStringInfo(buf, " catcache %d", msg->id); - else if (msg->id == SHAREDINVALCATALOG_ID) - appendStringInfo(buf, " catalog %u", msg->cat.catId); - else if (msg->id == SHAREDINVALRELCACHE_ID) - appendStringInfo(buf, " relcache %u", msg->rc.relId); - /* not expected, but print something anyway */ - else if (msg->id == SHAREDINVALSMGR_ID) - appendStringInfoString(buf, " smgr"); - /* not expected, but print something anyway */ - else if (msg->id == SHAREDINVALRELMAP_ID) - appendStringInfoString(buf, " relmap"); - else if (msg->id == SHAREDINVALSNAPSHOT_ID) - appendStringInfo(buf, " snapshot %u", msg->sn.relId); - else - appendStringInfo(buf, " unknown id %d", msg->id); - } + standby_desc_invalidations( + buf, parsed.nmsgs, parsed.msgs, parsed.dbId, parsed.tsId, + XactCompletionRelcacheInitFileInval(parsed.xinfo)); } if (XactCompletionForceSyncCommit(parsed.xinfo)) diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 7e373316139..95690ff36cb 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -1164,6 +1164,24 @@ RecordTransactionCommit(void) Assert(nchildren == 0); /* + * Transactions without an assigned xid can contain invalidation + * messages (e.g. explicit relcache invalidations or catcache + * invalidations for inplace updates); standbys need to process + * those. We can't emit a commit record without an xid, and we don't + * want to force assigning an xid, because that'd be problematic for + * e.g. vacuum. Hence we emit a bespoke record for the + * invalidations. We don't want to use that in case a commit record is + * emitted, so they happen synchronously with commits (besides not + * wanting to emit more WAL recoreds). + */ + if (nmsgs != 0) + { + LogStandbyInvalidations(nmsgs, invalMessages, + RelcacheInitFileInval); + wrote_xlog = true; /* not strictly necessary */ + } + + /* * If we didn't create XLOG entries, we're done here; otherwise we * should trigger flushing those entries the same as a commit record * would. This will primarily happen for HOT pruning and the like; we |