Skip to content

refactor(adapters): chi-style middleware via WithAdapters#6

Closed
klaidliadon wants to merge 3 commits into
feat/drain-and-tickerfrom
refactor/adapters-middleware
Closed

refactor(adapters): chi-style middleware via WithAdapters#6
klaidliadon wants to merge 3 commits into
feat/drain-and-tickerfrom
refactor/adapters-middleware

Conversation

@klaidliadon
Copy link
Copy Markdown
Contributor

@klaidliadon klaidliadon commented May 12, 2026

Reshapes the v0.2 adapters into chi-style middleware. Each adapter is now a config-only constructor that returns a runnable.Adapter, and runnable.WithAdapters wraps the runnable's runFunc with the supplied list (first-listed = outermost). Work stays positional on runnable.New; adapters become decoration that composes with other Options.

Closes the marino39 review item asking for a chi-style middleware factoring. Stacked on top of #4 — review against feat/drain-and-ticker, not master.


Commits

  • f7fa106 — RunFunc/Adapter types and WithAdapters Option in core; Draining and Ticker reshaped as runnable.Adapter constructors.
  • 2432924 — Recovering and Retry migrated to adapters; WithRecoverer and WithRetry removed; Status.Restarts dropped (it counted retry re-entries via an onStart side-channel that doesn't survive the migration).
  • 768f979 — README and adapters doc comment rewritten for the WithAdapters shape.

Shape

r := runnable.New(reconcile, runnable.WithAdapters(
    adapters.Draining(10*time.Second),
    adapters.Recovering(panicHandler),
    adapters.Retry(3, time.Minute),
    adapters.Ticker(2*time.Second),
))

vs. v0.1:

r := runnable.NewTicker(2*time.Second, reconcile,
    runnable.WithDrain(10*time.Second),
    runnable.WithRecoverer(reporter, nil),
    runnable.WithRetry(3, time.Minute),
)

Test plan

  • go test -race -count=1 ./... — passes (runnable 6.1s, runnable/adapters 2.8s).
  • golangci-lint run ./... — clean.
  • Each commit builds and tests green standalone (verified by stashing later commits and running build+test before each commit).

Notes

  • runnable.Adapter lives in the core package so runnable.WithAdapters doesn't have to import the adapters subpackage. The adapters package depends on runnable, not the other way around.
  • Status.Restarts is gone, pending a proper event/observer hook in a later release. Documented in the migration section of the README.

Introduce runnable.RunFunc and runnable.Adapter (func(next RunFunc) RunFunc)
as the core extension point, and runnable.WithAdapters Option that
wraps the runnable's runFunc with the supplied adapters left-to-right
(first listed = outermost wrapper).

Reshape adapters.Draining and adapters.Ticker as config-only
constructors that return runnable.Adapter — work is supplied via
runnable.New, not via the adapter call. Composition becomes:

    runnable.New(reconcile, runnable.WithAdapters(
        adapters.Draining(10*time.Second),
        adapters.Ticker(2*time.Second),
    ))

The adapters package now imports runnable for the Adapter type;
runnable does not import adapters. Stop and runnable lifecycle are
unchanged.
Add adapters.Recovering (with a single PanicHandler callback,
collapsing the v0.1 RecoveryReporter / StackPrinter split) and
adapters.Retry, and remove the runnable.WithRecoverer and
runnable.WithRetry Options they replace.

Drop Status.Restarts: the field counted WithRetry re-entries via
the onStart coupling and has no clean way to surface now that retry
lives outside core. A later release can reintroduce per-attempt
observability via an explicit event channel.

Update examples/main.go and examples/ticker-with-drain to use the new
adapters via runnable.WithAdapters.
Update the Adapters and Migrating-from-v0.1 sections of README and the
adapters package doc comment to show the runnable.WithAdapters Option
shape. Add Recovering and Retry to the migration table and call out
the Status.Restarts removal.
@klaidliadon
Copy link
Copy Markdown
Contributor Author

Superseded — content cherry-picked into #4 (commits b80a033, 92b5063, c5f4101). PR #4 now ships the final v0.2 chi-style middleware shape end-to-end.

klaidliadon added a commit that referenced this pull request May 13, 2026
Moves cross-cutting behaviors (drain-on-shutdown, periodic execution,
panic recovery, retry) out of the core lifecycle and into the new
runnable/adapters subpackage as chi-style middleware. Motivated by the
OMSX Frontegg reconciler needing shutdown-safe periodic execution.
Core Run/Stop semantics for existing callers are unchanged unless they
opt in.

Final shape:

    r := runnable.New(reconcile, runnable.WithAdapters(
        adapters.Draining(10*time.Second),
        adapters.Recovering(handler),
        adapters.Retry(3, time.Minute),
        adapters.Ticker(2*time.Second),
    ))

Core (runnable/):
- runnable.RunFunc and runnable.Adapter types as the extension point.
- runnable.WithAdapters(...Adapter) Option, applied left-to-right
  (first listed = outermost).
- Stop drain logic removed; drain is now an opt-in adapter.
- WithDrain, NewTicker, WithRetry, WithRecoverer Options removed.
- Status.Restarts removed (returns in a stacked PR via Publisher).
- CI workflow reads Go version from go.mod (was pinned to 1.20).

Adapters (runnable/adapters/):
- Draining(timeout) — graceful shutdown via Stopping(ctx); panics
  recovered on the spawned goroutine as a safety net.
- Ticker(interval) — periodic execution; composes with Draining so
  an in-flight tick finishes before exit.
- Recovering(handler) — converts panics into errors.
- Retry(maxRetries, resetAfter) — re-invokes on non-context errors.
- Stopping(ctx) — drain-start signal for long-running work.
- ErrDrainTimedOut — returned when the drain window expires.

NewGroup correctly propagates drain to Draining-wrapped children
(silently broken in v0.1).

Closes #4.
Supersedes #6 (folded in).
@klaidliadon klaidliadon deleted the refactor/adapters-middleware branch May 13, 2026 14:07
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