postgres_fdw: Add option to control whether to keep connections open.
authorFujii Masao <fujii@postgresql.org>
Fri, 2 Apr 2021 10:45:42 +0000 (19:45 +0900)
committerFujii Masao <fujii@postgresql.org>
Fri, 2 Apr 2021 10:45:42 +0000 (19:45 +0900)
This commit adds a new option keep_connections that controls
whether postgres_fdw keeps the connections to the foreign server open
so that the subsequent queries can re-use them. This option can only be
specified for a foreign server. The default is on. If set to off,
all connections to the foreign server will be discarded
at the end of transaction. Closed connections will be re-established
when they are necessary by future queries using a foreign table.

This option is useful, for example, when users want to prevent
the connections from eating up the foreign servers connections
capacity.

Author: Bharath Rupireddy
Reviewed-by: Alexey Kondratov, Vignesh C, Fujii Masao
Discussion: https://postgr.es/m/CALj2ACVvrp5=AVp2PupEm+nAC8S4buqR3fJMmaCoc7ftT0aD2A@mail.gmail.com

contrib/postgres_fdw/connection.c
contrib/postgres_fdw/expected/postgres_fdw.out
contrib/postgres_fdw/option.c
contrib/postgres_fdw/sql/postgres_fdw.sql
doc/src/sgml/postgres-fdw.sgml

index 54ab8edfab6013c97d6de9e72ae2b28261eb606a..6a61d8386212340b173604c9f4904db4bdc7045c 100644 (file)
@@ -59,6 +59,8 @@ typedef struct ConnCacheEntry
    bool        have_error;     /* have any subxacts aborted in this xact? */
    bool        changing_xact_state;    /* xact state change in process */
    bool        invalidated;    /* true if reconnect is pending */
+   bool        keep_connections;   /* setting value of keep_connections
+                                    * server option */
    Oid         serverid;       /* foreign server OID used to get server name */
    uint32      server_hashvalue;   /* hash value of foreign server OID */
    uint32      mapping_hashvalue;  /* hash value of user mapping OID */
@@ -286,6 +288,7 @@ static void
 make_new_connection(ConnCacheEntry *entry, UserMapping *user)
 {
    ForeignServer *server = GetForeignServer(user->serverid);
+   ListCell   *lc;
 
    Assert(entry->conn == NULL);
 
@@ -304,6 +307,26 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user)
                              ObjectIdGetDatum(user->umid));
    memset(&entry->state, 0, sizeof(entry->state));
 
+   /*
+    * Determine whether to keep the connection that we're about to make here
+    * open even after the transaction using it ends, so that the subsequent
+    * transactions can re-use it.
+    *
+    * It's enough to determine this only when making new connection because
+    * all the connections to the foreign server whose keep_connections option
+    * is changed will be closed and re-made later.
+    *
+    * By default, all the connections to any foreign servers are kept open.
+    */
+   entry->keep_connections = true;
+   foreach(lc, server->options)
+   {
+       DefElem    *def = (DefElem *) lfirst(lc);
+
+       if (strcmp(def->defname, "keep_connections") == 0)
+           entry->keep_connections = defGetBoolean(def);
+   }
+
    /* Now try to make the connection */
    entry->conn = connect_pg_server(server, user);
 
@@ -970,14 +993,16 @@ pgfdw_xact_callback(XactEvent event, void *arg)
        entry->xact_depth = 0;
 
        /*
-        * If the connection isn't in a good idle state or it is marked as
-        * invalid, then discard it to recover. Next GetConnection will open a
-        * new connection.
+        * If the connection isn't in a good idle state, it is marked as
+        * invalid or keep_connections option of its server is disabled, then
+        * discard it to recover. Next GetConnection will open a new
+        * connection.
         */
        if (PQstatus(entry->conn) != CONNECTION_OK ||
            PQtransactionStatus(entry->conn) != PQTRANS_IDLE ||
            entry->changing_xact_state ||
-           entry->invalidated)
+           entry->invalidated ||
+           !entry->keep_connections)
        {
            elog(DEBUG3, "discarding connection %p", entry->conn);
            disconnect_pg_server(entry);
index 2be14c543783d8258afb5b830386a5e503c26f10..59e4e27ffbef2be88dff87e028e17446c2a3d09a 100644 (file)
@@ -8913,7 +8913,7 @@ DO $d$
     END;
 $d$;
 ERROR:  invalid option "password"
-HINT:  Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size, async_capable
+HINT:  Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size, async_capable, keep_connections
 CONTEXT:  SQL statement "ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw')"
 PL/pgSQL function inline_code_block line 3 at EXECUTE
 -- If we add a password for our user mapping instead, we should get a different
@@ -9249,6 +9249,27 @@ DROP USER MAPPING FOR regress_multi_conn_user2 SERVER loopback;
 DROP ROLE regress_multi_conn_user1;
 DROP ROLE regress_multi_conn_user2;
 -- ===================================================================
+-- Test foreign server level option keep_connections
+-- ===================================================================
+-- By default, the connections associated with foreign server are cached i.e.
+-- keep_connections option is on. Set it to off.
+ALTER SERVER loopback OPTIONS (keep_connections 'off');
+-- connection to loopback server is closed at the end of xact
+-- as keep_connections was set to off.
+SELECT 1 FROM ft1 LIMIT 1;
+ ?column? 
+----------
+        1
+(1 row)
+
+-- No cached connections, so no records should be output.
+SELECT server_name FROM postgres_fdw_get_connections() ORDER BY 1;
+ server_name 
+-------------
+(0 rows)
+
+ALTER SERVER loopback OPTIONS (SET keep_connections 'on');
+-- ===================================================================
 -- batch insert
 -- ===================================================================
 BEGIN;
index 530d7a66d408557f96f408b4010ec9bc238d2a89..f1d0c8bd412a4a3a5145f2fc31f8f5c6b73dd654 100644 (file)
@@ -108,7 +108,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
         */
        if (strcmp(def->defname, "use_remote_estimate") == 0 ||
            strcmp(def->defname, "updatable") == 0 ||
-           strcmp(def->defname, "async_capable") == 0)
+           strcmp(def->defname, "async_capable") == 0 ||
+           strcmp(def->defname, "keep_connections") == 0)
        {
            /* these accept only boolean values */
            (void) defGetBoolean(def);
@@ -221,6 +222,7 @@ InitPgFdwOptions(void)
        /* async_capable is available on both server and table */
        {"async_capable", ForeignServerRelationId, false},
        {"async_capable", ForeignTableRelationId, false},
+       {"keep_connections", ForeignServerRelationId, false},
        {"password_required", UserMappingRelationId, false},
 
        /*
index 21a29cc06204d57e269808126424598a1bcb9f88..107d1c0e030b3a19841385aeb30e3a445e83428e 100644 (file)
@@ -2821,6 +2821,19 @@ DROP USER MAPPING FOR regress_multi_conn_user2 SERVER loopback;
 DROP ROLE regress_multi_conn_user1;
 DROP ROLE regress_multi_conn_user2;
 
+-- ===================================================================
+-- Test foreign server level option keep_connections
+-- ===================================================================
+-- By default, the connections associated with foreign server are cached i.e.
+-- keep_connections option is on. Set it to off.
+ALTER SERVER loopback OPTIONS (keep_connections 'off');
+-- connection to loopback server is closed at the end of xact
+-- as keep_connections was set to off.
+SELECT 1 FROM ft1 LIMIT 1;
+-- No cached connections, so no records should be output.
+SELECT server_name FROM postgres_fdw_get_connections() ORDER BY 1;
+ALTER SERVER loopback OPTIONS (SET keep_connections 'on');
+
 -- ===================================================================
 -- batch insert
 -- ===================================================================
index c21e9be209b7e859e5eff381e47602fd6b460db2..a7c695b000ff6dbcb6c163a830e7d0d94385b69a 100644 (file)
@@ -518,6 +518,33 @@ OPTIONS (ADD password_required 'false');
    </para>
 
   </sect3>
+
+  <sect3>
+    <title>Connection Management Options</title>
+
+    <para>
+     By default all the open connections that <filename>postgres_fdw</filename>
+     established to the foreign servers are kept in local session for re-use.
+    </para>
+    <variablelist>
+     <varlistentry>
+      <term><literal>keep_connections</literal></term>
+      <listitem>
+       <para>
+        This option controls whether <filename>postgres_fdw</filename> keeps
+        the connections to the foreign server open so that the subsequent
+        queries can re-use them. It can only be specified for a foreign server.
+        The default is <literal>on</literal>. If set to <literal>off</literal>,
+        all connections to this foreign server will be discarded at the end of
+        transaction.
+      </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </sect3>
  </sect2>
 
 <sect2>
@@ -605,8 +632,10 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   <para>
    <filename>postgres_fdw</filename> establishes a connection to a
    foreign server during the first query that uses a foreign table
-   associated with the foreign server.  This connection is kept and
-   re-used for subsequent queries in the same session.  However, if
+   associated with the foreign server.  By default this connection
+   is kept and re-used for subsequent queries in the same session.
+   This behavior can be controlled using
+   <literal>keep_connections</literal> option for a foreign server. If
    multiple user identities (user mappings) are used to access the foreign
    server, a connection is established for each user mapping.
   </para>
@@ -622,8 +651,10 @@ postgres=# SELECT postgres_fdw_disconnect_all();
 
   <para>
    Once a connection to a foreign server has been established,
-   it's usually kept until the local or corresponding remote
+   it's by default kept until the local or corresponding remote
    session exits.  To disconnect a connection explicitly,
+   <literal>keep_connections</literal> option for a foreign server
+   may be disabled, or
    <function>postgres_fdw_disconnect</function> and
    <function>postgres_fdw_disconnect_all</function> functions
    may be used.  For example, these are useful to close