From 73f7fb2a4c4480cfebc52dddf0049e69b830b214 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Tue, 12 Mar 2024 13:42:28 +0200 Subject: [PATCH] Set socket options in child process after forking Try to minimize the work done in the postmaster process for each accepted connection, so that postmaster can quickly proceed with its duties. These function calls are very fast so this doesn't make any measurable performance difference in practice, but it's nice to have all the socket options initialization code in one place for sake of readability too. This also paves the way for an upcoming commit that will move the initialization of the Port struct to the child process. Discussion: https://www.postgresql.org/message-id/7a59b073-5b5b-151e-7ed3-8b01ff7ce9ef@iki.fi --- src/backend/libpq/pqcomm.c | 196 ++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 100 deletions(-) diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index d9e49ca7028..c548d38c4df 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -171,9 +171,102 @@ WaitEventSet *FeBeWaitSet; void pq_init(void) { + Port *port = MyProcPort; int socket_pos PG_USED_FOR_ASSERTS_ONLY; int latch_pos PG_USED_FOR_ASSERTS_ONLY; + /* fill in the server (local) address */ + port->laddr.salen = sizeof(port->laddr.addr); + if (getsockname(port->sock, + (struct sockaddr *) &port->laddr.addr, + &port->laddr.salen) < 0) + { + ereport(FATAL, + (errmsg("%s() failed: %m", "getsockname"))); + } + + /* select NODELAY and KEEPALIVE options if it's a TCP connection */ + if (port->laddr.addr.ss_family != AF_UNIX) + { + int on; +#ifdef WIN32 + int oldopt; + int optlen; + int newopt; +#endif + +#ifdef TCP_NODELAY + on = 1; + if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, sizeof(on)) < 0) + { + ereport(FATAL, + (errmsg("%s(%s) failed: %m", "setsockopt", "TCP_NODELAY"))); + } +#endif + on = 1; + if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof(on)) < 0) + { + ereport(FATAL, + (errmsg("%s(%s) failed: %m", "setsockopt", "SO_KEEPALIVE"))); + } + +#ifdef WIN32 + + /* + * This is a Win32 socket optimization. The OS send buffer should be + * large enough to send the whole Postgres send buffer in one go, or + * performance suffers. The Postgres send buffer can be enlarged if a + * very large message needs to be sent, but we won't attempt to + * enlarge the OS buffer if that happens, so somewhat arbitrarily + * ensure that the OS buffer is at least PQ_SEND_BUFFER_SIZE * 4. + * (That's 32kB with the current default). + * + * The default OS buffer size used to be 8kB in earlier Windows + * versions, but was raised to 64kB in Windows 2012. So it shouldn't + * be necessary to change it in later versions anymore. Changing it + * unnecessarily can even reduce performance, because setting + * SO_SNDBUF in the application disables the "dynamic send buffering" + * feature that was introduced in Windows 7. So before fiddling with + * SO_SNDBUF, check if the current buffer size is already large enough + * and only increase it if necessary. + * + * See https://support.microsoft.com/kb/823764/EN-US/ and + * https://msdn.microsoft.com/en-us/library/bb736549%28v=vs.85%29.aspx + */ + optlen = sizeof(oldopt); + if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt, + &optlen) < 0) + { + ereport(FATAL, + (errmsg("%s(%s) failed: %m", "getsockopt", "SO_SNDBUF"))); + } + newopt = PQ_SEND_BUFFER_SIZE * 4; + if (oldopt < newopt) + { + if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt, + sizeof(newopt)) < 0) + { + ereport(FATAL, + (errmsg("%s(%s) failed: %m", "setsockopt", "SO_SNDBUF"))); + } + } +#endif + + /* + * Also apply the current keepalive parameters. If we fail to set a + * parameter, don't error out, because these aren't universally + * supported. (Note: you might think we need to reset the GUC + * variables to 0 in such a case, but it's not necessary because the + * show hooks for these variables report the truth anyway.) + */ + (void) pq_setkeepalivesidle(tcp_keepalives_idle, port); + (void) pq_setkeepalivesinterval(tcp_keepalives_interval, port); + (void) pq_setkeepalivescount(tcp_keepalives_count, port); + (void) pq_settcpusertimeout(tcp_user_timeout, port); + } + /* initialize state variables */ PqSendBufferSize = PQ_SEND_BUFFER_SIZE; PqSendBuffer = MemoryContextAlloc(TopMemoryContext, PqSendBufferSize); @@ -191,7 +284,7 @@ pq_init(void) * writes. */ #ifndef WIN32 - if (!pg_set_noblock(MyProcPort->sock)) + if (!pg_set_noblock(port->sock)) ereport(FATAL, (errmsg("could not set socket to nonblocking mode: %m"))); #endif @@ -199,13 +292,13 @@ pq_init(void) #ifndef WIN32 /* Don't give the socket to any subprograms we execute. */ - if (fcntl(MyProcPort->sock, F_SETFD, FD_CLOEXEC) < 0) + if (fcntl(port->sock, F_SETFD, FD_CLOEXEC) < 0) elog(FATAL, "fcntl(F_SETFD) failed on socket: %m"); #endif FeBeWaitSet = CreateWaitEventSet(NULL, FeBeWaitSetNEvents); socket_pos = AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE, - MyProcPort->sock, NULL, NULL); + port->sock, NULL, NULL); latch_pos = AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL); AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET, @@ -713,103 +806,6 @@ StreamConnection(pgsocket server_fd, Port *port) return STATUS_ERROR; } - /* fill in the server (local) address */ - port->laddr.salen = sizeof(port->laddr.addr); - if (getsockname(port->sock, - (struct sockaddr *) &port->laddr.addr, - &port->laddr.salen) < 0) - { - ereport(LOG, - (errmsg("%s() failed: %m", "getsockname"))); - return STATUS_ERROR; - } - - /* select NODELAY and KEEPALIVE options if it's a TCP connection */ - if (port->laddr.addr.ss_family != AF_UNIX) - { - int on; -#ifdef WIN32 - int oldopt; - int optlen; - int newopt; -#endif - -#ifdef TCP_NODELAY - on = 1; - if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY, - (char *) &on, sizeof(on)) < 0) - { - ereport(LOG, - (errmsg("%s(%s) failed: %m", "setsockopt", "TCP_NODELAY"))); - return STATUS_ERROR; - } -#endif - on = 1; - if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE, - (char *) &on, sizeof(on)) < 0) - { - ereport(LOG, - (errmsg("%s(%s) failed: %m", "setsockopt", "SO_KEEPALIVE"))); - return STATUS_ERROR; - } - -#ifdef WIN32 - - /* - * This is a Win32 socket optimization. The OS send buffer should be - * large enough to send the whole Postgres send buffer in one go, or - * performance suffers. The Postgres send buffer can be enlarged if a - * very large message needs to be sent, but we won't attempt to - * enlarge the OS buffer if that happens, so somewhat arbitrarily - * ensure that the OS buffer is at least PQ_SEND_BUFFER_SIZE * 4. - * (That's 32kB with the current default). - * - * The default OS buffer size used to be 8kB in earlier Windows - * versions, but was raised to 64kB in Windows 2012. So it shouldn't - * be necessary to change it in later versions anymore. Changing it - * unnecessarily can even reduce performance, because setting - * SO_SNDBUF in the application disables the "dynamic send buffering" - * feature that was introduced in Windows 7. So before fiddling with - * SO_SNDBUF, check if the current buffer size is already large enough - * and only increase it if necessary. - * - * See https://support.microsoft.com/kb/823764/EN-US/ and - * https://msdn.microsoft.com/en-us/library/bb736549%28v=vs.85%29.aspx - */ - optlen = sizeof(oldopt); - if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt, - &optlen) < 0) - { - ereport(LOG, - (errmsg("%s(%s) failed: %m", "getsockopt", "SO_SNDBUF"))); - return STATUS_ERROR; - } - newopt = PQ_SEND_BUFFER_SIZE * 4; - if (oldopt < newopt) - { - if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt, - sizeof(newopt)) < 0) - { - ereport(LOG, - (errmsg("%s(%s) failed: %m", "setsockopt", "SO_SNDBUF"))); - return STATUS_ERROR; - } - } -#endif - - /* - * Also apply the current keepalive parameters. If we fail to set a - * parameter, don't error out, because these aren't universally - * supported. (Note: you might think we need to reset the GUC - * variables to 0 in such a case, but it's not necessary because the - * show hooks for these variables report the truth anyway.) - */ - (void) pq_setkeepalivesidle(tcp_keepalives_idle, port); - (void) pq_setkeepalivesinterval(tcp_keepalives_interval, port); - (void) pq_setkeepalivescount(tcp_keepalives_count, port); - (void) pq_settcpusertimeout(tcp_user_timeout, port); - } - return STATUS_OK; }