Documentation
¶
Overview ¶
Package gorch is a composable Go orchestrator for managing goroutine lifecycles. It handles start, stop, cron scheduling, and inter-service pub-sub messaging with self-healing restart. Orchestrators can be nested — a service may create and manage its own gorch instance for sub-services.
Index ¶
- Variables
- func RegisterType[T any](m *Messenger) error
- func TypedPublish[T any](m *Messenger, msg T, topics ...string)
- func TypedRequest[TReq, TResp any](m *Messenger, ctx context.Context, req TReq, topic string) (TResp, error)
- func TypedSubscribe[T any](m *Messenger, topic string) (<-chan T, func())
- type Backoff
- type Config
- type ConstantBackoff
- type CronMode
- type ExponentialBackoff
- type HealthChecker
- type LogLevel
- type Message
- type Messenger
- func (m *Messenger) Drain()
- func (m *Messenger) Publish(msg any, topics ...string)
- func (m *Messenger) Request(ctx context.Context, msg any, topic string) (any, error)
- func (m *Messenger) RequestAsync(ctx context.Context, msg any, topic string) (<-chan any, error)
- func (m *Messenger) Subscribe(topic string) (<-chan any, func())
- func (m *Messenger) SubscribeWithBuffer(topic string, bufSize int) (<-chan any, func())
- type Metrics
- type Orchestrator
- func (o *Orchestrator) Count() int
- func (o *Orchestrator) Done() <-chan struct{}
- func (o *Orchestrator) Health() map[string]error
- func (o *Orchestrator) IsReady(name string) bool
- func (o *Orchestrator) Metrics() Metrics
- func (o *Orchestrator) Names() []string
- func (o *Orchestrator) Register(svc Service, opts ...RegisterOption) error
- func (o *Orchestrator) RegisterFunc(name string, startFn func(ctx ServiceContext) error, stopFn func() error, ...) error
- func (o *Orchestrator) Run(stopTimeout time.Duration, signals ...os.Signal) error
- func (o *Orchestrator) Start() error
- func (o *Orchestrator) StartGroup(group string) error
- func (o *Orchestrator) Status(name string) (ServiceStatus, bool)
- func (o *Orchestrator) Statuses() map[string]ServiceStatus
- func (o *Orchestrator) StatusesByGroup(group string) map[string]ServiceStatus
- func (o *Orchestrator) StatusesByLabel(key, value string) map[string]ServiceStatus
- func (o *Orchestrator) Stop(timeout time.Duration) error
- func (o *Orchestrator) StopGroup(group string, timeout time.Duration) error
- func (o *Orchestrator) WaitFor(name string, target ServiceStatus, timeout time.Duration) error
- type ReadinessChecker
- type RegisterOption
- func DependsOn(names ...string) RegisterOption
- func DependsOnSoft(names ...string) RegisterOption
- func WithBackoff(b Backoff) RegisterOption
- func WithCron(spec string, mode CronMode) RegisterOption
- func WithGroup(name string) RegisterOption
- func WithLabel(key, value string) RegisterOption
- func WithMaxRetries(max int) RegisterOption
- func WithName(name string) RegisterOption
- func WithOnAfterStart(fn func(name string, err error)) RegisterOption
- func WithOnAfterStop(fn func(name string, err error)) RegisterOption
- func WithOnBeforeStart(fn func(name string) error) RegisterOption
- func WithOnBeforeStop(fn func(name string) error) RegisterOption
- func WithResetAfter(d time.Duration) RegisterOption
- func WithRunOnce() RegisterOption
- func WithSelfHeal(factory func() Service) RegisterOption
- func WithStartCondition(fn func() bool) RegisterOption
- func WithStartTimeout(d time.Duration) RegisterOption
- func WithStopTimeout(d time.Duration) RegisterOption
- type Service
- type ServiceContext
- type ServiceLogger
- type ServiceStatus
- type Validator
Constants ¶
This section is empty.
Variables ¶
var ( ErrAlreadyStarted = errors.New("gorch: orchestrator already started") ErrInvalidCron = errors.New("gorch: invalid cron expression") ErrStopTimeout = errors.New("gorch: stop timed out waiting for services") ErrDuplicateName = errors.New("gorch: duplicate service name") ErrDependencyCycle = errors.New("gorch: dependency cycle detected") ErrStartAborted = errors.New("gorch: start aborted due to dependency failure") )
Sentinel errors
Functions ¶
func RegisterType ¶ added in v0.2.0
RegisterType registers T with encoding/gob so it can be used with TypedPublish and TypedSubscribe. Must be called before any typed operations for the type. Recovers from gob panics and returns an error if the type is not gob-compatible. Thread-safe. ponytail: standalone func (not method) because Go does not support generic methods on non-generic types.
func TypedPublish ¶ added in v0.2.0
TypedPublish gob-encodes msg and publishes it as a Message to the given topics. Silently drops the message if the type has not been registered via RegisterType. Thread-safe.
func TypedRequest ¶ added in v0.3.0
func TypedRequest[TReq, TResp any](m *Messenger, ctx context.Context, req TReq, topic string) (TResp, error)
TypedRequest sends a typed request and waits for a typed response. It gob-encodes the request, publishes it via Request, and gob-decodes the response. Returns the decoded response or an error.
func TypedSubscribe ¶ added in v0.2.0
TypedSubscribe subscribes to topic and returns a typed receive-only channel and an unsubscribe function. Messages published via TypedPublish are gob-decoded into T before delivery. Non-Message values and unrecognized types are silently dropped. Thread-safe.
Types ¶
type Backoff ¶ added in v0.2.0
Backoff computes the delay before the next retry attempt. retry is 1-based (first retry = 1).
type Config ¶
type Config struct {
LogLevel LogLevel // defaults to LogLevelInfo if zero
// DefaultStartTimeout is the default per-service start deadline.
// 0 means no timeout (use WithStartTimeout per-service).
DefaultStartTimeout time.Duration
// Health check configuration.
// HealthInterval: how often to probe. Default: 30s.
// HealthTimeout: per-probe deadline. Default: 5s.
// HealthThreshold: consecutive failures before restart. Default: 3.
// A zero HealthInterval disables health checks entirely.
HealthInterval time.Duration
HealthTimeout time.Duration
HealthThreshold int
// Global lifecycle hooks (called for every service unless overridden).
OnBeforeStart func(name string) error
OnAfterStart func(name string, err error)
OnBeforeStop func(name string) error
OnAfterStop func(name string, err error)
// State-change callbacks.
OnStateChange func(name string, from, to ServiceStatus)
OnCrash func(name string, err error)
// Health check hooks.
BeforeHealthCheck func(name string) error
AfterHealthCheck func(name string, err error)
}
Config holds orchestrator configuration.
type ConstantBackoff ¶ added in v0.2.0
ConstantBackoff always returns the same delay regardless of retry count.
type CronMode ¶
type CronMode int
CronMode — concurrency policy when cron fires while previous invocation still runs.
type ExponentialBackoff ¶ added in v0.2.0
ExponentialBackoff produces delays: initial * factor^(retry-1), capped at max. ponytail: no jitter; add WithJitter(bool) if thundering-herd becomes a problem.
type HealthChecker ¶ added in v0.2.0
HealthChecker is implemented by services that can report their own health. Health is called periodically by the orchestrator. A non-nil error means the service is unhealthy.
type Message ¶ added in v0.2.0
Message is the envelope for typed pub-sub and request-reply messaging. Publishers encode their payload into Payload; subscribers decode it. ReplyTopic is set automatically by Request/RequestAsync so responders know where to send the reply.
type Messenger ¶
type Messenger struct {
// contains filtered or unexported fields
}
Messenger — pub-sub with topics (Socket.IO rooms style). A nil or empty topics slice in Publish broadcasts to ALL subscribers.
func (*Messenger) Drain ¶ added in v0.3.0
func (m *Messenger) Drain()
Drain closes all subscriber channels and clears all subscriptions. After Drain, the Messenger is empty and no new publishes will be received by prior subscribers. Thread-safe.
func (*Messenger) Publish ¶
Publish sends msg to subscribers. Non-blocking: if a subscriber's channel is full, the message is dropped for that subscriber. Thread-safe. If topics is empty or nil, broadcasts to ALL subscribers on every topic.
func (*Messenger) Request ¶ added in v0.2.0
Request publishes a request message and waits for a single reply. It creates a temporary reply topic, subscribes to it, publishes the request, and returns the first response (or an error if ctx expires). The responding service receives a Message on its channel; it should Publish the response on msg.ReplyTopic. Thread-safe.
func (*Messenger) RequestAsync ¶ added in v0.2.0
RequestAsync is like Request but returns immediately with a response channel. The caller must select on the channel and ctx.Done(). Thread-safe.
func (*Messenger) Subscribe ¶
Subscribe registers interest in a topic. Returns a receive-only channel and an unsubscribe function. The channel is buffered (cap 16). Thread-safe.
func (*Messenger) SubscribeWithBuffer ¶ added in v0.3.0
SubscribeWithBuffer registers interest in a topic with a caller-specified buffer size. Returns a receive-only channel and an unsubscribe function. Thread-safe.
type Orchestrator ¶
type Orchestrator struct {
// contains filtered or unexported fields
}
Orchestrator manages service lifecycles.
func New ¶
func New(cfg Config) *Orchestrator
New creates a new Orchestrator. Each call returns a fresh, independent instance. Orchestrators can be nested: a service may create its own gorch to manage sub-services.
func (*Orchestrator) Count ¶ added in v0.2.0
func (o *Orchestrator) Count() int
Count returns the total number of registered services. Thread-safe.
func (*Orchestrator) Done ¶ added in v0.3.0
func (o *Orchestrator) Done() <-chan struct{}
Done returns a channel that closes when all managed goroutines (services, log-pump, health-check loop) have exited. The orchestrator must be stopped (via Stop or Run returning) before the channel closes.
func (*Orchestrator) Health ¶ added in v0.2.0
func (o *Orchestrator) Health() map[string]error
Health probes all registered services that implement HealthChecker. Returns a map of service name to error (nil = healthy). Services that don't implement HealthChecker are reported as nil. Thread-safe.
func (*Orchestrator) IsReady ¶ added in v0.3.0
func (o *Orchestrator) IsReady(name string) bool
IsReady reports whether a named service is running and ready to serve.
func (*Orchestrator) Metrics ¶ added in v0.3.0
func (o *Orchestrator) Metrics() Metrics
Metrics returns a snapshot of orchestrator-level event counters.
func (*Orchestrator) Names ¶ added in v0.2.0
func (o *Orchestrator) Names() []string
Names returns the names of all registered services in registration order. Thread-safe.
func (*Orchestrator) Register ¶
func (o *Orchestrator) Register(svc Service, opts ...RegisterOption) error
Register adds a service to the orchestrator. Must be called before Start(). Returns ErrAlreadyStarted if the orchestrator has already been started. Returns ErrDuplicateName if WithName conflicts with another service. Returns ErrDependencyCycle if DependsOn introduces a cycle. Thread-safe.
func (*Orchestrator) RegisterFunc ¶ added in v0.3.0
func (o *Orchestrator) RegisterFunc(name string, startFn func(ctx ServiceContext) error, stopFn func() error, opts ...RegisterOption) error
RegisterFunc registers a closure-based service.
func (*Orchestrator) Run ¶ added in v0.2.0
Run starts the orchestrator, blocks on SIGINT/SIGTERM, then stops. Returns any error from Start or aggregated errors from Stop. Optional signals override the default signal set.
func (*Orchestrator) Start ¶
func (o *Orchestrator) Start() error
Start begins the orchestrator lifecycle. Returns ErrAlreadyStarted if already started. Idempotent: calling Start multiple times returns nil after the first. Thread-safe.
func (*Orchestrator) StartGroup ¶ added in v0.3.0
func (o *Orchestrator) StartGroup(group string) error
StartGroup starts all services in the named group in topological order.
func (*Orchestrator) Status ¶ added in v0.2.0
func (o *Orchestrator) Status(name string) (ServiceStatus, bool)
Status returns the current lifecycle status of a named service. ok is false if no service with that name is registered. Thread-safe.
func (*Orchestrator) Statuses ¶ added in v0.2.0
func (o *Orchestrator) Statuses() map[string]ServiceStatus
Statuses returns a map of service name to status for all registered services. Thread-safe.
func (*Orchestrator) StatusesByGroup ¶ added in v0.3.0
func (o *Orchestrator) StatusesByGroup(group string) map[string]ServiceStatus
StatusesByGroup returns a map of service name to status for all services in the named group. Thread-safe.
func (*Orchestrator) StatusesByLabel ¶ added in v0.3.0
func (o *Orchestrator) StatusesByLabel(key, value string) map[string]ServiceStatus
StatusesByLabel returns a map of service name to status for all services matching the given label key-value pair. Thread-safe.
func (*Orchestrator) Stop ¶
func (o *Orchestrator) Stop(timeout time.Duration) error
Stop gracefully shuts down the orchestrator. Waits up to timeout for services to finish. Returns aggregated errors from all Stop failures, or ErrStopTimeout if services don't all stop within the timeout. Thread-safe. Safe to call on an orchestrator that was never started.
func (*Orchestrator) StopGroup ¶ added in v0.3.0
func (o *Orchestrator) StopGroup(group string, timeout time.Duration) error
StopGroup stops all non-cron, non-runOnce services in the named group in reverse topological order. Errors are aggregated via errors.Join.
func (*Orchestrator) WaitFor ¶ added in v0.3.0
func (o *Orchestrator) WaitFor(name string, target ServiceStatus, timeout time.Duration) error
WaitFor blocks until the named service reaches target status or timeout expires. Polls at 50ms intervals. Returns an error on timeout or if the service is not found.
type ReadinessChecker ¶ added in v0.3.0
ReadinessChecker is implemented by services that distinguish "running" from "ready to serve". Ready returns nil when the service can accept traffic.
type RegisterOption ¶
type RegisterOption func(*registerConfig)
RegisterOption — functional options for Register.
func DependsOn ¶ added in v0.2.0
func DependsOn(names ...string) RegisterOption
DependsOn declares that this service must start after the named services and stop before them. Cycles are detected at registration time.
func DependsOnSoft ¶ added in v0.3.0
func DependsOnSoft(names ...string) RegisterOption
DependsOnSoft declares soft dependencies: start after the named services if they are present, but ignore any that are not registered.
func WithBackoff ¶ added in v0.2.0
func WithBackoff(b Backoff) RegisterOption
WithBackoff sets the backoff strategy for self-heal restarts. If nil or not set, the default is 1s constant backoff.
func WithCron ¶
func WithCron(spec string, mode CronMode) RegisterOption
WithCron registers the service to run on a 6-field cron schedule (seconds included).
func WithGroup ¶ added in v0.3.0
func WithGroup(name string) RegisterOption
WithGroup assigns the service to a named group for filtering.
func WithLabel ¶ added in v0.3.0
func WithLabel(key, value string) RegisterOption
WithLabel attaches a key-value label to the service for filtering.
func WithMaxRetries ¶ added in v0.2.0
func WithMaxRetries(max int) RegisterOption
WithMaxRetries sets the maximum number of self-heal restarts. 0 means unlimited (up to context cancellation). After the limit is reached, the service transitions to StatusStopped.
func WithName ¶ added in v0.2.0
func WithName(name string) RegisterOption
WithName assigns a human-readable name used for dependency ordering, status queries, and lifecycle hooks. Names must be unique across all registered services.
func WithOnAfterStart ¶ added in v0.2.0
func WithOnAfterStart(fn func(name string, err error)) RegisterOption
WithOnAfterStart sets a per-service hook called after Start() returns.
func WithOnAfterStop ¶ added in v0.2.0
func WithOnAfterStop(fn func(name string, err error)) RegisterOption
WithOnAfterStop sets a per-service hook called after Stop() returns.
func WithOnBeforeStart ¶ added in v0.2.0
func WithOnBeforeStart(fn func(name string) error) RegisterOption
WithOnBeforeStart sets a per-service hook called just before Start(). If the hook returns an error, Start() is aborted for this service.
func WithOnBeforeStop ¶ added in v0.2.0
func WithOnBeforeStop(fn func(name string) error) RegisterOption
WithOnBeforeStop sets a per-service hook called just before Stop(). If the hook returns an error, Stop() is still called.
func WithResetAfter ¶ added in v0.2.0
func WithResetAfter(d time.Duration) RegisterOption
WithResetAfter sets a stability window. If the service runs continuously for this duration without crashing, the retry counter resets to zero.
func WithRunOnce ¶ added in v0.2.0
func WithRunOnce() RegisterOption
WithRunOnce marks a service as a one-shot init task. It runs before persistent services, never receives Stop(), and transitions to StatusStopped when Start returns. If Start returns an error, startup aborts.
func WithSelfHeal ¶
func WithSelfHeal(factory func() Service) RegisterOption
WithSelfHeal enables auto-restart: when the service crashes (returns error or panics), the orchestrator calls factory() for a fresh instance and restarts it.
func WithStartCondition ¶ added in v0.3.0
func WithStartCondition(fn func() bool) RegisterOption
WithStartCondition sets a function called at startup. If it returns false, the service is skipped (not started). nil or not set means always start.
func WithStartTimeout ¶ added in v0.2.0
func WithStartTimeout(d time.Duration) RegisterOption
WithStartTimeout sets the maximum time to wait for this service's Start to return. Overrides Config.DefaultStartTimeout. A zero duration means no timeout (use with caution).
func WithStopTimeout ¶ added in v0.3.0
func WithStopTimeout(d time.Duration) RegisterOption
WithStopTimeout sets a per-service timeout on Stop(). If Stop() does not return within this duration, the orchestrator proceeds with shutdown.
type Service ¶
type Service interface {
Start(ctx context.Context) error // blocks; for cron: runs per-tick; for non-cron: runs until ctx cancelled
Stop() error // cleanup signal beyond context cancellation
}
Service — every managed goroutine implements this.
type ServiceContext ¶
type ServiceContext struct {
context.Context
Logger *ServiceLogger
Messenger *Messenger
}
ServiceContext — what the orchestrator hands each service. Embeds context.Context so it satisfies the context.Context interface and can be passed directly to Service.Start. Carries the orchestrator's cancellation context.
type ServiceLogger ¶
type ServiceLogger struct {
// contains filtered or unexported fields
}
ServiceLogger — a logger that doesn't log; it sends entries to gorch's log channel. gorch consumes the channel and does the actual output (formatting, writing to stderr).
func (*ServiceLogger) Debug ¶
func (l *ServiceLogger) Debug(msg string, args ...any)
func (*ServiceLogger) Error ¶
func (l *ServiceLogger) Error(msg string, args ...any)
func (*ServiceLogger) Info ¶
func (l *ServiceLogger) Info(msg string, args ...any)
func (*ServiceLogger) Warn ¶
func (l *ServiceLogger) Warn(msg string, args ...any)
type ServiceStatus ¶ added in v0.2.0
type ServiceStatus int
ServiceStatus represents the lifecycle state of a registered service.
const ( StatusRegistered ServiceStatus = iota StatusStarting StatusRunning StatusStopping StatusStopped StatusCrashed )
func (ServiceStatus) String ¶ added in v0.2.0
func (s ServiceStatus) String() string
String returns a human-readable name for the status.