Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ env:
# for example after 8.2.1 is published, 8.2 image contains 8.2.1 content
CURRENT_REDIS_VERSION: '8.6.1'
REDIS_VERSION_CUSTOM_MAP: >-
8.8:unstable-23321515778-debian
8.8:unstable-24481262301-debian

jobs:
dependency-audit:
Expand Down
26 changes: 13 additions & 13 deletions redis/commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2433,28 +2433,28 @@ def gcra(
self: SyncClientProtocol,
key: KeyT,
max_burst: int,
requests_per_period: int,
tokens_per_period: int,
period: float,
num_requests: int | None = None,
tokens: int | None = None,
) -> GCRAResponse: ...

@overload
def gcra(
self: AsyncClientProtocol,
key: KeyT,
max_burst: int,
requests_per_period: int,
tokens_per_period: int,
period: float,
num_requests: int | None = None,
tokens: int | None = None,
) -> Awaitable[GCRAResponse]: ...

def gcra(
self,
key: KeyT,
max_burst: int,
requests_per_period: int,
tokens_per_period: int,
period: float,
num_requests: int | None = None,
tokens: int | None = None,
) -> GCRAResponse | Awaitable[GCRAResponse]:
"""
Rate limit via GCRA (Generic Cell Rate Algorithm).
Expand All @@ -2465,13 +2465,13 @@ def gcra(
``max_burst`` is the maximum number of tokens allowed as a burst
(in addition to the sustained rate). Minimum: 0.

``requests_per_period`` is the number of requests allowed per period.
``tokens_per_period`` is the number of tokens allowed per period.
Minimum: 1.

``period`` is the period in seconds as a floating point number used for
calculating the sustained rate. Minimum: 1.0, Maximum: 1e12.

``num_requests`` is the cost (or weight) of this rate-limiting request.
``tokens`` is the cost (or weight) of this rate-limiting request.
A higher cost drains the allowance faster. Default: 1.

Returns a GCRAResponse dataclass with:
Expand All @@ -2486,14 +2486,14 @@ def gcra(
"""
if max_burst < 0:
raise DataError("GCRA max_burst must be >= 0")
if requests_per_period < 1:
raise DataError("GCRA requests_per_period must be >= 1")
if tokens_per_period < 1:
raise DataError("GCRA tokens_per_period must be >= 1")
if period < 1.0 or period > 1e12:
raise DataError("GCRA period must be between 1.0 and 1e12")

pieces: list[EncodableT] = [key, max_burst, requests_per_period, period]
if num_requests is not None:
pieces.extend(["NUM_REQUESTS", num_requests])
pieces: list[EncodableT] = [key, max_burst, tokens_per_period, period]
if tokens is not None:
pieces.extend(["TOKENS", tokens])

return self.execute_command("GCRA", *pieces)

Expand Down
24 changes: 12 additions & 12 deletions tests/test_asyncio/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5144,7 +5144,7 @@ async def test_gcra_basic(self, r: redis.Redis):
await r.delete(key)

# First request should not be limited
result = await r.gcra(key, max_burst=10, requests_per_period=5, period=10.0)
result = await r.gcra(key, max_burst=10, tokens_per_period=5, period=10.0)

assert isinstance(result, GCRAResponse)

Expand All @@ -5160,14 +5160,14 @@ async def test_gcra_basic(self, r: redis.Redis):
assert result.full_burst_after >= 0

@skip_if_server_version_lt("8.7.0")
async def test_gcra_with_num_requests(self, r: redis.Redis):
"""Test GCRA command with NUM_REQUESTS option"""
key = "gcra_test_num_requests"
async def test_gcra_with_tokens(self, r: redis.Redis):
"""Test GCRA command with TOKENS option"""
key = "gcra_test_tokens"
await r.delete(key)

# Request with a cost of 3
result = await r.gcra(
key, max_burst=10, requests_per_period=5, period=10.0, num_requests=3
key, max_burst=10, tokens_per_period=5, period=10.0, tokens=3
)

assert isinstance(result, GCRAResponse)
Expand Down Expand Up @@ -5196,7 +5196,7 @@ async def test_gcra_rate_limiting(self, r: redis.Redis):
result = await r.gcra(
rate_limit_key,
max_burst=1,
requests_per_period=2,
tokens_per_period=2,
period=60.0,
)
assert isinstance(result, GCRAResponse)
Expand All @@ -5220,22 +5220,22 @@ async def test_gcra_rate_limiting(self, r: redis.Redis):
async def test_gcra_invalid_max_burst(self, r: redis.Redis):
"""Test GCRA command with invalid max_burst parameter"""
with pytest.raises(exceptions.DataError):
await r.gcra("test_key", max_burst=-1, requests_per_period=5, period=10.0)
await r.gcra("test_key", max_burst=-1, tokens_per_period=5, period=10.0)

async def test_gcra_invalid_requests_per_period(self, r: redis.Redis):
"""Test GCRA command with invalid requests_per_period parameter"""
async def test_gcra_invalid_tokens_per_period(self, r: redis.Redis):
"""Test GCRA command with invalid tokens_per_period parameter"""
with pytest.raises(exceptions.DataError):
await r.gcra("test_key", max_burst=10, requests_per_period=0, period=10.0)
await r.gcra("test_key", max_burst=10, tokens_per_period=0, period=10.0)

async def test_gcra_invalid_period_too_small(self, r: redis.Redis):
"""Test GCRA command with period less than 1.0"""
with pytest.raises(exceptions.DataError):
await r.gcra("test_key", max_burst=10, requests_per_period=5, period=0.5)
await r.gcra("test_key", max_burst=10, tokens_per_period=5, period=0.5)

async def test_gcra_invalid_period_too_large(self, r: redis.Redis):
"""Test GCRA command with period greater than 1e12"""
with pytest.raises(exceptions.DataError):
await r.gcra("test_key", max_burst=10, requests_per_period=5, period=1e13)
await r.gcra("test_key", max_burst=10, tokens_per_period=5, period=1e13)


@pytest.mark.onlynoncluster
Expand Down
26 changes: 12 additions & 14 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -7096,7 +7096,7 @@ def test_gcra_basic(self, r):
r.delete(key)

# First request should not be limited
result = r.gcra(key, max_burst=10, requests_per_period=5, period=10.0)
result = r.gcra(key, max_burst=10, tokens_per_period=5, period=10.0)

assert isinstance(result, GCRAResponse)

Expand All @@ -7112,15 +7112,13 @@ def test_gcra_basic(self, r):
assert result.full_burst_after >= 0

@skip_if_server_version_lt("8.7.0")
def test_gcra_with_num_requests(self, r):
"""Test GCRA command with NUM_REQUESTS option"""
key = "gcra_test_num_requests"
def test_gcra_with_tokens(self, r):
"""Test GCRA command with TOKENS option"""
key = "gcra_test_tokens"
r.delete(key)

# Request with a cost of 3
result = r.gcra(
key, max_burst=10, requests_per_period=5, period=10.0, num_requests=3
)
result = r.gcra(key, max_burst=10, tokens_per_period=5, period=10.0, tokens=3)

assert isinstance(result, GCRAResponse)
assert result.limited is False # Should not be limited initially
Expand Down Expand Up @@ -7148,7 +7146,7 @@ def test_gcra_rate_limiting(self, r):
result = r.gcra(
rate_limit_key,
max_burst=1,
requests_per_period=2,
tokens_per_period=2,
period=60.0,
)
assert isinstance(result, GCRAResponse)
Expand All @@ -7172,22 +7170,22 @@ def test_gcra_rate_limiting(self, r):
def test_gcra_invalid_max_burst(self, r):
"""Test GCRA command with invalid max_burst parameter"""
with pytest.raises(exceptions.DataError):
r.gcra("test_key", max_burst=-1, requests_per_period=5, period=10.0)
r.gcra("test_key", max_burst=-1, tokens_per_period=5, period=10.0)

def test_gcra_invalid_requests_per_period(self, r):
"""Test GCRA command with invalid requests_per_period parameter"""
def test_gcra_invalid_tokens_per_period(self, r):
"""Test GCRA command with invalid tokens_per_period parameter"""
with pytest.raises(exceptions.DataError):
r.gcra("test_key", max_burst=10, requests_per_period=0, period=10.0)
r.gcra("test_key", max_burst=10, tokens_per_period=0, period=10.0)

def test_gcra_invalid_period_too_small(self, r):
"""Test GCRA command with period less than 1.0"""
with pytest.raises(exceptions.DataError):
r.gcra("test_key", max_burst=10, requests_per_period=5, period=0.5)
r.gcra("test_key", max_burst=10, tokens_per_period=5, period=0.5)

def test_gcra_invalid_period_too_large(self, r):
"""Test GCRA command with period greater than 1e12"""
with pytest.raises(exceptions.DataError):
r.gcra("test_key", max_burst=10, requests_per_period=5, period=1e13)
r.gcra("test_key", max_burst=10, tokens_per_period=5, period=1e13)


@pytest.mark.onlynoncluster
Expand Down
Loading