In previous post, I explained how I wrote a rate limiter for dnsdist using fixed window algorithm and redis. In this post, I will show how I write a rate limiter without Redis, this method give me a higher performance.

dnsdist: implement own rate-limiting function - part 1

Dependencies

Install dependencies

$ apt-get install lua5.1 libpsl-dev cmake
$ luarocks install psl
$ luarocks install effil

In this way, I will use effil to create a Lua table to store counters. And it’s thread-safe.

local effil = require("effil")
local request_counters = effil.table()

Continue, create a simple counter function

function increment(counters, domain, current_time, window_size)
    if not counters[domain] then
        counters[domain] = {
            expire_at = current_time + window_size,
            count = 0
        }
    end
    local counter = counters[domain]
    if counter["expire_at"] <= current_time then
        counter["count"] = 0
        counter["expire_at"] = current_time + window_size
    end
    counter["count"] = counter["count"] + 1
    return counter["count"]
end

In rate limiting function, increase counter then get current rate

local current_time = os.time()
local rate = increment(request_counters, domain, current_time, window_size)

At last, I created a cleanup function to remove the expire counter. This function would be ran in another thread every 5 seconds

function cleanup(counters)
    while true do
        for domain, counter in effil.pairs(counters) do
            if counter["expire_at"] < os.time() and counter["count"] > 0 then
                counters[domain] = nil
            end
        end
        effil.sleep(5, "s")
    end
end

effil.thread(cleanup)(request_counters)

Benchmark

Dnsdist without Rate limiter

root@test1:~# ./dnsstresss -r 127.0.0.1:53 google.com
dnsstresss - dns stress tool

Testing resolver: 127.0.0.1:53.
Target domains: [google.com.].

Started 50 threads.
Requests sent:  10749r/s	Replies received:  10749r/s (mean=5ms / max=27ms)
Requests sent:  11245r/s	Replies received:  11245r/s (mean=4ms / max=15ms)
Requests sent:  11459r/s	Replies received:  11459r/s (mean=4ms / max=23ms)
Requests sent:  11085r/s	Replies received:  11085r/s (mean=5ms / max=20ms)
Requests sent:  10936r/s	Replies received:  10936r/s (mean=5ms / max=18ms)

Dnsdist with Rate limiter (with redis)

root@test1:~# ./dnsstresss -r 127.0.0.1:53 google.com
dnsstresss - dns stress tool

Testing resolver: 127.0.0.1:53.
Target domains: [google.com.].

Started 50 threads.
Requests sent:   5774r/s	Replies received:   5774r/s (mean=8ms / max=25ms)
Requests sent:   5055r/s	Replies received:   5055r/s (mean=10ms / max=24ms)
Requests sent:   5125r/s	Replies received:   5125r/s (mean=10ms / max=25ms)
Requests sent:   5232r/s	Replies received:   5232r/s (mean=10ms / max=30ms)
Requests sent:   5050r/s	Replies received:   5050r/s (mean=10ms / max=23ms)

Dnsdist with Rate limiter (without redis)

root@test1:~# ./dnsstresss -r 127.0.0.1:53 google.com
dnsstresss - dns stress tool

Testing resolver: 127.0.0.1:53.
Target domains: [google.com.].

Started 50 threads.
Requests sent:  11649r/s	Replies received:  11649r/s (mean=4ms / max=17ms)
Requests sent:  10926r/s	Replies received:  10926r/s (mean=5ms / max=23ms)
Requests sent:  11200r/s	Replies received:  11200r/s (mean=4ms / max=17ms)
Requests sent:  11417r/s	Replies received:  11417r/s (mean=4ms / max=20ms)
Requests sent:  10413r/s	Replies received:  10413r/s (mean=5ms / max=19ms)