From: Shigeru Hanada Date: Fri, 12 Nov 2010 07:09:57 +0000 (+0900) Subject: Fix postgresql_fdw to close foreign connections on error case. X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=284a24b6e06bc2d3172c1c9f191a5e78f6a941ce;p=users%2Fhanada%2Fpostgres.git Fix postgresql_fdw to close foreign connections on error case. To close all connections properly, a cleanup function is registered for each foreign server which has been used in the backend at least once with RegisterResourceReleaseCallback(). --- diff --git a/contrib/postgresql_fdw/fsconnection.c b/contrib/postgresql_fdw/fsconnection.c index f56b30eafd..9238f121a0 100644 --- a/contrib/postgresql_fdw/fsconnection.c +++ b/contrib/postgresql_fdw/fsconnection.c @@ -65,8 +65,6 @@ GetConnection(ForeignServer *server, UserMapping *user) { HASHCTL ctl; - elog(DEBUG1, "%s() cache not initialized(%s)", __FUNCTION__, conname); - /* hash key is the name of the connection */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = NAMEDATALEN; @@ -85,8 +83,7 @@ GetConnection(ForeignServer *server, UserMapping *user) if (entry->conn != NULL) { entry->refs++; - elog(DEBUG1, "%s() ref %d for %s", __FUNCTION__, - entry->refs, entry->name); + elog(DEBUG3, "ref %d for %s", entry->refs, entry->name); return entry->conn; } @@ -95,6 +92,16 @@ GetConnection(ForeignServer *server, UserMapping *user) * We reuse entry to store newly established connection later. */ } + else + { + /* + * Use ResourceOner to clean the connection up on error including + * user interrupt. + */ + entry->refs = 0; + entry->conn = NULL; + RegisterResourceReleaseCallback(cleanup_connection, entry); + } /* * Here we have to establish new connection. @@ -111,19 +118,12 @@ GetConnection(ForeignServer *server, UserMapping *user) */ entry->refs = 1; entry->conn = conn; - elog(DEBUG1, "%s() ref %d for %s", __FUNCTION__, - entry->refs, entry->name); - - /* - * Use ResourceOner to clean the connection up on error including - * user interrupt. - */ - RegisterResourceReleaseCallback(cleanup_connection, entry); - elog(DEBUG1, "%s() register %p(%s)", __FUNCTION__, entry, entry->name); + elog(DEBUG3, "connected to %s (%d)", entry->name, entry->refs); } PG_CATCH(); { PQfinish(conn); + entry->refs = 0; entry->conn = NULL; PG_RE_THROW(); } @@ -240,22 +240,24 @@ ReleaseConnection(PGconn *conn) } hash_seq_term(&scan); - /* Even if the PGconn has not been found, we don't treat it as error. */ + /* + * If the released connection was an orphan, just close it. + */ if (entry == NULL) - elog(WARNING, "could not find postgresql_fdw connection"); + { + PQfinish(conn); + return; + } - /* If this was the last referer, unregister it from cache. */ + /* If the caller was the last referer, unregister it from cache. */ entry->refs--; - elog(DEBUG1, "%s() ref %d for %s", __FUNCTION__, entry->refs, entry->name); + elog(DEBUG3, "ref %d for %s", entry->refs, entry->name); if (entry->refs == 0) { - elog(DEBUG1, "%s() closing postgresql_fdw connection \"%s\"", - __FUNCTION__, - entry->name); + elog(DEBUG3, "closing connection \"%s\"", entry->name); PQfinish(entry->conn); + entry->refs = 0; entry->conn = NULL; - UnregisterResourceReleaseCallback(cleanup_connection, entry); - hash_search(FSConnectionHash, entry->name, HASH_REMOVE, NULL); } } @@ -269,28 +271,40 @@ cleanup_connection(ResourceReleasePhase phase, bool isTopLevel, void *arg) { - static int count = 0; ConnCacheEntry *entry = (ConnCacheEntry *) arg; - /* Ignore callback from resource owners other than CurrentResourceOwner. */ - if (CurrentResourceOwner != CurTransactionResourceOwner) + /* + * If the transaction was committed, the connection has been closed via + * pgClose() and ReleaseConnection(). + */ + if (isCommit) return; - elog(DEBUG1, "%s() [%d] phase: %s isCommit: %s isTopLevel: %s arg: %p", - __FUNCTION__, count++, - phase == RESOURCE_RELEASE_BEFORE_LOCKS ? "BEFORE" : - phase == RESOURCE_RELEASE_LOCKS ? "LOCK" : - phase == RESOURCE_RELEASE_AFTER_LOCKS ? "AFTER" : "unknown", - isCommit ? "true" : "false", - isTopLevel ? "true" : "false", - arg); + /* + * We clean the connection up on post-lock because foreign connections are + * backend-internal resource. + */ + if (phase != RESOURCE_RELEASE_AFTER_LOCKS) + return; + + /* + * We ignore cleanup for ResourceOwners other than transaction. At this + * point, such a ResourceOwner is only Portal. + */ + if (CurrentResourceOwner != CurTransactionResourceOwner) + return; + /* + * We don't care whether we are in TopTransaction or Subtransaction. + * Anyway, we close the connection and reset the reference counter. + */ if (entry->conn != NULL) { - elog(DEBUG1, "%s() closed %s", __FUNCTION__, entry->name); + elog(DEBUG3, "closing connection to %s", entry->name); PQfinish(entry->conn); + entry->refs = 0; entry->conn = NULL; - //UnregisterResourceReleaseCallback(cleanup_connection, entry); - hash_search(FSConnectionHash, entry->name, HASH_REMOVE, NULL); } + else + elog(DEBUG3, "connection to %s already closed", entry->name); } diff --git a/doc/src/sgml/postgresql-fdw.sgml b/doc/src/sgml/postgresql-fdw.sgml index 36f1bbc7c4..dc91a96675 100644 --- a/doc/src/sgml/postgresql-fdw.sgml +++ b/doc/src/sgml/postgresql-fdw.sgml @@ -60,6 +60,22 @@ + + Connection management + + The postgresql_fdw connects to a remote PostgreSQL server when + pgConnectServer() is called for the foreign server + first time in the local query. The connection is used by all of remote + queries which are executed on same remote PostgreSQL server. + If the local query uses multiple foreign PostgreSQL servers, connections + are established for each server (not for each foreign table) and all of + them will be closed at the end of the query. This also means that + connection pooling is not implemented in postgresql_fdw. + + + + + Transaction management