]> granicus.if.org Git - pgbouncer/commitdiff
implement database-wide connection limit
authorCody Cutrer <cody@cutrer.us>
Mon, 4 Feb 2013 23:58:05 +0000 (16:58 -0700)
committerCody Cutrer <cody@instructure.com>
Mon, 7 Oct 2013 21:38:28 +0000 (15:38 -0600)
doc/config.txt
etc/pgbouncer.ini
include/bouncer.h
include/server.h
src/admin.c
src/loader.c
src/main.c
src/objects.c
src/server.c

index 7e3c646307279b143170cca0ac173c9cf8e004bf..fa3518c668015a3e0ade87ce273104708766e337 100644 (file)
@@ -179,6 +179,17 @@ use of additional connections from reserve pool.  0 disables.
 
 Default: 5.0
 
+==== max_db_connections ====
+
+Do not allow more than this many connections per-database (regardless of pool - i.e.
+user). It should be noted that when you hit the limit, closing a client connection
+to one pool will not immediately allow a server connection to be established for
+another pool, because the server connection for the first pool is still open.
+Once the server connection closes (due to idle timeout), a new server connection
+will immediately be opened for the waiting pool.
+
+Default: unlimited
+
 ==== server_round_robin ====
 
 By default, pgbouncer reuses server connections in LIFO (last-in, first-out) manner, 
@@ -551,6 +562,11 @@ they are logged but ignored otherwise.
 Set the pool mode specific to this database. If not set,
 the default pool_mode is used.
 
+==== max_db_connections ====
+
+Configure a database-wide maximum (i.e. all pools within the database will
+not have more than this many server connections).
+
 === Extra parameters ===
 
 They allow setting default parameters on server connection.
index 12ec22bd81cc3658c585015658bee2ef33d37081..bdcaef4154da85b7e0e96d21de0f7796f05c9eb5 100644 (file)
@@ -124,6 +124,9 @@ default_pool_size = 20
 ; if a clients needs to wait more than this many seconds, use reserve pool
 ;reserve_pool_timeout = 3
 
+; how many total connections to a single database to allow from all pools
+;max_db_connections = 50
+
 ; log if client connects or server connection is made
 ;log_connections = 1
 
index 4679cf20d315ae081d16f1b86aedf36738e2c88c..1548de36ce9e354848ead67235357c26415ea573 100644 (file)
@@ -279,6 +279,7 @@ struct PgDatabase {
        int pool_size;          /* max server connections in one pool */
        int res_pool_size;      /* additional server connections in case of trouble */
        int pool_mode;          /* pool mode for this database */
+       int max_db_connections; /* max server connections between all pools */
 
        const char *dbname;     /* server-side name, pointer to inside startup_msg */
 
@@ -288,6 +289,8 @@ struct PgDatabase {
        usec_t inactive_time;   /* when auto-database became inactive (to kill it after timeout) */
        unsigned active_stamp;  /* set if autodb has connections */
 
+       int connection_count;   /* total connections for this database in all pools */
+
        struct AATree user_tree;        /* users that have been queried on this database */
 };
 
@@ -371,6 +374,7 @@ extern int cf_default_pool_size;
 extern int cf_min_pool_size;
 extern int cf_res_pool_size;
 extern usec_t cf_res_pool_timeout;
+extern int cf_max_db_connections;
 
 extern char * cf_autodb_connstr;
 extern usec_t cf_autodb_idle_timeout;
index 9e3490c67ab58148e85ede7a654980ab5858d3a9..3f40eb1010683ca74cd60bea3aefeeae71162326 100644 (file)
@@ -18,3 +18,4 @@
 
 bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt)  _MUSTCHECK;
 int pool_pool_mode(PgPool *pool) _MUSTCHECK;
+int database_max_connections(PgDatabase *db) _MUSTCHECK;
index cfe7df999216c7613d774930c97d6c45513fe1b3..0f7e8714e9688baf5667da965fd47e2bc9485d82 100644 (file)
@@ -457,10 +457,10 @@ static bool admin_show_databases(PgSocket *admin, const char *arg)
                return true;
        }
 
-       pktbuf_write_RowDescription(buf, "ssissiis",
+       pktbuf_write_RowDescription(buf, "ssissiisii",
                                    "name", "host", "port",
                                    "database", "force_user", "pool_size", "reserve_pool",
-                                   "pool_mode");
+                                   "pool_mode", "max_connections", "current_connections");
        statlist_for_each(item, &database_list) {
                db = container_of(item, PgDatabase, head);
 
@@ -469,12 +469,14 @@ static bool admin_show_databases(PgSocket *admin, const char *arg)
                cv.value_p = &db->pool_mode;
                if (db->pool_mode != POOL_INHERIT)
                        pool_mode_str = cf_get_lookup(&cv);
-               pktbuf_write_DataRow(buf, "ssissiis",
+               pktbuf_write_DataRow(buf, "ssissiisii",
                                     db->name, db->host, db->port,
                                     db->dbname, f_user,
                                     db->pool_size,
                                     db->res_pool_size,
-                                    pool_mode_str);
+                                    pool_mode_str,
+                                    database_max_connections(db),
+                                    db->connection_count);
        }
        admin_flush(admin, buf, "SHOW");
        return true;
index 6b83774c3dc9e676efe7974758c7a1b2484fd253..bc00370eaa3ef4afd907e2f6b8ebfc03324dc3fb 100644 (file)
@@ -180,6 +180,7 @@ bool parse_database(void *base, const char *name, const char *connstr)
        struct CfValue cv;
        int pool_size = -1;
        int res_pool_size = -1;
+       int max_db_connections = -1;
        int dbname_ofs;
        int pool_mode = POOL_INHERIT;
 
@@ -241,6 +242,8 @@ bool parse_database(void *base, const char *name, const char *connstr)
                        pool_size = atoi(val);
                else if (strcmp("reserve_pool", key) == 0)
                        res_pool_size = atoi(val);
+               else if (strcmp("max_db_connections", key) == 0)
+                       max_db_connections = atoi(val);
                else if (strcmp("pool_mode", key) == 0) {
                        if (!cf_set_lookup(&cv, val)) {
                                log_error("skipping database %s because"
@@ -317,6 +320,7 @@ bool parse_database(void *base, const char *name, const char *connstr)
        db->pool_size = pool_size;
        db->res_pool_size = res_pool_size;
        db->pool_mode = pool_mode;
+       db->max_db_connections = max_db_connections;
 
        if (db->host)
                free(db->host);
index 79c8d25912d7b084dca7ba6d40c4b3ec6f6fba34..9ef693a03db630dcbb393b7e6cfc9a3bb9ef4615 100644 (file)
@@ -92,6 +92,7 @@ int cf_default_pool_size;
 int cf_min_pool_size;
 int cf_res_pool_size;
 usec_t cf_res_pool_timeout;
+int cf_max_db_connections;
 
 char *cf_server_reset_query;
 char *cf_server_check_query;
@@ -182,6 +183,7 @@ CF_ABS("default_pool_size", CF_INT, cf_default_pool_size, 0, "20"),
 CF_ABS("min_pool_size", CF_INT, cf_min_pool_size, 0, "0"),
 CF_ABS("reserve_pool_size", CF_INT, cf_res_pool_size, 0, "0"),
 CF_ABS("reserve_pool_timeout", CF_TIME_USEC, cf_res_pool_timeout, 0, "5"),
+CF_ABS("max_db_connections", CF_INT, cf_max_db_connections, 0, "0"),
 CF_ABS("syslog", CF_INT, cf_syslog, 0, "0"),
 CF_ABS("syslog_facility", CF_STR, cf_syslog_facility, 0, "daemon"),
 CF_ABS("syslog_ident", CF_STR, cf_syslog_ident, 0, "pgbouncer"),
index e20259b8fc3faaf1c38bf0c4873922a9675fa6ed..474c84ef8b50018d454c38f3b7fc67dc2466e495 100644 (file)
@@ -801,6 +801,8 @@ void disconnect_server(PgSocket *server, bool notify, const char *reason, ...)
                server->dns_token = NULL;
        }
 
+       server->pool->db->connection_count--;
+
        change_server_state(server, SV_JUSTFREE);
        if (!sbuf_close(&server->sbuf))
                log_noise("sbuf_close failed, retry later");
@@ -1015,6 +1017,13 @@ void launch_new_connection(PgPool *pool)
        }
 
 allow_new:
+       total = database_max_connections(pool->db);
+       if (total > 0 && pool->db->connection_count >= total) {
+               log_debug("launch_new_connection: database full (%d >= %d)",
+                               pool->db->connection_count, total);
+               return;
+       }
+
        /* get free conn object */
        server = slab_alloc(server_cache);
        if (!server) {
@@ -1028,6 +1037,7 @@ allow_new:
        server->connect_time = get_cached_time();
        pool->last_connect_time = get_cached_time();
        change_server_state(server, SV_LOGIN);
+       pool->db->connection_count++;
 
        dns_connect(server);
 }
index bbc6982d30b45cc348cd0f604f425d3b7f4fb1fb..dac4867932712a3502fa540a72d358e5baa7f560 100644 (file)
@@ -195,6 +195,15 @@ int pool_pool_mode(PgPool *pool)
        return pool_mode;
 }
 
+int database_max_connections(PgDatabase *db)
+{
+       if (db->max_db_connections <= 0) {
+               return cf_max_db_connections;
+        } else {
+               return db->max_db_connections;
+       }
+}
+
 /* process packets on logged in connection */
 static bool handle_server_work(PgSocket *server, PktHdr *pkt)
 {