Skip to content

Overlay runtime cache configuration on documented defaults #23

Description

@lan17

Priority

P1 — High — make runtime policy fallback semantics predictable before production use of partial configuration overrides.

Current problem

cacheConfigProvider currently has whole-object replacement semantics:

  • A provider result of null falls back to the cached definition's defaultConfig.
  • Any non-null provider result replaces defaultConfig completely.
  • Missing per-layer TTL or ramp values then disable that layer with reason="missing_config".
  • DialCacheKeyConfig normalizes an omitted requestLocal value to false, so runtime omission cannot be distinguished from an explicit disable.

That makes an empty or partial runtime result an implicit kill switch and contradicts the intended rule that omitted runtime values inherit documented defaults.

Current evidence:

Confirmed contract

Baseline defaults

  • defaultConfig remains optional.
  • When defaultConfig is absent, the use case's baseline is fully disabled.
  • requestLocal defaults to false.
  • A local or remote layer with no effective TTL is disabled by policy.
  • A layer with an effective TTL but no ramp defaults to ramp 100.
  • There is no universal TTL. A use case or runtime override must provide a TTL to enable a shared layer.

Runtime overlay

  • Resolve the provider once per enabled invocation.
  • A provider result of null, defensive JavaScript undefined, or an empty config applies no overrides.
  • A non-null runtime config overlays defaultConfig field by field.
  • Missing or undefined runtime requestLocal, TTL, or ramp values inherit the corresponding baseline value.
  • requestLocal: false explicitly disables request-local caching.
  • ramp[layer]: 0 explicitly disables that local or remote layer.
  • Explicit malformed values do not fall back to defaults. They retain fail-open validation behavior.
  • A provider exception is not absence: keep the current uncached config_error path so a control-plane failure cannot accidentally re-enable caching.

Metrics

  • Remove missing_config as a normal disabled reason.
  • Use a bounded policy_disabled reason when the fully resolved baseline plus overlay leaves a shared layer disabled.
  • Preserve invalid_ttl, ramped_down, config_error, and context.
  • Do not emit per-call request-local-disabled noise for the normal requestLocal: false default.

Minimal implementation scope

  • Keep the current public TTL/ramp map shape; do not introduce a new policy object or builder hierarchy.
  • Preserve omission in DialCacheKeyConfig.requestLocal until the baseline and runtime overlay are resolved.
  • Add one internal merge/resolution path used by request-local, process-local, and Redis layers.
  • Validate static/default policy when cached() registers the use case where validation is possible.
  • Keep dynamic malformed values fail open at invocation time.
  • Update Prometheus, Datadog, custom-adapter types, README examples, migration notes, and packed declaration/runtime coverage.
  • Do not change cache keys, Redis frames, Lua scripts, invalidation identity, serializers, or client ownership.

Compatibility and migration

This is a behavior and metrics-label compatibility change:

  • Partial runtime configs begin inheriting unspecified defaults instead of disabling those layers.
  • An empty runtime config becomes a no-op rather than disable-all.
  • Runtime omission of requestLocal inherits the baseline instead of forcing false.
  • A configured layer with omitted ramp becomes fully enabled at ramp 100.
  • Existing runtime kill switches that rely on omission must use explicit ramp: 0 and requestLocal: false.
  • Dashboards using reason="missing_config" migrate to reason="policy_disabled".
  • Definitions with no defaultConfig remain valid and default to all caching disabled until a runtime overlay explicitly enables a layer.

Acceptance criteria

  • Cover null, undefined, empty, partial, complete, and throwing provider results.
  • Cover leaf-level inheritance and overrides independently for request-local, local TTL/ramp, and remote TTL/ramp.
  • Prove explicit false and ramp 0 disable inherited enabled policies.
  • Prove an omitted default config is a fully disabled baseline rather than missing configuration.
  • Prove explicit invalid TTL/ramp values are not masked by defaults.
  • Preserve one provider resolution per invocation.
  • Cover local-only, remote-only, layered, request-local-only, asymmetric TTL, partial rollout, and disable-all examples.
  • Cover the complete backend-neutral/Prometheus/Datadog disabled-reason set.
  • Add a prominent migration example from omission-as-disable to explicit disable.
  • Document the precedence order and every default in one authoritative README section.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions