]> granicus.if.org Git - pgbouncer/commitdiff
evict old connections when needed (if possible) for max_db_connections
authorCody Cutrer <cody@instructure.com>
Tue, 27 Aug 2013 22:29:46 +0000 (16:29 -0600)
committerCody Cutrer <cody@instructure.com>
Mon, 7 Oct 2013 21:38:42 +0000 (15:38 -0600)
include/bouncer.h
include/objects.h
src/objects.c

index 1548de36ce9e354848ead67235357c26415ea573..ad6032e2ef0c32b9b5c9ac6e784b1f82fea32bf4 100644 (file)
@@ -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);
 
 
index b4a91a649f6ab6ec3a4dafd1af77a4baff2248c0..509749d06615fa6fd7baaac1d75d27f39fe378e7 100644 (file)
@@ -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;
index 474c84ef8b50018d454c38f3b7fc67dc2466e495..6550f40a8ca219a64aeba612a843b3bc49ef74a5 100644 (file)
@@ -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 */