ad28d222ed
In some cases, users will abuse lua eval. Each EVAL call generates a new lua script, which is added to the lua interpreter and cached to redis-server, consuming a large amount of memory over time. Since EVAL is mostly the one that abuses the lua cache, and these won't have pipeline issues (i.e. the script won't disappear unexpectedly, and cause errors like it would with SCRIPT LOAD and EVALSHA), we implement a plain FIFO LRU eviction only for these (not for scripts loaded with SCRIPT LOAD). ### Implementation notes: When not abused we'll probably have less than 100 scripts, and when abused we'll have many thousands. So we use a hard coded value of 500 scripts. And considering that we don't have many scripts, then unlike keys, we don't need to worry about the memory usage of keeping a true sorted LRU linked list. We compute the SHA of each script anyway, and put the script in a dict, we can store a listNode there, and use it for quick removal and re-insertion into an LRU list each time the script is used. ### New interfaces: At the same time, a new `evicted_scripts` field is added to INFO, which represents the number of evicted eval scripts. Users can check it to see if they are abusing EVAL. ### benchmark: `./src/redis-benchmark -P 10 -n 1000000 -r 10000000000 eval "return __rand_int__" 0` The simple abuse of eval benchmark test that will create 1 million EVAL scripts. The performance has been improved by 50%, and the max latency has dropped from 500ms to 13ms (this may be caused by table expansion inside Lua when the number of scripts is large). And in the INFO memory, it used to consume 120MB (server cache) + 310MB (lua engine), but now it only consumes 70KB (server cache) + 210KB (lua_engine) because of the scripts eviction. For non-abusive case of about 100 EVAL scripts, there's no noticeable change in performance or memory usage. ### unlikely potentially breaking change: in theory, a user can maybe load a script with EVAL and then use EVALSHA to call it (by calculating the SHA1 value on the client side), it could be that if we read the docs carefully we'll realized it's a valid scenario, but we suppose it's extremely rare. So it may happen that EVALSHA acts on a script created by EVAL, and the script is evicted and EVALSHA returns a NOSCRIPT error. that is if you have more than 500 scripts being used in the same transaction / pipeline. This solves the second point in #13102. |
||
---|---|---|
.. | ||
cluster | ||
moduleapi | ||
type | ||
acl-v2.tcl | ||
acl.tcl | ||
aofrw.tcl | ||
auth.tcl | ||
bitfield.tcl | ||
bitops.tcl | ||
client-eviction.tcl | ||
dump.tcl | ||
expire.tcl | ||
functions.tcl | ||
geo.tcl | ||
hyperloglog.tcl | ||
info-command.tcl | ||
info.tcl | ||
introspection-2.tcl | ||
introspection.tcl | ||
keyspace.tcl | ||
latency-monitor.tcl | ||
lazyfree.tcl | ||
limits.tcl | ||
maxmemory.tcl | ||
memefficiency.tcl | ||
multi.tcl | ||
networking.tcl | ||
obuf-limits.tcl | ||
oom-score-adj.tcl | ||
other.tcl | ||
pause.tcl | ||
printver.tcl | ||
protocol.tcl | ||
pubsub.tcl | ||
pubsubshard.tcl | ||
querybuf.tcl | ||
quit.tcl | ||
replybufsize.tcl | ||
scan.tcl | ||
scripting.tcl | ||
shutdown.tcl | ||
slowlog.tcl | ||
sort.tcl | ||
tls.tcl | ||
tracking.tcl | ||
violations.tcl | ||
wait.tcl |