diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 127bc58..3cd46af 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6871,6 +6871,20 @@ StartupXLOG(void)
 				XLogCtl->lastReplayedTLI = ThisTimeLineID;
 				SpinLockRelease(&XLogCtl->info_lck);
 
+				/*
+				 * Tell walreceiver to notify the master so that any backends
+				 * blocked in COMMIT with synchronous_commit = apply can
+				 * continue.
+				 */
+				if (record->xl_rmid == RM_XACT_ID)
+				{
+					uint8 xact_info = record->xl_info & XLOG_XACT_OPMASK;
+
+					if (xact_info == XLOG_XACT_COMMIT ||
+						xact_info == XLOG_XACT_COMMIT_PREPARED)
+						WalRcvWakeup();
+				}
+
 				/* Remember this record as the last-applied one */
 				LastRec = ReadRecPtr;
 
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 325239d..2e18768 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -462,6 +462,11 @@ SyncRepReleaseWaiters(void)
 		walsndctl->lsn[SYNC_REP_WAIT_FLUSH] = MyWalSnd->flush;
 		numflush = SyncRepWakeQueue(false, SYNC_REP_WAIT_FLUSH);
 	}
+	if (walsndctl->lsn[SYNC_REP_WAIT_APPLY] < MyWalSnd->apply)
+	{
+		walsndctl->lsn[SYNC_REP_WAIT_APPLY] = MyWalSnd->apply;
+		numflush = SyncRepWakeQueue(false, SYNC_REP_WAIT_APPLY);
+	}
 
 	LWLockRelease(SyncRepLock);
 
@@ -728,6 +733,9 @@ assign_synchronous_commit(int newval, void *extra)
 		case SYNCHRONOUS_COMMIT_REMOTE_FLUSH:
 			SyncRepWaitMode = SYNC_REP_WAIT_FLUSH;
 			break;
+		case SYNCHRONOUS_COMMIT_REMOTE_APPLY:
+			SyncRepWaitMode = SYNC_REP_WAIT_APPLY;
+			break;
 		default:
 			SyncRepWaitMode = SYNC_REP_NO_WAIT;
 			break;
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 41e57f2..279c096 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -96,6 +96,7 @@ static uint32 recvOff = 0;
  */
 static volatile sig_atomic_t got_SIGHUP = false;
 static volatile sig_atomic_t got_SIGTERM = false;
+static volatile sig_atomic_t check_applied_lsn_flag = false;
 
 /*
  * LogstreamResult indicates the byte positions that we have already
@@ -138,7 +139,7 @@ static void WalRcvDie(int code, Datum arg);
 static void XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len);
 static void XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr);
 static void XLogWalRcvFlush(bool dying);
-static void XLogWalRcvSendReply(bool force, bool requestReply);
+static void XLogWalRcvSendReply(bool force, bool requestReply, XLogRecPtr applyLsn);
 static void XLogWalRcvSendHSFeedback(bool immed);
 static void ProcessWalSndrMessage(XLogRecPtr walEnd, TimestampTz sendTime);
 
@@ -350,6 +351,7 @@ WalReceiverMain(void)
 								  slotname[0] != '\0' ? slotname : NULL))
 		{
 			bool		endofwal = false;
+			XLogRecPtr	last_sent_applied_lsn = InvalidXLogRecPtr;
 
 			if (first_stream)
 				ereport(LOG,
@@ -423,6 +425,9 @@ WalReceiverMain(void)
 							last_recv_timestamp = GetCurrentTimestamp();
 							ping_sent = false;
 							XLogWalRcvProcessMsg(buf[0], &buf[1], len - 1);
+							/* Check if we need to break out of this loop to send a reply. */
+							if (check_applied_lsn_flag)
+								break;
 						}
 						else if (len == 0)
 							break;
@@ -439,8 +444,20 @@ WalReceiverMain(void)
 						len = walrcv_receive(0, &buf);
 					}
 
-					/* Let the master know that we received some data. */
-					XLogWalRcvSendReply(false, false);
+					/* Let the master know that we applied commit records. */
+					if (check_applied_lsn_flag)
+					{
+						XLogRecPtr applied_lsn_snapshot = GetXLogReplayRecPtr(NULL);
+
+						check_applied_lsn_flag = false;
+						if (last_sent_applied_lsn != applied_lsn_snapshot)
+						{
+							last_sent_applied_lsn = applied_lsn_snapshot;
+							XLogWalRcvSendReply(true, true, applied_lsn_snapshot);
+						}
+					}
+					else
+						XLogWalRcvSendReply(false, false, InvalidXLogRecPtr);
 
 					/*
 					 * If we've written some records, flush them to disk and
@@ -461,6 +478,7 @@ WalReceiverMain(void)
 					 * WAL.
 					 */
 					bool		requestReply = false;
+					XLogRecPtr	applied_lsn = InvalidXLogRecPtr;
 
 					/*
 					 * Check if time since last receive from standby has
@@ -495,7 +513,29 @@ WalReceiverMain(void)
 						}
 					}
 
-					XLogWalRcvSendReply(requestReply, requestReply);
+					/*
+					 * Check if the startup process has signalled us to report
+					 * that an interesting commit record has been applied.
+					 */
+					if (check_applied_lsn_flag)
+					{
+						/*
+						 * Check if the apply LSN has moved since we last
+						 * notified the master of our apply position.
+						 */
+						XLogRecPtr applied_lsn_snapshot;
+
+						check_applied_lsn_flag = false;
+						applied_lsn_snapshot = GetXLogReplayRecPtr(NULL);
+						if (applied_lsn_snapshot != last_sent_applied_lsn)
+						{
+							requestReply = true;
+							last_sent_applied_lsn = applied_lsn_snapshot;
+							applied_lsn = applied_lsn_snapshot;
+						}
+					}
+
+					XLogWalRcvSendReply(requestReply, requestReply, applied_lsn);
 					XLogWalRcvSendHSFeedback(false);
 				}
 			}
@@ -734,6 +774,7 @@ WalRcvSigUsr1Handler(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	check_applied_lsn_flag = true;
 	latch_sigusr1_handler();
 
 	errno = save_errno;
@@ -846,7 +887,7 @@ XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len)
 
 				/* If the primary requested a reply, send one immediately */
 				if (replyRequested)
-					XLogWalRcvSendReply(true, false);
+					XLogWalRcvSendReply(true, false, InvalidXLogRecPtr);
 				break;
 			}
 		default:
@@ -1010,7 +1051,7 @@ XLogWalRcvFlush(bool dying)
 		/* Also let the master know that we made some progress */
 		if (!dying)
 		{
-			XLogWalRcvSendReply(false, false);
+			XLogWalRcvSendReply(false, false, InvalidXLogRecPtr);
 			XLogWalRcvSendHSFeedback(false);
 		}
 	}
@@ -1028,9 +1069,12 @@ XLogWalRcvFlush(bool dying)
  * If 'requestReply' is true, requests the server to reply immediately upon
  * receiving this message. This is used for heartbearts, when approaching
  * wal_receiver_timeout.
+ *
+ * If 'apply_lsn' is InvalidXLogRecPtr, the apply LSN is looked up in shmem if
+ * it is needed.  Otherwise, the value provided is used.
  */
 static void
-XLogWalRcvSendReply(bool force, bool requestReply)
+XLogWalRcvSendReply(bool force, bool requestReply, XLogRecPtr apply_lsn)
 {
 	static XLogRecPtr writePtr = 0;
 	static XLogRecPtr flushPtr = 0;
@@ -1221,3 +1265,19 @@ ProcessWalSndrMessage(XLogRecPtr walEnd, TimestampTz sendTime)
 		pfree(receipttime);
 	}
 }
+
+/*
+ * Wake up the walreceiver if it happens to be blocked in walrcv_receive,
+ * and tell it that a commit record has been applied.
+ *
+ * This is called by the startup process whenever interesting xlog records
+ * are applied, so that walreceiver can check if it needs to send an apply
+ * notification back to the master which may be waiting in a COMMIT with
+ * synchronous_commit = apply.
+ */
+void
+WalRcvWakeup(void)
+{
+	if (WalRcv->pid != 0)
+		kill(WalRcv->pid, SIGUSR1);
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b3dac51..bbabc58 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -351,6 +351,7 @@ static const struct config_enum_entry constraint_exclusion_options[] = {
 static const struct config_enum_entry synchronous_commit_options[] = {
 	{"local", SYNCHRONOUS_COMMIT_LOCAL_FLUSH, false},
 	{"remote_write", SYNCHRONOUS_COMMIT_REMOTE_WRITE, false},
+	{"apply", SYNCHRONOUS_COMMIT_REMOTE_APPLY, false},
 	{"on", SYNCHRONOUS_COMMIT_ON, false},
 	{"off", SYNCHRONOUS_COMMIT_OFF, false},
 	{"true", SYNCHRONOUS_COMMIT_ON, true},
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index cb1c2db..d8433e2 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -60,7 +60,9 @@ typedef enum
 	SYNCHRONOUS_COMMIT_LOCAL_FLUSH,		/* wait for local flush only */
 	SYNCHRONOUS_COMMIT_REMOTE_WRITE,	/* wait for local flush and remote
 										 * write */
-	SYNCHRONOUS_COMMIT_REMOTE_FLUSH		/* wait for local and remote flush */
+	SYNCHRONOUS_COMMIT_REMOTE_FLUSH,	/* wait for local and remote flush */
+	SYNCHRONOUS_COMMIT_REMOTE_APPLY		/* wait for local flush and remote
+										 * apply */
 }	SyncCommitLevel;
 
 /* Define the default setting for synchonous_commit */
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index 71e2857..8e0fe00 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -23,8 +23,9 @@
 #define SYNC_REP_NO_WAIT		-1
 #define SYNC_REP_WAIT_WRITE		0
 #define SYNC_REP_WAIT_FLUSH		1
+#define SYNC_REP_WAIT_APPLY		2
 
-#define NUM_SYNC_REP_WAIT_MODE	2
+#define NUM_SYNC_REP_WAIT_MODE	3
 
 /* syncRepState */
 #define SYNC_REP_NOT_WAITING		0
diff --git a/src/include/replication/walreceiver.h b/src/include/replication/walreceiver.h
index 61255a9..3256ed3 100644
--- a/src/include/replication/walreceiver.h
+++ b/src/include/replication/walreceiver.h
@@ -160,5 +160,6 @@ extern void RequestXLogStreaming(TimeLineID tli, XLogRecPtr recptr,
 extern XLogRecPtr GetWalRcvWriteRecPtr(XLogRecPtr *latestChunkStart, TimeLineID *receiveTLI);
 extern int	GetReplicationApplyDelay(void);
 extern int	GetReplicationTransferLatency(void);
+extern void WalRcvWakeup(void);
 
 #endif   /* _WALRECEIVER_H */
