mirror of
https://github.com/hanzoai/redis.git
synced 2026-10-30 18:25:12 +00:00
After introducing GCRA algorithm into redis https://github.com/redis/redis/pull/14826 and subsequent introduction of new RATE_LIMIT object type - https://github.com/redis/redis/pull/14905. It was internally decided not to introduce GCRA into the new release. As still no decision is made on whether it will be kept or not in the future, this PR only makes the code related to GCRA dead - commands are inaccessible and AOF/RDB load+save is disabled. --------- Co-authored-by: debing.sun <[email protected]>
377 lines
11 KiB
Tcl
377 lines
11 KiB
Tcl
start_server {tags {"gcra" "external:skip"}} {
|
|
if 0 {
|
|
test {GCRA - argument validation} {
|
|
# Wrong number of arguments (too few)
|
|
catch {r gcra} err
|
|
assert_match "*wrong number of arguments*" $err
|
|
catch {r gcra mykey} err
|
|
assert_match "*wrong number of arguments*" $err
|
|
catch {r gcra mykey 10} err
|
|
assert_match "*wrong number of arguments*" $err
|
|
catch {r gcra mykey 10 5} err
|
|
assert_match "*wrong number of arguments*" $err
|
|
|
|
# max_burst must be non-negative integer
|
|
catch {r gcra mykey -1 5 10} err
|
|
assert_match "*out of range*" $err
|
|
catch {r gcra mykey notanumber 5 10} err
|
|
assert_match "*out of range*" $err
|
|
|
|
# tokens_per_period must be >= 1
|
|
catch {r gcra mykey 10 0 10} err
|
|
assert_match "*out of range*" $err
|
|
catch {r gcra mykey 10 -1 10} err
|
|
assert_match "*out of range*" $err
|
|
catch {r gcra mykey 10 notanumber 10} err
|
|
assert_match "*not an integer*" $err
|
|
|
|
# period must be positive
|
|
catch {r gcra mykey 10 5 0} err
|
|
assert_match "*period must be > 0*" $err
|
|
catch {r gcra mykey 10 5 -1} err
|
|
assert_match "*period must be > 0*" $err
|
|
catch {r gcra mykey 10 5 notanumber} err
|
|
assert_match "*not a valid float*" $err
|
|
|
|
# tokens (optional) must be >= 1
|
|
catch {r gcra mykey 10 5 10 TOKENS} err
|
|
assert_match "*Missing TOKENS value*" $err
|
|
catch {r gcra mykey 10 5 10 TOKENS 0} err
|
|
assert_match "*out of range*" $err
|
|
catch {r gcra mykey 10 5 10 TOKENS -1} err
|
|
assert_match "*out of range*" $err
|
|
catch {r gcra mykey 10 5 10 TOKENS notanumber} err
|
|
assert_match "*not an integer*" $err
|
|
|
|
# Valid arguments with default tokens
|
|
r del mykey
|
|
set result [r gcra mykey 10 5 60]
|
|
assert_equal 5 [llength $result]
|
|
set limited [lindex $result 0]
|
|
set max_req_num [lindex $result 1]
|
|
assert_equal $limited 0
|
|
assert_equal 11 $max_req_num
|
|
|
|
# Valid arguments with explicit tokens
|
|
r del mykey
|
|
set result [r gcra mykey 10 5 60 TOKENS 2]
|
|
assert_equal 5 [llength $result]
|
|
assert_equal 11 [lindex $result 1]
|
|
|
|
# Period accepts fractional seconds
|
|
r del mykey
|
|
set result [r gcra mykey 10 5 0.5]
|
|
assert_equal 5 [llength $result]
|
|
}
|
|
|
|
test {GCRA - first request is allowed} {
|
|
r del mykey
|
|
set result [r gcra mykey 5 10 60]
|
|
set limited [lindex $result 0]
|
|
# First request should not be limited
|
|
assert_equal 0 $limited
|
|
}
|
|
|
|
test {GCRA - requests within burst are allowed} {
|
|
r del mykey
|
|
# max_burst=5, tokens_per_period=1, period=60
|
|
# This allows burst of 6 requests (max_burst + 1)
|
|
for {set i 0} {$i < 6} {incr i} {
|
|
set result [r gcra mykey 5 1 60]
|
|
set limited [lindex $result 0]
|
|
assert_equal 0 $limited "Request $i should be allowed"
|
|
}
|
|
# Request 6 should be limited
|
|
set result [r gcra mykey 5 1 60]
|
|
set limited [lindex $result 0]
|
|
assert_equal 1 $limited "Request 6 should be limited"
|
|
}
|
|
|
|
test {GCRA - retry_after is positive when limited} {
|
|
r del mykey
|
|
# Exhaust burst
|
|
for {set i 0} {$i < 3} {incr i} {
|
|
r gcra mykey 2 1 60
|
|
}
|
|
# Next request should be limited with positive retry_after
|
|
set result [r gcra mykey 2 1 60]
|
|
set limited [lindex $result 0]
|
|
set retry_after [lindex $result 3]
|
|
assert_equal 1 $limited
|
|
assert {$retry_after > 0}
|
|
}
|
|
|
|
test {GCRA - retry_after is negative when allowed} {
|
|
r del mykey
|
|
set result [r gcra mykey 5 1 60]
|
|
set limited [lindex $result 0]
|
|
set retry_after [lindex $result 3]
|
|
assert_equal 0 $limited
|
|
assert {$retry_after < 0}
|
|
}
|
|
|
|
test {GCRA - num_avail_req decreases with each request} {
|
|
r del mykey
|
|
set result1 [r gcra mykey 5 1 60]
|
|
set avail1 [lindex $result1 2]
|
|
|
|
set result2 [r gcra mykey 5 1 60]
|
|
set avail2 [lindex $result2 2]
|
|
|
|
assert {$avail2 < $avail1}
|
|
}
|
|
|
|
test {GCRA - multiple tokens consumed per request} {
|
|
r del mykey
|
|
# max_burst=5, so 6 tokens available initially
|
|
# Consume 1 token (default)
|
|
set result1 [r gcra mykey 5 1 60]
|
|
set avail1 [lindex $result1 2]
|
|
assert_equal 5 $avail1
|
|
|
|
r del mykey
|
|
# Consume 3 tokens from fresh state
|
|
set result2 [r gcra mykey 5 1 60 TOKENS 3]
|
|
set avail2 [lindex $result2 2]
|
|
assert_equal 3 $avail2
|
|
}
|
|
|
|
test {GCRA - rate recovery over time} {
|
|
r del mykey
|
|
# max_burst=1, tokens_per_period=1, period=1 (1 token per second)
|
|
# Exhaust: 2 allowed (burst+1), then limited
|
|
r gcra mykey 1 1 1
|
|
r gcra mykey 1 1 1
|
|
set result [r gcra mykey 1 1 1]
|
|
assert_equal 1 [lindex $result 0] "Should be limited"
|
|
|
|
# Wait for rate to recover
|
|
after 1100
|
|
|
|
# Should be allowed again
|
|
set result [r gcra mykey 1 1 1]
|
|
assert_equal 0 [lindex $result 0] "Should be allowed after recovery"
|
|
}
|
|
|
|
test {GCRA - full_burst_after indicates time to full recovery} {
|
|
r del mykey
|
|
# Consume some tokens
|
|
r gcra mykey 5 1 60
|
|
r gcra mykey 5 1 60
|
|
|
|
set result [r gcra mykey 5 1 60]
|
|
set full_burst_after [lindex $result 4]
|
|
|
|
# full_burst_after should be positive (time until full burst available)
|
|
# Since we've taken from the burst twice the reset time was incremented
|
|
# by the rate twice
|
|
assert {$full_burst_after >= 179}
|
|
|
|
r del mykey
|
|
r gcra mykey 5 1 60
|
|
set result [r gcra mykey 5 1 60 TOKENS 4]
|
|
set full_burst_after [lindex $result 4]
|
|
assert {$full_burst_after >= 299}
|
|
}
|
|
|
|
test {GCRA - different keys are independent} {
|
|
r del key1
|
|
r del key2
|
|
|
|
# Exhaust key1
|
|
for {set i 0} {$i < 3} {incr i} {
|
|
r gcra key1 2 1 60
|
|
}
|
|
set result1 [r gcra key1 2 1 60]
|
|
assert_equal 1 [lindex $result1 0] "key1 should be limited"
|
|
|
|
# key2 should still be available
|
|
set result2 [r gcra key2 2 1 60]
|
|
assert_equal 0 [lindex $result2 0] "key2 should be allowed"
|
|
}
|
|
|
|
test {GCRA - max_burst of 0 allows only sustained rate} {
|
|
r del mykey
|
|
# max_burst=0, tokens_per_period=1, period=1
|
|
# Only 1 request allowed initially (0+1)
|
|
set result [r gcra mykey 0 1 1]
|
|
assert_equal 0 [lindex $result 0] "First request allowed"
|
|
assert_equal 1 [lindex $result 1] "max_req_num should be 1"
|
|
|
|
# Second request should be limited
|
|
set result [r gcra mykey 0 1 1]
|
|
assert_equal 1 [lindex $result 0] "Second request limited"
|
|
}
|
|
|
|
test {GCRA - wrong type error} {
|
|
r del mykey
|
|
r lpush mykey "value"
|
|
catch {r gcra mykey 5 1 60} err
|
|
assert_match "*WRONGTYPE*" $err
|
|
|
|
r del mykey
|
|
r sadd mykey "value"
|
|
catch {r gcra mykey 5 1 60} err
|
|
assert_match "*WRONGTYPE*" $err
|
|
}
|
|
|
|
test {GCRA - overflow} {
|
|
r del mykey
|
|
catch {r gcra mykey 1 1 86400 TOKENS 200000000} err
|
|
assert_match "*would cause an overflow*" $err
|
|
|
|
r del mykey
|
|
catch {r gcra mykey 200000000 1 86400} err
|
|
assert_match "*would cause an overflow*" $err
|
|
|
|
r del mykey
|
|
catch {r gcra mykey 1 1 2147483647 TOKENS 2147483647} err
|
|
assert_match "*would cause an overflow*" $err
|
|
}
|
|
|
|
test {GCRASETVALUE - basic functionality} {
|
|
r del mykey
|
|
set tat_us [expr {[clock microseconds] + 60000000}]
|
|
assert_equal {OK} [r gcrasetvalue mykey $tat_us]
|
|
assert_equal {gcra} [r type mykey]
|
|
assert {[r pttl mykey] > 0}
|
|
}
|
|
}
|
|
}
|
|
|
|
start_server {tags {"gcra" "external:skip"}} {
|
|
if 0 {
|
|
test {GCRA - RDB save and reload preserves value} {
|
|
r del mykey
|
|
r gcra mykey 5 1 60
|
|
r gcra mykey 5 1 60
|
|
|
|
set dump_before [r dump mykey]
|
|
|
|
r debug reload
|
|
|
|
assert_equal [r type mykey] "gcra"
|
|
set dump_after [r dump mykey]
|
|
assert_equal $dump_before $dump_after
|
|
} {} {needs:debug}
|
|
|
|
test {GCRA - RDB save and reload preserves TTL} {
|
|
r del mykey
|
|
r gcra mykey 5 1 60
|
|
set ttl_before [r pexpiretime mykey]
|
|
assert_morethan $ttl_before 0
|
|
|
|
r debug reload
|
|
|
|
set ttl_after [r pexpiretime mykey]
|
|
assert_morethan $ttl_after 0
|
|
assert_equal $ttl_after $ttl_before
|
|
} {} {needs:debug}
|
|
|
|
test {GCRA - DUMP and RESTORE roundtrip} {
|
|
r del mykey mykey2
|
|
r gcra mykey 5 1 60
|
|
r gcra mykey 5 1 60
|
|
|
|
set dump [r dump mykey]
|
|
set ttl [r pttl mykey]
|
|
r restore mykey2 $ttl $dump
|
|
|
|
assert_equal [r type mykey2] "gcra"
|
|
|
|
set result_orig [r gcra mykey 5 1 60]
|
|
set result_restored [r gcra mykey2 5 1 60]
|
|
assert_equal [lindex $result_orig 2] [lindex $result_restored 2]
|
|
}
|
|
|
|
test {GCRA - AOF rewrite preserves value} {
|
|
r del mykey
|
|
r config set appendonly yes
|
|
waitForBgrewriteaof r
|
|
|
|
r gcra mykey 5 1 60
|
|
r gcra mykey 5 1 60
|
|
|
|
set dump_before [r dump mykey]
|
|
|
|
r BGREWRITEAOF
|
|
waitForBgrewriteaof r
|
|
r debug reload
|
|
|
|
assert_equal [r type mykey] "gcra"
|
|
set dump_after [r dump mykey]
|
|
assert_equal $dump_before $dump_after
|
|
} {} {external:skip needs:debug}
|
|
|
|
test {GCRA - AOF rewrite preserves TTL} {
|
|
r del mykey
|
|
r config set appendonly yes
|
|
waitForBgrewriteaof r
|
|
|
|
r gcra mykey 5 1 60
|
|
|
|
r BGREWRITEAOF
|
|
waitForBgrewriteaof r
|
|
|
|
set ttl_before [r pttl mykey]
|
|
assert {$ttl_before > 0}
|
|
|
|
r debug reload
|
|
|
|
set ttl_after [r pttl mykey]
|
|
assert {$ttl_after > 0}
|
|
assert {$ttl_after <= $ttl_before}
|
|
} {} {external:skip needs:debug}
|
|
|
|
test {GCRA - DEBUG DIGEST consistent after RDB reload} {
|
|
r del mykey
|
|
r gcra mykey 5 1 60
|
|
r gcra mykey 5 1 60
|
|
|
|
set digest_before [r debug digest]
|
|
|
|
r debug reload
|
|
|
|
set digest_after [r debug digest]
|
|
assert_equal $digest_before $digest_after
|
|
} {} {needs:debug}
|
|
}
|
|
}
|
|
|
|
start_server {tags {"gcra repl" "external:skip"}} {
|
|
if 0 {
|
|
set replica [srv 0 client]
|
|
set replica_host [srv 0 host]
|
|
set replica_port [srv 0 port]
|
|
set replica_log [srv 0 stdout]
|
|
|
|
start_server {tags {}} {
|
|
set master [srv 0 client]
|
|
set master_host [srv 0 host]
|
|
set master_port [srv 0 port]
|
|
|
|
test {GCRA - Replication works} {
|
|
$master flushdb
|
|
$replica flushdb
|
|
|
|
$replica replicaof $master_host $master_port
|
|
wait_for_condition 100 100 {
|
|
[s -1 master_link_status] eq "up"
|
|
} else {
|
|
fail "Master <-> Replica didn't finish sync"
|
|
}
|
|
|
|
set cmdinfo [$replica info commandstats]
|
|
assert_equal [lsearch -glob $cmdinfo "cmdstat_gcrasetvalue:*"] -1
|
|
|
|
$master del mykey
|
|
$master gcra mykey 2 1 1000 TOKENS 2
|
|
wait_for_ofs_sync $master $replica
|
|
|
|
set cmdinfo [$replica info commandstats]
|
|
assert_morethan_equal [lsearch -glob $cmdinfo "cmdstat_gcrasetvalue:*"] 0
|
|
} {} {external:skip}
|
|
}
|
|
}
|
|
}
|