From 119abb7fa1b89b4d6b06fb33f4091985ab33b369 Mon Sep 17 00:00:00 2001 From: Victoria Hall Date: Tue, 17 Mar 2026 15:21:22 -0500 Subject: [PATCH 1/7] fix @retry issue --- runtimes/v2/azure_functions_runtime/loader.py | 27 ++---- .../eventhub_retry_stein/function_app.py | 94 +++++++++++++++++++ .../emulator_tests/test_eventhub_functions.py | 51 ++++++++++ .../emulator_tests/utils/eventhub/config.json | 9 ++ workers/tests/unittests/test_loader.py | 16 ++++ 5 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py diff --git a/runtimes/v2/azure_functions_runtime/loader.py b/runtimes/v2/azure_functions_runtime/loader.py index d2edc0466..3f5b54e68 100644 --- a/runtimes/v2/azure_functions_runtime/loader.py +++ b/runtimes/v2/azure_functions_runtime/loader.py @@ -91,23 +91,16 @@ def build_fixed_delay_retry(protos, retry, max_retry_count, retry_strategy): def build_variable_interval_retry(protos, retry, max_retry_count, retry_strategy): - try: - from google.protobuf.duration_pb2 import Duration - except ImportError: - raise ImportError( - "protobuf not found when trying to " - "import Duration." - "Sys Path: %s. " - "Sys Modules: %s. ", - sys.path, sys.modules) - minimum_interval = Duration( - seconds=convert_to_seconds( - retry.get(RetryPolicy.MINIMUM_INTERVAL.value)) - ) - maximum_interval = Duration( - seconds=convert_to_seconds( - retry.get(RetryPolicy.MAXIMUM_INTERVAL.value)) - ) + # Get minimum_interval with default of 00:00:00 (0 seconds) + min_interval_str = retry.get(RetryPolicy.MINIMUM_INTERVAL.value) + min_seconds = convert_to_seconds(min_interval_str) if min_interval_str else 0 + minimum_interval = timedelta(seconds=min_seconds) + + # Get maximum_interval with default of TimeSpan.MaxValue equivalent (max int32 seconds) + max_interval_str = retry.get(RetryPolicy.MAXIMUM_INTERVAL.value) + max_seconds = convert_to_seconds(max_interval_str) if max_interval_str else 2147483647 + maximum_interval = timedelta(seconds=max_seconds) + return protos.RpcRetryOptions( max_retry_count=max_retry_count, retry_strategy=retry_strategy, diff --git a/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py b/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py new file mode 100644 index 000000000..f49b2e8d0 --- /dev/null +++ b/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py @@ -0,0 +1,94 @@ +import json +import logging + +import azure.functions as func + +app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) + +# Global counter to track retry attempts +retry_attempts = {} + + +# An HttpTrigger to generate EventHub event from EventHub Output Binding +@app.function_name(name="eventhub_retry_output") +@app.route(route="eventhub_retry_output") +@app.event_hub_output(arg_name="event", + event_hub_name="python-worker-ci-eventhub-retry", + connection="AzureWebJobsEventHubConnectionString") +def eventhub_retry_output(req: func.HttpRequest, event: func.Out[str]): + event.set(req.get_body().decode('utf-8')) + return 'OK' + + +# EventHub trigger with exponential backoff retry policy (no explicit intervals) +@app.function_name(name="eventhub_retry_trigger") +@app.retry( + strategy="exponential_backoff", + max_retry_count="3" + # Note: minimum_interval and maximum_interval are optional + # and will use default values if not specified +) +@app.event_hub_message_trigger( + arg_name="event", + event_hub_name="python-worker-ci-eventhub-retry", + connection="AzureWebJobsEventHubConnectionString" +) +@app.blob_output(arg_name="$return", + path="python-worker-tests/test-eventhub-retry-triggered.txt", + connection="AzureWebJobsStorage") +def eventhub_retry_trigger(event: func.EventHubEvent, context: func.Context) -> bytes: + event_id = event.get_body().decode('utf-8') + retry_count = context.retry_context.retry_count if context.retry_context else 0 + max_retry = context.retry_context.max_retry_count if context.retry_context else 0 + + logging.info(f'EventHub retry trigger processed event: {event_id}, ' + f'retry count: {retry_count}, max retry: {max_retry}') + + # Track retry attempts + if event_id not in retry_attempts: + retry_attempts[event_id] = [] + retry_attempts[event_id].append(retry_count) + + # Create result dictionary + result = { + 'event_id': event_id, + 'retry_count': retry_count, + 'max_retry_count': max_retry, + 'all_attempts': retry_attempts[event_id] + } + + # Fail on first two attempts to test retry + if retry_count < 2: + logging.warning(f'Simulating failure for retry test (attempt {retry_count})') + raise Exception(f"Simulated failure for retry testing (attempt {retry_count})") + + # Success on third attempt + logging.info(f'Success on attempt {retry_count}') + return json.dumps(result).encode('utf-8') + + +# Retrieve the event data from storage blob and return it as Http response +@app.function_name(name="get_eventhub_retry_triggered") +@app.route(route="get_eventhub_retry_triggered") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-eventhub-retry-triggered.txt", + connection="AzureWebJobsStorage") +def get_eventhub_retry_triggered(req: func.HttpRequest, + file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +# HTTP endpoint to check retry state (for testing) +@app.function_name(name="get_retry_state") +@app.route(route="get_retry_state") +def get_retry_state(req: func.HttpRequest) -> str: + return json.dumps(retry_attempts) + + +# HTTP endpoint to reset retry state +@app.function_name(name="reset_retry_state") +@app.route(route="reset_retry_state") +def reset_retry_state(req: func.HttpRequest) -> str: + global retry_attempts + retry_attempts = {} + return 'Reset complete' diff --git a/workers/tests/emulator_tests/test_eventhub_functions.py b/workers/tests/emulator_tests/test_eventhub_functions.py index d6559e676..d82e88f04 100644 --- a/workers/tests/emulator_tests/test_eventhub_functions.py +++ b/workers/tests/emulator_tests/test_eventhub_functions.py @@ -120,6 +120,57 @@ def get_script_dir(cls): 'eventhub_functions_stein' / 'generic' +class TestEventHubRetryStein(testutils.WebHostTestCase): + """Test EventHub Trigger with Retry Policy (exponential backoff without explicit intervals).""" + + @classmethod + def get_script_dir(cls): + return testutils.EMULATOR_TESTS_FOLDER / 'eventhub_functions' / \ + 'eventhub_retry_stein' + + @classmethod + def get_libraries_to_install(cls): + return ['azure-eventhub'] + + @testutils.retryable_test(3, 5) + def test_eventhub_retry_trigger_with_default_intervals(self): + """Test that exponential backoff retry works without explicit min/max intervals.""" + # Generate a unique event ID + event_id = f"retry-test-{round(time.time())}" + doc = {'id': event_id} + + # Reset retry state + r = self.webhost.request('POST', 'reset_retry_state') + self.assertEqual(r.status_code, 200) + + # Send event to EventHub + r = self.webhost.request('POST', 'eventhub_retry_output', + data=json.dumps(doc)) + self.assertEqual(r.status_code, 200) + self.assertEqual(r.text, 'OK') + + # Wait for retries to complete (with exponential backoff) + # First attempt: immediate + # Second attempt: after backoff + # Third attempt: after longer backoff + time.sleep(15) + + # Retrieve the result from blob storage + r = self.webhost.request('GET', 'get_eventhub_retry_triggered') + self.assertEqual(r.status_code, 200) + + result = json.loads(r.text) + + # Verify the event was processed after retries + self.assertEqual(result['event_id'], event_id) + self.assertEqual(result['retry_count'], 2) # Should succeed on third attempt (count 2) + self.assertEqual(result['max_retry_count'], 3) + + # Verify all retry attempts were tracked + self.assertEqual(len(result['all_attempts']), 3) # 0, 1, 2 + self.assertEqual(result['all_attempts'], [0, 1, 2]) + + @skipIf(sys.version_info.minor >= 14, "Skip to figure out uamqp.") class TestEventHubFunctionsSDK(TestEventHubFunctions): diff --git a/workers/tests/emulator_tests/utils/eventhub/config.json b/workers/tests/emulator_tests/utils/eventhub/config.json index ba14a13bb..2cf43126c 100644 --- a/workers/tests/emulator_tests/utils/eventhub/config.json +++ b/workers/tests/emulator_tests/utils/eventhub/config.json @@ -58,6 +58,15 @@ "Name": "cg1" } ] + }, + { + "Name": "python-worker-ci-eventhub-retry", + "PartitionCount": 2, + "ConsumerGroups": [ + { + "Name": "cg1" + } + ] } ] } diff --git a/workers/tests/unittests/test_loader.py b/workers/tests/unittests/test_loader.py index a6af8faa5..ef8ac15c1 100644 --- a/workers/tests/unittests/test_loader.py +++ b/workers/tests/unittests/test_loader.py @@ -66,6 +66,22 @@ def test_loader_building_exponential_retry_protos(self): self.assertEqual(protos.minimum_interval.seconds, 60) self.assertEqual(protos.maximum_interval.seconds, 120) + def test_loader_building_exponential_retry_protos_with_defaults(self): + """Test exponential backoff without explicit min/max intervals (uses defaults)""" + trigger = TimerTrigger(schedule="*/1 * * * * *", arg_name="mytimer", + name="mytimer") + self.func.add_trigger(trigger=trigger) + setting = RetryPolicy(strategy="exponential_backoff", + max_retry_count="3") + self.func.add_setting(setting=setting) + + protos = build_retry_protos(self.func) + self.assertEqual(protos.max_retry_count, 3) + self.assertEqual(protos.retry_strategy, + 0) # 0 enum for exponential backoff + self.assertEqual(protos.minimum_interval.seconds, 0) # Default: 0 seconds + self.assertEqual(protos.maximum_interval.seconds, 2147483647) # Default: max int32 + @patch('azure_functions_worker.logging.logger.warning') def test_loader_retry_policy_attribute_error(self, mock_logger): self.func = Mock() From f40a7ca1f8e4ef58124c98554a4e56e79cb0a490 Mon Sep 17 00:00:00 2001 From: Victoria Hall Date: Mon, 23 Mar 2026 15:54:23 -0500 Subject: [PATCH 2/7] temp --- runtimes/v2/azure_functions_runtime/loader.py | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/runtimes/v2/azure_functions_runtime/loader.py b/runtimes/v2/azure_functions_runtime/loader.py index 3f5b54e68..20addcfec 100644 --- a/runtimes/v2/azure_functions_runtime/loader.py +++ b/runtimes/v2/azure_functions_runtime/loader.py @@ -71,16 +71,8 @@ def get_retry_settings(indexed_function): def build_fixed_delay_retry(protos, retry, max_retry_count, retry_strategy): - try: - from google.protobuf.duration_pb2 import Duration - except ImportError: - raise ImportError( - "protobuf not found when trying to " - "import Duration." - "Sys Path: %s. " - "Sys Modules: %s. ", - sys.path, sys.modules) - delay_interval = Duration( + # In protobuf 5.x, Duration fields expect timedelta objects, not Duration objects + delay_interval = timedelta( seconds=convert_to_seconds(retry.get(RetryPolicy.DELAY_INTERVAL.value)) ) return protos.RpcRetryOptions( @@ -91,15 +83,15 @@ def build_fixed_delay_retry(protos, retry, max_retry_count, retry_strategy): def build_variable_interval_retry(protos, retry, max_retry_count, retry_strategy): - # Get minimum_interval with default of 00:00:00 (0 seconds) - min_interval_str = retry.get(RetryPolicy.MINIMUM_INTERVAL.value) - min_seconds = convert_to_seconds(min_interval_str) if min_interval_str else 0 - minimum_interval = timedelta(seconds=min_seconds) - - # Get maximum_interval with default of TimeSpan.MaxValue equivalent (max int32 seconds) - max_interval_str = retry.get(RetryPolicy.MAXIMUM_INTERVAL.value) - max_seconds = convert_to_seconds(max_interval_str) if max_interval_str else 2147483647 - maximum_interval = timedelta(seconds=max_seconds) + # In protobuf 5.x, Duration fields expect timedelta objects, not Duration objects + minimum_interval = timedelta( + seconds=convert_to_seconds( + retry.get(RetryPolicy.MINIMUM_INTERVAL.value)) + ) + maximum_interval = timedelta( + seconds=convert_to_seconds( + retry.get(RetryPolicy.MAXIMUM_INTERVAL.value)) + ) return protos.RpcRetryOptions( max_retry_count=max_retry_count, From e22c89a27bb586e957811b2cf28800411eb2ebcb Mon Sep 17 00:00:00 2001 From: Victoria Hall Date: Mon, 23 Mar 2026 16:09:14 -0500 Subject: [PATCH 3/7] fix default values --- runtimes/v2/azure_functions_runtime/loader.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/runtimes/v2/azure_functions_runtime/loader.py b/runtimes/v2/azure_functions_runtime/loader.py index 20addcfec..646266262 100644 --- a/runtimes/v2/azure_functions_runtime/loader.py +++ b/runtimes/v2/azure_functions_runtime/loader.py @@ -84,14 +84,19 @@ def build_fixed_delay_retry(protos, retry, max_retry_count, retry_strategy): def build_variable_interval_retry(protos, retry, max_retry_count, retry_strategy): # In protobuf 5.x, Duration fields expect timedelta objects, not Duration objects - minimum_interval = timedelta( - seconds=convert_to_seconds( - retry.get(RetryPolicy.MINIMUM_INTERVAL.value)) - ) - maximum_interval = timedelta( - seconds=convert_to_seconds( - retry.get(RetryPolicy.MAXIMUM_INTERVAL.value)) - ) + # Handle optional minimum_interval and maximum_interval with defaults + minimum_interval_str = retry.get(RetryPolicy.MINIMUM_INTERVAL.value) + maximum_interval_str = retry.get(RetryPolicy.MAXIMUM_INTERVAL.value) + + if minimum_interval_str: + minimum_interval = timedelta(seconds=convert_to_seconds(minimum_interval_str)) + else: + minimum_interval = timedelta(seconds=0) # Default: 0 seconds + + if maximum_interval_str: + maximum_interval = timedelta(seconds=convert_to_seconds(maximum_interval_str)) + else: + maximum_interval = timedelta(seconds=2147483647) # Default: max int32 return protos.RpcRetryOptions( max_retry_count=max_retry_count, From 0f9974db0b24a8827d371d1e997b3b970a073dad Mon Sep 17 00:00:00 2001 From: Victoria Hall Date: Mon, 23 Mar 2026 16:11:09 -0500 Subject: [PATCH 4/7] lint --- runtimes/v2/azure_functions_runtime/loader.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtimes/v2/azure_functions_runtime/loader.py b/runtimes/v2/azure_functions_runtime/loader.py index 646266262..abbafaa7c 100644 --- a/runtimes/v2/azure_functions_runtime/loader.py +++ b/runtimes/v2/azure_functions_runtime/loader.py @@ -87,17 +87,17 @@ def build_variable_interval_retry(protos, retry, max_retry_count, retry_strategy # Handle optional minimum_interval and maximum_interval with defaults minimum_interval_str = retry.get(RetryPolicy.MINIMUM_INTERVAL.value) maximum_interval_str = retry.get(RetryPolicy.MAXIMUM_INTERVAL.value) - + if minimum_interval_str: minimum_interval = timedelta(seconds=convert_to_seconds(minimum_interval_str)) else: minimum_interval = timedelta(seconds=0) # Default: 0 seconds - + if maximum_interval_str: maximum_interval = timedelta(seconds=convert_to_seconds(maximum_interval_str)) else: maximum_interval = timedelta(seconds=2147483647) # Default: max int32 - + return protos.RpcRetryOptions( max_retry_count=max_retry_count, retry_strategy=retry_strategy, From 8e94f9c97869506eb14296fc212828d117dcdbe7 Mon Sep 17 00:00:00 2001 From: Victoria Hall Date: Mon, 23 Mar 2026 17:07:32 -0500 Subject: [PATCH 5/7] skip indexing retry fx --- .../eventhub_retry_stein/function_app.py | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py b/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py index f49b2e8d0..76b48da9c 100644 --- a/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py +++ b/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py @@ -21,50 +21,50 @@ def eventhub_retry_output(req: func.HttpRequest, event: func.Out[str]): # EventHub trigger with exponential backoff retry policy (no explicit intervals) -@app.function_name(name="eventhub_retry_trigger") -@app.retry( - strategy="exponential_backoff", - max_retry_count="3" - # Note: minimum_interval and maximum_interval are optional - # and will use default values if not specified -) -@app.event_hub_message_trigger( - arg_name="event", - event_hub_name="python-worker-ci-eventhub-retry", - connection="AzureWebJobsEventHubConnectionString" -) -@app.blob_output(arg_name="$return", - path="python-worker-tests/test-eventhub-retry-triggered.txt", - connection="AzureWebJobsStorage") -def eventhub_retry_trigger(event: func.EventHubEvent, context: func.Context) -> bytes: - event_id = event.get_body().decode('utf-8') - retry_count = context.retry_context.retry_count if context.retry_context else 0 - max_retry = context.retry_context.max_retry_count if context.retry_context else 0 +# @app.function_name(name="eventhub_retry_trigger") +# @app.retry( +# strategy="exponential_backoff", +# max_retry_count="3" +# # Note: minimum_interval and maximum_interval are optional +# # and will use default values if not specified +# ) +# @app.event_hub_message_trigger( +# arg_name="event", +# event_hub_name="python-worker-ci-eventhub-retry", +# connection="AzureWebJobsEventHubConnectionString" +# ) +# @app.blob_output(arg_name="$return", +# path="python-worker-tests/test-eventhub-retry-triggered.txt", +# connection="AzureWebJobsStorage") +# def eventhub_retry_trigger(event: func.EventHubEvent, context: func.Context) -> bytes: +# event_id = event.get_body().decode('utf-8') +# retry_count = context.retry_context.retry_count if context.retry_context else 0 +# max_retry = context.retry_context.max_retry_count if context.retry_context else 0 - logging.info(f'EventHub retry trigger processed event: {event_id}, ' - f'retry count: {retry_count}, max retry: {max_retry}') +# logging.info(f'EventHub retry trigger processed event: {event_id}, ' +# f'retry count: {retry_count}, max retry: {max_retry}') - # Track retry attempts - if event_id not in retry_attempts: - retry_attempts[event_id] = [] - retry_attempts[event_id].append(retry_count) +# # Track retry attempts +# if event_id not in retry_attempts: +# retry_attempts[event_id] = [] +# retry_attempts[event_id].append(retry_count) - # Create result dictionary - result = { - 'event_id': event_id, - 'retry_count': retry_count, - 'max_retry_count': max_retry, - 'all_attempts': retry_attempts[event_id] - } +# # Create result dictionary +# result = { +# 'event_id': event_id, +# 'retry_count': retry_count, +# 'max_retry_count': max_retry, +# 'all_attempts': retry_attempts[event_id] +# } - # Fail on first two attempts to test retry - if retry_count < 2: - logging.warning(f'Simulating failure for retry test (attempt {retry_count})') - raise Exception(f"Simulated failure for retry testing (attempt {retry_count})") +# # Fail on first two attempts to test retry +# if retry_count < 2: +# logging.warning(f'Simulating failure for retry test (attempt {retry_count})') +# raise Exception(f"Simulated failure for retry testing (attempt {retry_count})") - # Success on third attempt - logging.info(f'Success on attempt {retry_count}') - return json.dumps(result).encode('utf-8') +# # Success on third attempt +# logging.info(f'Success on attempt {retry_count}') +# return json.dumps(result).encode('utf-8') # Retrieve the event data from storage blob and return it as Http response From 2812f38fdc88d5658d5cc61c88317618be62fcf7 Mon Sep 17 00:00:00 2001 From: Victoria Hall Date: Mon, 23 Mar 2026 17:55:20 -0500 Subject: [PATCH 6/7] ??? --- .../eventhub_retry_stein/function_app.py | 85 +++++++++---------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py b/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py index 76b48da9c..bd3ab1ce6 100644 --- a/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py +++ b/workers/tests/emulator_tests/eventhub_functions/eventhub_retry_stein/function_app.py @@ -21,50 +21,47 @@ def eventhub_retry_output(req: func.HttpRequest, event: func.Out[str]): # EventHub trigger with exponential backoff retry policy (no explicit intervals) -# @app.function_name(name="eventhub_retry_trigger") -# @app.retry( -# strategy="exponential_backoff", -# max_retry_count="3" -# # Note: minimum_interval and maximum_interval are optional -# # and will use default values if not specified -# ) -# @app.event_hub_message_trigger( -# arg_name="event", -# event_hub_name="python-worker-ci-eventhub-retry", -# connection="AzureWebJobsEventHubConnectionString" -# ) -# @app.blob_output(arg_name="$return", -# path="python-worker-tests/test-eventhub-retry-triggered.txt", -# connection="AzureWebJobsStorage") -# def eventhub_retry_trigger(event: func.EventHubEvent, context: func.Context) -> bytes: -# event_id = event.get_body().decode('utf-8') -# retry_count = context.retry_context.retry_count if context.retry_context else 0 -# max_retry = context.retry_context.max_retry_count if context.retry_context else 0 - -# logging.info(f'EventHub retry trigger processed event: {event_id}, ' -# f'retry count: {retry_count}, max retry: {max_retry}') - -# # Track retry attempts -# if event_id not in retry_attempts: -# retry_attempts[event_id] = [] -# retry_attempts[event_id].append(retry_count) - -# # Create result dictionary -# result = { -# 'event_id': event_id, -# 'retry_count': retry_count, -# 'max_retry_count': max_retry, -# 'all_attempts': retry_attempts[event_id] -# } - -# # Fail on first two attempts to test retry -# if retry_count < 2: -# logging.warning(f'Simulating failure for retry test (attempt {retry_count})') -# raise Exception(f"Simulated failure for retry testing (attempt {retry_count})") - -# # Success on third attempt -# logging.info(f'Success on attempt {retry_count}') -# return json.dumps(result).encode('utf-8') +@app.function_name(name="eventhub_retry_trigger") +@app.retry(strategy="exponential_backoff", max_retry_count="3", + minimum_interval="00:00:01", + maximum_interval="00:00:02") +@app.event_hub_message_trigger( + arg_name="event", + event_hub_name="python-worker-ci-eventhub-retry", + connection="AzureWebJobsEventHubConnectionString" +) +@app.blob_output(arg_name="$return", + path="python-worker-tests/test-eventhub-retry-triggered.txt", + connection="AzureWebJobsStorage") +def eventhub_retry_trigger(event: func.EventHubEvent, context: func.Context) -> bytes: + event_id = event.get_body().decode('utf-8') + retry_count = context.retry_context.retry_count if context.retry_context else 0 + max_retry = context.retry_context.max_retry_count if context.retry_context else 0 + + logging.info(f'EventHub retry trigger processed event: {event_id}, ' + f'retry count: {retry_count}, max retry: {max_retry}') + + # Track retry attempts + if event_id not in retry_attempts: + retry_attempts[event_id] = [] + retry_attempts[event_id].append(retry_count) + + # Create result dictionary + result = { + 'event_id': event_id, + 'retry_count': retry_count, + 'max_retry_count': max_retry, + 'all_attempts': retry_attempts[event_id] + } + + # Fail on first two attempts to test retry + if retry_count < 2: + logging.warning(f'Simulating failure for retry test (attempt {retry_count})') + raise Exception(f"Simulated failure for retry testing (attempt {retry_count})") + + # Success on third attempt + logging.info(f'Success on attempt {retry_count}') + return json.dumps(result).encode('utf-8') # Retrieve the event data from storage blob and return it as Http response From 1207950fd9e90801a0c42849742f68b6e2af8690 Mon Sep 17 00:00:00 2001 From: Victoria Hall Date: Tue, 24 Mar 2026 15:30:36 -0500 Subject: [PATCH 7/7] a test --- eng/ci/integration-tests.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/eng/ci/integration-tests.yml b/eng/ci/integration-tests.yml index 33f673339..4cb1d6582 100644 --- a/eng/ci/integration-tests.yml +++ b/eng/ci/integration-tests.yml @@ -45,11 +45,6 @@ extends: clean: all # Clean all build directories before starting stages: - - stage: RunE2ETests - dependsOn: [] + - stage: BuildPythonWorker jobs: - - template: /eng/templates/official/jobs/ci-e2e-tests.yml@self - - stage: RunLCTests - dependsOn: [] - jobs: - - template: /eng/templates/official/jobs/ci-lc-tests.yml@self + - template: /eng/templates/official/jobs/build-artifacts.yml@self