From 05a1ec8abc32d8a5287b86701bd4c964e4fbe193 Mon Sep 17 00:00:00 2001 From: Tilghman Lesher Date: Thu, 27 May 2010 19:25:16 +0000 Subject: Cache query results for one second. Queries from the PBX core come in 3's. Caching avoids the additional performance penalty from those two additional queries hitting the database. (closes issue #16521) Reported by: tilghman Patches: 20091229__issue16521.diff.txt uploaded by tilghman (license 14) Tested by: Hubguru, tilghman git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@266238 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- pbx/pbx_realtime.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) (limited to 'pbx') diff --git a/pbx/pbx_realtime.c b/pbx/pbx_realtime.c index 7db41571c..18ddfe7df 100644 --- a/pbx/pbx_realtime.c +++ b/pbx/pbx_realtime.c @@ -27,6 +27,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include + #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" @@ -47,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/crypto.h" #include "asterisk/astdb.h" #include "asterisk/app.h" +#include "asterisk/astobj2.h" #define MODE_MATCH 0 #define MODE_MATCHMORE 1 @@ -62,6 +65,80 @@ AST_APP_OPTIONS(switch_opts, { AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED), }); +struct cache_entry { + struct timeval when; + struct ast_variable *var; + int priority; + char *context; + char exten[2]; +}; + +struct ao2_container *cache; +pthread_t cleanup_thread = 0; + +static int cache_hash(const void *obj, const int flags) +{ + const struct cache_entry *e = obj; + return ast_str_case_hash(e->exten) + e->priority; +} + +static int cache_cmp(void *obj, void *arg, int flags) +{ + struct cache_entry *e = obj, *f = arg; + return e->priority != f->priority ? 0 : + strcmp(e->exten, f->exten) ? 0 : + strcmp(e->context, f->context) ? 0 : + CMP_MATCH; +} + +static struct ast_variable *dup_vars(struct ast_variable *v) +{ + struct ast_variable *new, *list = NULL; + for (; v; v = v->next) { + if (!(new = ast_variable_new(v->name, v->value, v->file))) { + ast_variables_destroy(list); + return NULL; + } + /* Reversed list in cache, but when we duplicate out of the cache, + * it's back to correct order. */ + new->next = list; + list = new; + } + return list; +} + +static void free_entry(void *obj) +{ + struct cache_entry *e = obj; + ast_variables_destroy(e->var); +} + +static int purge_old_fn(void *obj, void *arg, int flags) +{ + struct cache_entry *e = obj; + struct timeval *now = arg; + return ast_tvdiff_ms(*now, e->when) >= 1000 ? CMP_MATCH : 0; +} + +static void *cleanup(void *unused) +{ + struct timespec forever = { 999999999, 0 }, one_second = { 1, 0 }; + struct timeval now; + + for (;;) { + pthread_testcancel(); + if (ao2_container_count(cache) == 0) { + nanosleep(&forever, NULL); + } + pthread_testcancel(); + now = ast_tvnow(); + ao2_callback(cache, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, purge_old_fn, &now); + pthread_testcancel(); + nanosleep(&one_second, NULL); + } +} + + /* Realtime switch looks up extensions in the supplied realtime table. [context@][realtimetable][/options] @@ -141,6 +218,11 @@ static struct ast_variable *realtime_common(const char *context, const char *ext char *table; struct ast_variable *var=NULL; struct ast_flags flags = { 0, }; + struct cache_entry *ce; + struct { + struct cache_entry ce; + char exten[AST_MAX_EXTENSION]; + } cache_search = { { .priority = priority, .context = (char *) context }, }; char *buf = ast_strdupa(data); if (buf) { /* "Realtime" prefix is stripped off in the parent engine. The @@ -158,7 +240,36 @@ static struct ast_variable *realtime_common(const char *context, const char *ext if (!ast_strlen_zero(opts)) { ast_app_parse_options(switch_opts, &flags, NULL, opts); } - var = realtime_switch_common(table, ctx, exten, priority, mode, flags); + ast_copy_string(cache_search.exten, exten, sizeof(cache_search.exten)); + if (mode == MODE_MATCH && (ce = ao2_find(cache, &cache_search, OBJ_POINTER))) { + var = dup_vars(ce->var); + ao2_ref(ce, -1); + } else { + var = realtime_switch_common(table, ctx, exten, priority, mode, flags); + do { + struct ast_variable *new; + /* Only cache matches */ + if (mode != MODE_MATCH) { + break; + } + if (!(new = dup_vars(var))) { + break; + } + if (!(ce = ao2_alloc(sizeof(*ce) + strlen(exten) + strlen(context), free_entry))) { + ast_variables_destroy(new); + break; + } + ce->context = ce->exten + strlen(exten) + 1; + strcpy(ce->exten, exten); /* SAFE */ + strcpy(ce->context, context); /* SAFE */ + ce->priority = priority; + ce->var = new; + ce->when = ast_tvnow(); + ao2_link(cache, ce); + pthread_kill(cleanup_thread, SIGURG); + ao2_ref(ce, -1); + } while (0); + } } return var; } @@ -283,11 +394,24 @@ static struct ast_switch realtime_switch = static int unload_module(void) { ast_unregister_switch(&realtime_switch); + pthread_cancel(cleanup_thread); + pthread_kill(cleanup_thread, SIGURG); + pthread_join(cleanup_thread, NULL); + /* Destroy all remaining entries */ + ao2_ref(cache, -1); return 0; } static int load_module(void) { + if (!(cache = ao2_container_alloc(573, cache_hash, cache_cmp))) { + return AST_MODULE_LOAD_FAILURE; + } + + if (ast_pthread_create(&cleanup_thread, NULL, cleanup, NULL)) { + return AST_MODULE_LOAD_FAILURE; + } + if (ast_register_switch(&realtime_switch)) return AST_MODULE_LOAD_FAILURE; return AST_MODULE_LOAD_SUCCESS; -- cgit v1.2.3