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:
Binbin 2024-02-28 23:57:29 +08:00 committed by GitHub
parent 763827c981
commit a7abc2f067
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 45 additions and 11 deletions

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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

View File

@ -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