-
Notifications
You must be signed in to change notification settings - Fork 993
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dragonfly does not allow accessing undeclared keys in lua scripts #272
Comments
Well, it's not a bug yet - it's by design. |
the question is whether https://github.com/Suor/django-cacheops tests pass - #184 |
They don't, because the comparison in the snippet fails. It expects a number on the left |
please deep dive into this because when I look at their code: https://github.com/Suor/django-cacheops/blob/917a0fbd92dccd2f64a2a28a245436057c3f1f27/cacheops/query.py#L53 you can run dragonfly with |
-- the keys that are passed to EVAL
local prefix = KEYS[1]
local key = KEYS[2]
local precall_key = KEYS[3]
-- conj depends on arguments
local conj_cache_key = function (db_table, conj)
local parts = {}
for field, val in pairs(conj) do
table.insert(parts, field .. '=' .. tostring(val))
end
return prefix .. 'conj:' .. db_table .. ':' .. table.concat(parts, '&')
end
-- conj_key is generated here
local conj_key = conj_cache_key(db_table, conj)
redis.call('sadd', conj_key, key) So its programmatically generated The Redis docs state the following:
I guess django-cacheops is using it incorrectly |
hmmm, ok... then this task will be harder for you to fix. we can support undeclared keys as well, but it requires a really good understanding how transactions work in DF. |
Is there a timeline on this? |
Can you describe a use case or the context? |
Yes, the use case is being able to access and work on keys that do not exist in the namespace during a transaction. For example, incrementing the value of a key in a hash table that might not yet exist. |
You can declare a key that you may access does not exist. The question is
whether you know the name beforehand, before running the script. If yes,
then just.pass its name along
…On Sat, Jan 28, 2023, 01:04 Tayler King ***@***.***> wrote:
Can you describe a use case or the context?
Yes, the use case is being able to access and work on keys that do not
exist in the namespace during a transaction.
For example, incrementing the value of a key in a hash table that might
not yet exist.
—
Reply to this email directly, view it on GitHub
<#272 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA4BFCHQVA2J3DB2UQKYWRLWURH6NANCNFSM6AAAAAAQHDAUZU>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
Ah, yes. I see. My apologies. |
// Input:
// KEYS[1] -> asynq:{<qname>}:pending
// KEYS[2] -> asynq:{<qname>}:paused
// KEYS[3] -> asynq:{<qname>}:active
// KEYS[4] -> asynq:{<qname>}:lease
// --
// ARGV[1] -> initial lease expiration Unix time
// ARGV[2] -> task key prefix
//
// Output:
// Returns nil if no processable task is found in the given queue.
// Returns an encoded TaskMessage.
//
// Note: dequeueCmd checks whether a queue is paused first, before
// calling RPOPLPUSH to pop a task from the queue.
var dequeueCmd = redis.NewScript(`
if redis.call("EXISTS", KEYS[2]) == 0 then
local id = redis.call("RPOPLPUSH", KEYS[1], KEYS[3])
if id then
local key = ARGV[2] .. id
redis.call("HSET", key, "state", "active")
redis.call("HDEL", key, "pending_since")
redis.call("ZADD", KEYS[4], ARGV[1], id)
return redis.call("HGET", key, "msg")
end
end
return nil`) Specifically this part generates a key local id = redis.call("RPOPLPUSH", KEYS[1], KEYS[3])
if id then
local key = ARGV[2] .. id |
I spent time some time to think what if we had chosen another locking scheme, i.e. not VLL - would it improve our option to solve the issue? The answer is no - any "non-determenistic transaction that accesses undeclared keys would require rollback, and that contradicts the nature of Redis and Dragonfly. To summarize: To schedule distributed atomic transactions, we must either predeclare all the keys, or be able to rollback the transaction that was partially "applied". |
Does it mean, we are defeated? I do not think so. @dranikpg
I think, that option 2 is preferrable since it solves the latency issue as well. |
|
2 - it is, if we support "slot" locks. For a lua script to contend on such undeclared key, say |
True.... Slot locks nice feature for DF in general |
Done with script flags |
Should Dragonfly is advertized as "drop-in" replacement to Redis, but this is a behavorial difference that breaks this compatibility. |
Hi @silverwind The allow-undeclared-keys flag slows down script execution significantly (it makes it impossible to run them in parallel). Enabling it by default will add enormous overhead to scripts that actually don't need this feature. Redis itself discourages working with undeclared keys. See their docs:
|
@silverwind we are working to allow undeclared keys when running the script with slots. |
I use Dragonfly with Symfony messenger (PHP):
I get these errors. I was hoping Dragonfly was a drop-in replacement for Redis, hence I moved away from Redis to Dragonfly. |
Thanks! Can these options be set via dragonfly.conf file as well? I think so, right? Hopefully using: |
It still bothers the question: Why not make the default flags redis-compatible? Dragonfly advertizes with "Dragonfly is fully compatible with Redis APIs" but that is clearly not the case with |
That I can't agree more! This issue should be re-opened and fixed. |
While we strive to be as compatible as possible with Redis Core semantics (atomicity guarantees, Redis Protocol), some Redis use cases can heavily limit Dragonfly's performance. For example, the --default_lua_flags=allow-undeclared-keys flag necessitates the Global Interpreter Lock (GIL) on all EVAL calls. This makes multi-threaded Dragonfly less efficient than single-threaded Redis Core when calling EVALs at high throughput. Many users rely on Lua without using undeclared keys, so it wouldn't be fair to penalise them for this edge case. |
Describe the bug
Discovered when testing #182. The following script:
expects
conj_ttl
always to be a number. Dragonfly instead returns errors as lua tables (aka js-like objects) with a detailed cause.To Reproduce
Dragonfly:
Redis:
Expected behavior
Return sentinel errors in scripts as lua numbers
The text was updated successfully, but these errors were encountered: