; 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
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;
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;
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) ||
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");
+ }
}
}
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));
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);
} 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);
ast_mutex_lock(&class->lock);
obj = AST_LIST_REMOVE_HEAD(&class->connections, list);
+ if (obj) {
+ --class->cur_cache;
+ }
ast_mutex_unlock(&class->lock);