Weather enrichment with a bounded sweeper #14
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What to build
Weatheris the conditions at the place and instant aMoodEntrywas logged, resolved server-side from itsLocationand expressed in one canonical vocabulary so any two entries' weather are comparable. It is always attributed to aProvider, never to the User — weather is observed, not stated.Resolving server-side rather than accepting client-supplied weather means one implementation and one vocabulary. Three clients each implementing lookup would drift in vocabulary and units, and correlation across inconsistent labels is meaningless.
Timing: the lookup happens after the entry is written, never during it. Logging a mood is the app's most latency-sensitive action and must not wait on a third party. A failed or slow lookup leaves the entry saved with no weather.
The sweeper finds entries that have a
Locationbut noWeatherand fills them in — covering server restarts, upstream outages, and offline clients with one mechanism instead of three. It satisfies ADR 0004's rule: the work is rediscoverable from state, so losing a queued job costs only latency.The realistic backlog is small. The Daylio importer carries no location data, so imported history never triggers lookups. Bound the sweep from configuration anyway — as a safety limit rather than a throughput design. Where the upstream accepts a date range for one location, group a backlog by rounded coordinates so it collapses into few requests rather than one per entry.
Outbound lookups must be disableable in config for self-hosters who do not want coordinates leaving the box.
Acceptance criteria
Locationafter the entry is saved, never during the requestLocationand noWeatherand enriches themBlocked by
Left open deliberately: seven of the eight criteria are met, one is not.
Met — weather resolves after the save via an
ObserveWeatherjob, saving succeeds when the provider is slow/failing/disabled, one canonical condition vocabulary is stored,sweep_weather_backlogfinds entries with aLocationand noWeather, the sweep is bounded byworker.enqueued_per_sweepandworker.sweep_seconds,worker.look_up_weather = falsedisables outbound lookups by not constructing the lookup at all, and failures stay visible with attempt count and last error.Not met — "Backlogged lookups are grouped by location and date range rather than issued one per entry". Each job still resolves one entry with one request. ADR 0012's "What is not built" section records the reasoning: a batch job would have to stand for many entries, which breaks the one-job-one-subject rule the queue and its sweep are built on, and the realistic backlog is small because imported history carries no coordinates.
So this is either a wontfix to close against ADR 0012, or a real remaining task — a judgement call rather than something to tick off.
Closing as wontfix.
Everything in this issue is built except the coordinate/date-range grouping of backlogged lookups, and that one is declined rather than deferred. ADR 0012 carries the reasoning: grouping needs a job that stands for many entries, which breaks the one-job-one-subject rule the queue and its sweep are built on — and the sweep's rediscoverability guarantee (ADR 0004) depends on that rule. Paying that structural cost buys request-count savings on a backlog that does not realistically occur: the Daylio importer carries no coordinates, so imported history never enqueues weather work at all.
worker.enqueued_per_sweepremains the bound if that assumption ever stops holding. If it does, this is a new issue against the queue's shape, not a leftover checkbox on this one.