8000 Fix lambda destination default retries (#7933) · codeperl/localstack@2946514 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2946514

Browse files
Fix lambda destination default retries (localstack#7933)
1 parent 805b4a5 commit 2946514

File tree

5 files changed

+148
-13
lines changed

5 files changed

+148
-13
lines changed

localstack/services/awslambda/invocation/version_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,8 @@ def process_event_destinations(
576576
)
577577

578578
max_retry_attempts = event_invoke_config.maximum_retry_attempts
579+
if max_retry_attempts is None:
580+
max_retry_attempts = 2 # default
579581
previous_retry_attempts = queued_invocation.retries
580582

581583
if self.function.reserved_concurrent_executions == 0:

tests/integration/awslambda/test_lambda_api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,11 @@ def test_lambda_eventinvokeconfig_lifecycle(
17641764
)
17651765
snapshot.match("put_published_invokeconfig", put_published_invokeconfig)
17661766

1767+
get_published_invokeconfig = lambda_client.get_function_event_invoke_config(
1768+
FunctionName=function_name, Qualifier=publish_version_result["Version"]
1769+
)
1770+
snapshot.match("get_published_invokeconfig", get_published_invokeconfig)
1771+
17671772
# list paging
17681773
list_paging_single = lambda_client.list_function_event_invoke_configs(
17691774
FunctionName=function_name, MaxItems=1

tests/integration/awslambda/test_lambda_api.snapshot.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9906,7 +9906,7 @@
99069906
}
99079907
},
99089908
"tests/integration/awslambda/test_lambda_api.py::TestLambdaEventInvokeConfig::test_lambda_eventinvokeconfig_lifecycle": {
9909-
"recorded-date": "17-02-2023, 11:39:34",
9909+
"recorded-date": "22-03-2023, 18:49:13",
99109910
"recorded-content": {
99119911
"put_invokeconfig_retries_0": {
99129912
"DestinationConfig": {
@@ -10073,6 +10073,19 @@
1007310073
"HTTPStatusCode": 200
1007410074
}
1007510075
},
10076+
"get_published_invokeconfig": {
10077+
"DestinationConfig": {
10078+
"OnFailure": {},
10079+
"OnSuccess": {}
10080+
},
10081+
"FunctionArn": "arn:aws:lambda:<region>:111111111111:function:<function-name:1>:1",
10082+
"LastModified": "datetime",
10083+
"MaximumEventAgeInSeconds": 120,
10084+
"ResponseMetadata": {
10085+
"HTTPHeaders": {},
10086+
"HTTPStatusCode": 200
10087+
}
10088+
},
1007610089
"list_paging_nolimit_postdelete": {
1007710090
"FunctionEventInvokeConfigs": [
1007810091
{

tests/integration/awslambda/test_lambda_destinations.py

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from localstack import config
99
from localstack.aws.api.lambda_ import Runtime
10-
from localstack.testing.aws.lambda_utils import is_new_provider, is_old_provider
10+
from localstack.testing.aws.lambda_utils import is_old_provider
1111
from localstack.testing.aws.util import is_aws_cloud
1212
from localstack.utils.strings import short_uid, to_bytes, to_str
1313
from localstack.utils.sync import retry, wait_until
@@ -104,12 +104,6 @@ class TestLambdaDestinationSqs:
104104
"$..stackTrace",
105105
],
106106
)
107-
@pytest.mark.skip_snapshot_verify(
108-
condition=is_new_provider,
109-
paths=[
110-
"$..approximateInvokeCount", # TODO: retry support
111-
],
112-
)
113107
@pytest.mark.parametrize(
114108
"payload",
115109
[
@@ -142,12 +136,12 @@ def test_assess_lambda_destination_invocation(
142136
create_lambda_function(
143137
handler_file=TEST_LAMBDA_PYTHON,
144138
func_name 1E80 =lambda_name,
145-
libs=TEST_LAMBDA_LIBS,
146139
role=lambda_su_role,
147140
)
148141

149142
put_event_invoke_config_response = lambda_client.put_function_event_invoke_config(
150143
FunctionName=lambda_name,
144+
MaximumRetryAttempts=0,
151145
DestinationConfig={
152146
"OnSuccess": {"Destination": queue_arn},
153147
"OnFailure": {"Destination": queue_arn},
@@ -162,10 +156,70 @@ def test_assess_lambda_destination_invocation(
162156
)
163157

164158
def receive_message():
165-
rs = sqs_client.receive_message(QueueUrl=queue_url, MessageAttributeNames=["All"])
159+
rs = sqs_client.receive_message(
160+
QueueUrl=queue_url, WaitTimeSeconds=2, MessageAttributeNames=["All"]
161+
)
162+
assert len(rs["Messages"]) > 0
163+
return rs
164+
165+
receive_message_result = retry(receive_message, retries=120, sleep=1)
166+
snapshot.match("receive_message_result", receive_message_result)
167+
168+
@pytest.mark.skipif(
169+
condition=is_old_provider(), reason="config variable only supported in new provider"
170+
)
171+
def test_lambda_destination_default_retries(
172+
self,
173+
lambda_client,
174+
sqs_client,
175+
create_lambda_function,
176+
sqs_create_queue,
177+
sqs_queue_arn,
178+
lambda_su_role,
179+
snapshot,
180+
monkeypatch,
181+
):
182+
snapshot.add_transformer(snapshot.transform.lambda_api())
183+
snapshot.add_transformer(snapshot.transform.sqs_api())
184+
snapshot.add_transformer(snapshot.transform.key_value("MD5OfBody"))
185+
186+
if not is_aws_cloud():
187+
monkeypatch.setattr(config, "LAMBDA_RETRY_BASE_DELAY_SECONDS", 5)
188+
189+
# create DLQ and Lambda function
190+
queue_name = f"test-{short_uid()}"
191+
lambda_name = f"test-{short_uid()}"
192+
queue_url = sqs_create_queue(QueueName=queue_name)
193+
queue_arn = sqs_queue_arn(queue_url)
194+
create_lambda_function(
195+
handler_file=TEST_LAMBDA_PYTHON,
196+
func_name=lambda_name,
197+
role=lambda_su_role,
198+
)
199+
200+
put_event_invoke_config_response = lambda_client.put_function_event_invoke_config(
201+
FunctionName=lambda_name,
202+
DestinationConfig={
203+
"OnSuccess": {"Destination": queue_arn},
204+
"OnFailure": {"Destination": queue_arn},
205+
},
206+
)
207+
snapshot.match("put_function_event_invoke_config", put_event_invoke_config_response)
208+
209+
lambda_client.invoke(
210+
FunctionName=lambda_name,
211+
Payload=json.dumps({lambda_integration.MSG_BODY_RAISE_ERROR_FLAG: 1}),
212+
InvocationType="Event",
213+
)
214+
215+
def receive_message():
216+
rs = sqs_client.receive_message(
217+
QueueUrl=queue_url, WaitTimeSeconds=2, MessageAttributeNames=["All"]
218+
)
166219
assert len(rs["Messages"]) > 0
167220
return rs
168221

222+
# this will take at least 3 minutes on AWS
169223
receive_message_result = retry(receive_message, retries=120, sleep=3)
170224
snapshot.match("receive_message_result", receive_message_result)
171225

tests/integration/awslambda/test_lambda_destinations.snapshot.json

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
}
122122
},
123123
"tests/integration/awslambda/test_lambda_destinations.py::TestLambdaDestinationSqs::test_assess_lambda_destination_invocation[payload0]": {
124-
"recorded-date": "27-02-2023, 16:02:33",
124+
"recorded-date": "22-03-2023, 18:42:11",
125125
"recorded-content": {
126126
"put_function_event_invoke_config": {
127127
"DestinationConfig": {
@@ -134,6 +134,7 @@
134134
},
135135
"FunctionArn": "arn:aws:lambda:<region>:111111111111:function:<resource:2>:$LATEST",
136136
"LastModified": "datetime",
137+
"MaximumRetryAttempts": 0,
137138
"ResponseMetadata": {
138139
"HTTPHeaders": {},
139140
"HTTPStatusCode": 200
@@ -183,7 +184,7 @@
183184
}
184185
},
185186
"tests/integration/awslambda/test_lambda_destinations.py::TestLambdaDestinationSqs::test_assess_lambda_destination_invocation[payload1]": {
186-
"recorded-date": "27-02-2023, 16:08:45",
187+
"recorded-date": "22-03-2023, 18:42:16",
187188
"recorded-content": {
188189
"put_function_event_invoke_config": {
189190
"DestinationConfig": {
@@ -196,6 +197,7 @@
196197
},
197198
"FunctionArn": "arn:aws:lambda:<region>:111111111111:function:<resource:2>:$LATEST",
198199
"LastModified": "datetime",
200+
"MaximumRetryAttempts": 0,
199201
"ResponseMetadata": {
200202
"HTTPHeaders": {},
201203
"HTTPStatusCode": 200
@@ -211,7 +213,7 @@
211213
"requestId": "<uuid:1>",
212214
"functionArn": "arn:aws:lambda:<region>:111111111111:function:<resource:2>:$LATEST",
213215
"condition": "RetriesExhausted",
214-
"approximateInvokeCount": 3
216+
"approximateInvokeCount": 1
215217
},
216218
"requestPayload": {
217219
"raise_error": 1
@@ -525,5 +527,64 @@
525527
"ReceiptHandle": "<receipt-handle:2>"
526528
}
527529
}
530+
},
531+
"tests/integration/awslambda/test_lambda_destinations.py::TestLambdaDestinationSqs::test_lambda_destination_default_retries": {
532+
"recorded-date": "22-03-2023, 19:02:29",
533+
"recorded-content": {
534+
"put_function_event_invoke_config": {
535+
"DestinationConfig": {
536+
"OnFailure": {
537+
"Destination": "arn:aws:sqs:<region>:111111111111:<resource:1>"
538+
},
539+
"OnSuccess": {
540+
"Destination": "arn:aws:sqs:<region>:111111111111:<resource:1>"
541+
}
542+
},
543+
"FunctionArn": "arn:aws:lambda:<region>:111111111111:function:<resource:2>:$LATEST",
544+
"LastModified": "datetime",
545+
"ResponseMetadata": {
546+
"HTTPHeaders": {},
547+
"HTTPStatusCode": 200
548+
}
549+
},
550+
"receive_message_result": {
551+
"Messages": [
552+
{
553+
"Body": {
554+
"version": "1.0",
555+
"timestamp": "date",
556+
"requestContext": {
557+
"requestId": "<uuid:1>",
558+
"functionArn": "arn:aws:lambda:<region>:111111111111:function:<resource:2>:$LATEST",
559+
"condition": "RetriesExhausted",
560+
"approximateInvokeCount": 3
561+
},
562+
"requestPayload": {
563+
"raise_error": 1
564+
},
565+
"responseContext": {
566+
"statusCode": 200,
567+
"executedVersion": "$LATEST",
568+
"functionError": "Unhandled"
569+
},
570+
"responsePayload": {
571+
"errorMessage": "Test exception (this is intentional)",
572+
"errorType": "Exception",
573+
"stackTrace": [
574+
" File \"/var/task/handler.py\", line 54, in handler\n raise Exception(\"Test exception (this is intentional)\")\n"
575+
]
576+
}
577+
},
578+
"MD5OfBody": "<m-d5-of-body:1>",
579+
"MessageId": "<uuid:2>",
580+
"ReceiptHandle": "<receipt-handle:1>"
581+
}
582+
],
583+
"ResponseMetadata": {
584+
"HTTPHeaders": {},
585+
"HTTPStatusCode": 200
586+
}
587+
}
588+
}
528589
}
529590
}

0 commit comments

Comments
 (0)
0