nvflare.fuel.f3.streaming.transfer_outcome module
Normalized aggregate terminal outcome for DownloadService transactions.
TransactionDoneStatus.FINISHED only means a transaction reached its receiver count: a receiver that FAILED still counts toward num_receivers, and the transaction_done_cb carries no per-receiver outcomes. The Client API payload lifecycle (docs/design/client_api_execution_modes.md, “Terminal transfer outcome”) requires the distinction between “every expected receiver succeeded” and “the transaction merely terminated”. This module provides that distinction additively: TransactionDoneStatus values and the transaction_done_cb contract are unchanged.
Receiver truth wins over known termination mechanics: a transaction whose expected receivers all succeeded resolves COMPLETED even if it was terminated by routine cleanup (delete_transaction) or a late timeout. Everything else fails closed — including a FINISHED transaction with no objects (a mid-assembly race must not certify success) and any unknown/future termination status (validated before receiver truth is considered).
Outcome status values reuse the TransferProgressState terminal vocabulary (completed / failed / aborted) rather than introducing another status set.
Semantics with the full payload layer (receiver-confirmed completion, per-receiver budgets, awaitable transfer facade): - per-receiver statuses are receiver-confirmed where the receiver supports it (a served
EOF is provisional until the receiver confirms its finalization succeeded); legacy receivers remain producer-served — both skews and the runtime kill-switch degrade to producer-served semantics (download_service.py, receiver-confirmed completion);
expected receiver identities and per-receiver acquire/idle budgets bound the outcome’s resolution time (a stalled or never-pulling receiver is finalized FAILED without waiting the whole-transaction TTL); min_receivers surfaces the optional k-of-N quorum via quorum_met while completed stays the strict all-receivers certificate;
the outcome covers the refs present at termination; adding objects to a transaction after receivers already finished the earlier ones is not supported.
- class DownloadStatus[source]
Bases:
objectConstants for object download status.
- FAILED = 'failed'
- SUCCESS = 'success'
- class RefOutcome(ref_id: str, receiver_statuses: Mapping[str, str])[source]
Bases:
objectPer-object terminal outcome.
receiver_statuses maps receiver FQCN to a DownloadStatus value (success / failed) – receiver-confirmed where the receiver supports it, producer-served for legacy peers or when the receiver-confirm kill-switch is off, and budget-FAILED for receivers that exhausted their acquire/idle budget. It is deep-frozen at construction (a MappingProxyType over a private copy): the same instance is recorded in the service outcome table and handed to outcome_cb consumers, so a callback must not be able to mutate the recorded per-receiver truth. If outcomes ever cross a process boundary, the serializer must materialize it (dict(…)).
- receiver_statuses: Mapping[str, str]
- ref_id: str
- class TransactionDoneStatus[source]
Bases:
objectConstants for transaction completion status.
- DELETED = 'deleted'
- FINISHED = 'finished'
- TIMEOUT = 'timeout'
- class TransferOutcome(tx_id: str, status: str, reason: str, done_status: str, num_receivers: int, refs: Tuple[RefOutcome, ...], timestamp: float, min_receivers: int | None = None, receiver_ids: Tuple[str, ...] | None = None)[source]
Bases:
objectAggregate terminal outcome of one download transaction.
status is a TransferProgressState terminal value: COMPLETED only when every expected receiver of every ref succeeded; FAILED on any receiver failure, missing receiver, no objects, timeout, or unknown receiver count; ABORTED on explicit deletion before full success. done_status carries the raw TransactionDoneStatus for callers that need the untranslated termination cause.
- property completed: bool
- done_status: str
- min_receivers: int | None = None
- num_receivers: int
- property quorum_met: bool
True if every ref reached at least min_receivers confirmed successes.
The k-of-N surface for fan-out workflows (min_responses-style policies): completed stays the strict all-receivers certificate; a workflow that accepts partial fan-out checks quorum_met (or thresholds refs itself). Falls back to completed when no min_receivers was declared; fails closed with no refs.
- reason: str
- receiver_ids: Tuple[str, ...] | None = None
- refs: Tuple[RefOutcome, ...]
- status: str
- timestamp: float
- tx_id: str
- class TransferOutcomeReason[source]
Bases:
objectConstants explaining how a TransferOutcome status was determined.
- ALL_RECEIVERS_SUCCEEDED = 'all_receivers_succeeded'
- COMPUTATION_FAILED = 'computation_failed'
- DELETED = 'deleted'
- NO_OBJECTS = 'no_objects'
- RECEIVER_FAILED = 'receiver_failed'
- TIMEOUT = 'timeout'
- UNKNOWN_DONE_STATUS = 'unknown_done_status'
- UNKNOWN_RECEIVER_COUNT = 'unknown_receiver_count'
- compute_transfer_outcome(tx_id: str, done_status: str, num_receivers: int, refs: Sequence[RefOutcome], timestamp: float | None = None, min_receivers: int | None = None, receiver_ids=None) TransferOutcome[source]
Compute the aggregate terminal outcome for a terminated transaction.
The termination status is validated first: an unknown/future done_status fails closed even if every receiver succeeded. For known statuses, receiver truth wins: if every expected receiver of every ref succeeded, the outcome is COMPLETED regardless of how the transaction terminated (FINISHED, or routine cleanup via DELETED, or a late TIMEOUT). Otherwise the outcome fails closed based on the termination cause.
- Parameters:
tx_id – ID of the terminated transaction.
done_status – the TransactionDoneStatus value the transaction terminated with.
num_receivers – the transaction’s expected receiver count (0 means unknown).
refs – per-ref receiver statuses snapshotted at termination.
timestamp – termination time; defaults to now.
Returns: a TransferOutcome.