diffcache

package
v0.21.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package diffcache caches per-resource Diff results so a wfctl invocation that re-runs a Plan against unchanged inputs can skip the (sometimes network-expensive) provider-side Diff call. The cache is purely an amortization optimization, NOT a correctness mechanism — apply paths remain correct on a 100% miss rate (which is exactly what CI sees on every fresh runner).

Storage backends

Cache selection is driven by the WFCTL_DIFFCACHE env var, resolved by New:

  • `disabled` → noop cache (every Get misses; Put is a no-op). Use this when an operator wants fully-deterministic Plan/Apply timing with no shared state across invocations.
  • `:memory:` → in-memory cache that lives only for the current process. CI workflows in this repo set WFCTL_DIFFCACHE=:memory: explicitly so containerized runners never write to disk.
  • any other value (or unset) → filesystem cache rooted at `~/.cache/wfctl/diff/`.

CI ephemerality (load-bearing)

CI runners are ephemeral — each job starts with an empty cache. Workflow correctness MUST NOT depend on a cache hit. The diff cache is an operator-local performance optimization for repeated `wfctl infra plan` invocations against the same checkout.

Cache key

The cache key is a Key tuple of (PluginVersion, Type, ProviderID, SHAConfig, SHAOutputs). Plugin downgrades naturally invalidate entries since PluginVersion is part of the key — old entries persist on disk until the LRU eviction reclaims them; the size cap (1024 entries / 64 MiB) bounds the disk waste.

Schema versioning

Each cache file embeds [cacheSchemaVersion] in a JSON envelope. On Get, a mismatched version is treated identically to file corruption: the entry is silently evicted and the caller re-Diffs.

Known limitations

Windows: the filesystem cache uses os.Rename for the atomic publish step on Put. On Windows, os.Rename fails when the destination already exists, so updating an existing cache entry will fail (the entry is treated as a write failure and the operator gets a cache miss on the next Get — correct, since apply does not depend on cache hits). A future improvement is to vendor github.com/google/renameio for cross-platform atomic rename; deferred until there's a Windows-supported wfctl use case.

T3.5 / W-3a status

This package ships in W-3a. The consumer that wires it into platform.ComputePlan lands in W-3b/T3.6f. Until then, the cache is callable but never invoked from production code paths.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cache

type Cache interface {
	// Get returns the cached DiffResult for key. The boolean is true
	// iff the entry was found and successfully decoded; corruption,
	// schema-version mismatch, and missing entries all yield
	// (zero-value, false).
	Get(key Key) (interfaces.DiffResult, bool)
	// Put stores result under key. Errors during Put (e.g., disk
	// full, serialization failure) are silently swallowed because
	// cache misses are correct — apply behavior must not depend on
	// Put success.
	Put(key Key, result interfaces.DiffResult)
}

Cache is the diff-result cache. Implementations are safe for concurrent use (the backing fs cache uses os-level atomic file ops; the in-memory cache locks internally).

func New

func New() Cache

New returns a Cache configured by the WFCTL_DIFFCACHE env var.

  • "disabled" → NewNoop
  • ":memory:" → NewMemory
  • default → NewFilesystem rooted at the user cache directory (typically `~/.cache/wfctl/diff/`).

When the user cache directory cannot be resolved, falls back to the in-memory cache so the caller still gets a working Cache (the filesystem path being unavailable is the operator's hint that disk caching is off).

func NewFilesystem

func NewFilesystem(dir string) Cache

NewFilesystem returns a Cache whose entries are persisted under dir. The directory is created on first Put if absent. Callers may pass any directory; the New factory uses `~/.cache/wfctl/diff/`.

func NewMemory

func NewMemory() Cache

NewMemory returns an in-memory Cache. The returned Cache does not enforce a size cap — process lifetime is the eviction horizon. This matches the rev2 lifecycle constraint that the in-memory mode is the CI default and is not relied on for correctness.

func NewNoop

func NewNoop() Cache

NewNoop returns a Cache that always misses. Put is a no-op.

type Key

type Key struct {
	// PluginVersion is the plugin's name@version string, e.g.,
	// "do@v0.10.0". Plugin downgrades naturally invalidate cache
	// entries via this field.
	PluginVersion string `json:"plugin_version"`
	// Type is the canonical resource type, e.g., "infra.vpc".
	Type string `json:"type"`
	// ProviderID is the resource's cloud-side identifier; empty for
	// net-new resources.
	ProviderID string `json:"provider_id"`
	// SHAConfig is the sha256-hex of canonical-marshal(spec.Config).
	SHAConfig string `json:"sha_config"`
	// SHAOutputs is the sha256-hex of canonical-marshal(currentState.Outputs);
	// empty for net-new resources.
	SHAOutputs string `json:"sha_outputs"`
}

Key tuples the inputs to a single Diff. Two Keys are equal iff every field matches; the canonical sha256 fingerprint of the key determines the on-disk filename.

JSON tags on the fields are for log / transcript serialization only — cache keying uses NUL-separated string concatenation in [keyFingerprint], not JSON marshaling. A reader checking the fingerprint shape should follow the keyFingerprint code, not the tags.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL