- Docs
- Concepts
- Lifecycle
Lifecycle
Follow one Zever process from config load to container close, and which failures belong to which phase.
A Zever process runs four phases: load config, build the container, resolve services on first use, and close. Knowing the phase tells you whether a failure is fail-fast, retryable, or a shutdown problem.
The four phases
Section titled “The four phases”config.LoadEnv -> container.New -> resolve on first use -> Close(ctx) env one per c.DB(), c.Cache()... dependents process retry on error first, leaves laterBuild one container per process; no globals or init() wiring; container.New(nil) is valid.
- 1. Load config: env merges into one
Config; failures areInvalidOptionsErroror unparsable values. Validation runs at load, so an empty adapter name or invalid option value fails in phase 1, not mid-request. An adapter name nobody registered passes load and fails on first resolution withUnknownAdapterError.config.FromEnv()/config.LoadEnv()reads instance-aware env vars; legacy flat format triggerswarnLegacyConfigFilelogging; see the Config reference for keys.Default()dev secrets belong in tests only. - 2. Build container: one
Containerowns future connections;Newnever dials andnilcfg is valid. - 3a. First resolution: adapter constructed and cached; on error the cache clears and the next call retries.
- 3b. Steady serving: cached instance reused per accessor; per-call errors only, no rebuild.
- 3c. Shared resolution:
Scheduler()resolvesJob()thenQueue(); one shared connection. - 4. Close: resolved services shut down in order;
errors.Joinof per-service errors.
Load config once at boot
Section titled “Load config once at boot”Config layers as defaults < env: env is best-effort (stray variables ignored). Validation runs at load, so an empty adapter name or invalid option value fails in phase 1, not mid-request. An adapter name nobody registered passes load and fails on first resolution with UnknownAdapterError. config.FromEnv() / config.LoadEnv() reads instance-aware env vars (<SERVICE>__<NAME>__<FIELD>); legacy flat format (<SERVICE>_<FIELD>) triggers warnLegacyConfigFile logging; see the Config reference for keys. Default() dev secrets belong in tests only.
Resolve lazily and reuse
Section titled “Resolve lazily and reuse”Resolve inside handlers and reuse the cached instance. On error the cache clears, so the next call retries instead of pinning a broken handle. Job() dispatches over the resolved Queue; Scheduler() shares it.
Close in order
Section titled “Close in order”Stop the HTTP server first, then close the container under a timeout parent (context.WithTimeout, about 10s). See Deployment for the full shutdown wiring.
Close never opens anything, so an unused service cannot fail shutdown. Dependents (scheduler, job, agent, rag) close first, then leaves (cache, queue), then db and shared pools, and each service gets a 5s bound without a parent deadline; shared instances close once.
- Panic during close:
container.ClosePanicError; the adapter panicked and shutdown continued. - Per-service timeout:
container.CloseTimeoutError; the adapter exceeded its bound. - Several failures:
errors.Join; inspect each witherrors.As.
Design decisions and trade-offs
Section titled “Design decisions and trade-offs”Error values name the service only, never secrets; display config through config.RedactedServices. Both close error types are values, so a pointer errors.As target never matches.
Troubleshooting
Section titled “Troubleshooting”The process fails at boot before serving
An unparsable env value. The LoadEnv error names the service and field.
The first request fails but the next succeeds
A transient backend error cleared the cache. No fix needed unless it repeats.
Shutdown hangs
Close got a bare context while an adapter blocks. Use context.WithTimeout (about 10s).
errors.As for close errors never matches
A pointer target never matches a value type. Declare a value variable.
A goroutine leak is reported after a lifecycle test
A goroutine outlived Close (go test -tags=leak ./container/). Stop it in the adapter’s Close.