Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 73f7fb2

Browse files
committed
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
1 parent f8c5317 commit 73f7fb2

File tree

1 file changed

+96
-100
lines changed

1 file changed

+96
-100
lines changed

src/backend/libpq/pqcomm.c

+96-100
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,102 @@ WaitEventSet *FeBeWaitSet;
171171
void
172172
pq_init(void)
173173
{
174+
Port *port = MyProcPort;
174175
int socket_pos PG_USED_FOR_ASSERTS_ONLY;
175176
int latch_pos PG_USED_FOR_ASSERTS_ONLY;
176177

178+
/* fill in the server (local) address */
179+
port->laddr.salen = sizeof(port->laddr.addr);
180+
if (getsockname(port->sock,
181+
(struct sockaddr *) &port->laddr.addr,
182+
&port->laddr.salen) < 0)
183+
{
184+
ereport(FATAL,
185+
(errmsg("%s() failed: %m", "getsockname")));
186+
}
187+
188+
/* select NODELAY and KEEPALIVE options if it's a TCP connection */
189+
if (port->laddr.addr.ss_family != AF_UNIX)
190+
{
191+
int on;
192+
#ifdef WIN32
193+
int oldopt;
194+
int optlen;
195+
int newopt;
196+
#endif
197+
198+
#ifdef TCP_NODELAY
199+
on = 1;
200+
if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY,
201+
(char *) &on, sizeof(on)) < 0)
202+
{
203+
ereport(FATAL,
204+
(errmsg("%s(%s) failed: %m", "setsockopt", "TCP_NODELAY")));
205+
}
206+
#endif
207+
on = 1;
208+
if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
209+
(char *) &on, sizeof(on)) < 0)
210+
{
211+
ereport(FATAL,
212+
(errmsg("%s(%s) failed: %m", "setsockopt", "SO_KEEPALIVE")));
213+
}
214+
215+
#ifdef WIN32
216+
217+
/*
218+
* This is a Win32 socket optimization. The OS send buffer should be
219+
* large enough to send the whole Postgres send buffer in one go, or
220+
* performance suffers. The Postgres send buffer can be enlarged if a
221+
* very large message needs to be sent, but we won't attempt to
222+
* enlarge the OS buffer if that happens, so somewhat arbitrarily
223+
* ensure that the OS buffer is at least PQ_SEND_BUFFER_SIZE * 4.
224+
* (That's 32kB with the current default).
225+
*
226+
* The default OS buffer size used to be 8kB in earlier Windows
227+
* versions, but was raised to 64kB in Windows 2012. So it shouldn't
228+
* be necessary to change it in later versions anymore. Changing it
229+
* unnecessarily can even reduce performance, because setting
230+
* SO_SNDBUF in the application disables the "dynamic send buffering"
231+
* feature that was introduced in Windows 7. So before fiddling with
232+
* SO_SNDBUF, check if the current buffer size is already large enough
233+
* and only increase it if necessary.
234+
*
235+
* See https://support.microsoft.com/kb/823764/EN-US/ and
236+
* https://msdn.microsoft.com/en-us/library/bb736549%28v=vs.85%29.aspx
237+
*/
238+
optlen = sizeof(oldopt);
239+
if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt,
240+
&optlen) < 0)
241+
{
242+
ereport(FATAL,
243+
(errmsg("%s(%s) failed: %m", "getsockopt", "SO_SNDBUF")));
244+
}
245+
newopt = PQ_SEND_BUFFER_SIZE * 4;
246+
if (oldopt < newopt)
247+
{
248+
if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt,
249+
sizeof(newopt)) < 0)
250+
{
251+
ereport(FATAL,
252+
(errmsg("%s(%s) failed: %m", "setsockopt", "SO_SNDBUF")));
253+
}
254+
}
255+
#endif
256+
257+
/*
258+
* Also apply the current keepalive parameters. If we fail to set a
259+
* parameter, don't error out, because these aren't universally
260+
* supported. (Note: you might think we need to reset the GUC
261+
* variables to 0 in such a case, but it's not necessary because the
262+
* show hooks for these variables report the truth anyway.)
263+
*/
264+
(void) pq_setkeepalivesidle(tcp_keepalives_idle, port);
265+
(void) pq_setkeepalivesinterval(tcp_keepalives_interval, port);
266+
(void) pq_setkeepalivescount(tcp_keepalives_count, port);
267+
(void) pq_settcpusertimeout(tcp_user_timeout, port);
268+
}
269+
177270
/* initialize state variables */
178271
PqSendBufferSize = PQ_SEND_BUFFER_SIZE;
179272
PqSendBuffer = MemoryContextAlloc(TopMemoryContext, PqSendBufferSize);
@@ -191,21 +284,21 @@ pq_init(void)
191284
* writes.
192285
*/
193286
#ifndef WIN32
194-
if (!pg_set_noblock(MyProcPort->sock))
287+
if (!pg_set_noblock(port->sock))
195288
ereport(FATAL,
196289
(errmsg("could not set socket to nonblocking mode: %m")));
197290
#endif
198291

199292
#ifndef WIN32
200293

201294
/* Don't give the socket to any subprograms we execute. */
202-
if (fcntl(MyProcPort->sock, F_SETFD, FD_CLOEXEC) < 0)
295+
if (fcntl(port->sock, F_SETFD, FD_CLOEXEC) < 0)
203296
elog(FATAL, "fcntl(F_SETFD) failed on socket: %m");
204297
#endif
205298

206299
FeBeWaitSet = CreateWaitEventSet(NULL, FeBeWaitSetNEvents);
207300
socket_pos = AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE,
208-
MyProcPort->sock, NULL, NULL);
301+
port->sock, NULL, NULL);
209302
latch_pos = AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
210303
MyLatch, NULL);
211304
AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
@@ -713,103 +806,6 @@ StreamConnection(pgsocket server_fd, Port *port)
713806
return STATUS_ERROR;
714807
}
715808

716-
/* fill in the server (local) address */
717-
port->laddr.salen = sizeof(port->laddr.addr);
718-
if (getsockname(port->sock,
719-
(struct sockaddr *) &port->laddr.addr,
720-
&port->laddr.salen) < 0)
721-
{
722-
ereport(LOG,
723-
(errmsg("%s() failed: %m", "getsockname")));
724-
return STATUS_ERROR;
725-
}
726-
727-
/* select NODELAY and KEEPALIVE options if it's a TCP connection */
728-
if (port->laddr.addr.ss_family != AF_UNIX)
729-
{
730-
int on;
731-
#ifdef WIN32
732-
int oldopt;
733-
int optlen;
734-
int newopt;
735-
#endif
736-
737-
#ifdef TCP_NODELAY
738-
on = 1;
739-
if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY,
740-
(char *) &on, sizeof(on)) < 0)
741-
{
742-
ereport(LOG,
743-
(errmsg("%s(%s) failed: %m", "setsockopt", "TCP_NODELAY")));
744-
return STATUS_ERROR;
745-
}
746-
#endif
747-
on = 1;
748-
if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
749-
(char *) &on, sizeof(on)) < 0)
750-
{
751-
ereport(LOG,
752-
(errmsg("%s(%s) failed: %m", "setsockopt", "SO_KEEPALIVE")));
753-
return STATUS_ERROR;
754-
}
755-
756-
#ifdef WIN32
757-
758-
/*
759-
* This is a Win32 socket optimization. The OS send buffer should be
760-
* large enough to send the whole Postgres send buffer in one go, or
761-
* performance suffers. The Postgres send buffer can be enlarged if a
762-
* very large message needs to be sent, but we won't attempt to
763-
* enlarge the OS buffer if that happens, so somewhat arbitrarily
764-
* ensure that the OS buffer is at least PQ_SEND_BUFFER_SIZE * 4.
765-
* (That's 32kB with the current default).
766-
*
767-
* The default OS buffer size used to be 8kB in earlier Windows
768-
* versions, but was raised to 64kB in Windows 2012. So it shouldn't
769-
* be necessary to change it in later versions anymore. Changing it
770-
* unnecessarily can even reduce performance, because setting
771-
* SO_SNDBUF in the application disables the "dynamic send buffering"
772-
* feature that was introduced in Windows 7. So before fiddling with
773-
* SO_SNDBUF, check if the current buffer size is already large enough
774-
* and only increase it if necessary.
775-
*
776-
* See https://support.microsoft.com/kb/823764/EN-US/ and
777-
* https://msdn.microsoft.com/en-us/library/bb736549%28v=vs.85%29.aspx
778-
*/
779-
optlen = sizeof(oldopt);
780-
if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt,
781-
&optlen) < 0)
782-
{
783-
ereport(LOG,
784-
(errmsg("%s(%s) failed: %m", "getsockopt", "SO_SNDBUF")));
785-
return STATUS_ERROR;
786-
}
787-
newopt = PQ_SEND_BUFFER_SIZE * 4;
788-
if (oldopt < newopt)
789-
{
790-
if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt,
791-
sizeof(newopt)) < 0)
792-
{
793-
ereport(LOG,
794-
(errmsg("%s(%s) failed: %m", "setsockopt", "SO_SNDBUF")));
795-
return STATUS_ERROR;
796-
}
797-
}
798-
#endif
799-
800-
/*
801-
* Also apply the current keepalive parameters. If we fail to set a
802-
* parameter, don't error out, because these aren't universally
803-
* supported. (Note: you might think we need to reset the GUC
804-
* variables to 0 in such a case, but it's not necessary because the
805-
* show hooks for these variables report the truth anyway.)
806-
*/
807-
(void) pq_setkeepalivesidle(tcp_keepalives_idle, port);
808-
(void) pq_setkeepalivesinterval(tcp_keepalives_interval, port);
809-
(void) pq_setkeepalivescount(tcp_keepalives_count, port);
810-
(void) pq_settcpusertimeout(tcp_user_timeout, port);
811-
}
812-
813809
return STATUS_OK;
814810
}
815811

0 commit comments

Comments
 (0)