From ac96535c6f387ff84658b5a3420bd104aa65ced4 Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Tue, 20 Jan 2026 16:34:39 +0100
Subject: Add GUC for checkpointer request queue size

Currently, the checkpointer request queue size is auto-tuned to use
min(NBuffers, MAX_CHECKPOINT_REQUESTS). Contrary to other auto-tuned
subsystems, this setting isn't exposed through a GUC.

To make the behaviour consistent with other auto-tuned subsystems, this
patch introduces a new checkpoint_request_size GUC with the matching
auto-tune function.
---
 src/backend/postmaster/checkpointer.c         | 43 +++++++++++++++----
 src/backend/storage/ipc/ipci.c                |  1 +
 src/backend/utils/misc/guc_parameters.dat     |  9 ++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/postmaster/bgwriter.h             |  4 ++
 5 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 6482c21b8f9..775f06cfd64 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -148,14 +148,12 @@ static CheckpointerShmemStruct *CheckpointerShmem;
 /* Maximum number of checkpointer requests to process in one batch */
 #define CKPT_REQ_BATCH_SIZE 10000
 
-/* Max number of requests the checkpointer request queue can hold */
-#define MAX_CHECKPOINT_REQUESTS 10000000
-
 /*
  * GUC parameters
  */
 int			CheckPointTimeout = 300;
 int			CheckPointWarning = 30;
+int			CheckPointRequestSize = 0;
 double		CheckPointCompletionTarget = 0.9;
 
 /*
@@ -958,17 +956,44 @@ CheckpointerShmemSize(void)
 {
 	Size		size;
 
+	Assert(CheckPointRequestSize > 0);
+
+	size = offsetof(CheckpointerShmemStruct, requests);
+	size = add_size(size, mul_size(CheckPointRequestSize,
+								   sizeof(CheckpointerRequest)));
+	return size;
+}
+
+/*
+ * Auto-tune checkpoint_request_size based on shared_buffers
+ */
+void
+CheckpointerAutotune(void)
+{
+	char		buf[32];
+
+	if (CheckPointRequestSize != 0)
+		return;
+
 	/*
 	 * The size of the requests[] array is arbitrarily set equal to NBuffers.
 	 * But there is a cap of MAX_CHECKPOINT_REQUESTS to prevent accumulating
 	 * too many checkpoint requests in the ring buffer.
 	 */
-	size = offsetof(CheckpointerShmemStruct, requests);
-	size = add_size(size, mul_size(Min(NBuffers,
-									   MAX_CHECKPOINT_REQUESTS),
-								   sizeof(CheckpointerRequest)));
+	snprintf(buf, sizeof(buf), "%d", Min(NBuffers,
+									   MAX_CHECKPOINT_REQUESTS));
+	SetConfigOption("checkpoint_request_size", buf, PGC_POSTMASTER,
+					PGC_S_DYNAMIC_DEFAULT);
 
-	return size;
+	/*
+	 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
+	 * However, if the DBA explicitly set checkpoint_request_size = 0 in the config file,
+	 * then PGC_S_DYNAMIC_DEFAULT will fail to override that and we must force
+	 * the matter with PGC_S_OVERRIDE.
+	 */
+	if (CheckPointRequestSize == 0)	/* failed to apply it? */
+		SetConfigOption("checkpoint_request_size", buf, PGC_POSTMASTER,
+						PGC_S_OVERRIDE);
 }
 
 /*
@@ -995,7 +1020,7 @@ CheckpointerShmemInit(void)
 		 */
 		MemSet(CheckpointerShmem, 0, size);
 		SpinLockInit(&CheckpointerShmem->ckpt_lck);
-		CheckpointerShmem->max_requests = Min(NBuffers, MAX_CHECKPOINT_REQUESTS);
+		CheckpointerShmem->max_requests = CheckPointRequestSize;
 		CheckpointerShmem->head = CheckpointerShmem->tail = 0;
 		ConditionVariableInit(&CheckpointerShmem->start_cv);
 		ConditionVariableInit(&CheckpointerShmem->done_cv);
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 673a55313f4..88e52664ebe 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -386,6 +386,7 @@ AutotuneShmem(void)
 	Size		requested_size;
 
 	AioAutotune();
+	CheckpointerAutotune();
 	CLOGAutotune();
 	CommitTsAutotune();
 	SUBTRANSAutotune();
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index f0260e6e412..48590622b95 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -379,6 +379,15 @@
   max => 'WRITEBACK_MAX_PENDING_FLUSHES',
 },
 
+{ name => 'checkpoint_request_size', type => 'int', context => 'PGC_SIGHUP', group => 'WAL_CHECKPOINTS',
+  short_desc => 'Number of requests the checkpointer request queue can hold.',
+  long_desc => '0 means use the same value as "shared_buffers".',
+  variable => 'CheckPointRequestSize',
+  boot_val => '0',
+  min => '0',
+  max => 'MAX_CHECKPOINT_REQUESTS',
+},
+
 { name => 'checkpoint_timeout', type => 'int', context => 'PGC_SIGHUP', group => 'WAL_CHECKPOINTS',
   short_desc => 'Sets the maximum time between automatic WAL checkpoints.',
   flags => 'GUC_UNIT_S',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c4f92fcdac8..256e8040092 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -263,6 +263,7 @@
 #checkpoint_timeout = 5min              # range 30s-1d
 #checkpoint_completion_target = 0.9     # checkpoint target duration, 0.0 - 1.0
 #checkpoint_flush_after = 0             # measured in pages, 0 disables
+#checkpoint_request_size = 0            # 0 sets based on shared_buffers
 #checkpoint_warning = 30s               # 0 disables
 #max_wal_size = 1GB
 #min_wal_size = 80MB
diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h
index 47470cba893..674d6bbd8fd 100644
--- a/src/include/postmaster/bgwriter.h
+++ b/src/include/postmaster/bgwriter.h
@@ -21,11 +21,14 @@
 #include "storage/smgr.h"
 #include "storage/sync.h"
 
+/* Max number of requests the checkpointer request queue can hold */
+#define MAX_CHECKPOINT_REQUESTS 10000000
 
 /* GUC options */
 extern PGDLLIMPORT int BgWriterDelay;
 extern PGDLLIMPORT int CheckPointTimeout;
 extern PGDLLIMPORT int CheckPointWarning;
+extern PGDLLIMPORT int CheckPointRequestSize;
 extern PGDLLIMPORT double CheckPointCompletionTarget;
 
 pg_noreturn extern void BackgroundWriterMain(const void *startup_data, size_t startup_data_len);
@@ -40,6 +43,7 @@ extern bool ForwardSyncRequest(const FileTag *ftag, SyncRequestType type);
 extern void AbsorbSyncRequests(void);
 
 extern Size CheckpointerShmemSize(void);
+extern void CheckpointerAutotune(void);
 extern void CheckpointerShmemInit(void);
 
 extern bool FirstCallSinceLastCheckpoint(void);
-- 
2.52.0

