An overview of the

From Redis 3.2, built-in Lua Debugger (LDB for short), using Lua Debugger can be very convenient for us to write Lua scripts for debugging

Quick start

You can create a new Debug session using the following steps:

  • Create one locallyLuaThe script
  • useredis-cliThrough the--ldbParameter entry intodebugPattern, using--evalParameter specifies the needdebugtheLuaThe script

Lua/TMP /script.lua/TMP /script.lua

local foo = redis.call('ping')
return foo
Copy the code

/ TMP /script.lua: / TMP /script.lua: / TMP /script.lua: / TMP /script.lua: / TMP /script.lua

$ redis-cli --ldb --eval /tmp/script.lua
Lua debugging session started, please use:
quit    -- End the session.
restart -- Restart the script in debug mode again.
help    -- Show Lua script debugging commands.

* Stopped at 1, stop reason = step over
-> 1   local foo = redis.call('ping')
lua debugger>
Copy the code

After executing redis-cli — LDB –eval/TMP /script.lua, we can see from the output that we have started a debugging session, and the code stops at the first line, we can use step command to execute the script to the next line

lua debugger> step
<redis> ping
<reply> "+PONG"
* Stopped at 2, stop reason = step over
-> 2   return foo
Copy the code

In the output,

ping indicates that the ping command is executed, and

“+PONG” indicates the return information of redis-server. After the setp command is executed, the code does not continue to be executed, but stops at the second line. The step command is executed again, and the return foo code is executed. At this point, the script code has been executed, the debugging session ends, and the output of the script returns the result

lua debugger> step

PONG

(Lua debugging session ended -- dataset changes rolled back)
Copy the code

The complete process is as follows:

LDB working mode

By default, when a client sends debugging sessions to the server, the server is not blocked. That is, the server can normally process requests and process multiple debugging sessions at the same time because the sessions are parallel and do not interfere with each other

Another point is that when the debugging session ends, all changes made to redis data in Lua scripts are rolled back. The advantage of this is that when multiple sessions are simultaneously debugging, they do not interfere with each other. In addition, you can restart the debugging session without cleaning/restoring data. The result is the same each time

To initialize the data, run the following command:

$ rpush list_a a b c d
(integer) 4
Copy the code

Then write the script.lua script:

ocal src = KEYS[1]
local dst = KEYS[2]
local count = redis.call('llen',src)

while count > 0 do
    local item = redis.call('rpop',src)
    redis.call('lpush',dst,item)
    count = count - 1
end

return redis.call('llen',dst)
Copy the code

This script moves all elements from the SRC list to DST and then debugs the script

$ redis-cli --ldb --eval /tmp/script.lua list_a list_b
Lua debugging session started, please use:
quit    -- End the session.
restart -- Restart the script in debug mode again.
help    -- Show Lua script debugging commands.

* Stopped at 1, stop reason = step over
-> 1   local src = KEYS[1]
lua debugger> continue

(integer) 4

(Lua debugging session ended -- dataset changes rolled back)
Copy the code

Here we use the continue command (which we’ll describe later) to let the script run straight through, and then look at the list_A and list_b values in Redis

$ lrange list_a 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
$ lrange list_b 0 -1
(empty array)
Copy the code

It can be seen that the data in Redis is not changed, because in the default working mode, when the debugging session is closed, all the modifications in the session are rolled back

You can also use the –ldb-sync-mode parameter to specify the synchronization mode of LDB. In this mode, debugging Lua scripts blocks the server, and the server does not process other requests during debugging, and data is not rolled back

Execute the previous script in synchronous mode

$ redis-cli --ldb-sync-mode --eval /tmp/script.lua list_a list_b
Lua debugging session started, please use:
quit    -- End the session.
restart -- Restart the script in debug mode again.
help    -- Show Lua script debugging commands.

* Stopped at 1, stop reason = step over
-> 1   local src = KEYS[1]
Copy the code

At this point, any command executed on the other client will block until the script is finished executing, so we use the continue command to let the script finish executing. Then check list_A and list_B

$ lrange list_a 0 -1
(empty array)
$ lrange list_b 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
Copy the code

As you can see, the element in list_A has been moved to list_B, indicating that script operations are not rolled back in synchronous mode

The breakpoint

Add a breakpoint

Adding and removing endpoints in a Lua script is very simple. You can add endpoints in the LDB with break . For example, if we want to add a breakpoint at line 5 of the script, we just need to execute break 5 on the LDB

$ redis-cli --ldb --eval /tmp/script.lua list_a list_b
Lua debugging session started, please use:
quit    -- End the session.
restart -- Restart the script in debug mode again.
help    -- Show Lua script debugging commands.

* Stopped at 1, stop reason = step over
-> 1   local src = KEYS[1]
lua debugger> break 5
   4
  #5   while count > 0 do
   6       local item = redis.call('rpop',src)
Copy the code

Once you have added a breakpoint, you can use the continue command to execute the script to the breakpoint

lua debugger> continue
* Stopped at 5, stop reason = break point
->#5   while count > 0 do
Copy the code

Break can be followed by multiple line numbers. Multiple breakpoints can be added at once, separated by Spaces between line numbers

Check the breakpoint

You can use the break command to view all breakpoints set in the script

lua debugger> break
1 breakpoints set:
->#5   while count > 0 do
Copy the code

From the information returned by the LDB, you can see that the script set a breakpoint at line number 5

Delete the endpoint

Breakpoints can be deleted using break – , where is the line number of the breakpoint to be deleted. For example, break -5 can be used to delete the breakpoint on line 5

lua debugger> b -5
Breakpoint removed.
Copy the code

Similarly, multiple breakpoints can be deleted in batches using break-1-2

If you want to remove all breakpoints in a script at once, use break 0

lua debugger> b 0
All breakpoints removed.
Copy the code

Dynamically adding breakpoints

In addition to setting breakpoints using the break command, you can also set breakpoints by calling the redis.breakpoint() function in the script. When the script executes the redis.breakpoint() method, a breakpoint is simulated on the next line of redis.breakpoint(). For example, if you want to set a breakpoint in the while loop of/TMP /script.lua and only debug when count==3, you can change the script to

local src = KEYS[1]
local dst = KEYS[2]
local count = redis.call('llen',src)

while count > 0 do
    if (count == 3) then redis.breakpoint() end
    local item = redis.call('rpop',src)
    redis.call('lpush',dst,item)
    count = count - 1
end

return redis.call('llen',dst)
Copy the code

Debug the script using LDB

$ redis-cli --ldb --eval /tmp/script.lua list_a list_b
Lua debugging session started, please use:
quit    -- End the session.
restart -- Restart the script in debug mode again.
help    -- Show Lua script debugging commands.

* Stopped at 1, stop reason = step over
-> 1   local src = KEYS[1]
lua debugger> continue
* Stopped at 7, stop reason = redis.breakpoint() called
-> 7       local item = redis.call('rpop',src)
Copy the code

You can see that when the contiue is executed, the script stops at line 7, where we can print the value of count using the print command

lua debugger> print count
<value> 3
Copy the code

Redis.breakpoint () is used to set breakpoints dynamically, and breakpoints are entered only when certain conditions are met

Console output

In normal development, we are used to printing some prompt information (such as variable values) in the form of console output. In Java, for example, we can use the system.out.print () function to print information to the console. In Redis Lua, the method redis.debug() is also provided to output information to the console. Redis.debug () can accept multiple arguments separated by commas

For example, the following script

Local a = {1,2,3} local b = false redis.debug(a,b)Copy the code

Use LDB to execute the script

$ redis-cli --ldb --eval /tmp/script.lua
 Stopped at 1, stop reason = step over
-> 1   local a = {1,2,3}
lua debugger> continue
<debug> line 3: {1; 2; 3}, false
Copy the code

As you can see, when redis.debug(a,b) is executed, the console outputs the values of variables A and B

Common Commands

In LDB, enter the help command to view all the commands supported by LDB

lua debugger> help Redis Lua debugger help: [h]elp Show this help. [s]tep Run current line and stop again. [n]ext Alias for step. [c]continue Run till next breakpoint. [l]list List source code around current line. [l]list [line] List source code around [line]. line = 0 means:  current position. [l]list [line] [ctx] In this form [ctx] specifies how many lines to show before/after [line]. [w]hole  List all source code. Alias for 'list 1 1000000'. [p]rint Show all the local variables. [p]rint <var> Show the value of  the specified variable. Can also show global vars KEYS and ARGV. [b]reak Show all breakpoints. [b]reak <line> Add a breakpoint to the specified line. [b]reak -<line> Remove breakpoint from the specified line. [b]reak 0 Remove all breakpoints. [t]race Show a backtrace. [e]eval <code> Execute some Lua code (in a different callframe). [r]edis <cmd> Execute a Redis command. [m]axlen [len] Trim logged Redis replies and Lua var dumps to len. Specifying zero as <len> means unlimited. [a]bort Stop the execution of the script. In sync mode dataset changes will be retained. Debugger functions you can call from Lua scripts: redis.debug() Produce logs in the debugger console. redis.breakpoint() Stop execution like if there was a breakpoint in the next line of code.Copy the code

The instructions for these commands are very clear, some of which are useful above, but others are described below

step | next

Step is shown in quick start. Next is an alias for step. Using step or next, you can execute the current line of code and stop on the next line

list [line]

View the script code around the line. Specifically, when line=0 or not specified, view the code around the line in the current position

Lua debugger> list -> 1 local a = {1,2,3} 2 local b = false 3 redis.debug(a,b)Copy the code

eval <code>

Execute the Lua script

lua debugger> eval redis.call('ping')
<retval> {["ok"]="PONG"}
Copy the code

redis <cmd>

Run the redis command

lua debugger> redis ping
<redis> ping
<reply> "+PONG"
Copy the code

abort

The script is executed. In synchronous mode, the data modified by the executed script is retained, that is, the data is not rolled back

print

The print command prints out the values of variables in the script

Lua debugger> list 1 local a = {1,2,3} 2 local b = false -> 3 redis.debug(a,b) lua debugger> print a <value> {1; 2; 3}Copy the code

When no variable name is specified after print, all variables and their values are printed

Lua debugger> list 1 local a = {1,2,3} 2 local b = false -> 3 redis.debug(a,b) lua debugger> print <value> a = {1; 2; 3} <value> b = falseCopy the code

Command abbreviations

When LDB designs commands, the initial letter of each command is different. Therefore, it is possible to use an acronym instead of a full command. For example, print commands can use the initial letter P instead