summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTilghman Lesher <tilghman@meg.abyt.es>2010-05-27 19:25:16 +0000
committerTilghman Lesher <tilghman@meg.abyt.es>2010-05-27 19:25:16 +0000
commit05a1ec8abc32d8a5287b86701bd4c964e4fbe193 (patch)
tree457df0fae3d67cc69e88d8eb738c2b0913f9dc90
parentfb80119b87efc5ca0e8ee439ef70149601f4f24a (diff)
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
-rw-r--r--CHANGES2
-rw-r--r--pbx/pbx_realtime.c126
2 files changed, 127 insertions, 1 deletions
diff --git a/CHANGES b/CHANGES
index 1f0cd8848..0bae9efbc 100644
--- a/CHANGES
+++ b/CHANGES
@@ -446,6 +446,8 @@ Miscellaneous
of dynamic parkinglots.
* chan_dahdi now supports reporting alarms over AMI either by channel or span via
the reportalarms config option.
+ * The Realtime dialplan switch now caches entries for 1 second. This provides a
+ significant increase in performance (about 3X) for installations using this switchtype.
CLI Changes
-----------
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 <signal.h>
+
#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;