From a239c7ed803d3b991e790221a015ecbddb389b6e Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Mon, 1 Jul 2024 21:25:07 +0000 Subject: [PATCH 1/4] Add new pacer strategy, ignore pace --- kytos/core/pacing.py | 40 +++++++++++++++++++++++++++++ tests/unit/test_core/test_pacing.py | 17 +++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/kytos/core/pacing.py b/kytos/core/pacing.py index 15cce75f..e1c2c002 100644 --- a/kytos/core/pacing.py +++ b/kytos/core/pacing.py @@ -11,11 +11,51 @@ 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: + 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: + 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, diff --git a/tests/unit/test_core/test_pacing.py b/tests/unit/test_core/test_pacing.py index a538bf3a..a8c04159 100644 --- a/tests/unit/test_core/test_pacing.py +++ b/tests/unit/test_core/test_pacing.py @@ -19,6 +19,7 @@ def pacer(self) -> Pacer: @pytest.fixture( params=[ 'fixed_window', + 'ignore_pace', ] ) def strategy(self, request): @@ -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): @@ -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") @@ -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 @@ -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""" From 239d6de7ee47a71ba8529165f6d5a5257e1a479e Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Wed, 3 Jul 2024 20:34:14 +0000 Subject: [PATCH 2/4] Added some introspection on configured paces. --- kytos/core/pacing.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kytos/core/pacing.py b/kytos/core/pacing.py index e1c2c002..3f21ec3f 100644 --- a/kytos/core/pacing.py +++ b/kytos/core/pacing.py @@ -167,6 +167,11 @@ def hit(self, action_name: str, *keys): 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: """ @@ -215,5 +220,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}" From fbd451a0bbf54d4bdaab22296b67267c8df0cecc Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Mon, 8 Jul 2024 16:37:11 +0000 Subject: [PATCH 3/4] Linter cleanup --- kytos/core/pacing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kytos/core/pacing.py b/kytos/core/pacing.py index 3f21ec3f..4d417258 100644 --- a/kytos/core/pacing.py +++ b/kytos/core/pacing.py @@ -173,6 +173,7 @@ def is_configured(self, action_name): """ return action_name in self.pace_config + class PacerWrapper: """ Applies a namespace to various operations related to pacing. From de4f9174dbc7689ddd06df1a2597c92443746a66 Mon Sep 17 00:00:00 2001 From: David Ramirez Date: Fri, 12 Jul 2024 16:40:20 +0000 Subject: [PATCH 4/4] Add in comment about Empty strategies incrementing storage counters --- kytos/core/pacing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kytos/core/pacing.py b/kytos/core/pacing.py index 4d417258..124b0fc7 100644 --- a/kytos/core/pacing.py +++ b/kytos/core/pacing.py @@ -20,6 +20,7 @@ def hit( *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(), @@ -38,6 +39,7 @@ async def hit( *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(), @@ -137,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): @@ -161,7 +163,7 @@ 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