SCRIPT FLUSH run truly async, close lua interpreter in bio (#13087)
Even if we have SCRIPT FLUSH ASYNC now, when there are a lot of lua scripts, SCRIPT FLUSH ASYNC will still block the main thread. This is because lua_close is executed in the main thread, and lua heap needs to release a lot of memory. In this PR, we take the current lua instance on lctx.lua and call lua_close on it in a background thread, to close it in async way. This is MeirShpilraien's idea.
This commit is contained in:
parent
763827c981
commit
a7abc2f067
24
src/eval.c
24
src/eval.c
@ -264,15 +264,31 @@ void scriptingInit(int setup) {
|
||||
lctx.lua = lua;
|
||||
}
|
||||
|
||||
/* Free lua_scripts dict and close lua interpreter. */
|
||||
void freeLuaScriptsSync(dict *lua_scripts, lua_State *lua) {
|
||||
dictRelease(lua_scripts);
|
||||
lua_close(lua);
|
||||
|
||||
#if !defined(USE_LIBC)
|
||||
/* The lua interpreter may hold a lot of memory internally, and lua is
|
||||
* using libc. libc may take a bit longer to return the memory to the OS,
|
||||
* so after lua_close, we call malloc_trim try to purge it earlier.
|
||||
*
|
||||
* We do that only when Redis itself does not use libc. When Lua and Redis
|
||||
* use different allocators, one won't use the fragmentation holes of the
|
||||
* other, and released memory can take a long time until it is returned to
|
||||
* the OS. */
|
||||
zlibc_trim();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Release resources related to Lua scripting.
|
||||
* This function is used in order to reset the scripting environment. */
|
||||
void scriptingRelease(int async) {
|
||||
if (async)
|
||||
freeLuaScriptsAsync(lctx.lua_scripts);
|
||||
freeLuaScriptsAsync(lctx.lua_scripts, lctx.lua);
|
||||
else
|
||||
dictRelease(lctx.lua_scripts);
|
||||
lctx.lua_scripts_mem = 0;
|
||||
lua_close(lctx.lua);
|
||||
freeLuaScriptsSync(lctx.lua_scripts, lctx.lua);
|
||||
}
|
||||
|
||||
void scriptingReset(int async) {
|
||||
|
@ -42,8 +42,9 @@ void lazyFreeTrackingTable(void *args[]) {
|
||||
/* Release the lua_scripts dict. */
|
||||
void lazyFreeLuaScripts(void *args[]) {
|
||||
dict *lua_scripts = args[0];
|
||||
lua_State *lua = args[1];
|
||||
long long len = dictSize(lua_scripts);
|
||||
dictRelease(lua_scripts);
|
||||
freeLuaScriptsSync(lua_scripts, lua);
|
||||
atomicDecr(lazyfree_objects,len);
|
||||
atomicIncr(lazyfreed_objects,len);
|
||||
}
|
||||
@ -195,13 +196,14 @@ void freeTrackingRadixTreeAsync(rax *tracking) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Free lua_scripts dict, if the dict is huge enough, free it in async way. */
|
||||
void freeLuaScriptsAsync(dict *lua_scripts) {
|
||||
/* Free lua_scripts dict, if the dict is huge enough, free it in async way.
|
||||
* Close lua interpreter, if there are a lot of lua scripts, close it in async way. */
|
||||
void freeLuaScriptsAsync(dict *lua_scripts, lua_State *lua) {
|
||||
if (dictSize(lua_scripts) > LAZYFREE_THRESHOLD) {
|
||||
atomicIncr(lazyfree_objects,dictSize(lua_scripts));
|
||||
bioCreateLazyFreeJob(lazyFreeLuaScripts,1,lua_scripts);
|
||||
bioCreateLazyFreeJob(lazyFreeLuaScripts,2,lua_scripts,lua);
|
||||
} else {
|
||||
dictRelease(lua_scripts);
|
||||
freeLuaScriptsSync(lua_scripts, lua);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3381,7 +3381,8 @@ void ldbKillForkedSessions(void);
|
||||
int ldbPendingChildren(void);
|
||||
sds luaCreateFunction(client *c, robj *body);
|
||||
void luaLdbLineHook(lua_State *lua, lua_Debug *ar);
|
||||
void freeLuaScriptsAsync(dict *lua_scripts);
|
||||
void freeLuaScriptsSync(dict *lua_scripts, lua_State *lua);
|
||||
void freeLuaScriptsAsync(dict *lua_scripts, lua_State *lua);
|
||||
void freeFunctionsAsync(functionsLibCtx *lib_ctx);
|
||||
int ldbIsEnabled(void);
|
||||
void ldbLog(sds entry);
|
||||
|
@ -50,7 +50,6 @@ void zlibc_free(void *ptr) {
|
||||
}
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "zmalloc.h"
|
||||
#include "atomicvar.h"
|
||||
|
||||
@ -757,6 +756,15 @@ int jemalloc_purge(void) {
|
||||
|
||||
#endif
|
||||
|
||||
/* This function provides us access to the libc malloc_trim(). */
|
||||
void zlibc_trim(void) {
|
||||
#if defined(__GLIBC__) && !defined(USE_LIBC)
|
||||
malloc_trim(0);
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* For proc_pidinfo() used later in zmalloc_get_smap_bytes_by_field().
|
||||
* Note that this file cannot be included in zmalloc.h because it includes
|
||||
|
@ -71,6 +71,7 @@
|
||||
*/
|
||||
#ifndef ZMALLOC_LIB
|
||||
#define ZMALLOC_LIB "libc"
|
||||
#define USE_LIBC 1
|
||||
|
||||
#if !defined(NO_MALLOC_USABLE_SIZE) && \
|
||||
(defined(__GLIBC__) || defined(__FreeBSD__) || \
|
||||
@ -93,6 +94,11 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Includes for malloc_trim(), see zlibc_trim(). */
|
||||
#if defined(__GLIBC__) && !defined(USE_LIBC)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
/* We can enable the Redis defrag capabilities only if we are using Jemalloc
|
||||
* and the version used is our special version modified for Redis having
|
||||
* the ability to return per-allocation fragmentation hints. */
|
||||
@ -130,6 +136,7 @@ size_t zmalloc_get_private_dirty(long pid);
|
||||
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);
|
||||
size_t zmalloc_get_memory_size(void);
|
||||
void zlibc_free(void *ptr);
|
||||
void zlibc_trim(void);
|
||||
void zmadvise_dontneed(void *ptr);
|
||||
|
||||
#ifdef HAVE_DEFRAG
|
||||
|
Loading…
Reference in New Issue
Block a user