Skip to content

Commit

Permalink
Merge pull request #483 from kytos-ng/feat/ignore_pace
Browse files Browse the repository at this point in the history
Pacing additions
  • Loading branch information
Ktmi committed Jul 12, 2024
2 parents 3c577f7 + de4f917 commit 76a3667
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 6 deletions.
60 changes: 58 additions & 2 deletions kytos/core/pacing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,53 @@
LOG = logging.getLogger(__name__)


class EmptyStrategy(limits.strategies.FixedWindowRateLimiter):
"""Rate limiter, that doesn't actually rate limit."""

def hit(
self,
item: RateLimitItem,
*identifiers: str,
cost: int = 1
) -> bool:
# Increment storage, to collect data on usage rate of actions
self.storage.incr(
item.key_for(*identifiers),
item.get_expiry(),
elastic_expiry=False,
amount=cost,
)
return True


class AsyncEmptyStrategy(limits.aio.strategies.FixedWindowRateLimiter):
"""Rate limiter, that doesn't actually rate limit."""

async def hit(
self,
item: RateLimitItem,
*identifiers: str,
cost: int = 1
) -> bool:
# Increment storage, to collect data on usage rate of actions
await self.storage.incr(
item.key_for(*identifiers),
item.get_expiry(),
elastic_expiry=False,
amount=cost,
)
return True


available_strategies = {
"fixed_window": (
limits.strategies.FixedWindowRateLimiter,
limits.aio.strategies.FixedWindowRateLimiter,
),
"ignore_pace": (
EmptyStrategy,
AsyncEmptyStrategy,
)
# "elastic_window": (
# limits.strategies.FixedWindowElasticExpiryRateLimiter,
# limits.aio.strategies.FixedWindowElasticExpiryRateLimiter,
Expand Down Expand Up @@ -97,7 +139,7 @@ async def ahit(self, action_name: str, *keys):
*identifiers
)
sleep_time = window_reset - time.time()

LOG.info(f'Limited reached: {identifiers}')
await asyncio.sleep(sleep_time)

def hit(self, action_name: str, *keys):
Expand All @@ -121,12 +163,18 @@ def hit(self, action_name: str, *keys):
*identifiers
)
sleep_time = window_reset - time.time()

LOG.info(f'Limited reached: {identifiers}')
if sleep_time <= 0:
continue

time.sleep(sleep_time)

def is_configured(self, action_name):
"""
Check if the given action has been configured.
"""
return action_name in self.pace_config


class PacerWrapper:
"""
Expand Down Expand Up @@ -175,5 +223,13 @@ async def ahit(self, action_name: str, *keys):
*keys
)

def is_configured(self, action_name: str):
"""
Check if the given action has been configured.
"""
return self.pacer.is_configured(
self._localized_key(action_name)
)

def _localized_key(self, key):
return f"{self.namespace}.{key}"
17 changes: 13 additions & 4 deletions tests/unit/test_core/test_pacing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def pacer(self) -> Pacer:
@pytest.fixture(
params=[
'fixed_window',
'ignore_pace',
]
)
def strategy(self, request):
Expand Down Expand Up @@ -47,9 +48,11 @@ def test_check_strategies(self, pacer: Pacer):
"""Check which strategies are present."""
assert set(pacer.sync_strategies) == {
'fixed_window',
'ignore_pace'
}
assert set(pacer.async_strategies) == {
'fixed_window',
'ignore_pace'
}

def test_missing_pace(self, pacer: Pacer):
Expand All @@ -70,7 +73,7 @@ async def test_async_existing_pace(self, configured_pacer: Pacer):
"""Test what happens when a pace is set"""
await configured_pacer.ahit("paced_action")

async def test_async_pace_limit(self, configured_pacer: Pacer):
async def test_async_pace_limit(self, strategy, configured_pacer: Pacer):
"""Test that actions are being properly paced"""
async def micro_task():
await configured_pacer.ahit("paced_action")
Expand All @@ -89,9 +92,12 @@ async def micro_task():

elapsed = end - start

assert elapsed > 1
if strategy != 'ignore_pace':
assert elapsed > 1
else:
assert elapsed < 1

def test_pace_limit(self, configured_pacer: Pacer):
def test_pace_limit(self, strategy, configured_pacer: Pacer):
"""Test that actions are being properly paced"""
actions_executed = 0

Expand All @@ -105,7 +111,10 @@ def test_pace_limit(self, configured_pacer: Pacer):

elapsed = end - start

assert elapsed > 1
if strategy != 'ignore_pace':
assert elapsed > 1
else:
assert elapsed < 1

def test_nonexistant_strategy(self, pacer: Pacer):
"""Make sure that nonexistant strategies raise an exception"""
Expand Down

0 comments on commit 76a3667

Please sign in to comment.