summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/functions.c71
-rw-r--r--tests/unit/functions.tcl32
2 files changed, 97 insertions, 6 deletions
diff --git a/src/functions.c b/src/functions.c
index 1a94bacc3..57fb7ec24 100644
--- a/src/functions.c
+++ b/src/functions.c
@@ -41,13 +41,20 @@ static size_t engine_cache_memory = 0;
/* Forward declaration */
static void engineFunctionDispose(dict *d, void *obj);
+static void engineStatsDispose(dict *d, void *obj);
static void engineLibraryDispose(dict *d, void *obj);
static int functionsVerifyName(sds name);
+typedef struct functionsLibEngineStats {
+ size_t n_lib;
+ size_t n_functions;
+} functionsLibEngineStats;
+
struct functionsLibCtx {
- dict *libraries; /* Function name -> Function object that can be used to run the function */
- dict *functions; /* Function name -> Function object that can be used to run the function */
- size_t cache_memory /* Overhead memory (structs, dictionaries, ..) used by all the functions */;
+ dict *libraries; /* Library name -> Library object */
+ dict *functions; /* Function name -> Function object that can be used to run the function */
+ size_t cache_memory; /* Overhead memory (structs, dictionaries, ..) used by all the functions */
+ dict *engines_stats; /* Per engine statistics */
};
dictType engineDictType = {
@@ -70,6 +77,16 @@ dictType functionDictType = {
NULL /* allow to expand */
};
+dictType engineStatsDictType = {
+ dictSdsCaseHash, /* hash function */
+ dictSdsDup, /* key dup */
+ NULL, /* val dup */
+ dictSdsKeyCaseCompare,/* key compare */
+ dictSdsDestructor, /* key destructor */
+ engineStatsDispose, /* val destructor */
+ NULL /* allow to expand */
+};
+
dictType libraryFunctionDictType = {
dictSdsHash, /* hash function */
dictSdsDup, /* key dup */
@@ -111,6 +128,12 @@ static size_t libraryMallocSize(functionLibInfo *li) {
+ sdsZmallocSize(li->code);
}
+static void engineStatsDispose(dict *d, void *obj) {
+ UNUSED(d);
+ functionsLibEngineStats *stats = obj;
+ zfree(stats);
+}
+
/* Dispose function memory */
static void engineFunctionDispose(dict *d, void *obj) {
UNUSED(d);
@@ -147,6 +170,14 @@ static void engineLibraryDispose(dict *d, void *obj) {
void functionsLibCtxClear(functionsLibCtx *lib_ctx) {
dictEmpty(lib_ctx->functions, NULL);
dictEmpty(lib_ctx->libraries, NULL);
+ dictIterator *iter = dictGetIterator(lib_ctx->engines_stats);
+ dictEntry *entry = NULL;
+ while ((entry = dictNext(iter))) {
+ functionsLibEngineStats *stats = dictGetVal(entry);
+ stats->n_functions = 0;
+ stats->n_lib = 0;
+ }
+ dictReleaseIterator(iter);
curr_functions_lib_ctx->cache_memory = 0;
}
@@ -165,6 +196,7 @@ void functionsLibCtxFree(functionsLibCtx *functions_lib_ctx) {
functionsLibCtxClear(functions_lib_ctx);
dictRelease(functions_lib_ctx->functions);
dictRelease(functions_lib_ctx->libraries);
+ dictRelease(functions_lib_ctx->engines_stats);
zfree(functions_lib_ctx);
}
@@ -185,6 +217,15 @@ functionsLibCtx* functionsLibCtxCreate() {
functionsLibCtx *ret = zmalloc(sizeof(functionsLibCtx));
ret->libraries = dictCreate(&librariesDictType);
ret->functions = dictCreate(&functionDictType);
+ ret->engines_stats = dictCreate(&engineStatsDictType);
+ dictIterator *iter = dictGetIterator(engines);
+ dictEntry *entry = NULL;
+ while ((entry = dictNext(iter))) {
+ engineInfo *ei = dictGetVal(entry);
+ functionsLibEngineStats *stats = zcalloc(sizeof(*stats));
+ dictAdd(ret->engines_stats, ei->name, stats);
+ }
+ dictReleaseIterator(iter);
ret->cache_memory = 0;
return ret;
}
@@ -250,6 +291,12 @@ static void libraryUnlink(functionsLibCtx *lib_ctx, functionLibInfo* li) {
dictSetVal(lib_ctx->libraries, entry, NULL);
dictFreeUnlinkedEntry(lib_ctx->libraries, entry);
lib_ctx->cache_memory += libraryMallocSize(li);
+
+ /* update stats */
+ functionsLibEngineStats *stats = dictFetchValue(lib_ctx->engines_stats, li->ei->name);
+ serverAssert(stats);
+ stats->n_lib--;
+ stats->n_functions -= dictSize(li->functions);
}
static void libraryLink(functionsLibCtx *lib_ctx, functionLibInfo* li) {
@@ -264,6 +311,12 @@ static void libraryLink(functionsLibCtx *lib_ctx, functionLibInfo* li) {
dictAdd(lib_ctx->libraries, li->name, li);
lib_ctx->cache_memory += libraryMallocSize(li);
+
+ /* update stats */
+ functionsLibEngineStats *stats = dictFetchValue(lib_ctx->engines_stats, li->ei->name);
+ serverAssert(stats);
+ stats->n_lib++;
+ stats->n_functions += dictSize(li->functions);
}
/* Takes all libraries from lib_ctx_src and add to lib_ctx_dst.
@@ -401,12 +454,18 @@ void functionStatsCommand(client *c) {
}
addReplyBulkCString(c, "engines");
- addReplyArrayLen(c, dictSize(engines));
+ addReplyMapLen(c, dictSize(engines));
dictIterator *iter = dictGetIterator(engines);
dictEntry *entry = NULL;
while ((entry = dictNext(iter))) {
engineInfo *ei = dictGetVal(entry);
addReplyBulkCString(c, ei->name);
+ addReplyMapLen(c, 2);
+ functionsLibEngineStats *e_stats = dictFetchValue(curr_functions_lib_ctx->engines_stats, ei->name);
+ addReplyBulkCString(c, "libraries_count");
+ addReplyLongLong(c, e_stats->n_lib);
+ addReplyBulkCString(c, "functions_count");
+ addReplyLongLong(c, e_stats->n_functions);
}
dictReleaseIterator(iter);
}
@@ -979,11 +1038,13 @@ size_t functionsLibCtxfunctionsLen(functionsLibCtx *functions_ctx) {
* Should be called once on server initialization */
int functionsInit() {
engines = dictCreate(&engineDictType);
- curr_functions_lib_ctx = functionsLibCtxCreate();
if (luaEngineInitEngine() != C_OK) {
return C_ERR;
}
+ /* Must be initialized after engines initialization */
+ curr_functions_lib_ctx = functionsLibCtxCreate();
+
return C_OK;
}
diff --git a/tests/unit/functions.tcl b/tests/unit/functions.tcl
index 118362d25..187f7cfc4 100644
--- a/tests/unit/functions.tcl
+++ b/tests/unit/functions.tcl
@@ -245,7 +245,7 @@ start_server {tags {"scripting"}} {
after 200
catch {r ping} e
assert_match {BUSY*} $e
- assert_match {running_script {name test command {fcall test 0} duration_ms *} engines LUA} [r FUNCTION STATS]
+ assert_match {running_script {name test command {fcall test 0} duration_ms *} engines {*}} [r FUNCTION STATS]
r function kill
after 200 ; # Give some time to Lua to call the hook again...
assert_equal [r ping] "PONG"
@@ -1102,4 +1102,34 @@ start_server {tags {"scripting"}} {
catch {[r fcall f1 0]} e
assert_equal [r fcall get_version_v1 0] [r fcall get_version_v2 0]
}
+
+ test {FUNCTION - function stats} {
+ r FUNCTION FLUSH
+
+ r FUNCTION load lua test1 {
+ redis.register_function('f1', function() return 1 end)
+ redis.register_function('f2', function() return 1 end)
+ }
+
+ r FUNCTION load lua test2 {
+ redis.register_function('f3', function() return 1 end)
+ }
+
+ r function stats
+ } {running_script {} engines {LUA {libraries_count 2 functions_count 3}}}
+
+ test {FUNCTION - function stats reloaded correctly from rdb} {
+ r debug reload
+ r function stats
+ } {running_script {} engines {LUA {libraries_count 2 functions_count 3}}} {needs:debug}
+
+ test {FUNCTION - function stats delete library} {
+ r function delete test1
+ r function stats
+ } {running_script {} engines {LUA {libraries_count 1 functions_count 1}}}
+
+ test {FUNCTION - function stats cleaned after flush} {
+ r function flush
+ r function stats
+ } {running_script {} engines {LUA {libraries_count 0 functions_count 0}}}
}