Skip to main content
  1. System Design Components/

Reverse Proxy / Edge Cache Analysis Note

Reverse Proxy / Edge Cache Analysis Note #

This note captures the full step-by-step analysis for a reverse proxy / edge cache system: origin truth, local edge cache state, cache policy, request routing, invalidation/refresh, and bounded-stale serving.

Step 1 — Normalize #

Assume the baseline prompt is:

  • design a reverse proxy / edge cache
  • clients send requests to the proxy
  • proxy routes to origin/backends and caches cacheable responses
  • cache entries expire or are invalidated
  • system scales across many edge or proxy nodes
RequirementActorOperationState touchedPriority
Client request is served from cache or proxied upstreamClientread projectionS1
read projection target
CacheEntry
C1
Origin/backend response metadata is updatedClientoverwrite stateS1
update target
OriginState
C1
Proxy refreshes missing/stale cache entry from upstreamSystemasync processS1
hidden write target
CacheEntry
C1
System invalidates cache entrySystemstate transitionS1
update target
CacheEntry
C1
Admin updates cache/routing policyAdminoverwrite stateS1
update target
ProxyPolicy
C1
System propagates config snapshot to proxy nodesSystemasync processS1
hidden write target
ProxySnapshot
C1
Client reads proxy/cache statusClientread projectionS1
read projection target
ProxyStatusView
R2

Notes on normalization:

  • hot request path is read projection
    • cached response is a derived projection of origin truth
  • origin metadata/state is overwrite-state truth
  • refresh is async process
  • invalidation is a cache-entry lifecycle transition
  • policy update is overwrite-state control-plane config
  • snapshot propagation is async dissemination

This is a composition of:

  • Origin Projection + Edge Delivery Plane
  • Control Plane + Data Plane

Step 2 — Critical Path Selection #

RequirementPriority classWhy
Serve/proxy requestC1wrong/stale content or wrong upstream routing breaks serving correctness
Update origin/backend stateC1origin truth changes what cache should represent
Refresh missing/stale cache entryC1cache correctness depends on valid refresh
Invalidate cache entryC1invalidation is the main freshness safety path
Update cache/routing policyC1changes future serving behavior
Propagate proxy snapshotC1stale proxy nodes can enforce wrong cache/routing policy
Read status/analyticsR2operational only

Critical paths:

  • P1 serve/proxy request
  • P2 update origin/backend state
  • P3 refresh cache entry
  • P4 invalidate cache entry
  • P5 update policy
  • P6 propagate proxy snapshot

Step 3 — Primary State Extraction #

Candidate object labelCandidate sourceCandidate needed for C1/R1?Candidate decomposition actionClassPrimary?OwnerEvolutionScope kindScope value
OriginStatedirect nounYeskeep as candidateentityYesserviceoverwriteinstancecache_key or upstream_object_key
CacheEntryhidden write targetYeskeep as candidateprojectionYesserviceoverwriterelationproxy_node_id + cache_key
InvalidationStatelifecycle objectYeskeep as candidateprocessYesservicestate machineinstancecache_key or invalidation_id
ProxyPolicydirect nounYeskeep as candidateentityYesserviceoverwriteinstanceproxy_scope or route_scope
ProxySnapshothidden write targetYeskeep as candidateprojectionYesserviceoverwriteinstanceproxy_node_id
ProxyStatusViewderived read modelNoreject as UI artifactprojectionNoderivedoverwritecollectionproxy_cluster

Minimal primary set:

  • OriginState
  • CacheEntry
  • InvalidationState
  • ProxyPolicy
  • ProxySnapshot

Important modeling choices:

OriginState #

Primary because:

  • origin/backend truth determines what a valid cached representation should be

CacheEntry #

Modeled explicitly because:

  • reverse proxy correctness depends on current cached object version, expiry, validation metadata, and cacheability state

InvalidationState #

Worth keeping explicit because:

  • invalidation/ban/purge flows are a real lifecycle control path

ProxySnapshot #

Important because:

  • proxy nodes enforce cache and routing policy from local versioned state

Step 4 — Hard Invariants #

PathTierTypeInvariant statement
P1 serve/proxy requestHARDeligibilityserve_request is valid only if selected cached response or proxied upstream response is eligible under current cache state, policy, and freshness/invalidation rules for that request scope.
P2 update origin/backend stateHARDorderingOrigin/backend-state revisions are ordered by monotonic version within object or route scope.
P3 refresh cache entryHARDaccountingCacheEntry(proxy_node, cache_key) equals function of current origin/backend truth modulo bounded refresh/invalidation lag.
P4 invalidate cache entryHARDeligibilityinvalidate_cache_entry is valid only if current invalidation lifecycle and cache state allow transition for that cache scope.
P5 update cache/routing policyHARDorderingProxy-policy revisions are ordered by monotonic config version within policy scope.
P6 propagate proxy snapshotHARDfreshnessProxySnapshot(proxy_node) reflects authoritative invalidation and policy state within configured propagation bound.

What matters most:

  • proxy must not serve invalidated or excessively stale cached content beyond configured bounds
  • cache entry must track origin version semantics
  • proxy policy and invalidation state must move monotonically

Step 5 — Execution Context #

FieldValueWhy
Topologysingle service distributedone logical reverse-proxy/cache system with many serving nodes
Write coordination scopeper object scopecorrectness is per cache key, invalidation scope, and proxy snapshot scope
Read consistency targetbounded stale allowedhot path serves from local cache and snapshot, not strong origin/control reads
Holder modelnoneno lease-like per-request ownership is central
Compensation acceptable?Nowrong/stale response delivery is not a compensable workflow

Derived:

  • bounded_staleness_allowed = true
  • exclusive_claim_required = false
  • guarded_by_current_state = true

This implies:

  • origin truth plus bounded-stale proxy projection
  • versioned invalidation/policy propagation
  • local hot-path serving

Step 6 — Deterministic Mechanism Selection #

PathWrite shapeBase mechanismRequired companions
P2 update origin/backend stateoverwrite current valueCAS on versionobject version/ETag
P3 refresh cache entryoverwrite current valuesingle writer refresh or CAS on versionTTL/ETag/validation metadata
P4 invalidate cache entryguarded state transitionCAS on (state, version)invalidation version
P5 update cache/routing policyoverwrite current valueCAS on versionpolicy version
P6 propagate proxy snapshotoverwrite current valuesingle writer snapshot publicationsnapshot version

Hot path P1 is a read path.

Why these fit:

  • origin and policy are current-state config/truth
  • cache entry refresh is current-state overwrite from origin
  • invalidation is lifecycle/state-machine logic
  • proxy snapshots are versioned overwrite dissemination

Step 7 — Read Model / Source of Truth #

ConceptTruthRead pathRebuild path
C1 origin/backend truthOriginStateread source directlyauthoritative origin/backend store
C2 cache entryCacheEntryread projectionrefresh from origin/backend truth
C3 invalidation lifecycleInvalidationStateread source directlyauthoritative invalidation state
C4 proxy policyProxyPolicyread source directlyauthoritative policy store
C5 proxy local snapshotProxySnapshotmaterialized viewrebuild from latest invalidation + policy state
C6 proxy status/analyticsderivedmaterialized viewrecompute from primary state

Important point:

For the hot path:

  • proxy node reads local cache entry + local snapshot
  • not origin or control plane on every request

Step 8 — Failure Handling #

PathRetryCompeting writersCrash after commitPublish failureStale holder
origin/policy updateretry with versionstale update loses CAScommitted origin/policy survives crash if persistedproxy snapshot propagation may lagn/a
cache refreshretry with current validation metadatalatest valid refresh winsrefresh crash delays update; next request/refresher retriesupstream fetch retry/backoffn/a
invalidationretry with invalidation versionstale invalidation transition loses CAScommitted invalidation survives crash if persistedsnapshot propagation may lagn/a
snapshot propagationretry with versioned snapshotolder snapshot loses to newer versionproxy keeps last good snapshot until refreshfailed push retried or pulledn/a
request servingrequest retries are application-levelmany proxy nodes can serve concurrently from local cache/snapshotone proxy crash only affects local requestsn/astale cache/snapshot bounded by TTL/invalidation/version rules

What matters most:

  • invalidation and snapshot versions move monotonically
  • stale cache serving is bounded by explicit TTL/revalidation rules
  • cache refresh should use version/ETag semantics where possible

Step 9 — Scale Adjustments #

HotspotTypeFirst response
very high request volumeread hotspotadd more proxy nodes and keep hot path local
cache-miss stormscontention hotspotrequest coalescing and stale-while-revalidate
invalidation stormsfan-out hotspotbatch invalidations and publish incremental proxy updates
large hot objectsread hotspotsegmented caching and tiered cache hierarchy
policy/config churnfan-out hotspotincremental snapshot propagation and pull-on-version-miss
analytics/status readsread hotspotderived views only

Canonical design conclusion:

  • archetype composition:
    • Origin Projection + Edge Delivery Plane
    • Control Plane + Data Plane
  • primary truth:
    • OriginState
    • CacheEntry
    • InvalidationState
    • ProxyPolicy
    • ProxySnapshot
  • hot path:
    • local cache read + local routing/invalidation snapshot

Concrete Substrate #

  • origin/backend truth in authoritative app/origin store
  • control plane in Go/Java
  • invalidation/policy state in strongly consistent control-plane store
  • proxy fleet with local cache and versioned config snapshots
  • cache refresh with ETag/version validation against origin
  • snapshot propagation via watch streams / push / pull-on-version-miss

Operation Layer #

  1. HandleRequest(host, path, headers, context)
  • entry point: proxy node
  • authoritative decider: local CacheEntry + ProxySnapshot
  • transition: none on source truth
  • response: cached response, proxied response, or refresh-triggered response
  1. PutOriginState(cache_key, metadata, expected_version?)
  • entry point: origin/control API
  • authoritative decider: origin-state owner
  • transition: overwrite OriginState
  1. Invalidate(cache_key or prefix, expected_version?)
  • entry point: invalidation API
  • authoritative decider: invalidation-state owner
  • transition: guarded update to InvalidationState
  1. PutProxyPolicy(scope, config, expected_version?)
  • entry point: control-plane API
  • authoritative decider: policy owner
  • transition: overwrite ProxyPolicy
  1. internal refresh
  • validate cache entry against origin (ETag/version/TTL)
  • overwrite CacheEntry
  1. snapshot propagation
  • publish latest ProxySnapshot(version) to proxy nodes

Entry Point vs Decider vs Responder #

PathEntry pointAuthoritative deciderPhysical responderLogical responder
request handlingproxy nodelocal cache entry + local snapshotproxy nodereverse proxy/cache
origin updateorigin/control APIorigin-state ownercontrol-plane nodereverse proxy/cache
invalidationinvalidation APIinvalidation-state ownercontrol-plane nodereverse proxy/cache
policy updatecontrol-plane APIpolicy ownercontrol-plane nodereverse proxy/cache
cache refreshproxy nodelocal refresh worker + origin validationproxy nodereverse proxy/cache
snapshot propagationproxy / control planesnapshot publishercontrol/data-planereverse proxy/cache

Concrete HLD #

Main components:

  • origin/backend service
  • control-plane API
  • invalidation/policy state store
  • snapshot distribution layer
  • proxy/cache fleet
  • refresh/coalescing logic
  • derived status/analytics views

Short interview version #

“I’d design the reverse proxy / edge cache as origin truth plus bounded-stale local projection. The hot path on each proxy node reads local cache entries and local policy/invalidation snapshots, not origin or control plane. Origin metadata and cache policy are authoritative in control plane, invalidation is versioned, and proxies refresh cache entries from origin using TTL and ETag/version validation. The main correctness boundary is bounded stale serving under explicit invalidation and refresh rules.”