fix(orchestrator): losing pod logs must not fail an execution - #325
Merged
morgan-wowk merged 1 commit intoAug 13, 2026
Merged
Conversation
Collaborator
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Signed-off-by: Morgan Wowk <morgan.wowk@shopify.com>
morgan-wowk
force-pushed
the
08-12-fix_orchestrator_losing_pod_logs_must_not_fail_an_execution
branch
from
August 12, 2026 20:29
9802c98 to
b2bdd00
Compare
morgan-wowk
marked this pull request as ready for review
August 12, 2026 20:49
Ark-kun
approved these changes
Aug 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What breaks today
When the GKE cluster-autoscaler evicts and deletes a Job's pod mid-run, the Job object still reports
Failed, so the orchestrator has everything it needs to record a cleanFAILED. Today it doesn't:LaunchedKubernetesJob._get_log_by_pod_keyre-raises on a404— only a400("Bad Request", pod still initializing) was mapped to "no logs".FAILEDbranch callsupload_log()unguarded, so that raise propagates.upload_logreads logs by remembered pod name → the pod is gone →404→_retryburns all 5 attempts and re-raises → the outer handler overwritesFAILEDwithSYSTEM_ERROR.Observed twice on one production run:
Exception calling <bound method LaunchedKubernetesJob.upload_log ...>×5, thenError processing running container execution.New rules
FAILEDbranch now guardsupload_logexactly as theSUCCEEDEDbranch already does — a failed upload is logged and the execution staysFAILEDwith normal downstream skipping._get_log_by_pod_keyreturnsNoneon a404(pod deleted), just as it already does on a400(pod initializing), with a warning naming the pod._debug_podsdeliberately retains pods that no longer exist, so a deleted pod would otherwise re-404on every later read. See issue #139 for the original400case.Why both fixes ship as one unit
Each fix alone leaves the failure reachable: the orchestrator guard stops the
FAILED → SYSTEM_ERRORflip for any upload failure, while the404handling stops the vanished-pod read from raising in the first place — so logs from other still-present pods in the same Job still upload. Together they give one guarantee: losing logs never changes a terminal status.Scope
LaunchedKubernetesContainer.get_log/stream_log_lines(the single-pod launcher) are left unchanged. They currently raise on anyApiException, and the orchestrator guard already protects theFAILEDpath for both launcher types; extending them would change pod-launcher semantics, so it's deliberately out of scope.podFailurePolicy/backoffLimitPerIndex/maxFailedIndexesare untouched — a separate change.Tests
tests/test_kubernetes_launcher_error_classification.py:_get_log_by_pod_keyreturnsNoneon a404and still raises on403and500(mockedCoreV1Api).tests/test_orchestrator_failed_log_upload.py: an execution whose launcher raises fromupload_logendsFAILED(notSYSTEM_ERROR), with downstream markedSKIPPED.uv run pytest).Downstream
Shopify/oasis-backend pins this repo as the
backendsubmodule; it needs a submodule pointer bump to pick this fix up.