From: Jaco Kroon Date: Thu, 12 Dec 2024 22:35:55 +0000 (+0200) Subject: res_odbc: cache_size option to limit the cached connections. X-Git-Tag: 21.10.0-rc1~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=04b12a557daad453b15ec291c47482815030e64b;p=thirdparty%2Fasterisk.git res_odbc: cache_size option to limit the cached connections. Signed-off-by: Jaco Kroon 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) --- diff --git a/configs/samples/res_odbc.conf.sample b/configs/samples/res_odbc.conf.sample index 277f33148d..be4ff25b5d 100644 --- a/configs/samples/res_odbc.conf.sample +++ b/configs/samples/res_odbc.conf.sample @@ -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 diff --git a/res/res_odbc.c b/res/res_odbc.c index ea44e6f1bc..e863c4570f 100644 --- a/res/res_odbc.c +++ b/res/res_odbc.c @@ -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);