diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 7a48973b3c..f2d4119cce 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -4519,9 +4519,12 @@ ANY num_sync ( dbid, sub->oid, sub->name,
sub->owner, InvalidOid);
@@ -886,13 +885,16 @@ ApplyLauncherMain(Datum main_arg)
* usually means crash of the worker, so we should retry in
* wal_retrieve_retry_interval again.
*/
- wait_time = wal_retrieve_retry_interval;
+ work_done = true;
}
+ SET_DELAY_OR_HIBERNATE(work_done,
+ wal_retrieve_retry_interval);
+
/* Wait for more work. */
rc = WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- wait_time,
+ cur_timeout,
WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);
if (rc & WL_LATCH_SET)
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 82dcffc2db..deb4ed0583 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -196,8 +196,6 @@
#include "utils/syscache.h"
#include "utils/timeout.h"
-#define NAPTIME_PER_CYCLE 1000 /* max sleep time between cycles (1s) */
-
typedef struct FlushPosition
{
dlist_node node;
@@ -2656,6 +2654,7 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
bool ping_sent = false;
TimeLineID tli;
ErrorContextCallback errcallback;
+ DECLARE_HIBERNATE_VARS();
/*
* Init the ApplyMessageContext which we clean up after each replication
@@ -2692,7 +2691,6 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
int len;
char *buf = NULL;
bool endofstream = false;
- long wait_time;
CHECK_FOR_INTERRUPTS();
@@ -2726,6 +2724,7 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
/* Reset timeout. */
last_recv_timestamp = GetCurrentTimestamp();
ping_sent = false;
+ RESET_TO_NON_HIBERNATE();
/* Ensure we are reading the data into our memory context. */
MemoryContextSwitchTo(ApplyMessageContext);
@@ -2814,15 +2813,15 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
* no particular urgency about waking up unless we get data or a
* signal.
*/
- if (!dlist_is_empty(&lsn_mapping))
- wait_time = WalWriterDelay;
- else
- wait_time = NAPTIME_PER_CYCLE;
+ SET_DELAY_OR_HIBERNATE_OPT(!dlist_is_empty(&lsn_mapping),
+ WalWriterDelay,
+ wal_receiver_timeout / 2);
rc = WaitLatchOrSocket(MyLatch,
WL_SOCKET_READABLE | WL_LATCH_SET |
WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- fd, wait_time,
+ fd,
+ cur_timeout,
WAIT_EVENT_LOGICAL_APPLY_MAIN);
if (rc & WL_LATCH_SET)
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index ceaff097b9..f351038aca 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -95,7 +95,7 @@ bool hot_standby_feedback;
static WalReceiverConn *wrconn = NULL;
WalReceiverFunctionsType *WalReceiverFunctions = NULL;
-#define NAPTIME_PER_CYCLE 100 /* max sleep time between cycles (100ms) */
+#define WALRECEIVER_WAIT_PER_CYCLE 100L /* normal sleep time between cycles (100ms) */
/*
* These variables are used similarly to openLogFile/SegNo,
@@ -513,7 +513,7 @@ WalReceiverMain(void)
WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
WL_TIMEOUT | WL_LATCH_SET,
wait_fd,
- NAPTIME_PER_CYCLE,
+ wal_receiver_timeout / 2,
WAIT_EVENT_WAL_RECEIVER_MAIN);
if (rc & WL_LATCH_SET)
{
diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c
index 90798b9d53..dfb2d9836b 100644
--- a/src/backend/replication/walreceiverfuncs.c
+++ b/src/backend/replication/walreceiverfuncs.c
@@ -33,12 +33,6 @@
WalRcvData *WalRcv = NULL;
-/*
- * How long to wait for walreceiver to start up after requesting
- * postmaster to launch it. In seconds.
- */
-#define WALRCV_STARTUP_TIMEOUT 10
-
/* Report shared memory space needed by WalRcvShmemInit */
Size
WalRcvShmemSize(void)
@@ -96,7 +90,7 @@ WalRcvRunning(void)
{
pg_time_t now = (pg_time_t) time(NULL);
- if ((now - startTime) > WALRCV_STARTUP_TIMEOUT)
+ if ((now - startTime) > wal_receiver_timeout)
{
bool stopped = false;
@@ -147,7 +141,7 @@ WalRcvStreaming(void)
{
pg_time_t now = (pg_time_t) time(NULL);
- if ((now - startTime) > WALRCV_STARTUP_TIMEOUT)
+ if ((now - startTime) > wal_receiver_timeout)
{
bool stopped = false;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 932aefc777..f42bd18aa2 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3995,7 +3995,8 @@ static struct config_string ConfigureNamesString[] =
{
{"promote_trigger_file", PGC_SIGHUP, REPLICATION_STANDBY,
gettext_noop("Specifies a file name whose presence ends recovery in the standby."),
- NULL
+ NULL,
+ GUC_NOT_IN_SAMPLE
},
&PromoteTriggerFile,
"",
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 4cf5b26a36..e61a361e80 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -325,7 +325,6 @@
#primary_conninfo = '' # connection string to sending server
#primary_slot_name = '' # replication slot on sending server
-#promote_trigger_file = '' # file name whose presence ends recovery
#hot_standby = on # "off" disallows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 0dd79d73fa..4301f189f5 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -83,6 +83,30 @@
* only, so using any latch other than the process latch effectively precludes
* use of any generic handler.
*
+ * Since not all servers have a 24/7 duty cycle, if a worker has no current
+ * work then it can be enhanced to eventually hibernate. The goal here is
+ * to reduce the CPU wakeups and thus reduce electrical power consumption,
+ * making a small contribution to reducing global warming. To support
+ * hibernation, we add some additional macros to judge when, and if, to
+ * switch from a normal loop wait to hibernate timeout.
+ * Using these macros helps have a common design pattern for hibernation.
+ * 1. Add in DECLARE_HIBERNATE_VARS(); to add private vars to this module
+ * 2. Add SET_DELAY_OR_HIBERNATE() to eventually hibernate if idle
+ * 3. Use the variable "curr_timeout" in WaitLatch()
+ * Hibernation for this worker is independent of any other worker.
+ *
+ * #define normal_wait_timeout_ms = 100L;
+ * DECLARE_HIBERNATE_VARS();
+ * for (;;)
+ * {
+ * if (work to do)
+ * Do Stuff(); // in particular, exit loop if some condition satisfied
+ * SET_DELAY_OR_HIBERNATE(work_to_do, normal_wait_timeout_ms);
+ * WaitLatch(cur_timeout);
+ * ResetLatch();
+ * }
+ * A working example is shown in src/test/modules/worker_spi/worker_spi.c
+ * as well as in code for workers in src/backend/postmaster et al.
*
* WaitEventSets allow to wait for latches being set and additional events -
* postmaster dying and socket readiness of several sockets currently - at the
@@ -140,6 +164,51 @@ typedef struct Latch
WL_SOCKET_CONNECTED | \
WL_SOCKET_CLOSED)
+/*
+ * Common design pattern for Hibernation.
+ *
+ * Sleep until we are signaled or normal_delay has elapsed. If we
+ * haven't done any work for quite some time, lengthen the sleep
+ * time so as to reduce the server's idle power consumption.
+ *
+ * Avoids use static vars to allow each process to
+ * have its own private, independent counters.
+ */
+
+#define HIBERNATE_DELAY_SEC 300
+#define HIBERNATE_DELAY_MS (1000L * HIBERNATE_DELAY_SEC)
+#define LOOPS_UNTIL_HIBERNATE 50
+
+#define SET_DELAY_OR_HIBERNATE(work_done, normal_delay) \
+ if (work_done) \
+ left_till_hibernate = LOOPS_UNTIL_HIBERNATE;\
+ else if (left_till_hibernate > 0) \
+ left_till_hibernate--; \
+ if (left_till_hibernate > 0) \
+ cur_timeout = normal_delay; \
+ else \
+ cur_timeout = HIBERNATE_DELAY_MS;
+
+#define SET_DELAY_OR_HIBERNATE_OPT(work_done, normal_delay, hib_delay) \
+ if (work_done) \
+ left_till_hibernate = LOOPS_UNTIL_HIBERNATE;\
+ else if (left_till_hibernate > 0) \
+ left_till_hibernate--; \
+ if (left_till_hibernate > 0) \
+ cur_timeout = normal_delay; \
+ else \
+ cur_timeout = hib_delay;
+
+#define AM_HIBERNATING() (left_till_hibernate == 0)
+
+#define DECLARE_HIBERNATE_VARS() \
+int left_till_hibernate = LOOPS_UNTIL_HIBERNATE;\
+long cur_timeout;
+
+#define RESET_TO_NON_HIBERNATE() \
+ left_till_hibernate = LOOPS_UNTIL_HIBERNATE;
+
+
typedef struct WaitEvent
{
int pos; /* position in the event data structure */
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 48829df29c..fd907a6511 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -46,6 +46,8 @@ PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(worker_spi_launch);
+DECLARE_HIBERNATE_VARS();
+
void _PG_init(void);
void worker_spi_main(Datum) pg_attribute_noreturn();
@@ -137,6 +139,7 @@ worker_spi_main(Datum main_arg)
worktable *table;
StringInfoData buf;
char name[20];
+ bool work_done = true;
table = palloc(sizeof(worktable));
sprintf(name, "schema%d", index);
@@ -191,6 +194,13 @@ worker_spi_main(Datum main_arg)
{
int ret;
+ /*
+ * Use the standard design pattern for wait time/hibernation.
+ * After 50 consecutive loops with work_done=false the wait time
+ * will be set to the standard hibernation timeout.
+ */
+ SET_DELAY_OR_HIBERNATE(work_done, worker_spi_naptime * 1000L);
+
/*
* Background workers mustn't call usleep() or any direct equivalent:
* instead, they may wait on their process latch, which sleeps as
@@ -199,7 +209,7 @@ worker_spi_main(Datum main_arg)
*/
(void) WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- worker_spi_naptime * 1000L,
+ cur_timeout,
PG_WAIT_EXTENSION);
ResetLatch(MyLatch);
@@ -256,7 +266,10 @@ worker_spi_main(Datum main_arg)
elog(LOG, "%s: count in %s.%s is now %d",
MyBgworkerEntry->bgw_name,
table->schema, table->name, val);
+ work_done = true;
}
+ else
+ work_done = false;
/*
* And finish our transaction.