Skip to content

Support Worker-variant callbacks - #11589

Open
chrsmith wants to merge 3 commits into
chrsmith/wc-add-sano-completion-handlers_v2from
chrsmith/wc-support-worker-variant-callbacks
Open

Support Worker-variant callbacks#11589
chrsmith wants to merge 3 commits into
chrsmith/wc-add-sano-completion-handlers_v2from
chrsmith/wc-support-worker-variant-callbacks

Conversation

@chrsmith

Copy link
Copy Markdown
Contributor

⚠️ This is part of a stacked PR set, to be merged into feature/worker-callbacks. This will not go directly into main, until the overall feature is code complete.


What changed?

THIS IS IT! The actual PR that implements Worker callbacks!

This PR provides the implementation of the Worker-variant callback in the CHASM Callback component. A Worker-variant completion callback attached to a Workflow, Workflow Update, standalone Activity, or standalone Nexus Operation will result in a Nexus operation being invoked within the same namespace.

Additional changes/refactorings

The CallbackInfo.BlockedReason is now properly set. Previously it was left unimplemented in CHASM, and only callbacks attached to Workflows would report their status. We now wire through a destinationBlocked DestinationBlockedFn with the CHASM context for Callbacks, and inject it via CallbackDestinationBlockedProvider in service/history/fx.go.

Quirks/issues

We deliver all completion callbacks using the same request ID. This means however, that if multiple completion callbacks to the same (task queue, service, operation) were added in the same request (e.g. to StartNexusOperationExecution.) then all of the worker callback invocations would carry the same request ID. The Nexus handler could interpret subsequent calls as duplicates and ignore them.

This is something we can fix, by persisting a little more information in CallbackState but it didn't seem like a realistic scenario; attaching multiple worker callbacks, in the same request, to the same taskqueue/operation.

Why?

This is the new worker callbacks feature that will make developing "Nexus Connectors" (AKA fronting a Nexus service by a different API protocol) easier.

How did you test it?

  • built
  • run locally and tested manually
  • covered by existing tests
  • added new unit test(s)
  • added new functional test(s)

Potential risks

While behind configuration flags, it's possible to attach a Worker-variant callback to Workflows, Workflow Updates, standalone Activities, and standalone Nexus Operations. In all cases they should behave the exact same. But there is opportunity for some subtle difference to cause problems.

NOTE: Worker-variant callbacks are NOT supported for HSM-backed workflows. Attaching a Worker callback to an HSM workflow will fail in glorious ways.

@chrsmith
chrsmith marked this pull request as ready for review August 14, 2026 22:56
@chrsmith
chrsmith requested review from a team as code owners August 14, 2026 22:56
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @chrsmith's task in 8m 48s —— View job


Reviewed 20 files, 3 findings.
chrsmith/wc-support-worker-variant-callbacks

Comment thread tests/callbacks_circuitbreaker_test.go
Comment thread chasm/lib/callback/invocable_worker.go Outdated
Comment thread chasm/lib/callback/invocable_worker_test.go Outdated
Comment thread chasm/lib/callback/invocable_worker.go Outdated
@chrsmith
chrsmith requested a review from bergundy August 17, 2026 23:44
chrsmith added a commit to temporalio/api that referenced this pull request Aug 19, 2026
⚠️ This is to be merged into the `feature/worker-callbacks` branch, and
not `main`. Only after the feature is complete will that branch be
rebased and merged into `main`.

---

This PR makes three changes, all so that resources spanwed from the
invocation of a worker callback can be linked correctly.

(1) Remove the `Link_NexusOperationCallback` variant with a more general
`Link_Callback` proto

Previously we were scoping the feature to only be applicable for SANO
callbacks. But if we are going to support worker callbacks for any async
operation, having a general link type (that uses the existing [Execution
proto](https://github.com/temporalio/api/blob/0066de621239ca9ddc6c976e091e27a6bc474752/temporal/api/common/v1/message.proto#L73-L77))
will avoid needing to create additional link variants in the future.

(2) Add a `callbackpb.CallbackInfo::request_id` field

This type is used in the `Describe-` operations for standalone
Activities and standalone Nexus operations. Without it, there would be
no way to determine _which_ completion callback is being referred to.
(Instead, we couldn't be any more accurate than to have the link point
to "one of these N" callbacks.)

(3) Add `workflowpb.CallbackInfo::{request_id, result}`

The `workflowpb` namespace forked rather than embedded the
`callbackpb.CallbackInfo` message. The changes here add the missing
fields, so that `DescribeWorkflowExecution` can disambiguate callbacks
as well. (In addition to carrying the result of those callbacks.)

**Why?**

With these changes, the server will be able to properly cross-link
resources spawned from completion callbacks.

On the Caller-side, any resources spawned from the completion callbacks
would be available on the `commonpb.Callback::links` field. (*)

```graphql
query GetSpawnedResourceLinks(workflowID: string {
  DescribeWorkflowExecution(workflowID) {
    completion_callbacks {
      callback {
        links
      }
    }
  }
}
```

> (*) Only the resources _initially_ created from the worker callback
invocation will be present. e.g. the Workflow that backs an asynchronous
Nexus handler. It would not contain links for any subsequent resources
created.

On the Handler-side, a single `Link_Callback` would be supplied to the
Nexus handler receiving the worker callback. (This would be in the form
of a `nexuspb.Link`.)

**Breaking changes**

Yes, this PR contains breaking proto changes. However, in the context of
a PR into a long-lived feature branch for an unshipped feature this is
safe. (The protos haven't ever been persisted by a production service.)

**Server PR**

It isn't out yet, but will be stacked on top of this:
temporalio/temporal#11589
chrsmith added a commit to temporalio/api that referenced this pull request Aug 19, 2026
⚠️ This is to be merged into the `feature/worker-callbacks` branch, and
not `main`. Only after the feature is complete will that branch be
rebased and merged into `main`.

---

This PR makes three changes, all so that resources spanwed from the
invocation of a worker callback can be linked correctly.

(1) Remove the `Link_NexusOperationCallback` variant with a more general
`Link_Callback` proto

Previously we were scoping the feature to only be applicable for SANO
callbacks. But if we are going to support worker callbacks for any async
operation, having a general link type (that uses the existing [Execution
proto](https://github.com/temporalio/api/blob/0066de621239ca9ddc6c976e091e27a6bc474752/temporal/api/common/v1/message.proto#L73-L77))
will avoid needing to create additional link variants in the future.

(2) Add a `callbackpb.CallbackInfo::request_id` field

This type is used in the `Describe-` operations for standalone
Activities and standalone Nexus operations. Without it, there would be
no way to determine _which_ completion callback is being referred to.
(Instead, we couldn't be any more accurate than to have the link point
to "one of these N" callbacks.)

(3) Add `workflowpb.CallbackInfo::{request_id, result}`

The `workflowpb` namespace forked rather than embedded the
`callbackpb.CallbackInfo` message. The changes here add the missing
fields, so that `DescribeWorkflowExecution` can disambiguate callbacks
as well. (In addition to carrying the result of those callbacks.)

**Why?**

With these changes, the server will be able to properly cross-link
resources spawned from completion callbacks.

On the Caller-side, any resources spawned from the completion callbacks
would be available on the `commonpb.Callback::links` field. (*)

```graphql
query GetSpawnedResourceLinks(workflowID: string {
  DescribeWorkflowExecution(workflowID) {
    completion_callbacks {
      callback {
        links
      }
    }
  }
}
```

> (*) Only the resources _initially_ created from the worker callback
invocation will be present. e.g. the Workflow that backs an asynchronous
Nexus handler. It would not contain links for any subsequent resources
created.

On the Handler-side, a single `Link_Callback` would be supplied to the
Nexus handler receiving the worker callback. (This would be in the form
of a `nexuspb.Link`.)

**Breaking changes**

Yes, this PR contains breaking proto changes. However, in the context of
a PR into a long-lived feature branch for an unshipped feature this is
safe. (The protos haven't ever been persisted by a production service.)

**Server PR**

It isn't out yet, but will be stacked on top of this:
temporalio/temporal#11589
Comment thread chasm/lib/callback/component.go Outdated
Comment thread chasm/lib/callback/component.go Outdated
Comment thread chasm/lib/callback/component.go Outdated
Comment thread chasm/lib/callback/invocable_internal.go Outdated
Comment thread chasm/lib/callback/invocable_internal.go Outdated
Comment thread chasm/lib/callback/validator.go Outdated
Comment thread common/nexus/dispatch_response.go
Comment thread common/nexus/dispatch_response.go
Comment thread service/history/fx.go
Comment thread chasm/lib/callback/statemachine.go Outdated
@chrsmith
chrsmith requested a review from a team as a code owner August 23, 2026 00:14
@chrsmith
chrsmith force-pushed the chrsmith/wc-support-worker-variant-callbacks branch 2 times, most recently from 843d371 to ed07d2d Compare August 23, 2026 02:33
@chrsmith
chrsmith force-pushed the chrsmith/wc-support-worker-variant-callbacks branch 3 times, most recently from 820cd76 to a0a9d57 Compare August 23, 2026 21:04
@chrsmith

Copy link
Copy Markdown
Contributor Author

@bergundy PTAL. I took care of your suggestions, as well as addressed that TODO comment about enforcing an aggregate max size for all worker callback source contexts attached to an execution.

I'll add support for enabling Worker-callbacks for other execution types (#11566 (comment)) and rebase this on top. So there will be a few more functional tests to sanity check other execution types work end-to-end with worker callbacks.

@bergundy bergundy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not review the tests FTR.

Comment thread chasm/lib/callback/config.go
Comment thread chasm/lib/callback/config.go
Comment thread chasm/lib/callback/invocable_worker.go
Comment thread chasm/lib/callback/invocable_worker.go
Comment thread chasm/lib/callback/invocable_worker.go
Comment thread chasm/context_mock.go
Comment thread common/callbacks/validator.go
Comment thread common/callbacks/validator.go
Comment thread service/history/fx.go
Comment thread chasm/lib/nexusoperation/operation.go
@chrsmith
chrsmith force-pushed the chrsmith/wc-support-worker-variant-callbacks branch from 6acf89f to 18b6e0d Compare August 26, 2026 21:59
chrsmith added a commit to temporalio/api that referenced this pull request Aug 29, 2026
⚠️ This is to be merged into the `feature/worker-callbacks` branch, and
not `main`. Only after the feature is complete will that branch be
rebased and merged into `main`.

---

This PR makes three changes, all so that resources spanwed from the
invocation of a worker callback can be linked correctly.

(1) Remove the `Link_NexusOperationCallback` variant with a more general
`Link_Callback` proto

Previously we were scoping the feature to only be applicable for SANO
callbacks. But if we are going to support worker callbacks for any async
operation, having a general link type (that uses the existing [Execution
proto](https://github.com/temporalio/api/blob/0066de621239ca9ddc6c976e091e27a6bc474752/temporal/api/common/v1/message.proto#L73-L77))
will avoid needing to create additional link variants in the future.

(2) Add a `callbackpb.CallbackInfo::request_id` field

This type is used in the `Describe-` operations for standalone
Activities and standalone Nexus operations. Without it, there would be
no way to determine _which_ completion callback is being referred to.
(Instead, we couldn't be any more accurate than to have the link point
to "one of these N" callbacks.)

(3) Add `workflowpb.CallbackInfo::{request_id, result}`

The `workflowpb` namespace forked rather than embedded the
`callbackpb.CallbackInfo` message. The changes here add the missing
fields, so that `DescribeWorkflowExecution` can disambiguate callbacks
as well. (In addition to carrying the result of those callbacks.)

**Why?**

With these changes, the server will be able to properly cross-link
resources spawned from completion callbacks.

On the Caller-side, any resources spawned from the completion callbacks
would be available on the `commonpb.Callback::links` field. (*)

```graphql
query GetSpawnedResourceLinks(workflowID: string {
  DescribeWorkflowExecution(workflowID) {
    completion_callbacks {
      callback {
        links
      }
    }
  }
}
```

> (*) Only the resources _initially_ created from the worker callback
invocation will be present. e.g. the Workflow that backs an asynchronous
Nexus handler. It would not contain links for any subsequent resources
created.

On the Handler-side, a single `Link_Callback` would be supplied to the
Nexus handler receiving the worker callback. (This would be in the form
of a `nexuspb.Link`.)

**Breaking changes**

Yes, this PR contains breaking proto changes. However, in the context of
a PR into a long-lived feature branch for an unshipped feature this is
safe. (The protos haven't ever been persisted by a production service.)

**Server PR**

It isn't out yet, but will be stacked on top of this:
temporalio/temporal#11589
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