-
Notifications
You must be signed in to change notification settings - Fork 21.3k
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
Fix RedisCacheStore
INCR/DECR for Redis < v7.0.0
#49554
Fix RedisCacheStore
INCR/DECR for Redis < v7.0.0
#49554
Conversation
@fatkodima, given your expertise in this area, I'd love if you took a look! I might be missing some context around why the conditional was added. Thanks! 😄 |
The goal was to avoid calling |
aa77d60
to
6c52f18
Compare
Thank you for all the guidance and feedback, @byroot! I've pushed an update and added a few tests. Please let me know if there's anything else you'd like addressed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small stuff, but looks good to me.
This commit fixes a discrepancy in the behavior of the `#increment` and `#decrement` methods in `RedisCacheStore` when used with Redis versions less than 7.0.0. The existing condition `count != amount` prevented setting the Time-To-Live (TTL) for keys that were equal to the increment/decrement amount after the `INCRBY`/`DECRBY` operation. This occurs when incrementing a non-existent key by `1`, for example. Using Redis pipelining, we minimize the network overhead incurred by checking for existing TTLs. It decouples the TTL operations from the increment/decrement operation, allowing the TTL to be set correctly regardless of the resulting value from the `INCRBY`/`DECRBY`. New tests have been added to verify the correct behavior of `#increment` and `#decrement` methods, specifically when the `expires_in` option is not used. Using a separate cache store instance (`@cache_no_ttl`), these tests ensure that keys are correctly incremented or decremented and that their TTL remains unset. Co-authored-by: Benjamin Quorning <benjamin@quorning.net> Co-authored-by: Jury Razumau <jury.razumau@zendesk.com> Co-authored-by: Edyta Rozczypała <edyta.rozczypala@zendesk.com>
6c52f18
to
600a052
Compare
…-on-first-incr-decr Fix `RedisCacheStore` INCR/DECR for Redis < v7.0.0
Backported to 7-1-stable as c3117b5 |
Thank you! |
This commit fixes a discrepancy in the behavior of the
#increment
and#decrement
methods inRedisCacheStore
when used with Redis versions less than 7.0.0. The existing conditioncount != amount
prevented setting the Time-To-Live (TTL) for keys that were equal to the increment/decrement amount after theINCRBY
/DECRBY
operation. This occurs when incrementing a non-existent key by1
, for example.Using Redis pipelining, we minimize the network overhead incurred by checking for existing TTLs. It decouples the TTL operations from the increment/decrement operation, allowing the TTL to be set correctly regardless of the resulting value from the
INCRBY
/DECRBY
.New tests have been added to verify the correct behavior of
#increment
and#decrement
methods, specifically when theexpires_in
option is not used. Using a separate cache store instance (@cache_no_ttl
), these tests ensure that keys are correctly incremented or decremented and that their TTL remains unset.Motivation / Background
We noticed the test failures on the
main
branch for Redis versions<7.0.0
. The culprit was a change introduced in #45711, where a condition was added that prevents the TTL from being set. This happens when a key without TTL is incremented or decremented and the resulting value equals the amount it is incremented/decremented by.This breaks the expected behavior of setting the TTL on the first call to increment when
:expires_in
is provided, particularly when the operation results in the key's value set to1
. This happens often in rate limiting scenarios when the key is either initialized to0
by the application, or by Redis, before being incremented to1
.Detail
This PR modifies the
change_counter
method inRedisCacheStore
, removing thecount != amount
check.With this fix, TTL is set upon the first increment operation, irrespective of the counter's value, aligning the behavior outlined in the specs.
For example:
rails/activesupport/test/cache/stores/redis_cache_store_test.rb
Lines 196 to 201 in f11c651
Because the
foo
key is not defined, ...it is set to 0 before performing the operation... by Redis. When it performs increment, the value will be1
. Once the value is1
,#change_counter
will fail to issueEXPIRE
.rails/activesupport/lib/active_support/cache/redis_cache_store.rb
Lines 447 to 450 in f11c651
Here,
count
is1
andamount
is1
, making thecount != amount
conditionfalse
.In the original code before the patch, the TTL was set in the first call to
#increment
, given that the:expires_in
option was provided.rails/activesupport/lib/active_support/cache/redis_cache_store.rb
Lines 239 to 242 in 6bfc637
Here, the TTL was set within a
tap
block that executes after the increment operation, irrespective of the counter's value.Additional Details
Test Failures
Test Infrastructure Setup via Docker Compose
Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]