Skip to content

Fairness Keys & Weights - #2633

Merged
Sushisource merged 7 commits into
masterfrom
fairness
Aug 19, 2025
Merged

Fairness Keys & Weights#2633
Sushisource merged 7 commits into
masterfrom
fairness

Conversation

@Sushisource

Copy link
Copy Markdown
Member

What was changed

Added fairness keys/weights to priority

Why?

New feature

Checklist

  1. Closes

  2. How was this tested:
    Added test

  3. Any docs updates needed?

@Sushisource
Sushisource marked this pull request as ready for review August 19, 2025 06:14
@Sushisource
Sushisource requested a review from a team as a code owner August 19, 2025 06:14
@Quinn-With-Two-Ns

Quinn-With-Two-Ns commented Aug 19, 2025

Copy link
Copy Markdown
Contributor

You need to update the CLI to the pre release version

https://github.com/temporalio/sdk-java/blob/master/.github/workflows/ci.yml#L77

@Sushisource
Sushisource merged commit b6b4290 into master Aug 19, 2025
18 of 19 checks passed
@Sushisource
Sushisource deleted the fairness branch August 19, 2025 22:02
tekkaya added a commit that referenced this pull request Aug 31, 2026
…on handler

TemporalNexusClient guards its own activity/workflow/update start to at most
one per operation invocation, so a synchronous Nexus operation handler that
starts two or more activities inline has to bypass it and use a raw
ActivityClient obtained from Nexus.getOperationContext() instead. That bypass
path got no request links, because RootActivityClientInvoker.startActivity
derived link attachment, on-conflict dedup, and completion-callback
attachment all from the same NexusOperationMetadata, which only the guarded
TemporalNexusClient call ever sets.

Every activity start made during the invocation now reuses the inbound Nexus
task's request ID and gets its inbound links, regardless of which client
object issued it. NexusOperationMetadata keeps its narrow, one-shot scope and
remains the only thing that can attach a completion callback, since only the
guarded start should complete the Nexus operation. When metadata is present
its own request ID still takes precedence, so the guarded start's identity
never depends on the ambient value also having been set.

Reusing one ambient ID across every start in an invocation is a deliberate,
known tradeoff: a handler that starts a fresh run under an activity ID it
already used earlier in the same invocation can have that start incorrectly
resolve to the stale run instead of creating a new one. Two narrower,
redelivery-aware alternatives were tried and dropped -- raw ambient-ID reuse
scoped to only the guarded call, then a per-call ID derived from each start's
ordinal position -- because both require assuming the handler reissues an
identical sequence of calls on every retry, an assumption the SDK has no way
to verify. This change instead matches sdk-go's approved fix
(temporalnexus/temporal_operation.go, PR #2633) and sdk-python's current
behavior (temporalio/nexus/_operation_context.py): every activity start in a
Nexus context reuses the ambient request ID unconditionally. sdk-python's own
attempt at the narrower, backing-call-only scoping (PR #1722) was closed
without merging for the same reason.

ActivityOperationLinkingTest (functional, requires a real server) drives a
synchronous handler that starts two bypass-path activities and asserts both
the forward link (each activity's own ActivityExecutionInfo) and the
backward links (both activities' completions landing on the caller's single
NexusOperationCompleted event), the same way SignalOperationLinkingTest
already does for signals.

RootActivityClientInvokerTest covers metadata's request ID taking precedence
over the ambient one, the ambient-links-and-request-ID-without-metadata case,
the outside-Nexus-context case, and two bypass-path starts in one invocation
sharing the ambient request ID.
tekkaya added a commit that referenced this pull request Aug 31, 2026
…on handler

TemporalNexusClient guards its own activity/workflow/update start to at most
one per operation invocation, so a synchronous Nexus operation handler that
starts two or more activities inline has to bypass it and use a raw
ActivityClient obtained from Nexus.getOperationContext() instead. That bypass
path got no request links, because RootActivityClientInvoker.startActivity
derived link attachment, on-conflict dedup, and completion-callback
attachment all from the same NexusOperationMetadata, which only the guarded
TemporalNexusClient call ever sets.

Every activity start made during the invocation now reuses the inbound Nexus
task's request ID and gets its inbound links, regardless of which client
object issued it. NexusOperationMetadata keeps its narrow, one-shot scope and
remains the only thing that can attach a completion callback, since only the
guarded start should complete the Nexus operation. When metadata is present
its own request ID still takes precedence, so the guarded start's identity
never depends on the ambient value also having been set.

Reusing one ambient ID across every start in an invocation is a deliberate,
known tradeoff: a handler that starts a fresh run under an activity ID it
already used earlier in the same invocation can have that start incorrectly
resolve to the stale run instead of creating a new one. Two narrower,
redelivery-aware alternatives were tried and dropped -- raw ambient-ID reuse
scoped to only the guarded call, then a per-call ID derived from each start's
ordinal position -- because both require assuming the handler reissues an
identical sequence of calls on every retry, an assumption the SDK has no way
to verify. This change instead matches sdk-go's approved fix
(temporalnexus/temporal_operation.go, PR #2633) and sdk-python's current
behavior (temporalio/nexus/_operation_context.py): every activity start in a
Nexus context reuses the ambient request ID unconditionally. sdk-python's own
attempt at the narrower, backing-call-only scoping (PR #1722) was closed
without merging for the same reason.

ActivityOperationLinkingTest (functional, requires a real server) drives a
synchronous handler that starts two bypass-path activities and asserts both
the forward link (each activity's own ActivityExecutionInfo) and the
backward links (both activities' completions landing on the caller's single
NexusOperationCompleted event), the same way SignalOperationLinkingTest
already does for signals.

RootActivityClientInvokerTest covers metadata's request ID taking precedence
over the ambient one, the ambient-links-and-request-ID-without-metadata case,
the outside-Nexus-context case, and two bypass-path starts in one invocation
sharing the ambient request ID.
tekkaya added a commit that referenced this pull request Aug 31, 2026
The server rejects a StartActivityExecutionRequest whose OnConflictOptions
sets attach_request_id when the request carries neither a link nor a
completion callback (chasm/lib/activity/validator.go's
validateOnConflictOptions: "attach_request_id requires at least one
completion callback or link"). The previous code set attach_request_id and
attach_links unconditionally whenever any Nexus context existed, regardless
of whether the inbound task actually had links -- a bypass-path activity
start issued during an invocation whose inbound Nexus task carries no links
would send exactly that invalid combination and get rejected. This wasn't
caught before because the existing unit tests mock the client and never
exercise real server-side validation.

OnConflictOptions is now only set when there's something to attach, and each
flag reflects what the request actually carries, matching sdk-go's identical
gating in its own fix (temporalnexus/temporal_operation.go, PR #2633). This
also fixes the guarded (metadata-backed) path: it previously set
attach_completion_callbacks based on whether metadata was present rather
than whether a callback URL was actually set, so a guarded start with an
empty callback URL and no links would hit the same rejection.

RootActivityClientInvokerTest: flipped the assertion in
nexusMetadataWithEmptyCallbackUrlOmitsCompletionCallback (attach_completion_
callbacks now correctly reflects the absence of a real callback), rewrote
nexusContextWithoutAmbientStateStartsOrdinaryActivity to assert
OnConflictOptions is entirely absent, and added
metadataWithEmptyCallbackUrlAndNoLinksOmitsOnConflictOptions covering the
previously-untested guarded-call variant of the same bug.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants