6.4 KiB
Operations
This document describes how to run and observe the weatherfeeder daemon in its
current form. For configuration syntax, see configuration. For the
CLI surface, see CLI reference.
Normal Workflow
- Prepare
config.ymlin the process working directory. - Start the daemon with
./weatherfeederorgo run .fromcmd/weatherfeeder. - Watch stderr logs for startup or runtime errors.
- Consume events from the configured sinks.
- Stop the process with
Ctrl-CorSIGTERM.
The daemon has no admin subcommands and no runtime reload command. Change the config file and restart the process to apply configuration changes.
Maintained copyable configs are available under examples/.
Runtime Lifecycle
On startup, weatherfeeder:
- loads
config.ymlfrom the current working directory; - registers built-in source drivers;
- registers stdout, NATS, and weatherfeeder Postgres sink drivers;
- builds sources and validates configured
kindsagainst source metadata; - builds sinks and compiles routes;
- starts the scheduler and dispatcher;
- processes events through normalization, then in-memory dedupe;
- routes processed events to configured sinks.
Startup errors are fatal and terminate the process. Runtime poll, pipeline, and sink errors are logged and the process continues unless the scheduler or dispatcher returns a fatal error.
Logs
The process uses the Go standard logger with date, time, and microseconds. Logs go to stderr.
Common log prefixes:
| Prefix | Meaning |
|---|---|
config load failed |
config.yml could not be read, parsed, or validated. |
build source failed |
A source driver or its params are invalid. |
source expected kinds validation failed |
Configured source kinds do not match the source driver. |
build sink failed |
A sink driver or its params are invalid, or a sink could not initialize. |
compile routes failed |
Routes reference invalid sinks or kinds. |
scheduler: poll failed |
A source poll failed; the source will be polled again on its next interval. |
dispatcher: pipeline error |
Normalization or dedupe failed for one event. |
dispatch: sink ... failed consuming event |
A sink failed to consume one event. |
shutdown complete |
Scheduler and dispatcher have exited. |
Scheduling And Polling
Current weatherfeeder sources are polling sources. Each source uses its
configured every interval. The scheduler applies jitter before the first poll
and before each interval tick. If no jitter is configured in code, feedkit uses
min(every/10, 30s), capped at half the interval.
Poll failures are logged and do not stop the daemon. A failed poll emits no events for that source until a subsequent poll succeeds.
Unchanged Upstream Content
Most sources use feedkit's single-document HTTP polling helper. By default,
params.conditional is true for those sources, so the helper keeps ETag and
Last-Modified validators in memory for each source instance.
If the upstream returns 304 Not Modified, the source emits no events for that
poll. Validator state is in memory only; restarting the process starts with no
cached validators.
The SPC convective outlook source polls multiple required documents as one bundle. It emits no event when a later complete bundle has the same fetched document bodies as the previous successful poll.
Processing And Dedupe
Every event passes through normalization first and dedupe second.
Normalizers match raw source schemas and produce canonical weather.*.v1
payloads. If an event has no matching normalizer, the normalize processor passes
it through unchanged.
Dedupe keys by event ID and stores a bounded in-memory set of 2048 recent IDs. Duplicate IDs are dropped. Dedupe state is not persisted, so a restart starts with an empty dedupe set.
Routing And Sink Fanout
Routes choose sinks by event kind. If routes is omitted, every sink receives
every event kind. If a route omits kinds, that route also matches all kinds.
The dispatcher creates one queue and one worker goroutine per sink. The default
per-sink queue size is 64. weatherfeeder does not currently expose config
fields for sink queue size, enqueue timeout, or consume timeout.
Sink errors are logged per event. A sink failure does not stop other sinks from receiving the same event.
Sink Behavior
stdout
The stdout sink validates each event and prints one JSON object per line to stdout. This is useful for local inspection and log forwarding.
NATS
The NATS sink connects lazily on the first event, reuses the connection while it is open, and publishes each event as JSON to the configured subject. Connection, marshal, and publish failures are logged by the dispatch worker.
Postgres
The Postgres sink opens the database during startup. It creates missing tables
and indexes with CREATE TABLE IF NOT EXISTS and CREATE INDEX IF NOT EXISTS.
It does not modify existing table definitions.
Each mapped canonical event is written in one transaction. If params.prune is
set, the sink deletes rows older than now - prune from every weatherfeeder
table in that same transaction. See the
Postgres table contract.
State And Recovery
weatherfeeder keeps only runtime state in process memory:
- scheduler goroutines and timers;
- HTTP conditional request validators;
- source-local unchanged-content state;
- event channel buffers;
- per-sink fanout queues;
- the dedupe ID set.
Durable state is external sink state: NATS broker state outside this process and Postgres tables managed by the configured database.
There is no internal checkpoint, replay log, or resume marker. To recover from a
process failure, fix the underlying issue and restart the daemon from a working
directory containing the desired config.yml.
Shutdown
weatherfeeder listens for os.Interrupt and SIGTERM. On shutdown, the
shared context is canceled. Scheduler jobs stop polling, dispatch workers stop,
and the process logs shutdown complete.
Queued sink work may be dropped when shutdown context cancellation reaches the fanout workers. Use external sink durability, such as Postgres or broker retention, for durable downstream state.
Caveats
- There is no health-check endpoint.
- There is no runtime config reload.
- There are no built-in metrics.
- Source conditional request state and dedupe state are reset by restart.
- Existing Postgres schemas are not migrated automatically.