diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c index 4aff315b7c..30becdeddb 100644 --- a/contrib/postgres_fdw/connection.c +++ b/contrib/postgres_fdw/connection.c @@ -109,6 +109,7 @@ static void pgfdw_abort_cleanup(ConnCacheEntry *entry, const char *sql, bool toplevel); static bool UserMappingPasswordRequired(UserMapping *user); static bool disconnect_cached_connections(Oid serverid); +static bool pgfdw_connection_check_internal(PGconn *conn); /* * Get a PGconn which can be used to execute queries on the remote PostgreSQL @@ -153,6 +154,7 @@ GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state) pgfdw_inval_callback, (Datum) 0); CacheRegisterSyscacheCallback(USERMAPPINGOID, pgfdw_inval_callback, (Datum) 0); + RegisterCheckingRemoteServersCallback(pgfdw_connection_check, NULL); } /* Set flag that we did GetConnection during the current transaction */ @@ -1638,3 +1640,66 @@ disconnect_cached_connections(Oid serverid) return result; } + + +/* + * Callback function for checking remote servers. + * + * This function searches the hash table from the beginning + * and performs a health-check on each entry. + * + * Note that this might be expensive because create and wait + * eventset many times. + */ +void +pgfdw_connection_check(void *args) +{ + HASH_SEQ_STATUS scan; + ConnCacheEntry *entry; + + /* Exit immediately if hash is not initialized. */ + if (!ConnectionHash) + return; + + hash_seq_init(&scan, ConnectionHash); + while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) + { + if (entry->conn == NULL || entry->xact_depth == 0) + continue; + if (!pgfdw_connection_check_internal(entry->conn)) + { + ForeignServer *server = GetForeignServer(entry->serverid); + disconnect_pg_server(entry); + ereport(ERROR, + errcode(ERRCODE_CONNECTION_FAILURE), + errmsg("foreign server \"%s\"disconnected due to the health-check failure", + server->servername), + errdetail("Foreign server might be down."), + errhint("Please check the server and network health.")); + } + } +} + +/* + * helper fucntion for pgfdw_connection_check + */ +static bool +pgfdw_connection_check_internal(PGconn *conn) +{ + WaitEventSet *eventset; + WaitEvent events; + + eventset = CreateWaitEventSet(CurrentMemoryContext, 1); + AddWaitEventToSet(eventset, WL_SOCKET_CLOSED, PQsocket(conn), NULL, NULL); + + WaitEventSetWait(eventset, 0, &events, 1, 0); + + if (events.events & WL_SOCKET_CLOSED) + { + FreeWaitEventSet(eventset); + return false; + } + FreeWaitEventSet(eventset); + + return true; +} diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 90b72e9ec5..4630ca05d7 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -151,6 +151,7 @@ extern PGresult *pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state); extern void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql); +extern void pgfdw_connection_check(void *args); /* in option.c */ extern int ExtractConnectionOptions(List *defelems,