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,
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.
; 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
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 */
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 */
};
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;
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;
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);
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;
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;
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"
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);
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;
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"),
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");
}
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) {
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);
}
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)
{