feat(loadtest): sync add real 500-session capacity harness

This commit is contained in:
iamxvbaba 2026-08-02 12:02:07 +08:00
parent ac0566f779
commit 141f2f20c4
39 changed files with 4157 additions and 42 deletions

View file

@ -58,21 +58,32 @@ func (l *RateLimiter) AllowN(ctx context.Context, key string, cost, limit int, w
if err != nil {
return false, 0, fmt.Errorf("redis increment rate limit: %w", err)
}
items, ok := value.([]interface{})
if !ok || len(items) != 2 {
return false, 0, fmt.Errorf("redis increment rate limit: unexpected result %T", value)
}
count, countOK := items[0].(int64)
ttlMillis, ttlOK := items[1].(int64)
if !countOK || !ttlOK || ttlMillis <= 0 {
return false, 0, fmt.Errorf("redis increment rate limit: invalid result %#v", items)
count, ttlMillis, err := decodeRateLimitIncrementResult(value)
if err != nil {
return false, 0, err
}
if count <= int64(limit) {
return true, 0, nil
}
// Redis PTTL returns 0 when less than one millisecond remains. That is a
// valid fixed-window boundary, not a corrupt result. Round it up to the
// smallest protocol-safe FLOOD_WAIT instead of leaking a transient 500.
retry := (ttlMillis + 999) / 1000
if retry <= 0 {
retry = 1
}
return false, int(retry), nil
}
func decodeRateLimitIncrementResult(value any) (count int64, ttlMillis int64, err error) {
items, ok := value.([]interface{})
if !ok || len(items) != 2 {
return 0, 0, fmt.Errorf("redis increment rate limit: unexpected result %T", value)
}
count, countOK := items[0].(int64)
ttlMillis, ttlOK := items[1].(int64)
if !countOK || !ttlOK || count <= 0 || ttlMillis < 0 {
return 0, 0, fmt.Errorf("redis increment rate limit: invalid result %#v", items)
}
return count, ttlMillis, nil
}

View file

@ -0,0 +1,32 @@
package redisstore
import "testing"
func TestDecodeRateLimitIncrementResultAcceptsPTTLBoundary(t *testing.T) {
count, ttlMillis, err := decodeRateLimitIncrementResult([]interface{}{int64(7), int64(0)})
if err != nil {
t.Fatalf("decode zero PTTL: %v", err)
}
if count != 7 || ttlMillis != 0 {
t.Fatalf("decoded count=%d ttl=%d, want 7/0", count, ttlMillis)
}
}
func TestDecodeRateLimitIncrementResultRejectsInvalidShape(t *testing.T) {
tests := []struct {
name string
value any
}{
{name: "wrong type", value: "7,1"},
{name: "wrong length", value: []interface{}{int64(7)}},
{name: "zero count", value: []interface{}{int64(0), int64(1)}},
{name: "negative ttl", value: []interface{}{int64(7), int64(-1)}},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if _, _, err := decodeRateLimitIncrementResult(test.value); err == nil {
t.Fatal("expected decode error")
}
})
}
}