Skip to main content
Not everything happens during a request. This guide covers the patterns for running work in the background, from a quick goroutine to a dedicated worker binary.

Managed Goroutines

You should avoid starting a bare go func() in a Copper application. Instead, use Lifecycle.Go; Copper recovers panics for you and will automatically wait for the goroutine to finish during graceful shutdown:

Long-Running Workers

For a worker loop, combine Lifecycle.Go with Lifecycle.Shutdown(), which closes when the app begins shutting down:
pkg/telemetry/poller.go
To learn more about the shutdown mechanics, see App Lifecycle.

A Dedicated Worker Binary

When background work outgrows the web process, you may give it its own binary. Add a cmd/jobs directory with the same main.go/wire.go shape as cmd/app, wire the same app.WireModule, and start a runner instead of the HTTP server:
cmd/jobs/main.go
A runner is anything with a Run() error method (the copper.Runner interface). Both binaries share one dependency graph, and copper build compiles every target under cmd/, so deploying the worker is simply deploying another binary from the same codebase.

Work Tied to a Transaction

Sometimes background work should only happen if the surrounding database transaction commits, such as sending an email or publishing an event. In that case, register the work with querier.OnCommit instead of acting immediately; if the transaction rolls back, the callback never runs. To learn more, see Transactions.

Reacting to Events

When one action should trigger several reactions, you may publish an event and let interested packages subscribe. See Pub/Sub.

Scheduled Work

The simplest cron in a Copper app is an HTTP route. Add an /internal/... route that performs the work, and have your scheduler (crontab, Cloud Scheduler, Kubernetes CronJob) POST to it:
This approach gives you request logging, metrics, and manual triggering for free, since running the job by hand is just a curl. However, you should keep /internal/ routes unexposed at the network layer, since the application does not authenticate them.