diff --git a/src/backends/backends.c b/src/backends/backends.c index 59ec5a277..e05d37002 100644 --- a/src/backends/backends.c +++ b/src/backends/backends.c @@ -92,33 +92,17 @@ int RAI_ExportFunc(const char *func_name, void **targetFuncPtr) { return REDISMODULE_OK; } -RedisModuleString *RAI_GetModulePath(RedisModuleCtx *ctx) { +void RAI_SetBackendsDefaultPath(char **backends_path) { + RedisModule_Assert(*backends_path == NULL); Dl_info info; - RedisModuleString *module_path = NULL; - if (dladdr(RAI_GetModulePath, &info)) { - char *dli_fname = RedisModule_Strdup(info.dli_fname); - const char *dli_dirname = dirname(dli_fname); - module_path = RedisModule_CreateString(ctx, dli_dirname, strlen(dli_dirname)); - RedisModule_Free(dli_fname); - } - - return module_path; -} - -RedisModuleString *RAI_GetBackendsPath(RedisModuleCtx *ctx) { - Dl_info info; - RedisModuleString *backends_path = NULL; - if (Config_GetBackendsPath() != NULL) { - backends_path = RedisModule_CreateString(ctx, Config_GetBackendsPath(), - strlen(Config_GetBackendsPath())); - } else { - RedisModuleString *module_path = RAI_GetModulePath(ctx); - backends_path = RedisModule_CreateStringPrintf(ctx, "%s/backends", - RedisModule_StringPtrLen(module_path, NULL)); - RedisModule_FreeString(ctx, module_path); - } - - return backends_path; + // Retrieve the info about the module's dynamic library, and extract the .so file dir name. + RedisModule_Assert(dladdr(RAI_SetBackendsDefaultPath, &info) != 0); + const char *dyn_lib_dir_name = dirname((char *)info.dli_fname); + + // Populate backends_path global string with the default path. + size_t backends_default_path_len = strlen(dyn_lib_dir_name) + strlen("/backends"); + *backends_path = RedisModule_Alloc(backends_default_path_len + 1); + RedisModule_Assert(sprintf(*backends_path, "%s/backends", dyn_lib_dir_name) > 0); } const char *RAI_GetBackendName(RAI_Backend backend) { @@ -460,10 +444,8 @@ int RAI_LoadBackend(RedisModuleCtx *ctx, int backend, const char *path) { if (path[0] == '/') { fullpath = RedisModule_CreateString(ctx, path, strlen(path)); } else { - RedisModuleString *backends_path = RAI_GetBackendsPath(ctx); - fullpath = RedisModule_CreateStringPrintf( - ctx, "%s/%s", RedisModule_StringPtrLen(backends_path, NULL), path); - RedisModule_FreeString(ctx, backends_path); + const char *backends_path = Config_GetBackendsPath(); + fullpath = RedisModule_CreateStringPrintf(ctx, "%s/%s", backends_path, path); } int ret; diff --git a/src/backends/backends.h b/src/backends/backends.h index 6cb8b2ef3..b883c5bdb 100644 --- a/src/backends/backends.h +++ b/src/backends/backends.h @@ -110,3 +110,8 @@ int RAI_LoadDefaultBackend(RedisModuleCtx *ctx, int backend); * @brief Returns the backend name as string. */ const char *RAI_GetBackendName(RAI_Backend backend); + +/** + * @brief Set the default backends path (/backends) in backends_path place holder. + */ +void RAI_SetBackendsDefaultPath(char **backends_path); diff --git a/src/config/config.c b/src/config/config.c index 0a4e6a4e3..0a97ec1e7 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -3,21 +3,21 @@ #include "redismodule.h" #include "backends/backends.h" -// Default configs -char *BackendsPath = NULL; // Path to backends dir. - -long long BackendsIntraOpParallelism = 0; // number of threads used within an - // individual op for parallelism. -long long BackendsInterOpParallelism = 0; // number of threads used for parallelism - // between independent operations. -long long ModelChunkSize = 535822336; // size of chunks used to break up model payloads. - // default is 511 * 1024 * 1024 -long long ThreadPoolSizePerQueue = 1; // Number of working threads for device. - -long long ModelExecutionTimeout = 5000; // The maximum time in milliseconds - // before killing onnx run session. -long long BackendMemoryLimit = 0; // The maximum amount of memory in MB - // that backend is allowed to consume. +/* Default configs: */ +// Path to backends dir. Default value is set when parsing load_time configs. +char *BackendsPath; +// Number of threads used within an individual op for parallelism. +long long BackendsIntraOpParallelism = 0; +// Number of threads used for parallelism between independent operations. +long long BackendsInterOpParallelism = 0; +// Size of chunks used to break up model payloads. Default is 511 * 1024 * 1024 +long long ModelChunkSize = REDISAI_DEFAULT_MODEL_CHUNK_SIZE; +// Number of working threads for device. +long long ThreadPoolSizePerQueue = 1; +// The maximum time in milliseconds before killing onnx run session. +long long ModelExecutionTimeout = 5000; +// The maximum amount of memory in MB that backend is allowed to consume. +long long BackendMemoryLimit = 0; static int _Config_LoadTimeParamParse(RedisModuleCtx *ctx, const char *key, const char *val, RedisModuleString *rsval) { @@ -111,9 +111,8 @@ int Config_LoadBackend(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) } void Config_SetBackendsPath(const char *path) { - if (BackendsPath != NULL) { - RedisModule_Free(BackendsPath); - } + RedisModule_Assert(BackendsPath != NULL && path != NULL); + RedisModule_Free(BackendsPath); BackendsPath = RedisModule_Strdup(path); } @@ -188,6 +187,7 @@ int Config_SetLoadTimeParams(RedisModuleCtx *ctx, RedisModuleString *const *argv } // need BACKENDSPATH set up before loading specific backends + RAI_SetBackendsDefaultPath(&BackendsPath); for (int i = 0; i < argc / 2; i++) { const char *key = RedisModule_StringPtrLen(argv[2 * i], NULL); const char *val = RedisModule_StringPtrLen(argv[2 * i + 1], NULL); diff --git a/src/config/config.h b/src/config/config.h index d6798f2f3..7b7e00ecc 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -28,6 +28,8 @@ typedef enum { RAI_DEVICE_CPU = 0, RAI_DEVICE_GPU = 1 } RAI_Device; #define REDISAI_INFOMSG_MODEL_EXECUTION_TIMEOUT "Setting MODEL_EXECUTION_TIMEOUT parameter to" #define REDISAI_INFOMSG_BACKEND_MEMORY_LIMIT "Setting BACKEND_MEMORY_LIMIT parameter to" +#define REDISAI_DEFAULT_MODEL_CHUNK_SIZE (511 * 1024 * 1024) + /** * Get number of threads used for parallelism between independent operations, by * backend. @@ -81,9 +83,8 @@ char *Config_GetBackendsPath(void); int Config_LoadBackend(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); /** - * Helper method for AI.CONFIG BACKENDSPATH - * - * @param path string containing backend path + * Helper method for AI.CONFIG BACKENDSPATH + * @param path string containing backend path. */ void Config_SetBackendsPath(const char *path); diff --git a/src/redisai.c b/src/redisai.c index 419619691..dd4d182d0 100644 --- a/src/redisai.c +++ b/src/redisai.c @@ -1279,6 +1279,11 @@ void RAI_moduleInfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) { AI_dictReleaseIterator(iter); } +void RAI_CleanupModule(RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data) { + RedisModule_Log(ctx, "notice", "%s", "Clearing resources on shutdown"); + RedisModule_Free(Config_GetBackendsPath()); +} + int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { #ifndef REDISAI_LITE @@ -1424,6 +1429,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS); + RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_Shutdown, RAI_CleanupModule); + if (Config_SetLoadTimeParams(ctx, argv, argc) != REDISMODULE_OK) { return REDISMODULE_ERR; } diff --git a/tests/flow/includes.py b/tests/flow/includes.py index 1c61c175b..185d11c84 100755 --- a/tests/flow/includes.py +++ b/tests/flow/includes.py @@ -17,6 +17,7 @@ import paella ROOT = os.environ.get("ROOT", None) +MODULE = os.environ.get("MODULE", None) TESTMOD_PATH = os.environ.get("TESTMOD", None) MAX_ITERATIONS = 2 if os.environ.get("MAX_ITERATIONS") == None else os.environ.get("MAX_ITERATIONS") TEST_TF = os.environ.get("TEST_TF") != "0" and os.environ.get("WITH_TF") != "0" diff --git a/tests/flow/tests_commands.py b/tests/flow/tests_commands.py index 2c98ef471..c8b5e7c72 100644 --- a/tests/flow/tests_commands.py +++ b/tests/flow/tests_commands.py @@ -552,7 +552,7 @@ def test_ai_config(env): for con in conns: # Get the default configs. res = con.execute_command('AI.CONFIG', 'GET', 'BACKENDSPATH') - env.assertEqual(res, None) + env.assertEqual(res.decode(), f'{"/".join(MODULE.split("/")[:-1])}/backends') # /backends res = con.execute_command('AI.CONFIG', 'GET', 'MODEL_CHUNK_SIZE') env.assertEqual(res, 511*1024*1024) @@ -609,6 +609,7 @@ def test_ai_config_errors(env): check_error_message(env, con, 'BACKENDSPATH: missing path argument', 'AI.CONFIG', 'BACKENDSPATH') check_error_message(env, con, 'MODEL_CHUNK_SIZE: missing chunk size', 'AI.CONFIG', 'MODEL_CHUNK_SIZE') + check_error_message(env, con, 'MODEL_CHUNK_SIZE: invalid chunk size', 'AI.CONFIG', 'MODEL_CHUNK_SIZE', 'not_number') check_error_message(env, con, "wrong number of arguments for 'AI.CONFIG' command", 'AI.CONFIG', 'GET') env.assertEqual(con.execute_command('AI.CONFIG', 'GET', 'bad_config'), None) diff --git a/tests/flow/tests_onnx.py b/tests/flow/tests_onnx.py index fd16bce48..5755d3901 100644 --- a/tests/flow/tests_onnx.py +++ b/tests/flow/tests_onnx.py @@ -567,7 +567,7 @@ def test_synchronization(self): def launch_redis_and_run_onnx(con, proc_id, pipes): my_pipe = pipes[proc_id] - port = 6380 + proc_id # Let every subprocess run on a fresh port. + port = 6380 + 30*proc_id # Let every subprocess run on a fresh port (safe distance for RLTEST parallelism). redis_server = subprocess.Popen(['redis-server', '--port', str(port), '--loadmodule', f'{ROOT}/install-{DEVICE.lower()}/redisai.so', '--logfile', f'{self.env.logDir}/test_onnx_kill_switch_synchronization-{port}.log',