Skip to content

Commit 13d85d1

Browse files
authored
Lambda DevX: Bypass Concurrency Settings (#11418)
1 parent fdd9efb commit 13d85d1

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

localstack-core/localstack/services/lambda_/invocation/assignment.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
InitializationType,
1616
OtherServiceEndpoint,
1717
)
18+
from localstack.utils.lambda_debug_mode.lambda_debug_mode import is_lambda_debug_enabled_for
1819

1920
LOG = logging.getLogger(__name__)
2021

@@ -134,6 +135,21 @@ def scale_provisioned_concurrency(
134135
function_version: FunctionVersion,
135136
target_provisioned_environments: int,
136137
) -> list[Future[None]]:
138+
# Enforce a single environment per lambda version if this is a target
139+
# of an active Lambda Debug Mode.
140+
qualified_lambda_version_arn = function_version.qualified_arn
141+
if (
142+
is_lambda_debug_enabled_for(qualified_lambda_version_arn)
143+
and target_provisioned_environments > 0
144+
):
145+
LOG.warning(
146+
"Environments for '%s' enforced to '1' by Lambda Debug Mode, "
147+
"configurations will continue to report the set value '%s'",
148+
qualified_lambda_version_arn,
149+
target_provisioned_environments,
150+
)
151+
target_provisioned_environments = 1
152+
137153
current_provisioned_environments = [
138154
e
139155
for e in self.environments[version_manager_id].values()

localstack-core/localstack/services/lambda_/invocation/counting_service.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
InitializationType,
1212
)
1313
from localstack.services.lambda_.invocation.models import lambda_stores
14+
from localstack.utils.lambda_debug_mode.lambda_debug_mode import (
15+
is_lambda_debug_enabled_for,
16+
)
1417

1518
LOG = logging.getLogger(__name__)
1619

@@ -125,6 +128,24 @@ def get_invocation_lease(
125128
unqualified_function_arn = function_version.id.unqualified_arn()
126129
qualified_arn = function_version.id.qualified_arn()
127130

131+
# Enforce one lease per ARN if the global flag is set
132+
if is_lambda_debug_enabled_for(qualified_arn):
133+
with provisioned_tracker.lock, on_demand_tracker.lock:
134+
on_demand_executions: int = on_demand_tracker.concurrent_executions[
135+
unqualified_function_arn
136+
]
137+
provisioned_executions = provisioned_tracker.concurrent_executions[qualified_arn]
138+
if on_demand_executions or provisioned_executions:
139+
LOG.warning(
140+
"Concurrent lambda invocations disabled for '%s' by Lambda Debug Mode",
141+
qualified_arn,
142+
)
143+
raise TooManyRequestsException(
144+
"Rate Exceeded.",
145+
Reason="SingleLeaseEnforcement",
146+
Type="User",
147+
)
148+
128149
lease_type = None
129150
with provisioned_tracker.lock:
130151
# 1) Check for free provisioned concurrency

localstack-core/localstack/utils/lambda_debug_mode/lambda_debug_mode.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Optional
22

33
from localstack.aws.api.lambda_ import Arn
4+
from localstack.utils.lambda_debug_mode.lambda_debug_mode_config import LambdaDebugModeConfig
45
from localstack.utils.lambda_debug_mode.lambda_debug_mode_session import LambdaDebugModeSession
56

67
# Specifies the fault timeout value in seconds to be used by time restricted workflows when
@@ -13,19 +14,28 @@ def is_lambda_debug_mode() -> bool:
1314
return LambdaDebugModeSession.get().is_lambda_debug_mode()
1415

1516

16-
def lambda_debug_port_for(lambda_arn: Arn) -> Optional[int]:
17+
def _lambda_debug_config_for(lambda_arn: Arn) -> Optional[LambdaDebugModeConfig]:
1718
if not is_lambda_debug_mode():
1819
return None
1920
debug_configuration = LambdaDebugModeSession.get().debug_config_for(lambda_arn=lambda_arn)
21+
return debug_configuration
22+
23+
24+
def is_lambda_debug_enabled_for(lambda_arn: Arn) -> bool:
25+
"""Returns True if the given lambda arn is subject of an active debugging configuration; False otherwise."""
26+
debug_configuration = _lambda_debug_config_for(lambda_arn=lambda_arn)
27+
return debug_configuration is not None
28+
29+
30+
def lambda_debug_port_for(lambda_arn: Arn) -> Optional[int]:
31+
debug_configuration = _lambda_debug_config_for(lambda_arn=lambda_arn)
2032
if debug_configuration is None:
2133
return None
2234
return debug_configuration.debug_port
2335

2436

2537
def is_lambda_debug_timeout_enabled_for(lambda_arn: Arn) -> bool:
26-
if not is_lambda_debug_mode():
27-
return False
28-
debug_configuration = LambdaDebugModeSession.get().debug_config_for(lambda_arn=lambda_arn)
38+
debug_configuration = _lambda_debug_config_for(lambda_arn=lambda_arn)
2939
if debug_configuration is None:
3040
return False
3141
return not debug_configuration.enforce_timeouts

0 commit comments

Comments
 (0)