Skip to content

Added the command that unsubscribes a workflow from a channel. - #15

Closed
moetemp wants to merge 4 commits into
moe/AI-198-srv-11-describe-channelsfrom
moe/AI-198-srv-12-unsubscribe
Closed

moetemp wants to merge 4 commits into
moe/AI-198-srv-11-describe-channelsfrom
moe/AI-198-srv-12-unsubscribe

Conversation

@moetemp

@moetemp moetemp commented Oct 2, 2026

Copy link
Copy Markdown
Owner

This PR adds the command that ends a workflow's subscription to a notification channel.

What changed?

  • UnsubscribeNotificationChannel removes the run's subscription record and the notification waiting on it, and writes WorkflowNotificationChannelUnsubscribed with the channel and the id of the subscribe event it ends. A notification a scheduled event already carries stays in History.
  • Dropping the run from the channel's listeners is staged for the completion path, the registration path reversed: a new internal routed UnregisterWorkflowListener on the channel service forgets the run on the channel's shard. Deregistrations run before registrations and skip a channel the run subscribes to again by the end of the task, so unsubscribe-then-subscribe in one task keeps the listener and subscribe-then-unsubscribe leaves none. A registration for a channel the run left in the same task is skipped too.
  • A command for a channel the run does not listen to records its event with a zero subscribe event id and changes nothing, for the reason a duplicate subscribe does: every command needs its event or replay matching desyncs. A linked channel has no subscription, so a command naming one is this case.
  • Validation fails the task with BAD_UNSUBSCRIBE_NOTIFICATION_CHANNEL_ATTRIBUTES for missing attributes or an empty or over-long name only. The command is listed with the non-closing commands in the attribute validator.
  • A delivery that reaches the run after the unsubscribe finds no subscription and is dropped, and the channel forgets the listener, which is the existing fan-out rule. Subscribing again in the same run records a new event, registers again and is handed the channel's latest.
  • Reset re-applies subscribe and unsubscribe events in order, so a reset run ends with the subscriptions the source had at the reset point. DescribeWorkflowExecution drops the entry with the record.
  • tests/unsubscribe_channel_test.go covers the lifecycle, the no-subscription cases, same-task pairs, validation and reset. Unit tests cover the removal on the Workflow component.

Part of AI-198 (epic AI-37).

Why?

Until now a subscription ended only with the run, so a workflow that stopped reading a stream kept being woken by its channel for the rest of its life, and a long-lived workflow could not rotate channels under the per-workflow limit. The external stream reader can now unsubscribe when its reader closes or reaches the end.

How did you test it?

  • Unit Tests
  • Staging
  • End to End Tests

go build ./..., golangci-lint and the errortype vet are clean on the touched packages. Unit tests ran for ./chasm/lib/workflow/..., ./chasm/lib/channel/..., ./service/history/api/... and the completion handler. The functional tests ran with -tags disable_grpc_modules,test_dep: an unsubscribe records its event naming the subscribe event, the channel and describe drop the run, a notify afterwards wakes nothing while the channel retains it, and subscribing again records a new event and is handed the latest. An unknown channel and a linked name record events with no subscribe event and change nothing, subscribe and unsubscribe in one task leave no listener and never reach the channel, the reverse keeps the listener under the new event. An empty name fails the task with the new cause and the retry completes clean. A reset run keeps only the subscription the source still had, the dropped channel forgets the reset-away run on its next notify, and the kept channel reaches the reset run. The notification channel, linked channel, inline handoff, stream channel and describe suites pass.

A subscription ended only with the run, so a workflow that stopped reading kept being woken. The command removes the record and its pending notification, writes its event naming the subscribe event, and the completion drops the run from the channel's listeners the way the registration added it. A reset run re-applies both events in order.
@moetemp

moetemp commented Oct 3, 2026

Copy link
Copy Markdown
Owner Author

Replaced by #18, #19, #20, #21, #22, #23, #24, #25, #26, #27, #28, #29, #30, #31, #32, #33, #34, #35, #36 and #37.

Same content, split into 20 PRs in the v3 series: the notification channel first, then the streaming interface, then native streams and the rest. The branch stays as a pin.

@moetemp moetemp closed this Oct 3, 2026
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.

1 participant