From e94aeecaeebd277606f4e71ebfff55f68cdabca0 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Fri, 2 Jan 2009 10:54:47 +0000 Subject: [PATCH] Reserve pool implementation. Allow to increased pool size in case main pool size cannot serve clients anymore. This allows to continue use of small pool_size, which smooths small peaks, while also handling large peaks. (Eg, one bouncer server down, others need to handle load.) --- doc/config.txt | 16 ++++++++++++++++ etc/pgbouncer.ini | 8 ++++++++ include/bouncer.h | 3 +++ src/admin.c | 7 ++++--- src/janitor.c | 10 +++++++--- src/loader.c | 4 ++++ src/main.c | 4 ++++ src/objects.c | 14 ++++++++++++++ 8 files changed, 60 insertions(+), 6 deletions(-) diff --git a/doc/config.txt b/doc/config.txt index aa8f81e..85d666c 100644 --- a/doc/config.txt +++ b/doc/config.txt @@ -139,6 +139,22 @@ per-database config. Default: 20 +==== reserve_pool_size ==== + +How many additional connections to allow to a pool. + +How many server connection to allow per user/database pair. Can be overrided in +per-database config. 0 disables. + +Default: 0 (disabled) + +==== reserve_pool_timeout ==== + +If a client has not been services in this many seconds, pgbouncer enables +use of additional connections from reserve pool. 0 disables. + +Default: 5 + ==== server_round_robin ==== By default, pgbouncer reuses server connections in LIFO manner, so that few diff --git a/etc/pgbouncer.ini b/etc/pgbouncer.ini index 7c38f34..87bae3a 100644 --- a/etc/pgbouncer.ini +++ b/etc/pgbouncer.ini @@ -10,6 +10,8 @@ bardb = host=127.0.0.1 dbname=bazdb ; acceess to dest database will go with single user forcedb = host=127.0.0.1 port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO connect_query='SELECT 1' +nondefaultdb = pool_size=50 reserve_pool=10 + ;; Configuation section [pgbouncer] @@ -93,6 +95,12 @@ server_check_delay = 10 max_client_conn = 100 default_pool_size = 20 +; how many additional connection to allow in case of trouble +;reserve_pool_size = 5 + +; if a clients needs to wait more than this many seconds, use reserve pool +;reserve_pool_timeout = 3 + log_connections = 1 log_disconnections = 1 diff --git a/include/bouncer.h b/include/bouncer.h index b4239ac..87a5a6c 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -232,6 +232,7 @@ struct PgDatabase { char unix_socket_dir[UNIX_PATH_MAX]; /* custom unix socket dir */ int pool_size; /* max server connections in one pool */ + int res_pool_size; /* additional server connections in case of trouble */ const char *dbname; /* server-side name, pointer to inside startup_msg */ @@ -309,6 +310,8 @@ extern int cf_listen_port; extern int cf_pool_mode; extern int cf_max_client_conn; extern int cf_default_pool_size; +extern int cf_res_pool_size; +extern usec_t cf_res_pool_timeout; extern char * cf_autodb_connstr; extern usec_t cf_autodb_idle_timeout; diff --git a/src/admin.c b/src/admin.c index 55f559b..e757474 100644 --- a/src/admin.c +++ b/src/admin.c @@ -415,9 +415,9 @@ static bool admin_show_databases(PgSocket *admin, const char *arg) return true; } - pktbuf_write_RowDescription(buf, "ssissi", + pktbuf_write_RowDescription(buf, "ssissii", "name", "host", "port", - "database", "force_user", "pool_size"); + "database", "force_user", "pool_size", "reserve_pool"); statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); @@ -430,7 +430,8 @@ static bool admin_show_databases(PgSocket *admin, const char *arg) pktbuf_write_DataRow(buf, "ssissi", db->name, host, db->addr.port, db->dbname, f_user, - db->pool_size); + db->pool_size, + db->res_pool_size); } admin_flush(admin, buf, "SHOW"); return true; diff --git a/src/janitor.c b/src/janitor.c index c8e1217..8ec9b47 100644 --- a/src/janitor.c +++ b/src/janitor.c @@ -403,7 +403,7 @@ static void check_pool_size(PgPool *pool) * statlist_count(&pool->new_server_list) */ - int many = cur - pool->db->pool_size; + int many = cur - (pool->db->pool_size + pool->db->res_pool_size); Assert(pool->db->pool_size >= 0); @@ -591,10 +591,14 @@ void config_postprocess(void) statlist_for_each_safe(item, &database_list, tmp) { db = container_of(item, PgDatabase, head); - if (db->db_dead) + if (db->db_dead) { kill_database(db); - else if (db->pool_size < 0) + continue; + } + if (db->pool_size < 0) db->pool_size = cf_default_pool_size; + if (db->res_pool_size < 0) + db->res_pool_size = cf_res_pool_size; } } diff --git a/src/loader.c b/src/loader.c index 5c64f0f..5a8a262 100644 --- a/src/loader.c +++ b/src/loader.c @@ -173,6 +173,7 @@ void parse_database(char *name, char *connstr) PktBuf buf; PgDatabase *db; int pool_size = -1; + int res_pool_size = -1; char *dbname = name; char *host = NULL; @@ -220,6 +221,8 @@ void parse_database(char *name, char *connstr) timezone = val; else if (strcmp("pool_size", key) == 0) pool_size = atoi(val); + else if (strcmp("reserve_pool", key) == 0) + res_pool_size = atoi(val); else if (strcmp("connect_query", key) == 0) connect_query = val; else { @@ -319,6 +322,7 @@ void parse_database(char *name, char *connstr) /* if pool_size < 0 it will be set later */ db->pool_size = pool_size; + db->res_pool_size = res_pool_size; db->addr.port = v_port; db->addr.ip_addr.s_addr = v_addr; db->addr.is_unix = host ? 0 : 1; diff --git a/src/main.c b/src/main.c index 2209cfc..9472176 100644 --- a/src/main.c +++ b/src/main.c @@ -91,6 +91,8 @@ char *cf_auth_file = "unconfigured_file"; int cf_max_client_conn = 100; int cf_default_pool_size = 20; +int cf_res_pool_size = 0; +usec_t cf_res_pool_timeout = 5; char *cf_server_reset_query = ""; char *cf_server_check_query = "select 1"; @@ -143,6 +145,8 @@ ConfElem bouncer_params[] = { {"pool_mode", true, {get_mode, set_mode}}, {"max_client_conn", true, CF_INT, &cf_max_client_conn}, {"default_pool_size", true, CF_INT, &cf_default_pool_size}, +{"reserve_pool_size", true, CF_INT, &cf_res_pool_size}, +{"reserve_pool_timeout",true, CF_INT, &cf_res_pool_timeout}, {"syslog", true, CF_INT, &cf_syslog}, {"syslog_facility", true, CF_STR, &cf_syslog_facility}, #ifndef WIN32 diff --git a/src/objects.c b/src/objects.c index 8799ddd..18f9930 100644 --- a/src/objects.c +++ b/src/objects.c @@ -331,6 +331,8 @@ PgDatabase *register_auto_database(const char *name) /* do not forget to check pool_size like in config_postprocess */ if (db->pool_size < 0) db->pool_size = cf_default_pool_size; + if (db->res_pool_size < 0) + db->res_pool_size = cf_res_pool_size; } return db; @@ -772,11 +774,23 @@ void launch_new_connection(PgPool *pool) /* is it allowed to add servers? */ total = pool_server_count(pool); if (total >= pool->db->pool_size && pool->welcome_msg_ready) { + /* should we use reserve pool? */ + if (cf_res_pool_timeout && pool->db->res_pool_size) { + usec_t now = get_cached_time(); + PgSocket *c = first_socket(&pool->waiting_client_list); + if (c && (now - c->request_time) >= cf_res_pool_timeout) { + if (total < pool->db->pool_size + pool->db->res_pool_size) { + log_debug("reserve_pool activated"); + goto allow_new; + } + } + } log_debug("launch_new_connection: pool full (%d >= %d)", total, pool->db->pool_size); return; } +allow_new: /* get free conn object */ server = obj_alloc(server_cache); if (!server) { -- 2.40.0