From b78bc2cc89c920aa73a5dd8726dcecbed282722f Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 27 Aug 2013 16:29:46 -0600 Subject: [PATCH] evict old connections when needed (if possible) for max_db_connections --- include/bouncer.h | 8 +++++++ include/objects.h | 2 ++ src/objects.c | 55 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/include/bouncer.h b/include/bouncer.h index 1548de3..ad6032e 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -450,6 +450,14 @@ first_socket(struct StatList *slist) return container_of(slist->head.next, PgSocket, head); } +static inline PgSocket * +last_socket(struct StatList *slist) +{ + if (statlist_empty(slist)) + return NULL; + return container_of(slist->head.prev, PgSocket, head); +} + void load_config(void); diff --git a/include/objects.h b/include/objects.h index b4a91a6..509749d 100644 --- a/include/objects.h +++ b/include/objects.h @@ -32,6 +32,8 @@ extern struct Slab *iobuf_cache; PgDatabase *find_database(const char *name); PgUser *find_user(const char *name); PgPool *get_pool(PgDatabase *, PgUser *); +void check_oldest_connection(PgSocket **oldest_connection, PgSocket *connection); +bool evict_connection(PgDatabase *db) _MUSTCHECK; bool find_server(PgSocket *client) _MUSTCHECK; bool release_server(PgSocket *server) /* _MUSTCHECK */; bool finish_client_login(PgSocket *client) _MUSTCHECK; diff --git a/src/objects.c b/src/objects.c index 474c84e..6550f40 100644 --- a/src/objects.c +++ b/src/objects.c @@ -976,6 +976,45 @@ static void dns_connect(struct PgSocket *server) connect_server(server, sa, sa_len); } +void check_oldest_connection(PgSocket **oldest_connection, PgSocket *connection) +{ + if (!connection) + return; + if (!*oldest_connection) { + *oldest_connection = connection; + return; + } + if (connection->request_time < (*oldest_connection)->request_time) { + *oldest_connection = connection; + } +} + +/* evict the single most idle connection from among all pools to make room in the db */ +bool evict_connection(PgDatabase *db) +{ + struct List *item; + PgPool *pool; + PgSocket *oldest_connection = NULL; + + statlist_for_each(item, &pool_list) { + pool = container_of(item, PgPool, head); + if (pool->db != db) + continue; + check_oldest_connection(&oldest_connection, last_socket(&pool->idle_server_list)); + // only evict testing connections if nobody's waiting + if (statlist_empty(&pool->waiting_client_list)) { + check_oldest_connection(&oldest_connection, last_socket(&pool->used_server_list)); + check_oldest_connection(&oldest_connection, last_socket(&pool->tested_server_list)); + } + } + + if (oldest_connection) { + disconnect_server(oldest_connection, true, "evicted"); + return true; + } + return false; +} + /* the pool needs new connection, if possible */ void launch_new_connection(PgPool *pool) { @@ -1018,10 +1057,18 @@ 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; + if (total > 0) { + // try to evict unused connections first + while (pool->db->connection_count >= total) { + if (!evict_connection(pool->db)) { + break; + } + } + if (pool->db->connection_count >= total) { + log_debug("launch_new_connection: database full (%d >= %d)", + pool->db->connection_count, total); + return; + } } /* get free conn object */ -- 2.40.0