diff --git a/README.md b/README.md index 6839c03..0e06151 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,14 @@ shutdown complete - [func EveryInterval\(d time.Duration, fn func\(WorkerContext\) error\) func\(WorkerContext\) error](<#EveryInterval>) - [func Run\(ctx context.Context, workers \[\]\*Worker, opts ...RunOption\) error](<#Run>) - [func RunWorker\(ctx context.Context, w \*Worker, opts ...RunOption\)](<#RunWorker>) +- [type BaseMetrics](<#BaseMetrics>) + - [func \(BaseMetrics\) ObserveRunDuration\(string, time.Duration\)](<#BaseMetrics.ObserveRunDuration>) + - [func \(BaseMetrics\) SetActiveWorkers\(int\)](<#BaseMetrics.SetActiveWorkers>) + - [func \(BaseMetrics\) WorkerFailed\(string, error\)](<#BaseMetrics.WorkerFailed>) + - [func \(BaseMetrics\) WorkerPanicked\(string\)](<#BaseMetrics.WorkerPanicked>) + - [func \(BaseMetrics\) WorkerRestarted\(string, int\)](<#BaseMetrics.WorkerRestarted>) + - [func \(BaseMetrics\) WorkerStarted\(string\)](<#BaseMetrics.WorkerStarted>) + - [func \(BaseMetrics\) WorkerStopped\(string\)](<#BaseMetrics.WorkerStopped>) - [type Metrics](<#Metrics>) - [func NewPrometheusMetrics\(namespace string\) Metrics](<#NewPrometheusMetrics>) - [type RunOption](<#RunOption>) @@ -210,7 +218,7 @@ shutdown complete -## func BatchChannelWorker +## func [BatchChannelWorker]() ```go func BatchChannelWorker[T any](ch <-chan T, maxSize int, maxDelay time.Duration, fn func(WorkerContext, []T) error) func(WorkerContext) error @@ -265,7 +273,7 @@ func main() { -## func ChannelWorker +## func [ChannelWorker]() ```go func ChannelWorker[T any](ch <-chan T, fn func(WorkerContext, T) error) func(WorkerContext) error @@ -321,7 +329,7 @@ world -## func EveryInterval +## func [EveryInterval]() ```go func EveryInterval(d time.Duration, fn func(WorkerContext) error) func(WorkerContext) error @@ -373,7 +381,7 @@ tick 2 -## func Run +## func [Run]() ```go func Run(ctx context.Context, workers []*Worker, opts ...RunOption) error @@ -429,7 +437,7 @@ all workers stopped -## func RunWorker +## func [RunWorker]() ```go func RunWorker(ctx context.Context, w *Worker, opts ...RunOption) @@ -478,10 +486,93 @@ done

+ +## type [BaseMetrics]() + +BaseMetrics provides no\-op implementations of all Metrics methods. Embed it in custom Metrics implementations so that new methods added to the Metrics interface in future versions get safe no\-op defaults instead of breaking your build: + +``` +type myMetrics struct { + workers.BaseMetrics // forward-compatible + client *statsd.Client +} + +func (m *myMetrics) WorkerStarted(name string) { + m.client.Incr("worker.started", []string{"worker:" + name}, 1) +} +``` + +```go +type BaseMetrics struct{} +``` + + +### func \(BaseMetrics\) [ObserveRunDuration]() + +```go +func (BaseMetrics) ObserveRunDuration(string, time.Duration) +``` + + + + +### func \(BaseMetrics\) [SetActiveWorkers]() + +```go +func (BaseMetrics) SetActiveWorkers(int) +``` + + + + +### func \(BaseMetrics\) [WorkerFailed]() + +```go +func (BaseMetrics) WorkerFailed(string, error) +``` + + + + +### func \(BaseMetrics\) [WorkerPanicked]() + +```go +func (BaseMetrics) WorkerPanicked(string) +``` + + + + +### func \(BaseMetrics\) [WorkerRestarted]() + +```go +func (BaseMetrics) WorkerRestarted(string, int) +``` + + + + +### func \(BaseMetrics\) [WorkerStarted]() + +```go +func (BaseMetrics) WorkerStarted(string) +``` + + + + +### func \(BaseMetrics\) [WorkerStopped]() + +```go +func (BaseMetrics) WorkerStopped(string) +``` + + + -## type Metrics +## type [Metrics]() -Metrics collects worker lifecycle metrics. Implement this interface to provide custom metrics \(e.g., Datadog, StatsD\). Use NoopMetrics to disable metrics, or NewPrometheusMetrics for the built\-in Prometheus implementation. +Metrics collects worker lifecycle metrics. Implement this interface to provide custom metrics \(e.g., Datadog, StatsD\). Use BaseMetrics\{\} to disable metrics, or NewPrometheusMetrics for the built\-in Prometheus implementation. ```go type Metrics interface { @@ -495,14 +586,8 @@ type Metrics interface { } ``` -NoopMetrics is a no\-op implementation of Metrics. Used as the default when no metrics are configured via WithMetrics. - -```go -var NoopMetrics Metrics = &noopMetrics{} -``` - -### func NewPrometheusMetrics +### func [NewPrometheusMetrics]() ```go func NewPrometheusMetrics(namespace string) Metrics @@ -511,7 +596,7 @@ func NewPrometheusMetrics(namespace string) Metrics NewPrometheusMetrics creates a Metrics implementation backed by Prometheus. The namespace is prepended to all metric names \(e.g., "myapp" → "myapp\_worker\_started\_total"\). Metrics are auto\-registered with the default Prometheus registry. Safe to call multiple times with the same namespace — returns the cached instance. The cache is process\-global; use a small number of static namespaces \(not per\-request/tenant values\). -## type RunOption +## type [RunOption]() RunOption configures the behavior of Run. @@ -520,16 +605,16 @@ type RunOption func(*runConfig) ``` -### func WithMetrics +### func [WithMetrics]() ```go func WithMetrics(m Metrics) RunOption ``` -WithMetrics sets the metrics implementation for all workers started by Run. Workers inherit this unless they override via Worker.WithMetrics. If not set, NoopMetrics is used. +WithMetrics sets the metrics implementation for all workers started by Run. Workers inherit this unless they override via Worker.WithMetrics. If not set, BaseMetrics\{\} is used. -## type Worker +## type [Worker]() Worker represents a background goroutine managed by the framework. Create with NewWorker and configure with builder methods. @@ -540,7 +625,7 @@ type Worker struct { ``` -### func NewWorker +### func [NewWorker]() ```go func NewWorker(name string, run func(WorkerContext) error) *Worker @@ -588,7 +673,7 @@ worker "greeter" started (attempt 0) -### func \(\*Worker\) Every +### func \(\*Worker\) [Every]() ```go func (w *Worker) Every(d time.Duration) *Worker @@ -638,7 +723,7 @@ tick 2 -### func \(\*Worker\) WithBackoffJitter +### func \(\*Worker\) [WithBackoffJitter]() ```go func (w *Worker) WithBackoffJitter(jitter suture.Jitter) *Worker @@ -647,7 +732,7 @@ func (w *Worker) WithBackoffJitter(jitter suture.Jitter) *Worker WithBackoffJitter adds random jitter to the backoff duration to prevent thundering herd on coordinated restarts. -### func \(\*Worker\) WithFailureBackoff +### func \(\*Worker\) [WithFailureBackoff]() ```go func (w *Worker) WithFailureBackoff(d time.Duration) *Worker @@ -656,7 +741,7 @@ func (w *Worker) WithFailureBackoff(d time.Duration) *Worker WithFailureBackoff sets the duration to wait between restarts. Suture default is 15 seconds. -### func \(\*Worker\) WithFailureDecay +### func \(\*Worker\) [WithFailureDecay]() ```go func (w *Worker) WithFailureDecay(decay float64) *Worker @@ -665,7 +750,7 @@ func (w *Worker) WithFailureDecay(decay float64) *Worker WithFailureDecay sets the rate at which failure count decays over time. A value of 1.0 means failures decay by one per second. Suture default is 1.0. -### func \(\*Worker\) WithFailureThreshold +### func \(\*Worker\) [WithFailureThreshold]() ```go func (w *Worker) WithFailureThreshold(threshold float64) *Worker @@ -674,7 +759,7 @@ func (w *Worker) WithFailureThreshold(threshold float64) *Worker WithFailureThreshold sets the number of failures allowed before the supervisor gives up restarting. Suture default is 5. -### func \(\*Worker\) WithMetrics +### func \(\*Worker\) [WithMetrics]() ```go func (w *Worker) WithMetrics(m Metrics) *Worker @@ -683,7 +768,7 @@ func (w *Worker) WithMetrics(m Metrics) *Worker WithMetrics sets a per\-worker metrics implementation, overriding the metrics inherited from the parent WorkerContext or Run options. -### func \(\*Worker\) WithRestart +### func \(\*Worker\) [WithRestart]() ```go func (w *Worker) WithRestart(restart bool) *Worker @@ -732,7 +817,7 @@ func main() { -### func \(\*Worker\) WithTimeout +### func \(\*Worker\) [WithTimeout]() ```go func (w *Worker) WithTimeout(d time.Duration) *Worker @@ -741,7 +826,7 @@ func (w *Worker) WithTimeout(d time.Duration) *Worker WithTimeout sets the maximum time to wait for the worker to stop during graceful shutdown. Suture default is 10 seconds. -## type WorkerContext +## type [WorkerContext]() WorkerContext extends context.Context with worker metadata and dynamic child worker management. The framework creates these — users never need to implement this interface. diff --git a/metrics.go b/metrics.go index a30a86f..b1beb19 100644 --- a/metrics.go +++ b/metrics.go @@ -15,7 +15,7 @@ var ( // Metrics collects worker lifecycle metrics. // Implement this interface to provide custom metrics (e.g., Datadog, StatsD). -// Use NoopMetrics to disable metrics, or NewPrometheusMetrics for the built-in +// Use BaseMetrics{} to disable metrics, or NewPrometheusMetrics for the built-in // Prometheus implementation. type Metrics interface { WorkerStarted(name string) @@ -27,19 +27,28 @@ type Metrics interface { SetActiveWorkers(count int) } -// NoopMetrics is a no-op implementation of Metrics. Used as the default -// when no metrics are configured via WithMetrics. -var NoopMetrics Metrics = &noopMetrics{} - -type noopMetrics struct{} - -func (n *noopMetrics) WorkerStarted(string) {} -func (n *noopMetrics) WorkerStopped(string) {} -func (n *noopMetrics) WorkerPanicked(string) {} -func (n *noopMetrics) WorkerFailed(string, error) {} -func (n *noopMetrics) WorkerRestarted(string, int) {} -func (n *noopMetrics) ObserveRunDuration(string, time.Duration) {} -func (n *noopMetrics) SetActiveWorkers(int) {} +// BaseMetrics provides no-op implementations of all Metrics methods. +// Embed it in custom Metrics implementations so that new methods added +// to the Metrics interface in future versions get safe no-op defaults +// instead of breaking your build: +// +// type myMetrics struct { +// workers.BaseMetrics // forward-compatible +// client *statsd.Client +// } +// +// func (m *myMetrics) WorkerStarted(name string) { +// m.client.Incr("worker.started", []string{"worker:" + name}, 1) +// } +type BaseMetrics struct{} + +func (BaseMetrics) WorkerStarted(string) {} +func (BaseMetrics) WorkerStopped(string) {} +func (BaseMetrics) WorkerPanicked(string) {} +func (BaseMetrics) WorkerFailed(string, error) {} +func (BaseMetrics) WorkerRestarted(string, int) {} +func (BaseMetrics) ObserveRunDuration(string, time.Duration) {} +func (BaseMetrics) SetActiveWorkers(int) {} // prometheusMetrics implements Metrics using Prometheus counters, histograms, // and gauges registered via promauto. diff --git a/run.go b/run.go index 8cdb297..dd11fc7 100644 --- a/run.go +++ b/run.go @@ -20,10 +20,12 @@ type runConfig struct { // WithMetrics sets the metrics implementation for all workers started by Run. // Workers inherit this unless they override via Worker.WithMetrics. -// If not set, NoopMetrics is used. +// If not set, BaseMetrics{} is used. func WithMetrics(m Metrics) RunOption { return func(c *runConfig) { - c.metrics = m + if m != nil { + c.metrics = m + } } } @@ -97,7 +99,7 @@ func resolveMetrics(w *Worker, parent Metrics) Metrics { if parent != nil { return parent } - return NoopMetrics + return BaseMetrics{} } // addWorkerToSupervisor creates a child supervisor for the worker, @@ -116,7 +118,7 @@ func addWorkerToSupervisor(parent *suture.Supervisor, w *Worker, metrics Metrics // A worker exiting early (without restart) does not stop other workers. // Returns nil on clean shutdown. func Run(ctx context.Context, workers []*Worker, opts ...RunOption) error { - cfg := &runConfig{metrics: NoopMetrics} + cfg := &runConfig{metrics: BaseMetrics{}} for _, opt := range opts { opt(cfg) } diff --git a/worker.go b/worker.go index ef59284..b407385 100644 --- a/worker.go +++ b/worker.go @@ -117,7 +117,7 @@ func (wc *workerContext) Children() []string { func newWorkerContext(ctx context.Context, name string, attempt int, sup *suture.Supervisor, metrics Metrics, active *atomic.Int32) WorkerContext { if metrics == nil { - metrics = NoopMetrics + metrics = BaseMetrics{} } return &workerContext{Context: ctx, name: name, attempt: attempt, sup: sup, metrics: metrics, active: active} }