An Azure service that provides an event-driven serverless compute platform.
Hi @Phillip ,
I'm glad to hear that issue is resolved. However, we received an update from Product team on the root cause of the issue. Please find the details below.
Revised Root Cause -- ATOL Premature Pod Drain (Known Bug)
After further analysis with the FlexConsumption team, the root cause has been revised. Stamp waws-prod-fra-039 is confirmed ATOL-migrated, and this CRI is attributed to a __known ATOL platform bug__where pods without active HTTP requests are drained prematurely (~6-7 min) instead of waiting the intended 10-12 min grace period.
Evidence From Telemetry
FunctionsLogs drain sequence for this app on Jun 25 (pod 0--816d66a0):
| Time (UTC) | Event |
|---|---|
| 12:43:38 | Pod starts up |
| 12:44:20 | Pod health: Healthy |
| 12:51:02 | MarkedForDrain -- only ~7.4 min after startup |
| 12:51:03 | Drain InProgress, outstandingInvocations=1 (timer function still running) |
| 12:51:49 | RpcFunctionInvocationDispatcher shutdown initiated |
| 12:54:19 | Drain retry (poll interval 1m, max retries 1) |
| 12:54:51 | InvocationResponse received (invocation completed in time) |
| 12:55:03 | Drain completed, outstandingInvocations=0 |
The host log also shows: 5 HandleInstanceStateTimeoutSeconds timeouts occurred, returning unchanged state -- confirming pod state transition was forced after repeated timeouts.
How This Explains the Symptoms
- "Function stops after ~10k rows (~3-10 min)": The ATOL drain kicks in at ~6-7 min, terminating the pod while the timer-triggered function is still processing rows sequentially. The exact row count where it stops depends on how fast each row is processed and when the drain fires.
- "No error logged": The drain is infrastructure-level. The pod is terminated gracefully from the platform's perspective, but the function never gets a chance to log an error -- the process is simply killed.
- "Batching works": Parallel batch processing completes in ~2-3 minutes, finishing well before the ~6-7 min drain window. Sequential processing exceeds the drain window and gets killed.
- "Same behavior with Queue output": The issue is not the output target -- it is the pod being drained while the invocation is still running.
- "Different chunk sizes show different thresholds": Different levels of parallelism change the total execution time, which determines whether the function finishes before or after the premature drain kicks in.
Known Bug Details
- Bug: ATOL does not properly wait for pods without active HTTP requests (e.g., timer-triggered functions) before draining. Pods are drained at ~6 min instead of the intended 10-12 min grace period.
- Scope: Affects ATOL-migrated stamps with timer-triggered (non-HTTP) functions that run longer than ~6 minutes.
- Status: An internal work item for the known bug is being worked upon and will be deployed soon.
Updated Recommendations
- Short-term workaround: Use batch/parallel processing (
Promise.all()) to complete execution within the ~6 min drain window. Customer has already confirmed this works. - Platform fix needed: The ATOL drain logic needs to respect the full 10-12 min grace period for pods with active non-HTTP invocations (timer, queue, etc.). They are working on the fix.
Please let me know if there are any further question on this. If all good, please let me know if we can close the case.