On Fri, Jan 28, 2022 at 7:47 PM Andres Freund <and...@anarazel.de> wrote:
>
> On 2022-01-28 16:36:32 -0800, Andres Freund wrote:
> > On 2022-01-28 18:43:57 -0500, James Coleman wrote:
> > > Alternatively I see pg_attribute_aligned, but that's not defined
> > > (AFAICT) on clang, for example, so I'm not sure that'd be acceptable?
> >
> > clang should have it (it defines __GNUC__). The problem would be msvc, I
> > think. Not sure if there's a way to get to a common way of defining it 
> > between
> > gcc-like compilers and msvc (the rest is niche enough that we don't need to
> > care about the efficiency I think).
>
> Seems like it's doable:
>
> https://godbolt.org/z/3c5573bTW

Oh, thanks. I'd seen some discussion previously on the list about
clang not supporting it, but that seems to have been incorrect. Also I
didn't know about that compiler site -- that's really neat.

Here's an updated patch series using that approach; the first patch
can (and probably should be) committed separately/regardless to update
the pg_attribute_aligned to be used in MSVC.

Thanks,
James Coleman
From c104bec6ed93cea06f4d5ff0f9150490f159dfaa Mon Sep 17 00:00:00 2001
From: jcoleman <jtc...@gmail.com>
Date: Sat, 29 Jan 2022 11:28:45 -0500
Subject: [PATCH v4 1/2] Support pg_attribute_aligned in MSVC

---
 configure       | 2 ++
 src/include/c.h | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/configure b/configure
index 3f2aea0d7d..e9ab674f38 100755
--- a/configure
+++ b/configure
@@ -17862,6 +17862,8 @@ else
 /* This must match the corresponding code in c.h: */
 #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
 #define pg_attribute_aligned(a) __attribute__((aligned(a)))
+#elif defined(_MSC_VER)
+#define pg_attribute_aligned(a) __declspec(align(a))
 #endif
 typedef __int128 int128a
 #if defined(pg_attribute_aligned)
diff --git a/src/include/c.h b/src/include/c.h
index 4f16e589b3..0441b031b4 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -179,6 +179,10 @@
 #define pg_attribute_noreturn()
 #endif
 
+#if defined(_MSC_VER)
+#define pg_attribute_aligned(a) __declspec(align(a))
+#endif
+
 /*
  * Use "pg_attribute_always_inline" in place of "inline" for functions that
  * we wish to force inlining of, even when the compiler's heuristics would
-- 
2.17.1

From 1f456c9f3976e49cc9be31878c5ad5d7eba5c1b6 Mon Sep 17 00:00:00 2001
From: jcoleman <jtc...@gmail.com>
Date: Sat, 29 Jan 2022 12:18:45 -0500
Subject: [PATCH v4 2/2] Expose LSN of last commit via pg_last_committed_xact

---
 src/backend/access/transam/xact.c             |  6 +++-
 src/backend/access/transam/xlogfuncs.c        | 17 ++++++++++
 src/backend/storage/ipc/procarray.c           | 34 +++++++++++++++++++
 src/include/access/transam.h                  |  2 ++
 src/include/catalog/pg_proc.dat               |  6 ++++
 src/include/storage/proc.h                    | 10 ++++++
 src/include/storage/procarray.h               |  2 ++
 .../test_misc/t/002_last_commit_lsn.pl        | 31 +++++++++++++++++
 8 files changed, 107 insertions(+), 1 deletion(-)
 create mode 100644 src/test/modules/test_misc/t/002_last_commit_lsn.pl

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index c9516e03fa..f2ae4b0667 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,
@@ -1419,6 +1420,7 @@ RecordTransactionCommit(void)
 		TransactionTreeSetCommitTsData(xid, nchildren, children,
 									   replorigin_session_origin_timestamp,
 									   replorigin_session_origin);
+		MyProc->lastCommitLSN = commit_lsn;
 	}
 
 	/*
@@ -5883,6 +5885,8 @@ xact_redo_commit(xl_xact_parsed_commit *parsed,
 	TransactionTreeSetCommitTsData(xid, parsed->nsubxacts, parsed->subxacts,
 								   commit_time, origin_id);
 
+	MyProc->lastCommitLSN = lsn;
+
 	if (standbyState == STANDBY_DISABLED)
 	{
 		/*
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index d8af5aad58..05ee661fc2 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -29,6 +29,7 @@
 #include "replication/walreceiver.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
+#include "storage/procarray.h"
 #include "storage/smgr.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
@@ -425,6 +426,22 @@ pg_last_wal_replay_lsn(PG_FUNCTION_ARGS)
 	PG_RETURN_LSN(recptr);
 }
 
+/*
+ * pg_last_commit_lsn
+ *
+ * SQL-callable wrapper to obtain the lsn of the last commit.
+ */
+Datum
+pg_last_commit_lsn(PG_FUNCTION_ARGS)
+{
+	XLogRecPtr lsn = GetLastCommitLSN();
+
+	if (lsn == InvalidXLogRecPtr)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_DATUM(LSNGetDatum(lsn));
+}
+
 /*
  * Compute an xlog file name and decimal byte offset given a WAL location,
  * such as is returned by pg_stop_backup() or pg_switch_wal().
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3be6040289..92586d1492 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;
@@ -3914,6 +3918,36 @@ ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
 	LWLockRelease(ProcArrayLock);
 }
 
+/*
+ * GetLastCommitLSN
+ *
+ * Return LSN of the most recent COMMIT record written to WAL.
+ *
+ * For performance reasons we do not guarantee the result to be perfectly in
+ * line with current visibility.
+ */
+XLogRecPtr
+GetLastCommitLSN(void)
+{
+	ProcArrayStruct *arrayP = procArray;
+	XLogRecPtr	lsn;
+	int			i;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+	lsn = ShmemVariableCache->finishedProcsLastCommitLSN;
+	for (i = 0; i < arrayP->numProcs; i++)
+	{
+		int         pgprocno = arrayP->pgprocnos[i];
+		PGPROC     *proc = &allProcs[pgprocno];
+
+		if (proc->lastCommitLSN > lsn)
+			lsn = proc->lastCommitLSN;
+	}
+	LWLockRelease(ProcArrayLock);
+
+	return lsn;
+}
+
 /*
  * XidCacheRemoveRunningXids
  *
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..cdf73f3bd5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6164,6 +6164,12 @@
   proargnames => '{xid,timestamp,roident}',
   prosrc => 'pg_last_committed_xact' },
 
+{ oid => '9861',
+  descr => 'get commit lsn of latest transaction commit',
+  proname => 'pg_last_commit_lsn', provolatile => 'v',
+  prorettype => 'pg_lsn', proargtypes => '',
+  prosrc => 'pg_last_commit_lsn' },
+
 { oid => '3537', descr => 'get identification of SQL object',
   proname => 'pg_describe_object', provolatile => 's', prorettype => 'text',
   proargtypes => 'oid oid int4', prosrc => 'pg_describe_object' },
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index a58888f9e9..efaf6442f5 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -258,6 +258,16 @@ 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.
+	 */
+#ifndef pg_attribute_aligned
+	char	*pad[PG_CACHE_LINE_SIZE];
+#else
+	pg_attribute_aligned(PG_CACHE_LINE_SIZE)
+#endif
+	XLogRecPtr	lastCommitLSN;		/* cache of last committed LSN */
 };
 
 /* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053e..ec3baa29e8 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -94,4 +94,6 @@ extern void ProcArraySetReplicationSlotXmin(TransactionId xmin,
 extern void ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
 											TransactionId *catalog_xmin);
 
+extern XLogRecPtr GetLastCommitLSN(void);
+
 #endif							/* PROCARRAY_H */
diff --git a/src/test/modules/test_misc/t/002_last_commit_lsn.pl b/src/test/modules/test_misc/t/002_last_commit_lsn.pl
new file mode 100644
index 0000000000..f249326c6b
--- /dev/null
+++ b/src/test/modules/test_misc/t/002_last_commit_lsn.pl
@@ -0,0 +1,31 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 2;
+
+# Initialize a test cluster
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init();
+$node->start;
+
+my ($ret, $before_lsn, $after_lsn);
+
+$ret = $node->safe_psql('postgres', 'SELECT pg_last_commit_lsn()');
+is($ret, '', 'null at startup');
+
+$node->safe_psql('postgres', qq[CREATE TABLE t(i integer);]);
+
+$before_lsn = $node->safe_psql('postgres', 'SELECT pg_current_wal_lsn()');
+$node->safe_psql('postgres', 'INSERT INTO t(i) VALUES (1)');
+$after_lsn = $node->safe_psql('postgres', 'SELECT pg_current_wal_lsn()');
+
+$ret = $node->safe_psql('postgres', qq[
+  SELECT pg_last_commit_lsn() BETWEEN '$before_lsn'::pg_lsn AND '$after_lsn'::pg_lsn
+]);
+is($ret, 't', 'last commit lsn is set');
+
+$node->stop('fast');
-- 
2.17.1

Reply via email to