Skip to content

Callback for workflow update support#9614

Open
Quinn-With-Two-Ns wants to merge 10 commits into
temporalio:mainfrom
Quinn-With-Two-Ns:nexus-workflow-update
Open

Callback for workflow update support#9614
Quinn-With-Two-Ns wants to merge 10 commits into
temporalio:mainfrom
Quinn-With-Two-Ns:nexus-workflow-update

Conversation

@Quinn-With-Two-Ns
Copy link
Copy Markdown
Contributor

@Quinn-With-Two-Ns Quinn-With-Two-Ns commented Mar 21, 2026

What changed?

Added support for Nexus workflow update completion callbacks via CHASM. This allows a Nexus caller to be notified when a workflow update completes by attaching completion callbacks to the update request.

Why?

Nexus operations that target workflow updates need a way to receive completion notifications. Without this, a Nexus caller that sends an update has no async mechanism to learn when the update finishes. Completion callbacks enable the same async notification pattern that already exists for workflow-level Nexus operations.

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

Touches speculative workflow updates, they are always hard to reason about. Tried to compensate with lots of test coverage.

Note: Needs this API PR https://github.com/temporalio/api/pull/742/changes


Note

High Risk
Touches workflow update state transitions, mutable state/history event application, and callback scheduling paths (including retry/continue-as-new), which can affect correctness of update outcomes and callback delivery. Also adds a go.mod replace for go.temporal.io/api, increasing dependency and compatibility risk.

Overview
Adds CHASM-backed completion callbacks for workflow updates: update requests can register Nexus completion_callbacks, persist them via WorkflowExecutionOptionsUpdated/update events, and schedule them when the update completes, the workflow closes, or the run continues.

Introduces a new per-update CHASM component (WorkflowUpdate + UpdateState proto) with opt-in self-cleanup of terminal callbacks, plus new mutable-state support to fetch update completion data (GetNexusUpdateCompletion) and to fire callbacks on update completion/rejection and on close paths.

Extends update handling to validate request_id when callbacks are present, buffer callbacks pre-acceptance, persist/dedup them on acceptance using WorkflowExecutionOptionsUpdatedEventAttributes.WorkflowUpdateOptions, and return clearer response backlinks (workflow-event link for accepted updates, workflow link for validator rejections).

Reviewed by Cursor Bugbot for commit 579442b. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread common/dynamicconfig/constants.go Outdated
Comment thread chasm/tree.go
Copy link
Copy Markdown
Member

@bergundy bergundy left a comment

Choose a reason for hiding this comment

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

I think we need just one more round here. For when updates are already completed, let's make sure to generate the new link type we discussed server-side.

Comment thread chasm/lib/workflow/library.go Outdated
func (l *Library) Components() []*chasm.RegistrableComponent {
return []*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*Workflow](chasm.WorkflowComponentName),
chasm.NewRegistrableComponent[*WorkflowUpdate](chasm.WorkflowUpdateComponentName),
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.

Given that workflow update is tightly coupled to workflows, it makes total sense to put them in the same library.

*workflowpb.UpdateState

// MSPointer is a special in-memory field for accessing the underlying mutable state.
chasm.MSPointer
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.

This was only supposed to be embedded in the top level Workflow component but I can see why you'd want to access it here. No strong opinion because either way this would be a workaround. I wonder though if you need to embed this or if it'd be better to make it a named field.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was embed in the workflow component so I made it embed here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if it's not embedded then it would also need to be an exported field otherwise CHASM tree deserialization will not work. Probably to keep similar convention embedding is ok here

Comment thread chasm/lib/workflow/workflow_update.go Outdated
Comment thread chasm/workflow.go Outdated
)
MaxCallbacksPerUpdateID = NewNamespaceIntSetting(
"system.maxCallbacksPerUpdateID",
32,
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 think limiting all of the workflow callbacks, regardless of what component they're attached to makes more sense than a per component limit due to the fact that the entire tree needs to be loaded into memory when mutable state is accessed today.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I also limited all workflow callbacks as well. I added this limit as well to keep one update from using up all the callbacks limit on a workflow.

Comment thread tests/nexus_workflow_update_test.go Outdated
Comment thread tests/nexus_workflow_update_test.go Outdated
Comment thread tests/nexus_workflow_update_test.go Outdated
Comment thread tests/nexus_workflow_update_test.go Outdated
Comment thread service/history/workflow/update/update.go
Copy link
Copy Markdown
Contributor

@stephanos stephanos left a comment

Choose a reason for hiding this comment

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

Only made it half-way through so far; but figured I can send my first review comments now.

Comment thread .gitignore Outdated
Comment thread service/history/workflow/update/export_test.go
Comment thread tests/update_workflow_sdk_test.go
Comment thread tests/update_workflow_sdk_test.go Outdated
links []*commonpb.Link,
identity string,
priority *commonpb.Priority,
workflowUpdateOptions map[string]*historypb.WorkflowExecutionOptionsUpdatedEventAttributes_WorkflowUpdateOptionsUpdate,
Copy link
Copy Markdown
Contributor

@stephanos stephanos Mar 25, 2026

Choose a reason for hiding this comment

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

I know it's not wrong, but ... WorkflowUpdateOptionsUpdate 😬

(non-blocking; just noticing)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah I agree

Comment thread service/history/interfaces/mutable_state.go Outdated
Comment thread service/history/interfaces/mutable_state.go Outdated
@long-nt-tran long-nt-tran force-pushed the nexus-workflow-update branch 7 times, most recently from a453230 to 09ac27a Compare April 27, 2026 14:58
// - The event will be written atomically with acceptance
// If the Update struct is lost (registry cleared), the abort mechanism fires
// registryClearedErr on the caller's future, prompting an immediate retry.
if u.state == stateAdmitted || u.state == stateSent {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

added handling for stateAdmitted, should be same as stateSent but returns false, nil since IIUC caller still needs to create the speculative WFT at this stage

@long-nt-tran long-nt-tran force-pushed the nexus-workflow-update branch from 09ac27a to 9de5339 Compare April 27, 2026 16:05
@long-nt-tran
Copy link
Copy Markdown
Contributor

Made some updates to bring this to latest main, I squashed the base PR to first commit and group each type of change into a subsequent commit for ease of review.

Only logical changes are on on the top commit -- handling stateAdmitted and flushing callbacks to CHASM store before rejecting, and added some more unit tests to test nexus cases + backlinking.

cc @bergundy @Quinn-With-Two-Ns @stephanos

@long-nt-tran long-nt-tran marked this pull request as ready for review April 27, 2026 16:34
@long-nt-tran long-nt-tran requested review from a team as code owners April 27, 2026 16:34
Comment thread go.mod Outdated
Comment thread service/history/workflow/mutable_state_impl.go
Comment thread service/history/workflow/update/update.go
@long-nt-tran long-nt-tran force-pushed the nexus-workflow-update branch from 9de5339 to 4b0915d Compare April 27, 2026 17:52
Comment thread service/history/api/updateworkflow/api.go
@long-nt-tran long-nt-tran force-pushed the nexus-workflow-update branch 2 times, most recently from 8551a4f to 3ae1202 Compare April 27, 2026 20:22
Comment thread chasm/lib/workflow/workflow.go Outdated
@long-nt-tran long-nt-tran force-pushed the nexus-workflow-update branch from 3ae1202 to 2ce7339 Compare April 28, 2026 02:13
Comment thread tests/testcore/test_cluster_pool.go Outdated
long-nt-tran added a commit that referenced this pull request Apr 28, 2026
## What changed?

When we set Nexus callback URL in test_env.go, the dynamic config
override is still tied to the test's lifetime, not the cluster's
lifetime, so a subsequent test that reuse this cluster will not have
that override. Moving the override to onebox.go (similar pattern to
#9918) so this default lives
for the lifetime of the cluster.

## Why?

Ran into issue with task token not set in
#9614, this solves it.
Breaking the fix in a separate PR for ease of review + checking this in
first.

## How did you test it?
- [ ] built
- [ ] run locally and tested manually
- [x] covered by existing tests
- [ ] added new unit test(s)
- [ ] added new functional test(s)
long-nt-tran added a commit that referenced this pull request Apr 29, 2026
## What changed?

Added a `createExternalNexusServer(...)` which sets up an external Nexus
endpoint with user-provided handler and listens on a provided address.
This is used in nexus_workflow_test.go and will be used more in
#9614

Opportunistically did a couple more drive-by refactors/consistency
fixes, specifically:
* Force user to provide `ctx` into the endpoint creation functions
instead of making a new `ctx`
* Use `env.Context()` instead of `testcore.NewContext()` in all suites
that I touched here

## Why?

Pulling changes out of #9614
into targeted PRs to reduce load on reviewers.

## How did you test it?
- [ ] built
- [ ] run locally and tested manually
- [x] covered by existing tests
- [ ] added new unit test(s)
- [ ] added new functional test(s)
Comment thread tests/nexus_workflow_update_test.go Outdated
// TODO (alex-update): This method is noop because we don't currently write rejections to the history.
return nil
func (ms *MutableStateImpl) RejectWorkflowExecutionUpdate(updateID string, wfFailure *failurepb.Failure) error {
if !ms.chasmCallbacksEnabled() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

elsewhere we use chasmEnabled; why use chasmCallbacksEnabled here? if it's intentional, a comment would be helpful.

Comment thread chasm/lib/workflow/workflow.go
Comment thread service/history/workflow/mutable_state_impl.go Outdated
// but update callbacks must fire now because the update was aborted on the old run.
func (w *Workflow) ProcessAllUpdateCloseCallbacks(ctx chasm.MutableContext) error {
for _, updateField := range w.Updates {
if err := callback.ScheduleStandbyCallbacks(ctx, updateField.Get(ctx).Callbacks); err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is my understanding correct that once this returns an error; the entire attempt to schedule callbacks is aborted and retried from the top? We wouldn't want partial results.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

right, I think it's better to abort here and retry -- seems like it'd be harder to reason about if we only partially succeed some updates

Comment on lines +7087 to +7089
if len(wf.Updates) == 0 {
return nil
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this a meaningful perf optimization?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

seems like for now it still has some perf implication based on offline discussion -- added TODOs to cleanup this read-to-write upgrade when it's CHASM natively detects mutation

Comment thread service/history/workflow/mutable_state_impl.go
Comment thread service/history/workflow/mutable_state_impl.go
Comment thread service/history/api/updateworkflow/api.go Outdated
Comment thread service/history/api/updateworkflow/api.go Outdated
Comment thread service/history/workflow/update/update.go Outdated
Comment thread service/history/workflow/update/update.go Outdated
Comment thread service/history/workflow/update/update.go Outdated
Comment thread service/history/workflow/update/update.go Outdated

// flushPendingCallbacks writes one WorkflowExecutionOptionsUpdatedEvent per
// buffered AttachCallbacks callback, skipping any whose requestID is already persisted.
// Called from onAcceptanceMsg after the acceptance event has been written.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This line makes it sound like it's only called from onAcceptanceMsg but that's not true.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

replying for posterity from offline chat -- it should only be called from onAcceptanceMsg (not on rejection)

Comment thread service/history/workflow/update/update.go Outdated
Comment thread service/history/workflow/update/update.go Outdated
Comment thread service/history/workflow/update/update.go Outdated
Comment thread service/history/api/updateworkflow/api.go
"go.temporal.io/server/common/nexus/nexusrpc"
)

type WorkflowUpdate struct {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's not yet clearly documented anywhere that the semantics of a rejected Update with vs without callbacks are different now. If an update has callbacks it will incur a write on rejection now where it didn't before.

I'd update docs/architecture/workflow-update.md and add comments across the update package to clarify that (e.g. pendingCallbacks, onRejectionMsg).

Copy link
Copy Markdown
Contributor Author

@Quinn-With-Two-Ns Quinn-With-Two-Ns May 7, 2026

Choose a reason for hiding this comment

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

If an update has callbacks it will incur a write on rejection now where it didn't before.

That shouldn't be true, the callback is not used for rejection normally (speculative update case). It should only used if the update was durably admitted

Copy link
Copy Markdown
Contributor

@stephanos stephanos May 7, 2026

Choose a reason for hiding this comment

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

I see onRejectionMsg has eventStore EventStore and if the update is in "sent" or "admitted" (ie before "acceptance") it invokes flushPendingCallbacks which - if there are callbacks - invokes AddWorkflowExecutionOptionsUpdatedEvent which AFAICT is a write? And then RejectWorkflowExecutionUpdate also adds another one, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

modified the workflow-update.md to capture the expected buffering -> persist behavior for updates (tldr: buffered when they get admitted/sent, persisted on acceptance, but not on rejection). Essentially the same as before, with the CHASM persistence so we can attach completion callbacks

Comment thread service/history/workflow/mutable_state_impl.go Outdated
Comment thread service/history/workflow/update/update.go Outdated
@long-nt-tran long-nt-tran force-pushed the nexus-workflow-update branch from 239cb1f to 72b65be Compare May 11, 2026 17:33
@long-nt-tran long-nt-tran requested a review from a team as a code owner May 11, 2026 17:33
Comment on lines +26 to +28
type CallbackParent interface {
RemoveCallback(ctx chasm.MutableContext, c *Callback)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@stephanos LMK if this is an idiomatic pattern (self-cleanup of callbacks via optional interface)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AFAIK there's no established pattern for this yet. cc @yycptt

Comment thread service/history/workflow/update/update.go Outdated
Quinn-With-Two-Ns and others added 8 commits May 11, 2026 15:45
Squashed these commits, left for posterity:
- Add Nexus Workflow Update
- Update from rebase
- Fix sent state
- Cleanup
- Fix lint
- Fix more CI
- fix
- Review clean up
- Try suggestions from the review skill
- Fix some tests
- Add TODO for rejected event
- Remove .omc from gitignore
- Respond to PR comments
- Add NS Capability for this feature
- Respond to PR comments
- Update API
@long-nt-tran long-nt-tran force-pushed the nexus-workflow-update branch from 72b65be to 4b7757c Compare May 11, 2026 19:45
Comment thread service/history/api/updateworkflow/api.go
Comment thread service/history/api/updateworkflow/api.go
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 579442b. Configure here.

maxCallbacksPerUpdateID,
updateID,
currentCallbackCount,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicate callbacks can exceed configured limits

Medium Severity

AddUpdateCompletionCallbacks enforces limits before deduplicating existing callback IDs. When the same callbacks are re-applied for an update (for example admission plus acceptance replay paths), valid idempotent re-registration can fail with FailedPrecondition even though no new callbacks would be added.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 579442b. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems legit!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

right, good spot, it seems a bit clunky to assert this (see countNewCallbacks(...) but maybe better than not

Reference: &commonpb.Link_WorkflowEvent_RequestIdRef{
RequestIdRef: &commonpb.Link_WorkflowEvent_RequestIdReference{
RequestId: requestID,
EventType: enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Update backlink may reference wrong request id

Low Severity

The response link uses u.req...RequestId instead of the accepted update’s persisted identifier. For duplicate update_id calls or requests without request_id, Link_WorkflowEvent.RequestIdRef can be empty or mismatched, producing a backlink that does not correspond to the actual accepted event.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 579442b. Configure here.


func (u *Update) AcceptedEventID() int64 {
return u.acceptedEventID
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused exported update accessor added

Low Severity

AcceptedEventID is newly exported but has no callers in the repository. This adds dead public surface to update.Update, which increases maintenance burden and can mislead future code into relying on an accessor that currently has no supported use.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 579442b. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This connects to my comment about Link_WorkflowEvent_EventRef

eq(msg.GetMeta().GetUpdateId(), prefix+"meta.update_id", updateID, updateID, msg),
notZero(msg.GetInput(), prefix+"input", msg),
notZero(msg.GetInput().GetName(), prefix+"input.name", msg),
callbacksRequireRequestID(msg),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Rejection failure is not validated

Medium Severity

validateRejectionMsg only checks that the rejection message exists, not that rejection.failure is present. A nil failure is then forwarded through RejectWorkflowExecutionUpdate, so update callbacks lose the rejection payload and can resolve via the wrong completion path.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 579442b. Configure here.

@long-nt-tran long-nt-tran requested a review from stephanos May 12, 2026 14:10
awln-temporal pushed a commit that referenced this pull request May 12, 2026
## What changed?

Added a `createExternalNexusServer(...)` which sets up an external Nexus
endpoint with user-provided handler and listens on a provided address.
This is used in nexus_workflow_test.go and will be used more in
#9614

Opportunistically did a couple more drive-by refactors/consistency
fixes, specifically:
* Force user to provide `ctx` into the endpoint creation functions
instead of making a new `ctx`
* Use `env.Context()` instead of `testcore.NewContext()` in all suites
that I touched here

## Why?

Pulling changes out of #9614
into targeted PRs to reduce load on reviewers.

## How did you test it?
- [ ] built
- [ ] run locally and tested manually
- [x] covered by existing tests
- [ ] added new unit test(s)
- [ ] added new functional test(s)

if len(request.GetRequest().GetRequestId()) > wh.config.MaxIDLengthLimit() {
return errRequestIDTooLong
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good catch!

return nil, err
}
resp := u.CreateResponse(u.wfKey, status.Outcome, status.Stage)
// Attach a link to the response. For accepted/completed updates, use a WorkflowEvent link
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: let's give this code block a newline above and below to breath

Namespace: u.req.Request.Namespace,
WorkflowId: u.wfKey.WorkflowID,
RunId: u.wfKey.RunID,
Reference: &commonpb.Link_WorkflowEvent_RequestIdRef{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm new to these link events, but why would't we use Link_WorkflowEvent_EventRef since we have an EventId (ie AcceptedEventID)?

}
if got := workflowEvent.GetRequestIdRef().GetEventType(); got != enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED {
return nil, nexus.NewHandlerErrorf(nexus.HandlerErrorTypeInternal, "expected event type UPDATE_ACCEPTED, got %v", got)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this verify that it "points to the accepted event"?

s.NoError(err)

// hasUpdateChasmNode reports whether any CHASM node path references the given update ID.
hasUpdateChasmNode := func(desc *adminservice.DescribeMutableStateResponse) bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure about the functional/acceptance tests reaching that deep into the server internals. It couples the test too much to the impl details for me. Maybe this should be a unit/integration test inside the CHASM package instead?

// from saveResult on terminal transition.
func (u *WorkflowUpdate) RemoveCallback(ctx chasm.MutableContext, c *callback.Callback) {
for id, field := range u.Callbacks {
if field.Get(ctx) == c {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

side note; I was surprised that this is safe. But it appears to be safe. I didn't think that comparing the pointers here would work in CHASM, but apparently that's how it works. I'm not sure we can rely on that, though?

func (u *WorkflowUpdate) RemoveCallback(ctx chasm.MutableContext, c *callback.Callback) {
for id, field := range u.Callbacks {
if field.Get(ctx) == c {
delete(u.Callbacks, id)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm torn between paying the runtime cost of iteration over this and the fact that the map key is essentially "unrecoverable" as it's based on information that's thrown away again - vs storing the key/part of the key so this becomes an O(1) lookup. I acknowledge that there likely won't be many callbacks here, though. So I suppose runtime loop is fine.

// update-level scheduling are independent: failure of one does not stop the
// other; the errors are joined.
func (w *Workflow) ScheduleCloseCallbacks(ctx chasm.MutableContext) error {
wfErr := callback.ScheduleStandbyCallbacks(ctx, w.Callbacks)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: maybe a softassert invariant error if the workflow isn't closed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

needed to expose a function from ms_pointer.go to check state but maybe that's ok

maxCallbacksPerUpdateID,
updateID,
currentCallbackCount,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems legit!


func (u *Update) AcceptedEventID() int64 {
return u.acceptedEventID
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This connects to my comment about Link_WorkflowEvent_EventRef

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.

4 participants