aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2010-05-27 19:25:16 +0000
committertilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2010-05-27 19:25:16 +0000
commit25a50400c937b2feb0d08cb04696b5504d6df660 (patch)
tree457df0fae3d67cc69e88d8eb738c2b0913f9dc90
parent01e42965e966c0c7ba28925edbfff5d3e703a7ce (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: http://svn.digium.com/svn/asterisk/trunk@266238 f38db490-d61c-443f-a65b-d21fe96a405b
-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;