Skip to content

Commit

Permalink
fix: update formula and add tests
Browse files Browse the repository at this point in the history
Signed-off-by: Vladislav Oleshko <[email protected]>
  • Loading branch information
dranikpg committed Feb 7, 2024
1 parent 68c0ed2 commit 6724443
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 15 deletions.
2 changes: 1 addition & 1 deletion tests/dragonfly/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def create(self, existing_port=None, **kwargs) -> DflyInstance:
args = {**self.args, **kwargs}
args.setdefault("dbfilename", "")
vmod = "dragonfly_connection=1,accept_server=1,listener_interface=1,main_service=1,rdb_save=1,replica=1"
args.setdefault("vmodule", vmod)
# args.setdefault("vmodule", vmod)

for k, v in args.items():
args[k] = v.format(**self.params.env) if isinstance(v, str) else v
Expand Down
3 changes: 1 addition & 2 deletions tests/dragonfly/seeder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The seeder tries to maintain a specific number of keys, quickly filling or empty
# Configure how many keys we want
s = Seeder(key_target=10_000)

# Fill instance with keys until it's 10k +- 1%\
# Fill instance with keys until it's 10k +- 1%
# Will create many new keys with data and reach equilibrium
await s.run(client, target_deviation=0.01)
assert abs(client.dbsize() - 10_000) <= 100
Expand All @@ -21,7 +21,6 @@ await s.run(client, target_ops=5000)
# Now we want only 500 keys, issue many deletes
s.change_key_target(500)
await s.run(client, target_deviation=0.01)

```

### 2. Checking consistency
Expand Down
2 changes: 1 addition & 1 deletion tests/dragonfly/seeder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ async def stop(self, client: aioredis.Redis):
def change_key_target(self, target: int):
"""Change key target, applied only on succeeding runs"""

self.key_target = max(target, 10)
self.key_target = max(target, 100) # math breaks with low values

@classmethod
async def capture(clz, client: aioredis.Redis) -> typing.List[int]:
Expand Down
11 changes: 8 additions & 3 deletions tests/dragonfly/seeder/script-generate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ end

-- set equilibrium point as key target, see intensity calculations below
local real_target = key_target
key_target = key_target / 0.82
key_target = key_target / 0.956

-- accumulative probabilities: [add, add + delete, modify = 1-( add + delete) ]
local p_add = 0
Expand All @@ -62,6 +62,11 @@ while true do
break
end

if key_target < 100 and min_dev > 0 then
print(real_target, key_target, math.abs(#keys - real_target) / real_target)
print()
end

-- break if we reached our target deviation
if min_dev > 0 and math.abs(#keys - real_target) / real_target < min_dev then
break
Expand All @@ -88,11 +93,11 @@ while true do
-- the point where the intensities are equal is the equilibrium point,
-- based on the formulas it's ~0.82 * key_target
local i_add = math.max(0, 1 - (#keys / key_target) ^ 16)
local i_del = (#keys / key_target) ^ 8
local i_del = (#keys / key_target) ^ 16

-- we are only interested in large amounts of modification commands when we are in an
-- equilibrium, where there are no low intensities
local i_mod = math.max(0, 5 * math.min(i_add, i_del) ^ 3)
local i_mod = math.max(0, 7 * math.min(i_add, i_del) ^ 3)

-- transform intensities to [0, 1] probability ranges
local sum = i_add + i_del + i_mod
Expand Down
29 changes: 21 additions & 8 deletions tests/dragonfly/seeder_test.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import asyncio
import async_timeout
import string
from . import dfly_args
import random
from redis import asyncio as aioredis
from . import dfly_args
from .seeder import Seeder


@dfly_args({"proactor_threads": 4})
async def test_seeder_key_target(async_client: aioredis.Redis):
"""Ensure seeder reaches its key targets"""
s = Seeder(units=5, key_target=5000)
s = Seeder(units=random.randint(4, 12), key_target=5000)

# Ensure tests are not reasonably slow
async with async_timeout.timeout(1 + 4):
Expand All @@ -27,10 +28,10 @@ async def test_seeder_key_target(async_client: aioredis.Redis):
await s.stop(async_client)
await task

# Change key target, 10 is actual minimum because "math breaks"
# Change key target, 100 is actual minimum because "math breaks"
s.change_key_target(0)
await s.run(async_client, target_deviation=0.01)
assert await async_client.dbsize() < 50
await s.run(async_client, target_deviation=0.5) # don't set low precision with low values
assert await async_client.dbsize() < 200


@dfly_args({"proactor_threads": 4})
Expand All @@ -40,17 +41,29 @@ async def test_seeder_capture(async_client: aioredis.Redis):
async def set_data():
p = async_client.pipeline()
p.mset(mapping={f"string{i}": f"{i}" for i in range(100)})
p.lpush("list1", *(list(string.ascii_letters) + list(string.ascii_letters)))
p.sadd("set1", *(list(string.ascii_letters)))
# uncomment when seeder supports more than strings
# p.lpush("list1", *list(string.ascii_letters))
# p.sadd("set1", *list(string.ascii_letters))
# p.hset("hash1", mapping={f"{i}": l for i, l in enumerate(string.ascii_letters)})
# p.zadd("zset1", mapping={l: i for i, l in enumerate(string.ascii_letters)})
# p.json().set("json1", ".", {"a": [1, 2, 3], "b": {"c": 1, "d": 2, "e": [5, 6]}})
await p.execute()

# Capture with filled data
await set_data()
c1 = await Seeder.capture(async_client)

# Check hashes are 0 without data
await async_client.flushall()
assert all(h == 0 for h in (await Seeder.capture(async_client)))

# Check setting the same data results in same hashes
await set_data()
c2 = await Seeder.capture(async_client)

assert c1 == c2

# Check chaning the data gives different hahses
# await async_client.lpush("list1", "NEW")
await async_client.append("string1", "MORE-DATA")
c3 = await Seeder.capture(async_client)
assert c1 != c3

0 comments on commit 6724443

Please sign in to comment.