diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index e6706f7..b9cdead 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -51,6 +51,7 @@
 #include "storage/latch.h"
 #include "storage/pmsignal.h"
 #include "storage/shmem.h"
+#include "utils/memutils.h"
 
 /*
  * Select the fd readiness primitive to use. Normally the "most modern"
@@ -340,6 +341,15 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
 							 wait_event_info);
 }
 
+#define WAIT_EVENT_HASH_SIZE 113
+
+typedef struct WaitEventHashEntry
+{
+	WaitEventSet* set;
+	int wakeEvents;
+	pgsocket sock;
+} WaitEventHashEntry;
+
 /*
  * Like WaitLatch, but with an extra socket argument for WL_SOCKET_*
  * conditions.
@@ -359,29 +369,65 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 	int			ret = 0;
 	int			rc;
 	WaitEvent	event;
-	WaitEventSet *set = CreateWaitEventSet(CurrentMemoryContext, 3);
+	WaitEventSet *set;
+	static WaitEventHashEntry wait_event_hash[WAIT_EVENT_HASH_SIZE];
+	size_t h = (wakeEvents | ((size_t)sock << 6)) % WAIT_EVENT_HASH_SIZE;
+	set = wait_event_hash[h].set;
+	if (set == NULL || wait_event_hash[h].wakeEvents != wakeEvents || wait_event_hash[h].sock != sock)
+	{
+		if (set)
+			FreeWaitEventSet(set);
+		set = CreateWaitEventSet(TopMemoryContext, 3);
+		wait_event_hash[h].set = set;
+		wait_event_hash[h].wakeEvents = wakeEvents;
+		wait_event_hash[h].sock = sock;
+
+		if (wakeEvents & WL_LATCH_SET)
+			AddWaitEventToSet(set, WL_LATCH_SET, PGINVALID_SOCKET,
+							  (Latch *) latch, NULL);
+
+		if (wakeEvents & WL_POSTMASTER_DEATH && IsUnderPostmaster)
+			AddWaitEventToSet(set, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
+							  NULL, NULL);
+
+		if (wakeEvents & WL_SOCKET_MASK)
+		{
+			int			ev;
+
+			ev = wakeEvents & WL_SOCKET_MASK;
+			AddWaitEventToSet(set, ev, sock, NULL, NULL);
+		}
+	}
+#if defined(WAIT_USE_EPOLL)
+	/*
+	 * Socket descriptor can be closed and reused.
+	 * Closed descriptor is automatically excluded from epoll set,
+	 * but cached epoll set will not correctly work in this case.
+	 * So we will try to always add socket descriptor and ignore EEXIST error.
+	 */
+	else if (wakeEvents & WL_SOCKET_MASK)
+	{
+		struct epoll_event epoll_ev;
+		int rc;
+		epoll_ev.data.ptr = &set->events[set->nevents-1];
+		epoll_ev.events = EPOLLERR | EPOLLHUP;
+		if (wakeEvents & WL_SOCKET_READABLE)
+			epoll_ev.events |= EPOLLIN;
+		if (wakeEvents & WL_SOCKET_WRITEABLE)
+			epoll_ev.events |= EPOLLOUT;
+		rc = epoll_ctl(set->epoll_fd, EPOLL_CTL_ADD, sock, &epoll_ev);
+		if (rc < 0 && errno != EEXIST)
+			ereport(ERROR,
+					(errcode_for_socket_access(),
+					 errmsg("epoll_ctl() failed: %m")));
+	}
+#endif
 
 	if (wakeEvents & WL_TIMEOUT)
 		Assert(timeout >= 0);
 	else
 		timeout = -1;
 
-	if (wakeEvents & WL_LATCH_SET)
-		AddWaitEventToSet(set, WL_LATCH_SET, PGINVALID_SOCKET,
-						  (Latch *) latch, NULL);
-
-	if (wakeEvents & WL_POSTMASTER_DEATH && IsUnderPostmaster)
-		AddWaitEventToSet(set, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
-						  NULL, NULL);
-
-	if (wakeEvents & WL_SOCKET_MASK)
-	{
-		int			ev;
-
-		ev = wakeEvents & WL_SOCKET_MASK;
-		AddWaitEventToSet(set, ev, sock, NULL, NULL);
-	}
-
 	rc = WaitEventSetWait(set, timeout, &event, 1, wait_event_info);
 
 	if (rc == 0)
@@ -392,9 +438,6 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 							   WL_POSTMASTER_DEATH |
 							   WL_SOCKET_MASK);
 	}
-
-	FreeWaitEventSet(set);
-
 	return ret;
 }
 
