Skip to content

feat: decouple Prometheus metrics adapter - #56

Merged
lan17 merged 5 commits into
mainfrom
agent/decouple-prometheus
Jul 17, 2026
Merged

lan17 merged 5 commits into
mainfrom
agent/decouple-prometheus

Conversation

@lan17

@lan17 lan17 commented Jul 17, 2026 •

Copy link
Copy Markdown
Owner

Summary

Makes DialCache metrics explicitly opt-in and removes Prometheus from the core package boundary.

  • Metrics are disabled when metrics is omitted from new DialCache().
  • The backend-neutral DialCacheMetricsAdapter remains in the root API.
  • Prometheus support moves to the explicit dialcache/prometheus subpath.
  • Applications install prom-client separately and pass a caller-owned registry.
  • Existing metric names, labels, buckets, counts, timers, and fail-open instrumentation behavior are preserved.

Closes #53

Architecture and design

Backend-neutral core

Core owns only the cache-domain metrics contract, label/event types, instrumentation call sites, and synchronous fail-open isolation. DialCacheConfig exposes one optional field:

metrics?: DialCacheMetricsAdapter;

Omitting that field stores no adapter, imports no metrics backend, and registers no collectors. The redundant metrics: false mode and Prometheus-specific metricsRegistry / metricsPrefix constructor fields are removed.

The root source, ESM/CJS bundles, and declarations contain no runtime or type dependency on prom-client.

Explicit Prometheus adapter

The Prometheus mapping lives at dialcache/prometheus and exports:

  • PrometheusDialCacheMetrics
  • createPrometheusDialCacheMetrics
  • PrometheusMetricsOptions

PrometheusMetricsOptions.registry is required. Prometheus and OpenMetrics registries are supported. The adapter never falls back to the global default registry and does not clear or own the caller's registry lifecycle.

Multiple adapters with the same registry and prefix reuse collectors only when their type, help, ordered labels, histogram buckets, and exemplar mode are compatible. All existing collectors are validated before any new DialCache collector is registered, so incompatible collisions fail transactionally with guidance to use a unique prefix or separate registry. Exemplar-enabled collectors are rejected because prom-client changes their call signatures; omitted, false, and explicit undefined exemplar modes are treated as disabled.

This does not change the public metrics schema. The post-request-local schema remains intact, including layer="request_local" and coalescing scope="request_local" | "process".

Dependency and installation model

prom-client moves from required production dependency to optional peer plus development dependency. This matches the separately installed, caller-owned Redis integration model.

The packed-package suite proves two consumer modes:

  1. Root ESM, CJS, and TypeScript imports work while both prom-client and Valkey GLIDE are physically absent.
  2. The Prometheus ESM, CJS, and TypeScript subpath works after the consumer separately installs prom-client, including caller-owned Prometheus and OpenMetrics registries.

The suite also asserts that removed constructor fields and metrics: false are absent from the public type surface and that Prometheus exports are absent from the root module.

API migration

Before After
new DialCache() implicitly registered Prometheus collectors new DialCache() has metrics disabled
new DialCache({ metrics: false }) new DialCache()
new DialCache({ metricsRegistry, metricsPrefix }) new DialCache({ metrics: createPrometheusDialCacheMetrics({ registry, prefix }) })
Prometheus imports from dialcache Prometheus imports from dialcache/prometheus

No Redis data, cache key, serializer, invalidation, or wire-protocol migration is required.

Review-loop hardening

  • Locks the exact schema of all ten collectors: names, types, ordered labels, timer/size buckets, and both serialization operations.
  • Covers compatible collector reuse and transactional rejection for mismatched type, help, labels, buckets, and exemplar mode.
  • Rebases cleanly over the semantic-release, badge, and serializer changes now on main.
  • The final fresh confirmation round returned no findings.

Validation

Exact head: 47ae82b8b8904a855eb4575edbd044aa16f1a1ce

  • corepack pnpm check on Node v22.22.0
    • 141 tests passed
    • coverage: 97.53% statements, 91.31% branches, 97.45% functions, 97.80% lines
    • ESM/CJS builds, declarations, and packed-package consumers passed
  • corepack pnpm test:integration
    • 46 Redis, Valkey, and Redis Cluster integration tests passed
  • corepack pnpm benchmark:request-local
    • semantic fallback-count and result assertions passed
  • corepack pnpm audit --prod --audit-level high
    • no known vulnerabilities
  • Node 20 packed-package consumer test passed.
  • git diff --check passed.
  • Exact-head CI passed: https://github.com/lan17/DialCache/actions/runs/29607737174

@lan17
lan17 force-pushed the agent/decouple-prometheus branch from 6f43297 to 2e7d422 Compare July 17, 2026 06:25
@lan17
lan17 force-pushed the agent/decouple-prometheus branch from 2e7d422 to 0d1c80a Compare July 17, 2026 19:16
@lan17
lan17 marked this pull request as ready for review July 17, 2026 20:10
@lan17
lan17 merged commit a407a80 into main Jul 17, 2026
4 checks passed
@lan17
lan17 deleted the agent/decouple-prometheus branch July 17, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decouple core observability from Prometheus

1 participant