@@ -1595,6 +1595,32 @@ def upload_json_blob(
15951595 and hashlib .sha256 (current_payload ).hexdigest () == digest
15961596 ):
15971597 return digest
1598+ # Sequential executes deliberately reuse the assignment's
1599+ # fixed request/result blob names. The worker record can
1600+ # still carry the ETag from the prior execute, so adopt the
1601+ # observed same-assignment blob and retry one CAS rather
1602+ # than permanently wedging retained-disk recovery. Another
1603+ # assignment never passes the complete expected tag subset,
1604+ # and a concurrent writer loses the fresh If-Match.
1605+ current_metadata = current .get ("metadata" ) or {}
1606+ expected_metadata = tags_to_metadata (tags )
1607+ if overwrite and all (
1608+ current_metadata .get (key ) == item
1609+ for key , item in expected_metadata .items ()
1610+ ):
1611+ retry_args = list (upload_args )
1612+ match_index = retry_args .index ("--if-match" )
1613+ retry_args [match_index + 1 ] = current_etag
1614+ _ , retry_rc , retry_stderr = az (
1615+ controller , retry_args , check = False
1616+ )
1617+ if retry_rc == 0 :
1618+ return digest
1619+ raise ProviderError (
1620+ "conditionally retried worker staging upload failed: {}" .format (
1621+ retry_stderr
1622+ )
1623+ )
15981624 finally :
15991625 with contextlib .suppress (FileNotFoundError ):
16001626 Path (current_path ).unlink ()
@@ -2546,6 +2572,24 @@ def mutate_deallocate(controller, action):
25462572 return final
25472573
25482574
2575+ def service_cancel_allows_missing_task_command (action ):
2576+ proof = action .get ("service_cancel_proof" )
2577+ if not isinstance (proof , dict ):
2578+ return False
2579+ unsigned = dict (proof )
2580+ supplied = unsigned .pop ("proof_digest" , None )
2581+ bindings = action .get ("bindings" ) or {}
2582+ return (
2583+ proof .get ("schema" ) == "fm.worker-service-cancel/v1"
2584+ and proof .get ("verdict" ) == "cancelled-before-execution"
2585+ and supplied == hashlib .sha256 (canonical_bytes (unsigned )).hexdigest ()
2586+ and proof .get ("task" ) == bindings .get ("task" )
2587+ and proof .get ("task_generation" ) == bindings .get ("task_generation" )
2588+ and proof .get ("assignment_generation" ) == bindings .get ("assignment_generation" )
2589+ and proof .get ("cloud_instance_id" ) == action .get ("cloud_instance_id" )
2590+ )
2591+
2592+
25492593def mutate_delete_compute (controller , action ):
25502594 snapshot = inventory (controller , include_metrics = False )
25512595 worker = worker_by_slot (snapshot , action ["slot" ])
@@ -2566,7 +2610,11 @@ def mutate_delete_compute(controller, action):
25662610 or not cleanup_marker (container , EXECUTE_ABANDON_MARKER , retired_execute_key )
25672611 ):
25682612 raise ProviderError ("retired-execute custody marker is not exact" )
2569- if task_command_missing and retired_execute_key is None :
2613+ if (
2614+ task_command_missing
2615+ and retired_execute_key is None
2616+ and not service_cancel_allows_missing_task_command (action )
2617+ ):
25702618 raise ProviderError (
25712619 "missing task-command has no exact retired-execute custody proof"
25722620 )
@@ -3236,6 +3284,11 @@ def mutate_execute(controller, action):
32363284 if disposition in (EXECUTE_DISPOSITION_TERMINAL , EXECUTE_DISPOSITION_RECOVERED ):
32373285 if disposition == EXECUTE_DISPOSITION_RECOVERED :
32383286 persist_execute_result (controller , action , names , tags , recovered )
3287+ worker = worker_by_slot (
3288+ inventory (controller , include_metrics = False ), action ["slot" ]
3289+ )
3290+ if worker is None :
3291+ raise ProviderError ("execution result persistence lost its exact worker" )
32393292 return worker , recovered
32403293 if disposition != EXECUTE_DISPOSITION_SUBMIT :
32413294 raise ProviderError ("worker execution disposition is unsupported" )
@@ -3336,7 +3389,10 @@ def mutate_execute(controller, action):
33363389 if execution is None :
33373390 raise ProviderError ("private worker execution returned no exact result" )
33383391 persist_execute_result (controller , action , names , tags , execution )
3339- return worker_by_slot (inventory (controller , include_metrics = False ), action ["slot" ]), execution
3392+ worker = worker_by_slot (inventory (controller , include_metrics = False ), action ["slot" ])
3393+ if worker is None :
3394+ raise ProviderError ("execution result persistence lost its exact worker" )
3395+ return worker , execution
33403396
33413397
33423398def mutate_steer (controller , action ):
0 commit comments