Skip to content

Add Scope API: context and settlement support - #109

Merged
destel merged 18 commits into
mainfrom
f/context
Sep 3, 2026
Merged

destel merged 18 commits into
mainfrom
f/context

Conversation

@destel

@destel destel commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Blocking sinks in rill return as soon as their outcome is known, which can happen before the entire input is
consumed and processed. That's deliberate – the sooner we get control back, the sooner we can cancel the context
to prevent the current and future work.

Such a model gives promptness, but does not expose an "everything is done" event (also called
settlement), so any work happening after the early return stays unobservable. That event might be
necessary to free a shared resource, or to safely observe side effects the callbacks produced. Simply
put, users sometimes need sync.WaitGroup.Wait(), but for pipelines.

This PR adds a Scope API that combines both the first class Context integration
and a Wait method that blocks until the pipeline settles.

scope, ctx := rill.NewScope(ctx)
defer scope.Cancel()

// other pipeline stages go here

err := rill.ForEach(stream, 5, func(x int) error {
	return process(ctx, x)
}, scope)
scope.Wait()

A scope exposes two methods, Wait and Cancel. Both cancel the associated context, but Wait also
blocks until the pipeline is settled. At least one of them must be called to ensure the underlying
context does not leak.

Settlement is opt-in. Wait goes wherever the code first needs the pipeline fully stopped: right after the sink, after the error handling, or in a defer, so the function returns settled. If Wait is omitted, a scope with a deferred Cancel behaves like a normal cancellable context, which is still useful for stopping the remaining context-aware work.

How it works

We say a pipeline stage is settled once it won't do any more work: it has fully consumed its input, and
every callback it started has returned.

Most stages already expose this, they just don't call it that. Map, Filter, FromSlice and friends
close their output channel only after they've settled, so a closed output is the settlement signal. And
it composes backwards: a settled stage means the stage before it closed its output, and so on up to the
source. Sinks were the hole – no output channel, nothing to close, no signal.

A scope's Wait blocks until every sink attached to it settles. By the inductive argument above, that means
that all upstream stages, and hence the entire pipeline, have also settled.

Compared with errgroup

Most Go developers know errgroup, and the scope API was designed to be similar, while staying
pipeline friendly. The table below shows both similarities and differences.

errgroup rill
creates a cancellable context yes yes
on cancellation, stops work already in flight no – callbacks must watch the context no – callbacks must watch the context
on cancellation, prevents new work from starting no – the code must watch the context and stop calling g.Go no – the source must watch the context and stop producing
nothing runs after Wait yes yes
how the error is delivered via Wait via the sink, before Wait
when Wait can be called almost anytime after the sink has returned
when Wait cancels the context when it returns before it blocks (sink outcome already known)
who cancels the context after an error the group itself typically the Wait that follows the sink call, or a deferred Cancel

Compatibility

Existing call sites keep working, since the options are variadic. But every sink's signature changed, so
code that assigns a sink to an exact function type has to be updated. Technically, this is a breaking
API change.

@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.87%. Comparing base (8f859f2) to head (bec442b).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #109   +/-   ##
=======================================
  Coverage   99.87%   99.87%           
=======================================
  Files          16       17    +1     
  Lines         788      806   +18     
=======================================
+ Hits          787      805   +18     
  Misses          1        1           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@destel destel changed the title Add Scope: context and settlement support Add context and settlement support Sep 1, 2026
@destel destel added the new label Sep 1, 2026
@destel destel changed the title Add context and settlement support Add Scope API: context and settlement support Sep 2, 2026
@destel
destel merged commit 049d498 into main Sep 3, 2026
6 checks passed
@destel
destel deleted the f/context branch September 3, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant