Now Reading
Leveraging the ability of Lua scripting with Dragonfly

Leveraging the ability of Lua scripting with Dragonfly

2023-06-13 10:18:00


Builders typically flip to Lua scripting as a way to add rate-limiting, customized knowledge sorts, or intricate transactional logic to Redis. However whereas Redis’ lightning quick knowledge operations and Lua’s flexibility make for a formidable mixture, there are nonetheless limitations.

Amongst these challenges are points associated to long-running scripts blocking the info retailer and constraints on non-atomic script execution, all of which might influence the efficiency and scalability of functions counting on Redis. Moreover, Redis’s single-threaded structure and its method to horizontal scaling introduce difficulties, significantly when executing Lua scripts throughout a distributed knowledge surroundings.

That is the place Dragonfly is available in. It’s a drop-in substitute for Redis that not solely preserves the strengths of Lua scripting but in addition addresses a few of these key challenges, due to its vertically scalable, multi-threaded, asynchronous structure. On this weblog put up we are going to discover the place Redis falls brief in the case of Lua scripting, and the way Dragonfly presents a superior expertise.

1. Dragonfly’s multi-threaded, asynchronous structure gives higher efficiency for lengthy operating and/or computationally-heavy scripts

Redis operates in a single-threaded method, which means it processes one operation at a time. This turns into a bottleneck when executing long-running or computationally heavy Lua scripts, as they will block different operations till completion.

Dragonfly, then again, is constructed round a multi-threaded, asynchronous structure. This method gives a number of benefits. First, it promotes increased throughput because of parallel processing. Second, it permits a number of script execution items to run concurrently, which is especially helpful for computationally intensive scripts, equivalent to these computing hashes or aggregating values.

Moreover, Dragonfly helps asynchronicity, which means common instructions will be blended with instructions from an already operating script so long as the script’s atomicity is preserved. This retains Dragonfly out there for incoming requests even whereas a script is executing.

As an example this, let’s take into account a easy Lua script that pushes values into a listing:

native n = tonumber(ARGV[1])
for i = 1, n do'LPUSH', KEYS[1], i)

We will run this script on a Dragonfly occasion with solely a single core. If the argument is massive sufficient, it’s going to take fairly a while. But the occasion is absolutely responsive and may deal with instructions.

2. Dragonfly presents particular optimizations for write-heavy scripts

Lua scripts are sometimes used to insert or replace many values directly. In these instances they normally concern many easy sequential write instructions. If their output is discarded – i.e. not saved in variables or used inside situations within the script – then there may be nice potential for parallelization.

Dragonfly, in its current variations, introduces optimizations for this precise state of affairs. It’s able to executing all of the write instructions in parallel so long as they don’t affect the general execution circulate of the script. This characteristic enhances efficiency by using multi-threading. It may be enabled by passing the next flag: lua_auto_async=true.

Take, for instance, a script that pushes values from a JSON array to a number of lists and trims these lists in the event that they turn out to be too lengthy. We take a look at this script in each Redis and Dragonfly utilizing a single connection on a single thread to make sure that there is no such thing as a interference from a number of scripts operating concurrently.

native messages = cjson.decode(ARGV[1])
for _, key in ipairs(KEYS) do
  for _, message in ipairs(messages) do
    redis.pcall("LPUSH", key, message)
    redis.pcall("LTRIM", key, 0, 50)
return "OK"

Our benchmark outcomes present that Dragonfly is round 35% sooner than Redis for executing sequential instructions from a single connection, highlighting its efficient use of parallelization.

memtier_benchmark --command="EVALSHA {sha} 5 l1 l2 l3 l4 l5 '[1,2,3,4,5]'" --hide-histogram --test-time=5 --distinct-client-seed -t 1 -c 1 --pipeline 5
Dragonfly 16k QPS
Redis 12k QPS

Furthermore, once we run the benchmark permitting a number of scripts to execute in parallel from 4 threads, Dragonfly demonstrates nearly triple the efficiency in comparison with Redis. It is able to successfully dealing with greater than 2 million operations per second on an occasion operating on solely 4 cores (40k invocations per second, every carry out 50 operations).

memtier_benchmark --command="EVALSHA {sha} 5 __key__ __key__ __key__ __key__ __key__ '[1,2,3,4,5]'" --hide-histogram --test-time=5 --distinct-client-seed -t 4 -c 50 --pipeline 5
Dragonfly 41.5k QPS
Redis 14.5k QPS

This means that Dragonfly scales extra effectively in comparison with Redis. Its structure leverages multi-threading, making it well-suited for high-throughput use instances, whereas Redis’ single-threaded nature may restrict its scalability in comparable eventualities.

3. Dragonfly lets you scale Lua scripts vertically

Redis helps solely horizontal scaling with Redis Cluster. The absence of vertical scaling brings its personal set of challenges when operating Lua scripts. Whenever you’re utilizing a cluster, knowledge is distributed throughout a number of nodes primarily based on a hash slot mechanism, which implies totally different keys could reside on totally different nodes.

In Redis, Lua scripts are executed atomically. Because of this a script is a single, indivisible operation which runs from begin to end with out another operation interrupting it. Whenever you’re utilizing a cluster, this atomicity is preserved, however with a key limitation: a single Lua script can not function on keys which can be saved on a number of nodes.

Because of this once you’re writing Lua scripts for a Redis Cluster, it’s a must to make sure that all keys utilized in a single Lua script are situated on the identical node. In follow, this normally means it’s a must to use an idea known as “hash tags” to make sure sure keys find yourself on the identical node. But it many instances, when the info is barely loosely interconnected and totally different entry patterns are used within the utility, accurately assigning “hash tags” to all keys may be merely inconceivable

This additionally implies that script-heavy methods are likely to scale extra successfully vertically than horizontally. Vertical scaling permits each script to entry all keys absolutely.

Let’s study a particular instance. Suppose we run a recreation improvement firm, and we retailer leaderboards for various recreation rooms in our datastore. Our anti-cheat crew develops a brand new heuristic that should shortly determine customers who made it into the highest 10 in at the very least half of a selected set of recreation rooms. These suspicious customers should be saved in a separate set, continuously accessed by our anti-cheat software program.

If we use a cluster, then our script must execute on all nodes. The outcomes of that script (suspicious customers for every node) are despatched again to the applying, which then aggregates them right into a last set of suspicious customers.

With Dragonfly, we will conduct your entire operation with a single operation, eradicating the necessity for expensive spherical journeys between our utility and a number of nodes.


4. Dragonfly presents a approach to run Lua scripts non-atomically

Lua scripts run atomically by default, which means that every operation is executed in a single uninterrupted sequence with out another command having the ability to interleave. That is essential when executing Lua scripts that require atomic execution for consistency. Nonetheless, it could pose challenges once you wish to use scripts simply to lower community spherical journeys with out truly needing atomicity for your entire script.

For example, suppose it’s worthwhile to compute a dwell metric on all lately energetic customers whose profiles are cached within the datastore. If atomicity is enforced whereas the script calculates this metric, it could block all entry to consumer profiles, probably inflicting vital slowdowns in your utility. But the metric doesn’t should be precise, which means it could run in a non remoted state the place consumer profiles are consistently up to date. A possible resolution could be calculating the metric within the utility by querying the datastore for consumer profiles with pipelined requests. Nonetheless if the metric is fast to compute and the consumer profiles are massive, our method would produce a lot of unneeded and extreme visitors.

See Also

Dragonfly presents an answer to this drawback by providing the flexibility to run scripts non-atomically. It implies that different instructions can interleave with a script’s instructions even when they entry the identical keys. The script’s execution turns into analogous to a collection of pipelined instructions, solely that it is not produced by a consumer, however the script itself.

Here is the way it works: Dragonfly gives particular script flags equivalent to:

  • “disable-atomicity” permits non-atomic execution
  • “allow-undeclared-keys” permits the script to entry undeclared keys.

(Be aware that accessing undeclared keys is mostly discouraged by Redis and is disabled by default in Dragonfly, because of its unpredictable conduct in a multi-threaded asynchronous surroundings.)

Here is a easy Lua script instance which computes metrics for an anticheat crew:

native cursor = "0" 
  native end result ="SCAN", cursor, "MATCH", "consumer:*") 
  cursor = end result[1] 
  for _, consumer in ipairs(end result[2]) do 
till cursor == "0"

Even when this script takes a couple of seconds to execute, consumer profiles stay absolutely accessible, and the cache continues to function usually, with solely a slight discount in throughput.

5. Dragonfly will be configured in order that sure keys are usually not evictable

When Redis is used as a cache, it implements an eviction coverage: knowledge that isn’t accessed continuously is robotically eliminated to liberate reminiscence for brand new or extra continuously accessed knowledge. This eviction technique is environment friendly; nevertheless, it creates an issue once you wish to retailer auxiliary knowledge that will not be accessed typically however remains to be necessary and must be readily accessible.

For example, let’s say that you’ve a Lua script that calculates debug metrics operating on Redis as soon as a day. The output metrics are occasionally accessed and subsequently topic to eviction. Alternatively, you don’t wish to must retailer them in a separate database or datastore – ideally you’ll be able to retailer the debug metrics within the cache itself.

In a state of affairs like this, Dragonfly presents considerably extra management than Redis. It gives a STICK key [keys…] command that makes sure keys not evictable.

To return to our instance, we will write a easy Lua script that may calculate the debug metrics, and to make the end result not evictable, we use the STICK choice of the SET command.

native end result = calc_metric()'SET', 'last-metric', end result, 'STICK')


As we have explored, Dragonfly gives you with the instruments to mitigate widespread points encountered with Redis and Lua scripting, whether or not it is dealing with long-running scripts, executing massive numbers of write instructions effectively, offering higher scalability choices, or providing the flexibility to run non-atomic scripts. You may attempt Dragonfly at present by viewing our docs or trying out the Dragonfly mission on Github.

Source Link

What's Your Reaction?
In Love
Not Sure
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top