diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index bc2a2feb0b..fad49aba82 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1315,6 +1315,17 @@ SELCT 1/0;<!-- this typo is intentional -->
     channel name.
    </para>
 
+   <para>
+    When the parameter <varname>listen_transaction_id</varname> is enabled, at
+    the begining of a transaction (not a subtransaction) the backend sends a
+    NotificationResponse message containing the transaction ID on the <literal>
+    _my_transaction_id<literal> channel. This may be useful for the frontend to
+    use after a crash-recovery, to inspect the state of their transaction from
+    before the crash; see <function>pg_xact_status()</function>. Unlike other
+    NotificationResponse messaegs, the process ID in this message is the same as
+    that of the current backend.
+   </para>
+
    <note>
     <para>
      At present, NotificationResponse can only be sent outside a
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 441445927e..e80dfb3a7d 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -68,6 +68,8 @@
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
+extern char *FullTransactionIdToStr(FullTransactionId fxid);
+
 /*
  *	User-tweakable parameters
  */
@@ -107,6 +109,7 @@ bool		bsysscan = false;
  * XactTopFullTransactionId stores the XID of our toplevel transaction, which
  * will be the same as TopTransactionStateData.fullTransactionId in an
  * ordinary backend; but in a parallel backend, which does not have the entire
+ * ordinary backend; but in a parallel backend, which does not have the entire
  * transaction state, it will instead be copied from the backend that started
  * the parallel operation.
  *
@@ -714,6 +717,21 @@ AssignTransactionId(TransactionState s)
 			TopTransactionStateData.didLogXid = true;
 		}
 	}
+
+	// NOTIFY FrontEnd, if it wants to know the top transaction's ID.
+	if (!isSubXact && listen_transaction_id)
+	{
+		char *xidStr;
+
+		Assert(s->parent == NULL);
+
+		// Should we Assert(!IsParallelWorker()) here?
+
+		xidStr = FullTransactionIdToStr(s->fullTransactionId);
+
+		NotifyMyFrontEnd("_my_transaction_id", xidStr, MyProcPid);
+		pfree(xidStr);
+	}
 }
 
 /*
@@ -6005,7 +6023,7 @@ xact_redo(XLogReaderState *record)
 	}
 	else if (info == XLOG_XACT_COMMIT_PREPARED)
 	{
-		xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
+		xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
 		xl_xact_parsed_commit parsed;
 
 		ParseCommitRecord(XLogRecGetInfo(record), xlrec, &parsed);
diff --git a/src/backend/utils/adt/xid.c b/src/backend/utils/adt/xid.c
index 24c1c93732..755cc53d36 100644
--- a/src/backend/utils/adt/xid.c
+++ b/src/backend/utils/adt/xid.c
@@ -161,13 +161,19 @@ xid8in(PG_FUNCTION_ARGS)
 	PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(pg_strtouint64(str, NULL, 0)));
 }
 
+char *
+FullTransactionIdToStr(FullTransactionId fxid)
+{
+	char	   *result = (char *) palloc(21);
+	snprintf(result, 21, UINT64_FORMAT, U64FromFullTransactionId(fxid));
+	return result;
+}
+
 Datum
 xid8out(PG_FUNCTION_ARGS)
 {
 	FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
-	char	   *result = (char *) palloc(21);
-
-	snprintf(result, 21, UINT64_FORMAT, U64FromFullTransactionId(fxid));
+	char	   *result = FullTransactionIdToStr(fxid);
 	PG_RETURN_CSTRING(result);
 }
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 68b62d523d..1df14b9745 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -947,11 +947,21 @@ static const unit_conversion time_unit_conversion_table[] =
  *	  variable_is_guc_list_quote() in src/bin/pg_dump/dumputils.c.
  */
 
+bool listen_transaction_id = false;
 
 /******** option records follow ********/
 
 static struct config_bool ConfigureNamesBool[] =
 {
+	{
+		{"listen_transaction_id", PGC_USERSET, UNGROUPED,
+			gettext_noop("Emit top-level transaction ID on transaction start."),
+			NULL
+		},
+		&listen_transaction_id,
+		false,
+		NULL, NULL, NULL
+	},
 	{
 		{"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
 			gettext_noop("Enables the planner's use of sequential-scan plans."),
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index a7c3a4958e..ed86a7a16d 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -278,6 +278,8 @@ extern int	tcp_keepalives_interval;
 extern int	tcp_keepalives_count;
 extern int	tcp_user_timeout;
 
+extern bool listen_transaction_id;
+
 #ifdef TRACE_SORT
 extern bool trace_sort;
 #endif
