Unverified Commit 5a38ccc2 authored by Meir Shpilraien (Spielrein)'s avatar Meir Shpilraien (Spielrein) Committed by GitHub
Browse files

Added engine stats to FUNCTION STATS command. (#10179)

Added the following statistics (per engine) to FUNCTION STATS command:
* number of functions
* number of libraries

Output example:
```
> FUNCTION stats
1) "running_script"
2) (nil)
3) "engines"
4) 1) "LUA"
   2) 1) "libraries_count"
      2) (integer) 1
      3) "functions_count"
      4) (integer) 1
```

To collect the stats, added a new dictionary to libraries_ctx that contains
for each engine, the engine statistics representing the current libraries_ctx.
Update the stats on:
1. Link library to libraries_ctx
2. Unlink library from libraries_ctx
3. Flushing libraries_ctx
parent d26453a3
...@@ -41,13 +41,20 @@ static size_t engine_cache_memory = 0; ...@@ -41,13 +41,20 @@ static size_t engine_cache_memory = 0;
/* Forward declaration */ /* Forward declaration */
static void engineFunctionDispose(dict *d, void *obj); static void engineFunctionDispose(dict *d, void *obj);
static void engineStatsDispose(dict *d, void *obj);
static void engineLibraryDispose(dict *d, void *obj); static void engineLibraryDispose(dict *d, void *obj);
static int functionsVerifyName(sds name); static int functionsVerifyName(sds name);
typedef struct functionsLibEngineStats {
size_t n_lib;
size_t n_functions;
} functionsLibEngineStats;
struct functionsLibCtx { struct functionsLibCtx {
dict *libraries; /* Function name -> Function object that can be used to run the function */ dict *libraries; /* Library name -> Library object */
dict *functions; /* 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 */; size_t cache_memory; /* Overhead memory (structs, dictionaries, ..) used by all the functions */
dict *engines_stats; /* Per engine statistics */
}; };
dictType engineDictType = { dictType engineDictType = {
...@@ -70,6 +77,16 @@ dictType functionDictType = { ...@@ -70,6 +77,16 @@ dictType functionDictType = {
NULL /* allow to expand */ 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 = { dictType libraryFunctionDictType = {
dictSdsHash, /* hash function */ dictSdsHash, /* hash function */
dictSdsDup, /* key dup */ dictSdsDup, /* key dup */
...@@ -111,6 +128,12 @@ static size_t libraryMallocSize(functionLibInfo *li) { ...@@ -111,6 +128,12 @@ static size_t libraryMallocSize(functionLibInfo *li) {
+ sdsZmallocSize(li->code); + sdsZmallocSize(li->code);
} }
static void engineStatsDispose(dict *d, void *obj) {
UNUSED(d);
functionsLibEngineStats *stats = obj;
zfree(stats);
}
/* Dispose function memory */ /* Dispose function memory */
static void engineFunctionDispose(dict *d, void *obj) { static void engineFunctionDispose(dict *d, void *obj) {
UNUSED(d); UNUSED(d);
...@@ -147,6 +170,14 @@ static void engineLibraryDispose(dict *d, void *obj) { ...@@ -147,6 +170,14 @@ static void engineLibraryDispose(dict *d, void *obj) {
void functionsLibCtxClear(functionsLibCtx *lib_ctx) { void functionsLibCtxClear(functionsLibCtx *lib_ctx) {
dictEmpty(lib_ctx->functions, NULL); dictEmpty(lib_ctx->functions, NULL);
dictEmpty(lib_ctx->libraries, 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; curr_functions_lib_ctx->cache_memory = 0;
} }
...@@ -165,6 +196,7 @@ void functionsLibCtxFree(functionsLibCtx *functions_lib_ctx) { ...@@ -165,6 +196,7 @@ void functionsLibCtxFree(functionsLibCtx *functions_lib_ctx) {
functionsLibCtxClear(functions_lib_ctx); functionsLibCtxClear(functions_lib_ctx);
dictRelease(functions_lib_ctx->functions); dictRelease(functions_lib_ctx->functions);
dictRelease(functions_lib_ctx->libraries); dictRelease(functions_lib_ctx->libraries);
dictRelease(functions_lib_ctx->engines_stats);
zfree(functions_lib_ctx); zfree(functions_lib_ctx);
} }
...@@ -185,6 +217,15 @@ functionsLibCtx* functionsLibCtxCreate() { ...@@ -185,6 +217,15 @@ functionsLibCtx* functionsLibCtxCreate() {
functionsLibCtx *ret = zmalloc(sizeof(functionsLibCtx)); functionsLibCtx *ret = zmalloc(sizeof(functionsLibCtx));
ret->libraries = dictCreate(&librariesDictType); ret->libraries = dictCreate(&librariesDictType);
ret->functions = dictCreate(&functionDictType); 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; ret->cache_memory = 0;
return ret; return ret;
} }
...@@ -250,6 +291,12 @@ static void libraryUnlink(functionsLibCtx *lib_ctx, functionLibInfo* li) { ...@@ -250,6 +291,12 @@ static void libraryUnlink(functionsLibCtx *lib_ctx, functionLibInfo* li) {
dictSetVal(lib_ctx->libraries, entry, NULL); dictSetVal(lib_ctx->libraries, entry, NULL);
dictFreeUnlinkedEntry(lib_ctx->libraries, entry); dictFreeUnlinkedEntry(lib_ctx->libraries, entry);
lib_ctx->cache_memory += libraryMallocSize(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);
} }
static void libraryLink(functionsLibCtx *lib_ctx, functionLibInfo* li) { static void libraryLink(functionsLibCtx *lib_ctx, functionLibInfo* li) {
...@@ -264,6 +311,12 @@ 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); dictAdd(lib_ctx->libraries, li->name, li);
lib_ctx->cache_memory += libraryMallocSize(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. /* Takes all libraries from lib_ctx_src and add to lib_ctx_dst.
...@@ -401,12 +454,18 @@ void functionStatsCommand(client *c) { ...@@ -401,12 +454,18 @@ void functionStatsCommand(client *c) {
} }
addReplyBulkCString(c, "engines"); addReplyBulkCString(c, "engines");
addReplyArrayLen(c, dictSize(engines)); addReplyMapLen(c, dictSize(engines));
dictIterator *iter = dictGetIterator(engines); dictIterator *iter = dictGetIterator(engines);
dictEntry *entry = NULL; dictEntry *entry = NULL;
while ((entry = dictNext(iter))) { while ((entry = dictNext(iter))) {
engineInfo *ei = dictGetVal(entry); engineInfo *ei = dictGetVal(entry);
addReplyBulkCString(c, ei->name); 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); dictReleaseIterator(iter);
} }
...@@ -979,11 +1038,13 @@ size_t functionsLibCtxfunctionsLen(functionsLibCtx *functions_ctx) { ...@@ -979,11 +1038,13 @@ size_t functionsLibCtxfunctionsLen(functionsLibCtx *functions_ctx) {
* Should be called once on server initialization */ * Should be called once on server initialization */
int functionsInit() { int functionsInit() {
engines = dictCreate(&engineDictType); engines = dictCreate(&engineDictType);
curr_functions_lib_ctx = functionsLibCtxCreate();
if (luaEngineInitEngine() != C_OK) { if (luaEngineInitEngine() != C_OK) {
return C_ERR; return C_ERR;
} }
/* Must be initialized after engines initialization */
curr_functions_lib_ctx = functionsLibCtxCreate();
return C_OK; return C_OK;
} }
...@@ -245,7 +245,7 @@ start_server {tags {"scripting"}} { ...@@ -245,7 +245,7 @@ start_server {tags {"scripting"}} {
after 200 after 200
catch {r ping} e catch {r ping} e
assert_match {BUSY*} $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 r function kill
after 200 ; # Give some time to Lua to call the hook again... after 200 ; # Give some time to Lua to call the hook again...
assert_equal [r ping] "PONG" assert_equal [r ping] "PONG"
...@@ -1102,4 +1102,34 @@ start_server {tags {"scripting"}} { ...@@ -1102,4 +1102,34 @@ start_server {tags {"scripting"}} {
catch {[r fcall f1 0]} e catch {[r fcall f1 0]} e
assert_equal [r fcall get_version_v1 0] [r fcall get_version_v2 0] 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}}}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment