Fix EMR Serverless connector cold start timeout and script mode outputs - #3458
Merged
samhita-alla merged 1 commit intoAug 7, 2026
Conversation
Two bugs surface the first time a task runs against a stopped or newly created EMR Serverless application. 1. Cold start exceeds the CreateTask deadline create() called ensure_application_started(), which polls until the application reaches STARTED. A cold application takes tens of seconds to minutes to start, which is longer than the FlytePropeller CreateTask gRPC deadline, so the task failed with DeadlineExceeded before a job run was ever submitted. create() now requests startup without waiting. When the application is not yet STARTED it returns metadata carrying the prepared StartJobRun request, and get() submits it once the application is ready, reporting RUNNING while it starts. A client token is attached to the deferred request so a retried submission cannot create a duplicate job run. 2. Script mode tasks fail after succeeding Script and Hive mode tasks declare no Flyte outputs, because the entrypoint that writes outputs.pb only runs in Pythonic mode. get() returned a Resource without outputs, so on success FlytePropeller looked for outputs.pb, did not find it, and failed the node with OutputsNotFoundError even though the EMR Serverless job had succeeded. get() now returns an empty LiteralMap for succeeded script mode jobs. Also adds flytekit-aws-emr-serverless to the plugin CI matrix. The plugin was added in flyteorg#3427 but never wired into the matrix, so its tests have never run in CI. Signed-off-by: Rohit Sharma <rohitrsh@gmail.com>
rohitrsh
marked this pull request as ready for review
August 6, 2026 09:49
rohitrsh
requested review from
cosmicBboy,
davidmirror-ops,
kumare3,
machichima,
pingsutw,
samhita-alla and
wild-endeavor
as code owners
August 6, 2026 09:49
samhita-alla
approved these changes
Aug 7, 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.
Why
Two bugs in the AWS EMR Serverless connector (added in #3427) surface the first time a task runs against a stopped or newly created application. Neither is caught by the existing unit tests, which only exercise the already-
STARTEDand Pythonic-mode paths.Tracking issue: flyteorg/flyte#7286
1. Cold start exceeds the
CreateTaskdeadlinecreate()calledensure_application_started(), which polls until the application reachesSTARTED. A cold EMR Serverless application takes tens of seconds to minutes to start, which is longer than the FlytePropellerCreateTaskgRPC deadline. The task failed withDeadlineExceededbefore a job run was ever submitted, so the first run against a cold application always failed. A retry then succeeds, because by then the application has started — which makes this look intermittent rather than deterministic.2. Script mode tasks fail after succeeding
Script and Hive mode tasks declare no Flyte outputs — the entrypoint that writes
outputs.pbonly runs in Pythonic mode.get()returned aResourcewithoutoutputs, so on success FlytePropeller looked foroutputs.pb, did not find it, and failed the node:The EMR Serverless job itself succeeded, so the execution reported success and failure at the same time.
What changed
Cold start.
create()now requests startup without waiting, via a newEMRServerlessHandler.start_application_if_needed()that sendsStartApplicationand reports whether the application is alreadySTARTED. If it is not,create()returns metadata carrying the preparedStartJobRunrequest, andget()submits it once the application is ready, reportingRUNNINGin the meantime. This moves the blocking wait out of theCreateTaskRPC and into the polling loop.A
clientTokenis generated increate()and attached to the deferred request, so ifget()retries after a partially failed submission, EMR Serverless deduplicates instead of starting a second job run.delete()handles the deferred state as well, so aborting during startup does not leave an orphaned job run.Script mode outputs.
EMRServerlessJobMetadatanow recordsis_script_mode, andget()returns an emptyLiteralMapfor succeeded script mode jobs so the node closes out successfully.CI. Adds
flytekit-aws-emr-serverlessto the plugin matrix inpythonbuild.yml. The plugin was added in #3427 but never wired into the matrix, so its tests have never run in CI.Tests
146 passedfor the plugin, up from 138. New coverage:create()defers submission and issues noStartJobRunwhen the application is notSTARTEDget()submits the deferred job onceSTARTED, and reportsRUNNINGwhile it is still startingget()returns an emptyLiteralMapfor succeeded script mode jobs, andNonefor Pythonic modedelete()on a deferred job, and on a job that was never submittedstart_application_if_needed()across already-started, needs-start, transitional and terminal statesclientTokenpassthrough instart_job_runVerified against the versions CI pins (
pytest==8.2.1,pytest-asyncio==0.23.7) rather than only local versions, andpre-commitpasses with the pinnedruffv0.8.3.Note for reviewers
delete()currently submits a deferred job before cancelling it when the application has since becomeSTARTED. Returning early without submitting would be cheaper; I kept submit-then-cancel because that is the path validated end to end, but happy to simplify if you would prefer.ensure_application_started()is left in place but is no longer called by the connector, sincecreate()now uses the non-blockingstart_application_if_needed(). I did not remove it because it is a public method on the handler and its own tests still cover it, but I am happy to delete it if you would rather not carry the dead path.The metadata change is backwards compatible:
ResourceMetais JSON-encoded, and both new fields have defaults, so job metadata written by the currently released version still decodes after a connector upgrade. Verified by round-tripping a legacy payload with neither field present.The API surface is unchanged — the connector calls exactly the same five EMR Serverless operations as before, in a different order, so no IAM policy change is required to adopt this.