From f87cf8bfbcf943b21fd84c8b12b00d92f5367b1d Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Thu, 9 Feb 2023 11:58:35 +0000 Subject: [PATCH v31 4/4] add kqueue support for PQconnCheck and PQcanConnCheck --- doc/src/sgml/libpq.sgml | 16 +++---- doc/src/sgml/postgres-fdw.sgml | 34 +++++++------- src/interfaces/libpq/fe-misc.c | 84 ++++++++++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 28 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index f49b6e7452..2abc2ed508 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2692,14 +2692,14 @@ int PQconnCheck(PGconn *conn); Unlike , this function checks socket - health. This check is performed by polling the socket. This function is - currently available only on systems that support the non-standard - POLLRDHUP extension to the poll system - call, including Linux. returns - greater than zero if the remote peer seems to be closed, returns - 0 if the socket is valid, and returns - -1 if the connection has been already invalid or - an error is error occurred. + health. This check is performed by polling the socket. This check is + performed by polling the socket. This option relies on kernel events + exposed by Linux, macOS, illumos and the BSD family of operating + systems, and is not currently available on other systems + returns greater than zero if the + remote peer seems to be closed, returns 0 if the + socket is valid, and returns -1 if the connection + has been already invalid or an error is error occurred. diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index e1abdb38cb..75ca74a16e 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -835,13 +835,13 @@ postgres=# SELECT postgres_fdw_disconnect_all(); postgres_fdw from the local session to the foreign server with the given name. This check is performed by polling the socket and allows long-running transactions to be aborted sooner if the kernel - reports that the connection is closed. This function is currently - available only on systems that support the non-standard POLLRDHUP - extension to the poll system call, including Linux. This - returns true if any of the following condition is - satisfied: 1) existing connection is not closed by the remote peer, 2) - there is no connection for specified server yet, and 3) the checking is - not supported on this platform. false + reports that the connection is closed. This check is performed by polling + the socket. This option relies on kernel events exposed by Linux, macOS, + illumos and the BSD family of operating systems, and is not currently + available on other systems. This returns true if any + of the following condition is satisfied: 1) existing connection is not + closed by the remote peer, 2) there is no connection for specified server + yet, and 3) the checking is not supported on this platform. false is returned if the local session seems to be disconnected from other servers. NULL is returned if the local session does not have a connection cache. Example usage of the function: @@ -862,16 +862,16 @@ postgres=# SELECT postgres_fdw_verify_connection_states('loopback1'); by postgres_fdw from the local session to the foreign servers. This check is performed by polling the socket and allows long-running transactions to be aborted sooner if the kernel reports - that the connection is closed. This function is currently available only - on systems that support the non-standard POLLRDHUP - extension to the poll system call, including Linux. This - returns true if any of the following condition is - satisfied: 1) all connections are not closed by the remote peer, 2) - there are no connections yet, and 3) the checking is - not supported on this platform. false is returned if - the local session seems to be disconnected from at least one remote - server. NULL is returned if the local session does - not have a connection cache. Example usage of the function: + that the connection is closed. This check is performed by polling the + socket. This option relies on kernel events exposed by Linux, macOS, + illumos and the BSD family of operating systems, and is not currently + available on other systems. This returns true if any + of the following condition is satisfied: 1) all connections are not + closed by the remote peer, 2) there are no connections yet, and 3) the + checking is not supported on this platform. false is + returned if the local session seems to be disconnected from at least one + remote server. NULL is returned if the local session + does not have a connection cache. Example usage of the function: postgres=# SELECT postgres_fdw_verify_connection_states_all(); postgres_fdw_verify_connection_states_all diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 3bcd760cd7..caa33cd2e4 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -45,6 +45,18 @@ #include #endif +/* We use poll(2) for checking the health of a socket, otherwise kqueue(2) */ +#if (!(defined(HAVE_POLL) && defined(POLLRDHUP)) && \ + defined(HAVE_KQUEUE)) +#define CHECK_USE_KQUEUE +#endif + +#if defined(CHECK_USE_KQUEUE) +#include +#include +#include +#endif + #include "libpq-fe.h" #include "libpq-int.h" #include "mb/pg_wchar.h" @@ -57,6 +69,10 @@ static int pqSocketIsReadableOrWritableOrValid(PGconn *conn, int forRead, int forWrite, time_t end_time); static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time); +#if defined(CHECK_USE_KQUEUE) +static int pqSocketKqueue(int sock); +#endif + /* * PQlibVersion: return the libpq version number */ @@ -1104,9 +1120,12 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) if (end_time == 0) #if defined(POLLRDHUP) input_fd.events = POLLRDHUP | POLLHUP | POLLNVAL; +#elif defined(CHECK_USE_KQUEUE) + /* Use kqueue(2) instead */ + return pqSocketKqueue(sock); #else return 0; -#endif /* defined(POLLRDHUP) */ +#endif else return 0; } @@ -1143,7 +1162,22 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) struct timeval *ptr_timeout; if (!forRead && !forWrite) - return 0; + { + /* + * Try to check the health if requested + * + * XXX: Is there any systems that cannot use poll(2) but have + * kqueue(2) system call? + */ + if (end_time == 0) +#if defined(CHECK_USE_KQUEUE) + return pqSocketKqueue(sock); +#else + return 0; +#endif /* defined(CHECK_USE_KQUEUE) */ + else + return 0; + } FD_ZERO(&input_mask); FD_ZERO(&output_mask); @@ -1175,6 +1209,49 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) #endif /* HAVE_POLL */ } +#if defined(CHECK_USE_KQUEUE) +static int +pqSocketKqueue(int sock) +{ + struct kevent kev, + ret; + struct timespec timeout = {0}; + static int kq = -1; + int result; + + /* If this function has never called yet, create a kernel event queue */ + if (kq < 0) + { + kq = kqueue(); + if (kq < 0) + return -1; + } + + /* Prepare kevent structure */ + EV_SET(&kev, sock, EVFILT_READ, EV_ADD, 0, 0, NULL); + if (kevent(kq, &kev, 1, NULL, 0, NULL)) + return -1; + + /* + * Check the status of socket. Note that we will retry as long as we get + * EINTR. + */ + do + result = kevent(kq, NULL, 0, &ret, 1, &timeout); + while (result < 0 && errno == EINTR); + + /* Clean up the queue */ + EV_SET(&kev, sock, EVFILT_READ, EV_DELETE, 0, 0, NULL); + kevent(kq, &kev, 1, NULL, 0, NULL); + + if (result < 0) + return -1; + + printf("debug print"); + + return ret.flags & EV_EOF; +} +#endif /* defined(CHECK_USE_KQUEUE) */ /* * A couple of "miscellaneous" multibyte related functions. They used @@ -1255,7 +1332,8 @@ PQconnCheck(PGconn *conn) int PQcanConnCheck(void) { -#if (defined(HAVE_POLL) && defined(POLLRDHUP)) +#if ((defined(HAVE_POLL) && defined(POLLRDHUP)) || \ + defined(CHECK_USE_KQUEUE)) return true; #else return false; -- 2.27.0