]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_odbc: cache_size option to limit the cached connections.
authorJaco Kroon <jaco@uls.co.za>
Thu, 12 Dec 2024 22:35:55 +0000 (00:35 +0200)
committerGeorge Joseph <gjoseph@sangoma.com>
Thu, 26 Jun 2025 18:15:05 +0000 (12:15 -0600)
Signed-off-by: Jaco Kroon <jaco@uls.co.za>
UserNote: New cache_size option for res_odbc to on a per class basis limit the
number of cached connections. Please reference the sample configuration
for details.

(cherry picked from commit e125410e5c0e24ce3d49fed3982b0633b2882b37)

configs/samples/res_odbc.conf.sample
res/res_odbc.c

index 277f33148d4f6972618185df3f5452c001f2fd60..be4ff25b5d0a2603ba6418830715bb71ac71cfa3 100644 (file)
@@ -97,6 +97,18 @@ pre-connect => yes
 ; The default remains a stack to be backwards compatible, setting this value to
 ; any of rr, roundrobin or queue will result in a round-robin queue being used.
 ;cache_type => roundrobin
+;
+; Limit the number of connections to cache.  This is separate from
+; max_connections which is the total number of connections that open, whereas
+; this value specifies how many idle connections will be stored in the connection
+; cache.  On extremely spiky loads this could be useful to limit the overall
+; number of connections that's kept open when the spike comes down.  The default
+; is to not limit the cache size (thus effectively limit to max_connections).
+; When a returned connection would result in the cache size being exceeded, the
+; longest idle connection in the cache shall be dropped.
+; Setting this to zero will effectively disable the connection cache, setting to -1
+; will set this to "infinite" (default).
+;cache_size => 10
 
 [mysql2]
 enabled => no
index ea44e6f1bcd77937d4b22c8db2b2a417afc157e0..e863c4570fa0693975a04120593a8f1e35ba3a4f 100644 (file)
@@ -101,6 +101,10 @@ struct odbc_class
        char *sql_text;
        /*! Slow query limit (in milliseconds) */
        unsigned int slowquerylimit;
+       /*! Maximum number of cached connections, default is maxconnections */
+       unsigned int max_cache_size;
+       /*! Current cached connection count, when cache_size will exceed max_cache_size, longest-idle connection will be dropped from the cache */
+       unsigned int cur_cache;
 };
 
 static struct ao2_container *class_container;
@@ -564,6 +568,7 @@ static int load_odbc_config(void)
        struct timeval ncache = { 0, 0 };
        int preconnect = 0, res = 0, cache_is_queue = 0;
        struct ast_flags config_flags = { 0 };
+       unsigned int max_cache_size;
 
        struct odbc_class *new;
 
@@ -591,6 +596,7 @@ static int load_odbc_config(void)
                        logging = 0;
                        slowquerylimit = 5000;
                        cache_is_queue = 0;
+                       max_cache_size = UINT_MAX;
                        for (v = ast_variable_browse(config, cat); v; v = v->next) {
                                if (!strcasecmp(v->name, "pooling") ||
                                                !strncasecmp(v->name, "share", 5) ||
@@ -650,6 +656,12 @@ static int load_odbc_config(void)
                                        cache_is_queue = !strcasecmp(v->value, "rr") ||
                                                !strcasecmp(v->value, "roundrobin") ||
                                                !strcasecmp(v->value, "queue");
+                               } else if (!strcasecmp(v->name, "cache_size")) {
+                                       if (!strcasecmp(v->value, "-1")) {
+                                               max_cache_size = UINT_MAX;
+                                       } else if (sscanf(v->value, "%u", &max_cache_size) != 1) {
+                                               ast_log(LOG_WARNING, "cache_size must be a non-negative integer or -1 (infinite)\n");
+                                       }
                                }
                        }
 
@@ -679,6 +691,8 @@ static int load_odbc_config(void)
                                new->logging = logging;
                                new->slowquerylimit = slowquerylimit;
                                new->cache_is_queue = cache_is_queue;
+                               new->max_cache_size = max_cache_size;
+                               new->cur_cache = 0;
 
                                if (cat)
                                        ast_copy_string(new->name, cat, sizeof(new->name));
@@ -766,6 +780,8 @@ static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_c
 
                        ast_cli(a->fd, "    Number of active connections: %zd (out of %d)\n", class->connection_cnt, class->maxconnections);
                        ast_cli(a->fd, "    Cache Type: %s\n", class->cache_is_queue ? "round-robin queue" : "stack (last release, first re-use)");
+                       ast_cli(a->fd, "    Cache Usage: %u cached out of %u\n", class->cur_cache,
+                                       class->max_cache_size < class->maxconnections ? class->max_cache_size : class->maxconnections);
                        ast_cli(a->fd, "    Logging: %s\n", class->logging ? "Enabled" : "Disabled");
                        if (class->logging) {
                                ast_cli(a->fd, "    Number of prepares executed: %d\n", class->prepares_executed);
@@ -836,6 +852,29 @@ void ast_odbc_release_obj(struct odbc_obj *obj)
        } else {
                AST_LIST_INSERT_HEAD(&class->connections, obj, list);
        }
+
+       if (class->cur_cache >= class->max_cache_size) {
+               /* cache is full */
+               if (class->cache_is_queue) {
+                       /* HEAD will be oldest */
+                       obj = AST_LIST_REMOVE_HEAD(&class->connections, list);
+               } else {
+                       /* TAIL will be oldest */
+                       obj = AST_LIST_LAST(&class->connections);
+                       AST_LIST_REMOVE(&class->connections, obj, list);
+               }
+               --class->connection_cnt;
+               ast_mutex_unlock(&class->lock);
+
+               ast_debug(2, "ODBC Pool '%s' exceeded cache size, dropping '%p', connection count is %zd (%u cached)\n",
+                       class->name, obj, class->connection_cnt, class->cur_cache);
+
+               ao2_ref(obj, -1);
+
+               ast_mutex_lock(&class->lock);
+       } else {
+               ++class->cur_cache;
+       }
        ast_cond_signal(&class->cond);
        ast_mutex_unlock(&class->lock);
 
@@ -931,6 +970,9 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
                ast_mutex_lock(&class->lock);
 
                obj = AST_LIST_REMOVE_HEAD(&class->connections, list);
+               if (obj) {
+                       --class->cur_cache;
+               }
 
                ast_mutex_unlock(&class->lock);