Auto-tune shared_buffers to use available huge pages

Started by Anthonin Bonnefoyabout 10 hours ago2 messages
#1Anthonin Bonnefoy
anthonin.bonnefoy@datadoghq.com
5 attachment(s)

Hi,

Under a normal environment, the instance's number of huge pages can be
adjusted to the size reported by shared_memory_size_in_huge_pages,
then Postgres can be started and the requested shared memory fit in
the available huge pages.

A similar approach is harder to implement with environments like
kubernetes. If I want to modify the huge pages on a pod, I need to:
- Modify the host's huge pages
- Restart the host's kubelet so it detects the new amount of huge pages
- Modify the pod's huge page request

Most of those steps are far from practical. An alternative would be to
have a fixed number of huge pages (like 25% of the node's memory), and
to adjust the configuration, like the amount of shared_buffers.
However, adjusting the configuration to fit in a fixed amount of
memory is tricky:
- shared_buffers is used to auto-tune multiple parameters so there's
no easy formula to get the correct amount. The only way I've found is
to basically increase shared_buffers until
shared_memory_size_in_huge_pages matches the desired amount of huge
pages
- changing other parameters like max_connections mean shared_buffers
has to be adjusted again

To help with that, the attached patch provides a new option,
huge_pages_autotune_buffers, to automatically use leftover huge pages
as shared_buffers. This requires some changes in the auto-tune logic:
- Subsystems that are using shared_buffers for auto-tuning will rely
on the configured shared_buffers, not the auto-tuned shared_buffers
and they should save the auto-tuned value in a GUC. This will be done
in dedicated auto-tune functions.
- Once the auto-tune functions are called, modifying NBuffers won't
change the requested memory except for the shared buffer pool in
BufferManagerShmemSize
- We can get the leftover memory (free huge pages - requested memory),
and estimate how much shared_buffers we can add
- Increasing shared_buffers will also increase the freelist hashmap,
so the auto-tuned shared_buffers needs to be reduced

The patch is split in the following sub-patches:

0001: Extract the current auto-tune logic in dedicated functions,
making the behaviour more consistent across subsystems.

0002: The checkpointer auto-tunes the request size using NBuffers, but
doesn't save the result in a GUC. This adds a new
checkpoint_request_size GUC with the same auto-tune logic.

0003: Extract HugePages_Free value when /proc/meminfo is parsed in
GetHugePageSize.

0004: Pass NBuffers as parameters to StrategyShmemSize. This is
necessary to get how much memory will be used by the freelist using
'StrategyShmemSize(candidate_nbuffers) - StrategyShmemSize(NBuffers)'.

0005: Add BufferManagerAutotune to auto-tune the amount of shared_buffers.

Regards,
Anthonin Bonnefoy

Attachments:

v1-0003-Extract-HugePages_Free-value-in-GetHugePageSize.patchapplication/octet-stream; name=v1-0003-Extract-HugePages_Free-value-in-GetHugePageSize.patchDownload
From 65145d500725db28c27abf1a4a73c535675abd1a Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Mon, 27 Oct 2025 10:10:32 +0100
Subject: Extract HugePages_Free value in GetHugePageSize

Currently, GetHugePageSize only extracts the huge page size from
/proc/meminfo. This patch adds the extraction of HugePages_Free which
will be necessary to auto-tune shared_buffers.
---
 src/backend/port/sysv_shmem.c   | 23 ++++++++++++++++-------
 src/backend/port/win32_shmem.c  |  4 +++-
 src/backend/storage/ipc/ipci.c  |  2 +-
 src/backend/storage/ipc/shmem.c |  2 +-
 src/include/storage/pg_shmem.h  |  2 +-
 5 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index de491897118..09a02271a15 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -470,18 +470,19 @@ PGSharedMemoryAttach(IpcMemoryId shmId,
  * hugepage sizes, we might want to think about more invasive strategies,
  * such as increasing shared_buffers to absorb the extra space.
  *
- * Returns the (real, assumed or config provided) page size into
- * *hugepagesize, and the hugepage-related mmap flags to use into
- * *mmap_flags if requested by the caller.  If huge pages are not supported,
- * *hugepagesize and *mmap_flags are set to 0.
+ * Returns the (real, assumed or config provided) page size into *hugepagesize,
+ * the amount of free huge pages into *hugepagefree, and the hugepage-related
+ * mmap flags to use into *mmap_flags if requested by the caller.  If huge pages
+ * are not supported, *hugepagesize, *hugepagefree and *mmap_flags are set to 0.
  */
 void
-GetHugePageSize(Size *hugepagesize, int *mmap_flags)
+GetHugePageSize(Size *hugepagesize, Size *hugepagefree, int *mmap_flags)
 {
 #ifdef MAP_HUGETLB
 
 	Size		default_hugepagesize = 0;
 	Size		hugepagesize_local = 0;
+	Size		hugepagefree_local = 0;
 	int			mmap_flags_local = 0;
 
 	/*
@@ -502,7 +503,11 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags)
 		{
 			while (fgets(buf, sizeof(buf), fp))
 			{
-				if (sscanf(buf, "Hugepagesize: %u %c", &sz, &ch) == 2)
+				if (sscanf(buf, "HugePages_Free: %u", &sz) == 1)
+				{
+					hugepagefree_local = sz;
+				}
+				else if (sscanf(buf, "Hugepagesize: %u %c", &sz, &ch) == 2)
 				{
 					if (ch == 'k')
 					{
@@ -560,11 +565,15 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags)
 		*mmap_flags = mmap_flags_local;
 	if (hugepagesize)
 		*hugepagesize = hugepagesize_local;
+	if (hugepagefree)
+		*hugepagefree = hugepagefree_local * hugepagesize_local;
 
 #else
 
 	if (hugepagesize)
 		*hugepagesize = 0;
+	if (hugepagefree)
+		*hugepagefree = 0;
 	if (mmap_flags)
 		*mmap_flags = 0;
 
@@ -614,7 +623,7 @@ CreateAnonymousSegment(Size *size)
 		Size		hugepagesize;
 		int			mmap_flags;
 
-		GetHugePageSize(&hugepagesize, &mmap_flags);
+		GetHugePageSize(&hugepagesize, NULL, &mmap_flags);
 
 		if (allocsize % hugepagesize != 0)
 			allocsize += hugepagesize - (allocsize % hugepagesize);
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index 7cb8b4c9b60..9402aa5da28 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -627,10 +627,12 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
  * use GetLargePageMinimum() instead.
  */
 void
-GetHugePageSize(Size *hugepagesize, int *mmap_flags)
+GetHugePageSize(Size *hugepagesize, Size *hugepagefree, int *mmap_flags)
 {
 	if (hugepagesize)
 		*hugepagesize = 0;
+	if (hugepagefree)
+		*hugepagefree = 0;
 	if (mmap_flags)
 		*mmap_flags = 0;
 }
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 88e52664ebe..a4f182a9081 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -358,7 +358,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the number of huge pages required.
 	 */
-	GetHugePageSize(&hp_size, NULL);
+	GetHugePageSize(&hp_size, NULL, NULL);
 	if (hp_size != 0)
 	{
 		Size		hp_required;
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index d2f4710f141..0972e9c16a4 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -746,7 +746,7 @@ pg_get_shmem_pagesize(void)
 	Assert(huge_pages_status != HUGE_PAGES_UNKNOWN);
 
 	if (huge_pages_status == HUGE_PAGES_ON)
-		GetHugePageSize(&os_page_size, NULL);
+		GetHugePageSize(&os_page_size, NULL, NULL);
 
 	return os_page_size;
 }
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3aeada554b2..7b6efc9f306 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -89,6 +89,6 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size,
 										   PGShmemHeader **shim);
 extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
-extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
+extern void GetHugePageSize(Size *hugepagesize, Size *hugepagefree, int *mmap_flags);
 
 #endif							/* PG_SHMEM_H */
-- 
2.52.0

v1-0004-Pass-NBuffers-as-parameter-to-StrategyShmemSize.patchapplication/octet-stream; name=v1-0004-Pass-NBuffers-as-parameter-to-StrategyShmemSize.patchDownload
From 0504a7c998309056459c6cfae981c1f9e8d7a997 Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Tue, 20 Jan 2026 10:47:37 +0100
Subject: Pass NBuffers as parameter to StrategyShmemSize

When computing the necessary shared memory for the buffer pool, the
NBuffer global variable is directly accessed. This prevents the possible
auto-tuning of NBuffer as we can't accurately compute the additional
memory from modifying NBuffer.

This patch passes NBuffers as a parameter, allowing to call
StrategyShmemSize with different values of NBuffers.
---
 src/backend/storage/buffer/buf_init.c | 2 +-
 src/backend/storage/buffer/freelist.c | 4 ++--
 src/include/storage/buf_internals.h   | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index c0c223b2e32..6a57ab9cf30 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -163,7 +163,7 @@ BufferManagerShmemSize(void)
 	size = add_size(size, mul_size(NBuffers, BLCKSZ));
 
 	/* size of stuff controlled by freelist.c */
-	size = add_size(size, StrategyShmemSize());
+	size = add_size(size, StrategyShmemSize(NBuffers));
 
 	/* size of I/O condition variables */
 	size = add_size(size, mul_size(NBuffers,
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index b7687836188..a8fe67ac766 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -377,12 +377,12 @@ StrategyNotifyBgWriter(int bgwprocno)
  * is also determined here.
  */
 Size
-StrategyShmemSize(void)
+StrategyShmemSize(int num_buffers)
 {
 	Size		size = 0;
 
 	/* size of lookup hash table ... see comment in StrategyInitialize */
-	size = add_size(size, BufTableShmemSize(NBuffers + NUM_BUFFER_PARTITIONS));
+	size = add_size(size, BufTableShmemSize(num_buffers + NUM_BUFFER_PARTITIONS));
 
 	/* size of the shared replacement strategy control block */
 	size = add_size(size, MAXALIGN(sizeof(BufferStrategyControl)));
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 27f12502d19..cb1106df515 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -571,7 +571,7 @@ extern bool StrategyRejectBuffer(BufferAccessStrategy strategy,
 extern int	StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc);
 extern void StrategyNotifyBgWriter(int bgwprocno);
 
-extern Size StrategyShmemSize(void);
+extern Size StrategyShmemSize(int num_buffers);
 extern void StrategyInitialize(bool init);
 
 /* buf_table.c */
-- 
2.52.0

v1-0005-Auto-tune-shared_buffers-to-use-available-huge-pa.patchapplication/octet-stream; name=v1-0005-Auto-tune-shared_buffers-to-use-available-huge-pa.patchDownload
From bc4a62b90a5361e22beb3f10190bcff224a8b7b0 Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Tue, 20 Jan 2026 10:55:33 +0100
Subject: Auto-tune shared_buffers to use available huge pages

With some environments, it's not possible to modify the amount of huge
pages and only a fixed quantity is available. With this fixed amount,
shared_buffers can be adjusted to try to fit and use as much huge pages
as possible. However, this is very brittle as any modifying other
parameters, like the amount of max_connections, will change the
requested shared memory, and shared_buffers will need to be further
adjusted.

This patch introduces a new option to dynamically increase
shared_buffers to use all available huge pages.
---
 src/backend/storage/buffer/buf_init.c         | 51 +++++++++++++++++++
 src/backend/utils/misc/guc_parameters.dat     |  6 +++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/storage/bufmgr.h                  |  1 +
 src/include/storage/pg_shmem.h                |  1 +
 5 files changed, 60 insertions(+)

diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index 6a57ab9cf30..8f3f3055c15 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -14,16 +14,19 @@
  */
 #include "postgres.h"
 
+#include "utils/guc.h"
 #include "storage/aio.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
 #include "storage/proclist.h"
+#include "storage/pg_shmem.h"
 
 BufferDescPadded *BufferDescriptors;
 char	   *BufferBlocks;
 ConditionVariableMinimallyPadded *BufferIOCVArray;
 WritebackContext BackendWritebackContext;
 CkptSortItem *CkptBufferIds;
+bool		huge_pages_autotune_buffers = false;
 
 
 /*
@@ -142,6 +145,54 @@ BufferManagerShmemInit(void)
 						 &backend_flush_after);
 }
 
+/*
+ * BufferManagerAutotune
+ *
+ * auto-tune shared_buffers to use all remaining huge pages if
+ * huge_pages_autotune_buffers is enabled
+ */
+void
+BufferManagerAutotune(Size requested_size)
+{
+	char		buf[32];
+	Size		leftover_memory;
+	Size		hugepagefree;
+	Size		size_per_buffer;
+	Size		additional_freelist_memory;
+	int			candidate_nbuffers;
+
+	if (!huge_pages_autotune_buffers)
+		/* No auto-tune requested */
+		return;
+
+	GetHugePageSize(NULL, &hugepagefree, NULL);
+	leftover_memory = hugepagefree - requested_size;
+	if (leftover_memory <= 0)
+		/* No leftover */
+		return;
+
+	size_per_buffer = sizeof(BufferDescPadded) +
+		sizeof(ConditionVariableMinimallyPadded) +
+		sizeof(CkptSortItem) + BLCKSZ;
+	candidate_nbuffers = NBuffers + leftover_memory / size_per_buffer;
+
+	/*
+	 * With the additional shared_buffers, the shared memory necessary for
+	 * freelist-related structures will increase. We need to estimated this
+	 * additional memory, and reduce the auto-tuned shared_buffers to fit in
+	 * the available memory.
+	 */
+	additional_freelist_memory = StrategyShmemSize(candidate_nbuffers) - StrategyShmemSize(NBuffers);
+	candidate_nbuffers -= additional_freelist_memory / size_per_buffer;
+
+	if (candidate_nbuffers <= 0)
+		return;
+
+	snprintf(buf, sizeof(buf), "%d", candidate_nbuffers);
+	SetConfigOption("shared_buffers", buf, PGC_POSTMASTER,
+					PGC_S_OVERRIDE);
+}
+
 /*
  * BufferManagerShmemSize
  *
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 48590622b95..ff5ed89b307 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1209,6 +1209,12 @@
   options => 'huge_pages_options',
 },
 
+{ name => 'huge_pages_autotune_buffers', type => 'bool', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
+  short_desc => 'Autotune shared_buffers to use all free huge pages.',
+  variable => 'huge_pages_autotune_buffers',
+  boot_val => 'false',
+},
+
 { name => 'huge_pages_status', type => 'enum', context => 'PGC_INTERNAL', group => 'PRESET_OPTIONS',
   short_desc => 'Indicates the status of huge pages.',
   flags => 'GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 256e8040092..ea600188841 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -133,6 +133,7 @@
                                         # (change requires restart)
 #huge_pages = try                       # on, off, or try
                                         # (change requires restart)
+#huge_pages_autotune_buffers = off      # (change requires restart)
 #huge_page_size = 0                     # zero for system default
                                         # (change requires restart)
 #temp_buffers = 8MB                     # min 800kB
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index a40adf6b2a8..09fe0258095 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -368,6 +368,7 @@ extern void MarkDirtyAllUnpinnedBuffers(int32 *buffers_dirtied,
 /* in buf_init.c */
 extern void BufferManagerShmemInit(void);
 extern Size BufferManagerShmemSize(void);
+extern void BufferManagerAutotune(Size requested_size);
 
 /* in localbuf.c */
 extern void AtProcExit_LocalBuffers(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 7b6efc9f306..4b1d61e6b33 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -46,6 +46,7 @@ extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
 extern PGDLLIMPORT int huge_page_size;
 extern PGDLLIMPORT int huge_pages_status;
+extern PGDLLIMPORT bool huge_pages_autotune_buffers;
 
 /* Possible values for huge_pages and huge_pages_status */
 typedef enum
-- 
2.52.0

v1-0002-Add-GUC-for-checkpointer-request-queue-size.patchapplication/octet-stream; name=v1-0002-Add-GUC-for-checkpointer-request-queue-size.patchDownload
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

v1-0001-Create-dedicated-shmem-Autotune-functions.patchapplication/octet-stream; name=v1-0001-Create-dedicated-shmem-Autotune-functions.patchDownload
From c3a348938ad6eb3abf7ce7f520f10fb8c1404b14 Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Mon, 27 Oct 2025 09:27:06 +0100
Subject: Create dedicated shmem Autotune functions

Some subsystems auto-tune their shmem requests based on the shared_buffers
value. The current behaviour is inconsistent:
- Aio and XLOG modify their respective GUCs in *ShmemSize
- CLOG, CommitTs, SUBTRANS modify their respective GUCs in *ShmemInit
- Checkpointer doesn't save the auto-tuned value

This patch introduces dedicated auto-tune functions for Aio, XLOG, CLOG,
CommitTS and SUBTRANS. A new AutotuneShmem function is responsible for calling
all the new auto-tune functions.
---
 src/backend/access/transam/clog.c      | 52 ++++++++++++------------
 src/backend/access/transam/commit_ts.c | 52 ++++++++++++------------
 src/backend/access/transam/subtrans.c  | 56 +++++++++++++-------------
 src/backend/access/transam/xlog.c      | 46 ++++++++++++---------
 src/backend/bootstrap/bootstrap.c      |  5 +++
 src/backend/postmaster/postmaster.c    |  5 +++
 src/backend/storage/aio/aio_init.c     | 31 ++++++++------
 src/backend/storage/ipc/ipci.c         | 22 ++++++++++
 src/backend/tcop/postgres.c            |  5 +++
 src/include/access/clog.h              |  1 +
 src/include/access/commit_ts.h         |  1 +
 src/include/access/subtrans.h          |  1 +
 src/include/access/xlog.h              |  1 +
 src/include/storage/aio_subsys.h       |  1 +
 src/include/storage/ipc.h              |  1 +
 15 files changed, 170 insertions(+), 110 deletions(-)

diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index b5c38bbb162..2925ecbece4 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -756,6 +756,32 @@ TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
 	return status;
 }
 
+/*
+ * Auto-tune transaction_buffers based on shared buffers
+ */
+void
+CLOGAutotune(void)
+{
+	char		buf[32];
+
+	if (transaction_buffers != 0)
+		return;
+
+	snprintf(buf, sizeof(buf), "%d", SimpleLruAutotuneBuffers(512, 1024));
+	SetConfigOption("transaction_buffers", buf, PGC_POSTMASTER,
+					PGC_S_DYNAMIC_DEFAULT);
+
+	/*
+	 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
+	 * However, if the DBA explicitly set transaction_buffers = 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 (transaction_buffers == 0)	/* failed to apply it? */
+		SetConfigOption("transaction_buffers", buf, PGC_POSTMASTER,
+						PGC_S_OVERRIDE);
+}
+
 /*
  * Number of shared CLOG buffers.
  *
@@ -766,10 +792,7 @@ TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
 static int
 CLOGShmemBuffers(void)
 {
-	/* auto-tune based on shared buffers */
-	if (transaction_buffers == 0)
-		return SimpleLruAutotuneBuffers(512, 1024);
-
+	Assert(transaction_buffers > 0);
 	return Min(Max(16, transaction_buffers), CLOG_MAX_ALLOWED_BUFFERS);
 }
 
@@ -785,27 +808,6 @@ CLOGShmemSize(void)
 void
 CLOGShmemInit(void)
 {
-	/* If auto-tuning is requested, now is the time to do it */
-	if (transaction_buffers == 0)
-	{
-		char		buf[32];
-
-		snprintf(buf, sizeof(buf), "%d", CLOGShmemBuffers());
-		SetConfigOption("transaction_buffers", buf, PGC_POSTMASTER,
-						PGC_S_DYNAMIC_DEFAULT);
-
-		/*
-		 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
-		 * However, if the DBA explicitly set transaction_buffers = 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 (transaction_buffers == 0)	/* failed to apply it? */
-			SetConfigOption("transaction_buffers", buf, PGC_POSTMASTER,
-							PGC_S_OVERRIDE);
-	}
-	Assert(transaction_buffers != 0);
-
 	XactCtl->PagePrecedes = CLOGPagePrecedes;
 	SimpleLruInit(XactCtl, "transaction", CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE,
 				  "pg_xact", LWTRANCHE_XACT_BUFFER,
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 082b564da8f..f76b43bf25a 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -493,6 +493,32 @@ pg_xact_commit_timestamp_origin(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
 }
 
+/*
+ * Auto-tune commit_timestamp_buffers based on shared buffers
+ */
+void
+CommitTsAutotune(void)
+{
+	char		buf[32];
+
+	if (commit_timestamp_buffers != 0)
+		return;
+
+	snprintf(buf, sizeof(buf), "%d", SimpleLruAutotuneBuffers(512, 1024));
+	SetConfigOption("commit_timestamp_buffers", buf, PGC_POSTMASTER,
+					PGC_S_DYNAMIC_DEFAULT);
+
+	/*
+	 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
+	 * However, if the DBA explicitly set commit_timestamp_buffers = 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 (commit_timestamp_buffers == 0)	/* failed to apply it? */
+		SetConfigOption("commit_timestamp_buffers", buf, PGC_POSTMASTER,
+						PGC_S_OVERRIDE);
+}
+
 /*
  * Number of shared CommitTS buffers.
  *
@@ -503,10 +529,7 @@ pg_xact_commit_timestamp_origin(PG_FUNCTION_ARGS)
 static int
 CommitTsShmemBuffers(void)
 {
-	/* auto-tune based on shared buffers */
-	if (commit_timestamp_buffers == 0)
-		return SimpleLruAutotuneBuffers(512, 1024);
-
+	Assert(commit_timestamp_buffers > 0);
 	return Min(Max(16, commit_timestamp_buffers), SLRU_MAX_ALLOWED_BUFFERS);
 }
 
@@ -529,27 +552,6 @@ CommitTsShmemInit(void)
 {
 	bool		found;
 
-	/* If auto-tuning is requested, now is the time to do it */
-	if (commit_timestamp_buffers == 0)
-	{
-		char		buf[32];
-
-		snprintf(buf, sizeof(buf), "%d", CommitTsShmemBuffers());
-		SetConfigOption("commit_timestamp_buffers", buf, PGC_POSTMASTER,
-						PGC_S_DYNAMIC_DEFAULT);
-
-		/*
-		 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
-		 * However, if the DBA explicitly set commit_timestamp_buffers = 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 (commit_timestamp_buffers == 0)	/* failed to apply it? */
-			SetConfigOption("commit_timestamp_buffers", buf, PGC_POSTMASTER,
-							PGC_S_OVERRIDE);
-	}
-	Assert(commit_timestamp_buffers != 0);
-
 	CommitTsCtl->PagePrecedes = CommitTsPagePrecedes;
 	SimpleLruInit(CommitTsCtl, "commit_timestamp", CommitTsShmemBuffers(), 0,
 				  "pg_commit_ts", LWTRANCHE_COMMITTS_BUFFER,
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index c0987f43f11..32433b0333f 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -191,18 +191,11 @@ SubTransGetTopmostTransaction(TransactionId xid)
 
 /*
  * Number of shared SUBTRANS buffers.
- *
- * If asked to autotune, use 2MB for every 1GB of shared buffers, up to 8MB.
- * Otherwise just cap the configured amount to be between 16 and the maximum
- * allowed.
  */
 static int
 SUBTRANSShmemBuffers(void)
 {
-	/* auto-tune based on shared buffers */
-	if (subtransaction_buffers == 0)
-		return SimpleLruAutotuneBuffers(512, 1024);
-
+	Assert(subtransaction_buffers > 0);
 	return Min(Max(16, subtransaction_buffers), SLRU_MAX_ALLOWED_BUFFERS);
 }
 
@@ -215,30 +208,39 @@ SUBTRANSShmemSize(void)
 	return SimpleLruShmemSize(SUBTRANSShmemBuffers(), 0);
 }
 
+/*
+ * Auto-tune subtransaction_buffers based on shared_buffers
+ *
+ * If asked to autotune, use 2MB for every 1GB of shared buffers, up to 8MB.
+ * Otherwise just cap the configured amount to be between 16 and the maximum
+ * allowed.
+ */
 void
-SUBTRANSShmemInit(void)
+SUBTRANSAutotune(void)
 {
-	/* If auto-tuning is requested, now is the time to do it */
-	if (subtransaction_buffers == 0)
-	{
-		char		buf[32];
+	char		buf[32];
 
-		snprintf(buf, sizeof(buf), "%d", SUBTRANSShmemBuffers());
-		SetConfigOption("subtransaction_buffers", buf, PGC_POSTMASTER,
-						PGC_S_DYNAMIC_DEFAULT);
+	if (subtransaction_buffers != 0)
+		return;
 
-		/*
-		 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
-		 * However, if the DBA explicitly set subtransaction_buffers = 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 (subtransaction_buffers == 0)	/* failed to apply it? */
-			SetConfigOption("subtransaction_buffers", buf, PGC_POSTMASTER,
-							PGC_S_OVERRIDE);
-	}
-	Assert(subtransaction_buffers != 0);
+	snprintf(buf, sizeof(buf), "%d", SimpleLruAutotuneBuffers(512, 1024));
+	SetConfigOption("subtransaction_buffers", buf, PGC_POSTMASTER,
+					PGC_S_DYNAMIC_DEFAULT);
 
+	/*
+	 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
+	 * However, if the DBA explicitly set subtransaction_buffers = 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 (subtransaction_buffers == 0)	/* failed to apply it? */
+		SetConfigOption("subtransaction_buffers", buf, PGC_POSTMASTER,
+						PGC_S_OVERRIDE);
+}
+
+void
+SUBTRANSShmemInit(void)
+{
 	SubTransCtl->PagePrecedes = SubTransPagePrecedes;
 	SimpleLruInit(SubTransCtl, "subtransaction", SUBTRANSShmemBuffers(), 0,
 				  "pg_subtrans", LWTRANCHE_SUBTRANS_BUFFER,
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 81dc86847c0..fee808dd8d7 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4716,7 +4716,7 @@ check_wal_buffers(int *newval, void **extra, GucSource source)
 	{
 		/*
 		 * If we haven't yet changed the boot_val default of -1, just let it
-		 * be.  We'll fix it when XLOGShmemSize is called.
+		 * be.  We'll fix it when XLOGAutotune is called.
 		 */
 		if (XLOGbuffers == -1)
 			return true;
@@ -4956,35 +4956,41 @@ GetActiveWalLevelOnStandby(void)
 }
 
 /*
- * Initialization of shared memory for XLOG
+ * Auto-tune wal_buffers value
+ *
+ * If the value of wal_buffers is -1, use the preferred auto-tune value.
  */
-Size
-XLOGShmemSize(void)
+void
+XLOGAutotune(void)
 {
-	Size		size;
+	char		buf[32];
+
+	if (XLOGbuffers != -1)
+		return;
+
+	snprintf(buf, sizeof(buf), "%d", XLOGChooseNumBuffers());
+	SetConfigOption("wal_buffers", buf, PGC_POSTMASTER,
+					PGC_S_DYNAMIC_DEFAULT);
 
 	/*
-	 * If the value of wal_buffers is -1, use the preferred auto-tune value.
-	 * This isn't an amazingly clean place to do this, but we must wait till
-	 * NBuffers has received its final value, and must do it before using the
-	 * value of XLOGbuffers to do anything important.
-	 *
 	 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
 	 * However, if the DBA explicitly set wal_buffers = -1 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 (XLOGbuffers == -1)
-	{
-		char		buf[32];
-
-		snprintf(buf, sizeof(buf), "%d", XLOGChooseNumBuffers());
+	if (XLOGbuffers == -1)		/* failed to apply it? */
 		SetConfigOption("wal_buffers", buf, PGC_POSTMASTER,
-						PGC_S_DYNAMIC_DEFAULT);
-		if (XLOGbuffers == -1)	/* failed to apply it? */
-			SetConfigOption("wal_buffers", buf, PGC_POSTMASTER,
-							PGC_S_OVERRIDE);
-	}
+						PGC_S_OVERRIDE);
+}
+
+/*
+ * Initialization of shared memory for XLOG
+ */
+Size
+XLOGShmemSize(void)
+{
+	Size		size;
+
 	Assert(XLOGbuffers > 0);
 
 	/* XLogCtl */
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index dd57624b4f9..4bbedb44b53 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -337,6 +337,11 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
 
 	InitializeFastPathLocks();
 
+	/*
+	 * Give the chance for subsystems to auto-tune their values.
+	 */
+	AutotuneShmem();
+
 	CreateSharedMemoryAndSemaphores();
 
 	/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index d6133bfebc6..f4e60274924 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -963,6 +963,11 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	process_shmem_requests();
 
+	/*
+	 * Give the chance for subsystems to auto-tune their values.
+	 */
+	AutotuneShmem();
+
 	/*
 	 * Now that loadable modules have had their chance to request additional
 	 * shared memory, determine the value of any runtime-computed GUCs that
diff --git a/src/backend/storage/aio/aio_init.c b/src/backend/storage/aio/aio_init.c
index d3c68d8b04c..ec8d61dfb9c 100644
--- a/src/backend/storage/aio/aio_init.c
+++ b/src/backend/storage/aio/aio_init.c
@@ -109,10 +109,17 @@ AioChooseMaxConcurrency(void)
 	return Min(max_proportional_pins, 64);
 }
 
-Size
-AioShmemSize(void)
+void
+AioAutotune(void)
 {
-	Size		sz = 0;
+	char		buf[32];
+
+	if (io_max_concurrency != -1)
+		return;
+
+	snprintf(buf, sizeof(buf), "%d", AioChooseMaxConcurrency());
+	SetConfigOption("io_max_concurrency", buf, PGC_POSTMASTER,
+					PGC_S_DYNAMIC_DEFAULT);
 
 	/*
 	 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
@@ -120,17 +127,15 @@ AioShmemSize(void)
 	 * config file, then PGC_S_DYNAMIC_DEFAULT will fail to override that and
 	 * we must force the matter with PGC_S_OVERRIDE.
 	 */
-	if (io_max_concurrency == -1)
-	{
-		char		buf[32];
-
-		snprintf(buf, sizeof(buf), "%d", AioChooseMaxConcurrency());
+	if (io_max_concurrency == -1)	/* failed to apply it? */
 		SetConfigOption("io_max_concurrency", buf, PGC_POSTMASTER,
-						PGC_S_DYNAMIC_DEFAULT);
-		if (io_max_concurrency == -1)	/* failed to apply it? */
-			SetConfigOption("io_max_concurrency", buf, PGC_POSTMASTER,
-							PGC_S_OVERRIDE);
-	}
+						PGC_S_OVERRIDE);
+}
+
+Size
+AioShmemSize(void)
+{
+	Size		sz = 0;
 
 	sz = add_size(sz, AioCtlShmemSize());
 	sz = add_size(sz, AioBackendShmemSize());
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 85c67b2c183..673a55313f4 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -372,3 +372,25 @@ InitializeShmemGUCs(void)
 	sprintf(buf, "%d", ProcGlobalSemas());
 	SetConfigOption("num_os_semaphores", buf, PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT);
 }
+
+/*
+ * Auto-tune shared memory configuration
+ *
+ * Some subsystems auto-tune their configurations based on the NBuffers
+ * value. This must be called before CalculateShmemSize and will change the GUC
+ * values (when there is one) with the auto-tuned value.
+ */
+void
+AutotuneShmem(void)
+{
+	Size		requested_size;
+
+	AioAutotune();
+	CLOGAutotune();
+	CommitTsAutotune();
+	SUBTRANSAutotune();
+	XLOGAutotune();
+
+	requested_size = CalculateShmemSize();
+	BufferManagerAutotune(requested_size);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e54bf1e760f..0e8744ed37c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4133,6 +4133,11 @@ PostgresSingleUserMain(int argc, char *argv[],
 	 */
 	process_shmem_requests();
 
+	/*
+	 * Give the chance for subsystems to auto-tune their values.
+	 */
+	AutotuneShmem();
+
 	/*
 	 * Now that loadable modules have had their chance to request additional
 	 * shared memory, determine the value of any runtime-computed GUCs that
diff --git a/src/include/access/clog.h b/src/include/access/clog.h
index a1cfed5f43c..b2a9c0ac255 100644
--- a/src/include/access/clog.h
+++ b/src/include/access/clog.h
@@ -41,6 +41,7 @@ extern void TransactionIdSetTreeStatus(TransactionId xid, int nsubxids,
 extern XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn);
 
 extern Size CLOGShmemSize(void);
+extern void CLOGAutotune(void);
 extern void CLOGShmemInit(void);
 extern void BootStrapCLOG(void);
 extern void StartupCLOG(void);
diff --git a/src/include/access/commit_ts.h b/src/include/access/commit_ts.h
index bc3b81687b1..80b35d2e0dc 100644
--- a/src/include/access/commit_ts.h
+++ b/src/include/access/commit_ts.h
@@ -28,6 +28,7 @@ extern TransactionId GetLatestCommitTsData(TimestampTz *ts,
 										   RepOriginId *nodeid);
 
 extern Size CommitTsShmemSize(void);
+extern void CommitTsAutotune(void);
 extern void CommitTsShmemInit(void);
 extern void BootStrapCommitTs(void);
 extern void StartupCommitTs(void);
diff --git a/src/include/access/subtrans.h b/src/include/access/subtrans.h
index 11b7355dbdf..3772af83f68 100644
--- a/src/include/access/subtrans.h
+++ b/src/include/access/subtrans.h
@@ -16,6 +16,7 @@ extern TransactionId SubTransGetParent(TransactionId xid);
 extern TransactionId SubTransGetTopmostTransaction(TransactionId xid);
 
 extern Size SUBTRANSShmemSize(void);
+extern void SUBTRANSAutotune(void);
 extern void SUBTRANSShmemInit(void);
 extern void BootStrapSUBTRANS(void);
 extern void StartupSUBTRANS(TransactionId oldestActiveXID);
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 0591a885dd1..21741eb9254 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -245,6 +245,7 @@ extern bool DataChecksumsEnabled(void);
 extern bool GetDefaultCharSignedness(void);
 extern XLogRecPtr GetFakeLSNForUnloggedRel(void);
 extern Size XLOGShmemSize(void);
+extern void XLOGAutotune(void);
 extern void XLOGShmemInit(void);
 extern void BootStrapXLOG(uint32 data_checksum_version);
 extern void InitializeWalConsistencyChecking(void);
diff --git a/src/include/storage/aio_subsys.h b/src/include/storage/aio_subsys.h
index 276cb3e31c4..1d305d75000 100644
--- a/src/include/storage/aio_subsys.h
+++ b/src/include/storage/aio_subsys.h
@@ -21,6 +21,7 @@
 
 /* aio_init.c */
 extern Size AioShmemSize(void);
+extern void AioAutotune(void);
 extern void AioShmemInit(void);
 
 extern void pgaio_init_backend(void);
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index da32787ab51..f3cbf8abaad 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -83,5 +83,6 @@ extern void CreateSharedMemoryAndSemaphores(void);
 extern void AttachSharedMemoryStructs(void);
 #endif
 extern void InitializeShmemGUCs(void);
+extern void AutotuneShmem(void);
 
 #endif							/* IPC_H */
-- 
2.52.0

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Anthonin Bonnefoy (#1)
Re: Auto-tune shared_buffers to use available huge pages

Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com> writes:

To help with that, the attached patch provides a new option,
huge_pages_autotune_buffers, to automatically use leftover huge pages
as shared_buffers. This requires some changes in the auto-tune logic:

Not expressing an opinion on whether we should do this, but
there is a comment on GetHugePageSize() that you seem to have
falsified without bothering to correct:

* Doing the round-up ourselves also lets us make use of the extra memory,
* rather than just wasting it. Currently, we just increase the available
* space recorded in the shmem header, which will make the extra usable for
* purposes such as additional locktable entries. Someday, for very large
* hugepage sizes, we might want to think about more invasive strategies,
* such as increasing shared_buffers to absorb the extra space.

regards, tom lane