From 57c03f7dbae1d0540a9bd45d475b93900ec64d85 Mon Sep 17 00:00:00 2001 From: Filip Janus Date: Wed, 12 Nov 2025 14:29:43 +0100 Subject: [PATCH 1/2] Add configurable receive buffer size This commit introduces a new GUC parameter 'pq_recv_buffer_size' to allow users to configure the size of the network receive buffer for each backend connection. The receive buffer is now dynamically allocated instead of using a fixed compile-time size. Key changes: - Make PqRecvBuffer dynamically allocated based on pq_recv_buffer_size GUC - Add pq_recv_buffer_size GUC parameter (default 8KB, min 8KB, max configurable) has enough space Benefits: - Configurable buffer size allows tuning for workloads with large messages - Maintains backward compatibility with 8KB default --- src/backend/libpq/pqcomm.c | 14 +++++++++----- src/backend/utils/init/globals.c | 3 +++ src/backend/utils/misc/guc_parameters.dat | 10 ++++++++++ src/backend/utils/misc/postgresql.conf.sample | 1 + src/include/miscadmin.h | 1 + 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index e33623fdbace..453dea850a5b 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -112,19 +112,21 @@ static List *sock_paths = NIL; /* * Buffers for low-level I/O. * - * The receive buffer is fixed size. Send buffer is usually 8k, but can be - * enlarged by pq_putmessage_noblock() if the message doesn't fit otherwise. + * Both send and receive buffers are dynamically allocated. Send buffer is + * usually 8k, but can be enlarged by pq_putmessage_noblock() if the message + * doesn't fit otherwise. Receive buffer size is configurable via the + * pq_recv_buffer_size GUC parameter. */ #define PQ_SEND_BUFFER_SIZE 8192 -#define PQ_RECV_BUFFER_SIZE 8192 static char *PqSendBuffer; static int PqSendBufferSize; /* Size send buffer */ static size_t PqSendPointer; /* Next index to store a byte in PqSendBuffer */ static size_t PqSendStart; /* Next index to send a byte in PqSendBuffer */ -static char PqRecvBuffer[PQ_RECV_BUFFER_SIZE]; +static char *PqRecvBuffer; /* Dynamically allocated receive buffer */ +static int PqRecvBufferSize; /* Size of receive buffer */ static int PqRecvPointer; /* Next index to read a byte from PqRecvBuffer */ static int PqRecvLength; /* End of data available in PqRecvBuffer */ @@ -278,6 +280,8 @@ pq_init(ClientSocket *client_sock) /* initialize state variables */ PqSendBufferSize = PQ_SEND_BUFFER_SIZE; PqSendBuffer = MemoryContextAlloc(TopMemoryContext, PqSendBufferSize); + PqRecvBufferSize = pq_recv_buffer_size * 1024; /* GUC is in KB */ + PqRecvBuffer = MemoryContextAlloc(TopMemoryContext, PqRecvBufferSize); PqSendPointer = PqSendStart = PqRecvPointer = PqRecvLength = 0; PqCommBusy = false; PqCommReadingMsg = false; @@ -921,7 +925,7 @@ pq_recvbuf(void) errno = 0; r = secure_read(MyProcPort, PqRecvBuffer + PqRecvLength, - PQ_RECV_BUFFER_SIZE - PqRecvLength); + PqRecvBufferSize - PqRecvLength); if (r < 0) { diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index d31cb45a0588..5fdf1293d2e8 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -165,3 +165,6 @@ int notify_buffers = 16; int serializable_buffers = 32; int subtransaction_buffers = 0; int transaction_buffers = 0; + +/* network buffer sizes */ +int pq_recv_buffer_size = 8; diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat index 3b9d8349078b..63d217a5a360 100644 --- a/src/backend/utils/misc/guc_parameters.dat +++ b/src/backend/utils/misc/guc_parameters.dat @@ -2266,6 +2266,16 @@ max => 'INT_MAX / 1000000', }, +{ name => 'pq_recv_buffer_size', type => 'int', context => 'PGC_BACKEND', group => 'RESOURCES_MEM', + short_desc => 'Sets the size of the network receive buffer for each backend.', + long_desc => 'Larger values can improve performance when receiving large messages, but consume more memory per connection.', + flags => 'GUC_UNIT_KB', + variable => 'pq_recv_buffer_size', + boot_val => '8', + min => '8', + max => 'MAX_KILOBYTES', +}, + # Not for general use { name => 'pre_auth_delay', type => 'int', context => 'PGC_SIGHUP', group => 'DEVELOPER_OPTIONS', short_desc => 'Sets the amount of time to wait before authentication on connection startup.', diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index dc9e2255f8a7..4bb56b4cbb0f 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -136,6 +136,7 @@ #huge_page_size = 0 # zero for system default # (change requires restart) #temp_buffers = 8MB # min 800kB +#pq_recv_buffer_size = 8kB # min 8kB #max_prepared_transactions = 0 # zero disables the feature # (change requires restart) # Caution: it is not advisable to set max_prepared_transactions nonzero unless diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 9a7d733ddeff..2e51235ee6a9 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -186,6 +186,7 @@ extern PGDLLIMPORT int notify_buffers; extern PGDLLIMPORT int serializable_buffers; extern PGDLLIMPORT int subtransaction_buffers; extern PGDLLIMPORT int transaction_buffers; +extern PGDLLIMPORT int pq_recv_buffer_size; extern PGDLLIMPORT int MyProcPid; extern PGDLLIMPORT pg_time_t MyStartTime; From 17100d1fcf5ad0b5faeda8a869d9400d5a581750 Mon Sep 17 00:00:00 2001 From: Filip Janus Date: Wed, 26 Nov 2025 15:00:11 +0100 Subject: [PATCH 2/2] Remove dead memmove code from pq_recvbuf() The memmove block in pq_recvbuf() is unreachable dead code that has never been executed in practice. Analysis: pq_recvbuf() is only called from pq_getbytes() when the condition PqRecvPointer >= PqRecvLength is true (buffer is empty or exhausted). However, the memmove is only executed when PqRecvLength > PqRecvPointer (buffer contains unread data). These conditions are mutually exclusive: - Caller requires: PqRecvPointer >= PqRecvLength - memmove requires: PqRecvLength > PqRecvPointer --- src/backend/libpq/pqcomm.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index 453dea850a5b..d52c99d5b613 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -902,15 +902,6 @@ pq_recvbuf(void) { if (PqRecvPointer > 0) { - if (PqRecvLength > PqRecvPointer) - { - /* still some unread data, left-justify it in the buffer */ - memmove(PqRecvBuffer, PqRecvBuffer + PqRecvPointer, - PqRecvLength - PqRecvPointer); - PqRecvLength -= PqRecvPointer; - PqRecvPointer = 0; - } - else PqRecvLength = PqRecvPointer = 0; }