RedisのLuaで時間のかかる処理をするとどうなるか?

Redis は基本的にシングルスレッド、イベントドリブンモデルなので、時間のかかるLua スクリプトを実行したらどうなるかというのは想像に難くないのですが確認してみました。

Redis は 2.6.14 です。

redis-cli eval 'while true do print("hello") end' 0

な感じのを実行します。

実行中に、別クライアントから GET, INFO, PING すると全てブロックされて結果が返ってきません。


redis.conf で lua-time-limit 5000 と設定している場合は、5秒後に redis-server のログに

[18068] 22 Jul 13:51:23.798 # Lua slow script detected: still in execution after 5000 milliseconds. You can try killing the script using the SCRIPT KILL command

と記録されると共に、GET等リクエストしていた別クライアントには

(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

という応答が返ってきます。

以降、GET, INFO, PING リクエストに対しては、即座にこのエラーが返ってくるようになります。


次に redis-cli コマンドで実行してた無限ループする Lua スクリプトを ^C で中断します。

中断後、GET, INFO, PING リクエストを送っても、依然としてこのエラーが (即座に) 返ってきて、サーバーサイドではまだ無限ループのスクリプトが実行中のままです。つまり、Luaスクリプトを実行したクライアントを中断しても、サーバーサイドで実行中のLuaスクリプトは停止しません


Luaスクリプトの実行を中断するには、メッセージの通りに SCRIPT KILL コマンドを実行します。

$ redis-cli SCRIPT KILL
OK

redis-serverのログには

[20536] 22 Jul 14:10:49.097 # Lua script killed by user with SCRIPT KILL.

と記録され、これで GET, INFO, PING 等のリクエストにも正常な応答が返されるようになります。


注意しなければならないのは、PING にも応答しなくなるという点です。

Redis SentinelはPINGを送ってredis-serverの死活監視を行なっているので、監視が失敗してフェイルオーバーが発動します。この際、SentinelはSCRIPT KILLをマスタに対して送るので、Sentinelが実際にフェイルオーバー処理を行い始めるまでの猶予時間内 (5〜15秒のランダム) にマスタが復帰すればフェイルオーバーはキャンセルされます。とはいえ、フェイルオーバーがキャンセルされても遂行されても、再度問題のあるLuaスクリプトが実行されるとまたフェイルオーバーが発動してしまいます。Sentinelがダウンと認識する down-after-milliseconds 以上の時間がかかるLuaスクリプトの実行には注意しましょう。