7 Commits

Author SHA1 Message Date
ee67cf05bc
test(dataset): update loader signatures to func(ctx) (*T, error)
Follow-up to the loader ctx plumbing — existing unit tests didn't
compile against the new signature.
2026-04-20 20:09:36 -06:00
da2b230bc3
refactor(dataset)!: plumb ctx into loader callbacks
Loader signature changes from func() (*T, error) to
func(context.Context) (*T, error). Set.Load(ctx) already accepts a
ctx; it now flows through reload() into the loader so long-running
parses or downloads can honor ctx.Err() for graceful shutdown.

check-ip's loaders don't consume ctx yet (ipcohort/geoip are
in-memory and fast), but the hook is in place for future work.

BREAKING: dataset.Add and dataset.AddInitial signatures changed.
2026-04-20 20:02:48 -06:00
bcda75d673
feat(dataset): close-on-swap, AddInitial, LoadedAt, Loaded
- reload() Closes replaced value if it implements io.Closer
  (geoip readers leak mmap/file handles on hot-swap without this)
- AddInitial pre-populates a view so Value() is non-nil before first
  Load — enables async-load startup paths
- View.LoadedAt() and Set.Loaded() expose load state for health checks
2026-04-20 19:26:15 -06:00
e329c0f86b
refactor(dataset): rename Group to Set, accept variadic fetchers
Set handles both single-fetcher (one git repo) and multi-fetcher
(GeoLite2 City + ASN) cases uniformly. Any fetcher reporting an update
triggers a view reload. This replaces the per-caller FetcherFunc wrapper
that combined the two MaxMind cachers and the ad-hoc atomic.Pointer +
ticker goroutine in cmd/check-ip — geoip now rides on the same
Set/View/Load/Tick surface as the blocklists.
2026-04-20 16:50:33 -06:00
3b5812ffcd
feat(dataset): add PollFiles fetcher for local-file sources
Stats the given paths and reports updated when any size/modtime
changes since the last call. First call always reports true so the
initial Load populates views.

check-ip uses it for --inbound/--outbound so edits to local lists
get picked up by Group.Tick without a restart.
2026-04-20 15:39:23 -06:00
786463cecd
refactor(dataset): Tick takes an onError callback, no more stderr
Libraries shouldn't decide where errors go. Tick now passes Load
errors to onError (nil to ignore); callers pick log/count/page.
check-ip supplies its own stderr writer.
2026-04-20 14:19:26 -06:00
11743c9a10
feat(sync/dataset): minimal group/view/fetcher for hot-swap refresh
Distilled from the previous net/dataset experiment and the inline
closure version in check-ip. Keeps what actually earned its keep:

  - Group ties one Fetcher to N views; a single Load drives all swaps,
    so shared sources (one git pull, one zip download) don't get
    re-fetched per view.
  - View[T].Value() is a lock-free atomic read; the atomic.Pointer is
    hidden so consumers never see in-flight reloads.
  - Tick runs Load on a ticker with stderr error logging.

Dropped from the v1 design: MultiSyncer (callers fan-out inline when
needed), Close (unused outside geoip), Name (callers wrap the logger),
standalone Dataset type (Group with one view covers it), Sync vs Init
asymmetry (Load handles first-call vs update internally).

check-ip rewires to use it — file/git/http modes all build a Group
with two views, uniform shape.
2026-04-20 13:33:05 -06:00