Skip to main content
  1. System Design Components/

Distributed Counter Service Analysis Note

Distributed Counter Service Analysis Note #

This note captures the full step-by-step analysis from the infra interview recipe for an exact distributed counter service.

Step 1 — Normalize #

Assume the baseline prompt is:

  • design a distributed counter service
  • clients can increment, decrement, and read counters
  • counters should scale across nodes
  • assume exact values unless told otherwise

Normalize into state-affecting paths.

RequirementActorOperationState touchedPriority
Client increments counterClientstate transitionS1
update target
CounterState
C1
Client decrements counterClientstate transitionS1
update target
CounterState
C1
Client reads current counter valueClientread sourceS1
read source target
CounterState
R1
System routes counter key to shard ownerSystemread sourceS1
read source target
PartitionMap
C1
System reassigns shard ownership after node failureSystemstate transitionS1
update target
PartitionOwnership
C1
System replicates committed counter updatesSystemasync processS1
hidden write target
ReplicaState
C1
Client reads usage/metrics dashboardClientread projectionS1
read projection target
CounterStatsView
R2

Notes on normalization:

Important choices:

  • increment/decrement are state transition
    • because current counter state changes under arithmetic rules
  • read current value is read source
  • routing, ownership, and replication are explicit because it is distributed infra

Likely C1:

  • increment
  • decrement
  • routing
  • shard ownership
  • replication

Likely R1:

  • read current counter value

Step 2 — Critical Path Selection #

RequirementPriority classWhy
Increment counterC1wrong count directly breaks product truth
Decrement counterC1same as increment; current value must remain exact
Read current counter valueR1core serving path
Route counter key to shard ownerC1wrong routing can split updates and corrupt exactness
Reassign shard ownership after node failureC1failover must preserve exact count and authority
Replicate committed counter updatesC1durability and replica convergence depend on it
Read dashboard/statsR2secondary operational view

Baseline critical paths:

Main C1 paths:

  • P1 increment
  • P2 decrement
  • P3 route to shard owner
  • P4 reassign shard ownership
  • P5 replicate committed updates

Main R1 path:

  • P6 read counter value

For an exact distributed counter, the core issue is:

  • one authoritative update order per counter key
  • no lost increments/decrements
  • correct failover and replica catch-up

So this is closer to:

  • distributed KV / register-with-arithmetic transitions

than to:

  • approximate telemetry or CRDT counters

Step 3 — Primary State Extraction #

For an exact distributed counter service, the minimal primary state is the current counter value plus ownership/routing/replica state.

Candidate object labelCandidate sourceCandidate needed for C1/R1?Candidate decomposition actionClassPrimary?OwnerEvolutionScope kindScope value
CounterStatedirect nounYeskeep as candidateprocessYesservicestate machineinstancecounter_key
PartitionOwnershiphidden write targetYeskeep as candidateprocessYesservicestate machineinstanceshard_id
PartitionMaphidden write targetYeskeep as candidateentityYesserviceoverwritecollectioncounter keyspace shards
ReplicaStatehidden write targetYeskeep as candidateprocessYesservicestate machinerelationshard_id + replica_id
CounterStatsViewderived read modelNoreject as UI artifactprojectionNoderivedoverwritecollectionshard or cluster
MutationLoghidden write targetNoreject as implementation choiceeventNoderivedappend-onlycollectioncounter_key or shard

Important modeling choices:

CounterState #

This is the main truth object.

Likely fields:

  • value
  • version
  • maybe updated_at

I’m marking it as a process because:

  • increments/decrements are state transitions on current value
  • if you prefer, entity with overwrite evolution is also defensible
  • the key point is that current value is authoritative

PartitionOwnership #

Needed because:

  • one owner must serialize exact updates for counters in that shard

PartitionMap #

Needed because:

  • exactness requires consistent routing of a counter key to one authority

ReplicaState #

Needed because:

  • failover must know which replicas are caught up enough to promote

Minimal strict primary set:

  • CounterState
  • PartitionOwnership
  • PartitionMap
  • ReplicaState

Step 4 — Hard Invariants #

For an exact distributed counter service, the hard invariants are about one authoritative update order per counter, exact arithmetic transitions, and valid ownership/failover.

PathTierTypeInvariant templateInvariant statement
P1
write path
Increment counter
HARDeligibilityeligibility templateAction increment_counter is valid only if request is routed to current authoritative owner for counter_key and current counter state/version allows the transition at decision time.
P1
write path
Increment counter
HARDorderingordering templateInstances counter mutations are ordered by authoritative commit order within counter_key.
P2
write path
Decrement counter
HARDeligibilityeligibility templateAction decrement_counter is valid only if request is routed to current authoritative owner for counter_key and current counter state/version allows the transition at decision time.
P2
write path
Decrement counter
HARDorderingordering templateInstances counter mutations are ordered by authoritative commit order within counter_key.
P3
write path
Route to shard owner
HARDuniquenessuniqueness templateKey shard_id maps to at most one logical outcome current authoritative owner within shard_id.
P4
write path
Reassign shard ownership
HARDeligibilityeligibility templateAction reassign_shard is valid only if current owner is failed or relinquished and candidate replica is eligible and sufficiently current on shard_id at decision time.
P5
write path
Replicate committed updates
HARDaccountingaccounting templateReplica state for shard_id equals authoritative committed counter mutation stream modulo bounded replication lag.
P6
read path
Read counter value
HARD or SOFTfreshnessfreshness templateRead path read_counter reflects authoritative counter state within configured consistency bound.

What matters most:

1. One authoritative order per counter key #

This is the core of exact counting. If two unsynchronized owners both accept increments for the same key, exactness breaks.

2. Exact arithmetic transitions #

No lost increment No duplicate decrement No stale overwrite of current value

3. Valid failover #

A stale replica must not become new owner if it would lose committed updates.

4. Read consistency choice #

If strong reads are required:

  • reads must come from authoritative state If stale reads are allowed:
  • follower reads become possible

Step 5 — Execution Context #

For the exact distributed counter baseline:

FieldValueWhy
Topologysingle service distributedone logical counter service across many nodes
Write coordination scopeper object scopecorrectness is per counter_key and per shard ownership scope
Read consistency targetstrong onlysafest baseline for exact counter reads
Holder modelnodeshard ownership is held by nodes
Compensation acceptable?Nolost or duplicated counter updates cannot be repaired cleanly after the fact in the exact model

Derived implications:

  • holder_may_crash = true

    • shard owners can fail while serving updates
  • cross_service_write = false

    • baseline keeps counter state, routing, and replication inside one logical service
  • bounded_staleness_allowed = false

    • exact baseline disallows stale reads on the main read path
  • cross_service_atomicity_required = false

    • no multi-service transaction is needed in the baseline
  • exclusive_claim_required = true

    • shard ownership must be exclusive
  • guarded_by_current_state = true

    • arithmetic transitions and failover depend on current state/version

This pushes us toward:

  • one authoritative writer per shard
  • ordered mutation stream for exact updates
  • lease-backed shard ownership
  • strong reads from authoritative source by default

So the likely design is very similar to a leader-based partitioned KV store, except the value semantics are arithmetic rather than arbitrary overwrite.

Step 6 — Deterministic Mechanism Selection #

6A. Write Shape #

PathWhyWrite shape
P1 increment countervalid only against current counter state/versionguarded state transition
P2 decrement countersame as increment; current state mattersguarded state transition
P3 route to shard ownerone current authoritative owner per shardexclusive claim
P4 reassign shard ownershipvalid only if current owner failed/relinquished and candidate is current enoughguarded state transition
P5 replicate committed updatesfollowers consume authoritative mutation streamappend-only event

6B. Base Mechanism #

PathWrite shapeBase mechanismRequired companions
P1 increment counterguarded state transitionsingle writer per partitionversion, commit index
P2 decrement counterguarded state transitionsingle writer per partitionversion, commit index
P3 route to shard ownerexclusive claimleasefencing token, heartbeat
P4 reassign shard ownershipguarded state transitionCAS on (state, version)fencing token, replica catch-up check
P5 replicate committed updatesappend-only eventappend logreplica progress / ack index

Why these fit:

Increment / decrement #

Even though arithmetic feels special, for an exact distributed system the simplest safe realization is:

  • one authoritative shard owner serializes updates for keys in that shard

So the hot path is:

  • single writer per partition
  • not multi-writer merge

Routing / ownership #

Still classic exclusive-claim semantics:

  • one shard owner at a time

Replication #

Exact counter updates are best replicated as an ordered mutation stream.

Canonical substrate implied:

  • partitioned leader-based counter service
  • one leader per shard
  • updates appended to shard mutation log
  • replicas follow the committed stream
  • reads from leader for strong exact value

Step 7 — Read Model / Source of Truth #

For an exact distributed counter service, truth is mostly direct source state plus replica progress.

ConceptTruthRead pathRebuild path
C1
source concept
Current counter value
CounterState on authoritative shard ownerread source directlyauthoritative shard state / replay from committed mutation log
C2
source concept
Shard ownership
PartitionOwnershipread source directlyauthoritative ownership store
C3
source concept
Shard routing map
PartitionMapread source directlyauthoritative routing metadata
C4
status concept
Replica catch-up state
ReplicaStateread source directlyrecompute from commit index and replica progress
C5
projection concept
Counter analytics / dashboard
derived from counter mutations or snapshotsmaterialized viewrecompute from snapshots/logs

Important point:

For the exact baseline:

  • reads of current counter value should come from authoritative source state
  • ownership and routing metadata should be authoritative
  • operational/analytics views are projections only

Optional variant:

  • if interviewer later allows stale reads:
    • follower reads become possible
  • but source-of-truth is still authoritative shard state

Step 8 — Failure Handling #

PathRetryCompeting writersCrash after commitPublish failureStale holder
P1 increment counterretry must be idempotent by request id or may double-incrementshard owner serializes competing updates for same keycommitted increment survives leader crash if replicated past commit pointreplica lag acceptable behind commit pointstale leader blocked by fencing token
P2 decrement countersame as increment; retries need idempotency or explicit semanticsshard owner serializes competing updatescommitted decrement survives crash if replicated past commit pointreplica lag acceptable behind commit pointstale leader blocked by fencing token
P3 route to shard ownerretry after refreshing shard maponly one valid owner should existif ownership changed, refreshed map points to new ownern/astale owner rejected by epoch/fencing
P4 reassign shard ownershipretry failover transition safelyonly one reassignment winspromoted owner crash triggers later reassignmentn/aold owner fenced and must not serve updates
P5 replicate committed updatesreplica retries from last known indexfollowers independently catch up from committed streamleader crash stops new replication until failover; committed log remains truthmissing replica append retried from commit indexstale replica cannot become owner unless caught up

What matters most:

1. Idempotency for increment/decrement retries #

This is the key difference from ordinary overwrite systems.

Bad case:

  • client sends Increment
  • leader commits but response is lost
  • client retries
  • same logical operation increments twice

So for an interview-grade exact counter:

  • you should call out request_id or idempotency token support for mutation APIs

2. Stale leader protection #

Same as the KV store:

  • old owner must not continue accepting counter updates after failover

3. Failover only to sufficiently current replica #

Otherwise exact value can go backward or lose committed updates.

Failure summary:

Exact distributed counters stay correct if:

  • one shard owner serializes updates
  • mutation retries are idempotent
  • committed updates survive failover
  • stale owners are fenced

Step 9 — Scale Adjustments #

HotspotTypeFirst response
hot counter keycontention hotspotisolate that key, shard it specially, or move to partial-counter variant if exact single-owner becomes bottleneck
hot shard leaderwrite throughput hotspotincrease shard count and rebalance keys across shards
replication lagwrite throughput hotspotbatch mutation log replication, tune follower catch-up, and separate hot shards
strong-read pressure on leadersread hotspotadd follower reads only if relaxed consistency is acceptable, otherwise add more shards
failover churn / leader movementcontention hotspotstabilize leases/elections and avoid aggressive reassignment
analytics/dashboard readsread hotspotmove to materialized views from snapshots/logs

What scales well:

The exact baseline scales by:

  • partitioning counter keys across shards
  • keeping one authoritative writer per shard
  • replicating per shard independently

So throughput grows mainly with:

  • number of shards
  • number of healthy leaders
  • efficiency of log replication

What fails first:

Usually:

  • one very hot counter
  • one very hot shard
  • retry/idempotency overhead on mutation path
  • leader bottleneck for strong reads and writes

Canonical design conclusion:

The mechanical outcome is:

  • primary state:
    • CounterState
    • PartitionOwnership
    • PartitionMap
    • ReplicaState
  • critical invariants:
    • one authoritative update order per counter key
    • no lost or double-applied increments/decrements
    • failover only to sufficiently current replica
  • mechanisms:
    • single writer per partition
    • append log
    • lease
    • idempotency key for mutation retries
  • reads:
    • direct authoritative reads for exact values
    • projections only for dashboards/analytics

Polished interview answer:

“I’d design the distributed counter service as a partitioned leader-based system. Each counter key maps to one shard, and that shard leader is the only authority allowed to serialize increments and decrements for that key. Mutations are appended to a replicated per-shard log and applied to current CounterState after commit, which gives exact arithmetic and safe failover. Reads for exact values go to the leader, and mutation APIs carry idempotency keys so retries don’t double-apply. The main scaling levers are more shards, hot-key isolation, and efficient replication.”

Canonical Interview Answer #

For a generic distributed counter service interview question, the canonical answer is usually:

  • exact counter
  • one authoritative owner per counter key or shard
  • increments/decrements serialized by that owner
  • replication via ordered mutation log
  • strong reads from the owner
  • idempotency key for retry-safe increment/decrement

So the canonical shape is:

  • archetype: Key-Scoped Mutable State
  • write path:
    • increment/decrement -> guarded state transition
  • mechanism:
    • single writer per partition
    • append log for replication
    • lease for shard ownership

A compact HLD answer:

  • hash counter_key to a shard
  • shard leader is the only writer for counters in that shard
  • leader appends increments/decrements to a replicated log
  • committed updates are applied to CounterState
  • reads go to leader for exact value
  • failover promotes only a sufficiently caught-up replica
  • client mutations carry request_id to avoid double-apply on retry

Why this is canonical:

  • simplest exact semantics
  • no scatter-gather on reads
  • no lost updates under concurrency
  • clean failover story

If the interviewer pushes for very hot counters or higher availability, then mention the main variant:

  • shard one logical counter into partial counters
  • local increments on multiple owners
  • read by summing partials with scatter-gather
  • this is higher-throughput but no longer the simplest exact baseline

Scatter-Gather Clarification #

Scatter-gather is not needed in the exact single-owner baseline.

In this design, each counter_key has one authoritative shard owner, so a read for one counter is:

  • route to shard
  • read from that owner

No scatter-gather needed.

Scatter-gather appears only in different variants.

When you do not need scatter-gather #

  • one key maps to one shard
  • one shard has one authoritative owner
  • read one counter value

That is the current model.

When you would need scatter-gather #

  • a logical counter is sharded across many nodes
    • example: hot counter split into 100 partial counters
  • read requires summing partials from multiple shards
  • or you allow local increments on many replicas and merge on read

Then the read path becomes:

  • scatter-gather

Examples:

  • approximate high-throughput counters
  • CRDT G-counter / PN-counter style systems
  • intentionally sharded hot counters

So the distinction is:

  • exact single-owner counter

    • no scatter-gather
    • direct source read
  • distributed partial counter

    • yes, scatter-gather or aggregation read
    • often eventual or merge-based