On Tue, Jan 18, 2022 at 4:32 PM Andres Freund <and...@anarazel.de> wrote: > > I wonder if a very different approach could make sense here. Presumably this > wouldn't need to be queried at a very high frequency, right? If so, what about > storing the latest commit LSN for each backend in PGPROC? That could be > maintained without a lock/atomics, and should be just about free. > pg_last_committed_xact() then would have to iterate over all PGPROCs to > complete the LSN, but that's not too bad for an operation like that. We'd also > need to maintain a value for all disconnected backends, but that's also not a > hot > path.
Is something roughly like the attached what you'd envisioned? I wouldn't expect the final implementation to be in commit_ts.c, but I left it there for expediency's sake in demonstrating the idea since pg_last_committed_xact() currently finds its home there. I think we need a shared ProcArrayLock to read the array, correct? We also need to do the global updating under lock, but given it's when a proc is removed, that shouldn't be a performance issue if I'm following what you are saying. Thanks, James Coleman
From 7e8a4810f5b4cd16ca2a6f76585513711dbf529a Mon Sep 17 00:00:00 2001 From: jcoleman <jtc...@gmail.com> Date: Fri, 14 Jan 2022 22:26:10 +0000 Subject: [PATCH v2] Expose LSN of last commit via pg_last_committed_xact --- src/backend/access/transam/commit_ts.c | 34 ++++++++++++++++++++------ src/backend/access/transam/twophase.c | 2 +- src/backend/access/transam/xact.c | 10 +++++--- src/backend/storage/ipc/procarray.c | 4 +++ src/include/access/commit_ts.h | 5 ++-- src/include/access/transam.h | 2 ++ src/include/catalog/pg_proc.dat | 6 ++--- src/include/storage/proc.h | 5 ++++ 8 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c index 659109f8d4..50a7be82bc 100644 --- a/src/backend/access/transam/commit_ts.c +++ b/src/backend/access/transam/commit_ts.c @@ -34,9 +34,11 @@ #include "miscadmin.h" #include "pg_trace.h" #include "storage/shmem.h" +#include "storage/proc.h" #include "utils/builtins.h" #include "utils/snapmgr.h" #include "utils/timestamp.h" +#include "utils/pg_lsn.h" /* * Defines for CommitTs page sizes. A page is the same BLCKSZ as is used @@ -93,6 +95,7 @@ static SlruCtlData CommitTsCtlData; typedef struct CommitTimestampShared { TransactionId xidLastCommit; + XLogRecPtr lsnLastCommit; CommitTimestampEntry dataLastCommit; bool commitTsActive; } CommitTimestampShared; @@ -135,7 +138,7 @@ static void WriteTruncateXlogRec(int pageno, TransactionId oldestXid); void TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids, TransactionId *subxids, TimestampTz timestamp, - RepOriginId nodeid) + XLogRecPtr commitLsn, RepOriginId nodeid) { int i; TransactionId headxid; @@ -414,10 +417,22 @@ pg_last_committed_xact(PG_FUNCTION_ARGS) TransactionId xid; RepOriginId nodeid; TimestampTz ts; - Datum values[3]; - bool nulls[3]; + XLogRecPtr lsn; + Datum values[4]; + bool nulls[4]; TupleDesc tupdesc; HeapTuple htup; + int index; + + LWLockAcquire(ProcArrayLock, LW_SHARED); + lsn = ShmemVariableCache->finishedProcsLastCommitLSN; + for (index = 0; index < ProcGlobal->allProcCount; index++) + { + XLogRecPtr procLSN = ProcGlobal->allProcs[index].lastCommitLSN; + if (procLSN > lsn) + lsn = procLSN; + } + LWLockRelease(ProcArrayLock); /* and construct a tuple with our data */ xid = GetLatestCommitTsData(&ts, &nodeid); @@ -426,12 +441,14 @@ pg_last_committed_xact(PG_FUNCTION_ARGS) * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ - tupdesc = CreateTemplateTupleDesc(3); + tupdesc = CreateTemplateTupleDesc(4); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xid", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "timestamp", TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "roident", + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "lsn", + PG_LSNOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "roident", OIDOID, -1, 0); tupdesc = BlessTupleDesc(tupdesc); @@ -447,8 +464,11 @@ pg_last_committed_xact(PG_FUNCTION_ARGS) values[1] = TimestampTzGetDatum(ts); nulls[1] = false; - values[2] = ObjectIdGetDatum((Oid) nodeid); - nulls[2] = false; + values[2] = LSNGetDatum(lsn); + nulls[2] = lsn == InvalidXLogRecPtr; + + values[3] = ObjectIdGetDatum((Oid) nodeid); + nulls[3] = false; } htup = heap_form_tuple(tupdesc, values, nulls); diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 271a3146db..cd33849aea 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -2300,7 +2300,7 @@ RecordTransactionCommitPrepared(TransactionId xid, TransactionTreeSetCommitTsData(xid, nchildren, children, replorigin_session_origin_timestamp, - replorigin_session_origin); + recptr, replorigin_session_origin); /* * We don't currently try to sleep before flush here ... nor is there any diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index c9516e03fa..9da257b83a 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -1355,6 +1355,7 @@ RecordTransactionCommit(void) else { bool replorigin; + XLogRecPtr commit_lsn; /* * Are we using the replication origins feature? Or, in other words, @@ -1391,7 +1392,7 @@ RecordTransactionCommit(void) SetCurrentTransactionStopTimestamp(); - XactLogCommitRecord(xactStopTimestamp, + commit_lsn = XactLogCommitRecord(xactStopTimestamp, nchildren, children, nrels, rels, nmsgs, invalMessages, RelcacheInitFileInval, @@ -1418,7 +1419,8 @@ RecordTransactionCommit(void) TransactionTreeSetCommitTsData(xid, nchildren, children, replorigin_session_origin_timestamp, - replorigin_session_origin); + commit_lsn, replorigin_session_origin); + MyProc->lastCommitLSN = commit_lsn; } /* @@ -5881,7 +5883,9 @@ xact_redo_commit(xl_xact_parsed_commit *parsed, /* Set the transaction commit timestamp and metadata */ TransactionTreeSetCommitTsData(xid, parsed->nsubxacts, parsed->subxacts, - commit_time, origin_id); + commit_time, lsn, origin_id); + + MyProc->lastCommitLSN = lsn; if (standbyState == STANDBY_DISABLED) { diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 3be6040289..2fb75a2079 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -430,6 +430,7 @@ CreateSharedProcArray(void) procArray->replication_slot_xmin = InvalidTransactionId; procArray->replication_slot_catalog_xmin = InvalidTransactionId; ShmemVariableCache->xactCompletionCount = 1; + ShmemVariableCache->finishedProcsLastCommitLSN = InvalidXLogRecPtr; } allProcs = ProcGlobal->allProcs; @@ -580,6 +581,9 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid) /* Same with xactCompletionCount */ ShmemVariableCache->xactCompletionCount++; + if (proc->lastCommitLSN > ShmemVariableCache->finishedProcsLastCommitLSN) + ShmemVariableCache->finishedProcsLastCommitLSN = proc->lastCommitLSN; + ProcGlobal->xids[myoff] = InvalidTransactionId; ProcGlobal->subxidStates[myoff].overflowed = false; ProcGlobal->subxidStates[myoff].count = 0; diff --git a/src/include/access/commit_ts.h b/src/include/access/commit_ts.h index 7662f8e1a9..c7a146ec17 100644 --- a/src/include/access/commit_ts.h +++ b/src/include/access/commit_ts.h @@ -21,11 +21,10 @@ extern PGDLLIMPORT bool track_commit_timestamp; extern void TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids, TransactionId *subxids, TimestampTz timestamp, - RepOriginId nodeid); + XLogRecPtr commitLsn, RepOriginId nodeid); extern bool TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts, RepOriginId *nodeid); -extern TransactionId GetLatestCommitTsData(TimestampTz *ts, - RepOriginId *nodeid); +extern TransactionId GetLatestCommitTsData(TimestampTz *ts, RepOriginId *nodeid); extern Size CommitTsShmemBuffers(void); extern Size CommitTsShmemSize(void); diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 338dfca5a0..c53f2d3810 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -237,6 +237,8 @@ typedef struct VariableCacheData */ FullTransactionId latestCompletedXid; /* newest full XID that has * committed or aborted */ + /* */ + XLogRecPtr finishedProcsLastCommitLSN; /* * Number of top-level transactions with xids (i.e. which may have diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index d6bf1f3274..d5fe8f4161 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6157,11 +6157,11 @@ prosrc => 'pg_xact_commit_timestamp_origin' }, { oid => '3583', - descr => 'get transaction Id, commit timestamp and replication origin of latest transaction commit', + descr => 'get transaction Id, commit timestamp, commit lsn and replication origin of latest transaction commit', proname => 'pg_last_committed_xact', provolatile => 'v', prorettype => 'record', proargtypes => '', - proallargtypes => '{xid,timestamptz,oid}', proargmodes => '{o,o,o}', - proargnames => '{xid,timestamp,roident}', + proallargtypes => '{xid,timestamptz,pg_lsn,oid}', proargmodes => '{o,o,o,o}', + proargnames => '{xid,timestamp,lsn,roident}', prosrc => 'pg_last_committed_xact' }, { oid => '3537', descr => 'get identification of SQL object', diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index a58888f9e9..2a026b0844 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -258,6 +258,11 @@ struct PGPROC PGPROC *lockGroupLeader; /* lock group leader, if I'm a member */ dlist_head lockGroupMembers; /* list of members, if I'm a leader */ dlist_node lockGroupLink; /* my member link, if I'm a member */ + + /* + * Last transaction metadata. + */ + XLogRecPtr lastCommitLSN; /* cache of last committed LSN */ }; /* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */ -- 2.17.1