Abhishek Goyal
Abhishek Goyal

Reputation: 877

Redis Lua script not working as expected

As a practice exercise, I'm writing a lua script for Redis which basically executes the JavaScript method Array#copyWithin().

Quoting from MDN,

The copyWithin() method copies the sequence of array elements within the array to the position starting at target. The copy is taken from the index positions of the second and third arguments start and end.

Here is the script I've written so far :

local list = redis.call('lrange', KEYS[1], 0, -1)
local target = tonumber(ARGV[1])
local startIndex = tonumber(ARGV[2])
local len = #list
local endIndex = len

--Handle negative startIndex
if startIndex < 0 then
    startIndex = len+startIndex
end

--If the third argument is provided, get the endIndex from it
if #ARGV > 2 then
    local arg = tonumber(ARGV[3])
    if arg >= 0 then
        if arg < endIndex then
            endIndex = arg
        end
    else
        if len+arg >= 0 then
            endIndex = len+arg
        else
            endIndex = 0
        end
    end
end

--An array containing the elements which will be copied
local targeted_elements = {}

--Fill elements from the list
for i=1, (endIndex-startIndex+1) do
    targeted_elements[i] = list[startIndex+i]
end

--Make sure no additional elements are pushed to the end of array in case of range overflow
local target_end = #targeted_elements

if target + target_end > len then
    target_end = len-target
end

--replace all the changed elements of the list in redis
for i=1, target_end do
    redis.call('lset', KEYS[1], target+(i-1), targeted_elements[i])
end

During testing, the first test case is successfully cleared :

Test case : convert [1, 2, 3, 4, 5] into [4, 5, 3, 4, 5] using copyWithin(0, 3)

LRANGE MyList 0 -1
> [1, 2, 3, 4, 5]

EVALSHA "sha1 of script" 1 MyList 0 3
(basically the same as `[1, 2, 3, 4, 5].copyWithin(0, 3)`)
> nil

LRANGE MyList 0 -1
> [4, 5, 3, 4, 5]

The second test case however, did not go that smooth.

Test case : convert [1, 2, 3, 4, 5] into [4, 2, 3, 4, 5] using copyWithin(0, 3, 4)

LRANGE MyList 0 -1
> [1, 2, 3, 4, 5]

EVALSHA "sha1 of script" 1 MyList 0 3 4
(basically the same as `[1, 2, 3, 4, 5].copyWithin(0, 3, 4)`)
> nil

LRANGE MyList 0 -1
> [4, 5, 3, 4, 5]

After some debugging, I found that the value of targeted_elements is {4, 5} in both the cases, whereas it should be {4} in case 2.

Is there anything suspicious in the loops? Any help would be great.

Upvotes: 2

Views: 273

Answers (1)

Abhishek Goyal
Abhishek Goyal

Reputation: 877

I fixed this by modifying the following part of the script :

--Fill elements from the list
for i=1, (endIndex-startIndex+1) do
    targeted_elements[i] = list[startIndex+i]
end

Changed it into this:

--Fill elements from the list
    for i=1, (endIndex-startIndex) do
        targeted_elements[i] = list[startIndex+i]
    end

The +1 in the for expression added an additional element to the array. It worked in the first case because the selected part was from 3, 5, so 5-3+1 = 3 which means 3 elements should be selected. But since only 2 elements are left, the case still works.

Whereas for the second case, 4-3+1 = 2, which means 2 elements were being selected, instead of 1.

Upvotes: 2

Related Questions