All insights

Inference economics

How Should Wallet Balance Reads Remain Correct During Concurrent Top-Ups, Reservations, and Settlements?

Wallet balance reads should remain correct by defining the wallet’s accounting invariants, serializing correctness-sensitive decisions at a clear boundary—commonly one wallet or account—and performing the authoritative balance read and corresponding mutation within that same consistency boundary. Top-ups, reservations, captures, settlements, releases, expirations, and reversals should be explicit state transitions. Cached or asynchronously projected balances may support display use cases, but they should not authorize spending unless their consistency contract makes them safe for that decision.

Wallet balance reads should remain correct by defining the wallet’s accounting invariants, serializing correctness-sensitive decisions at a clear boundary—commonly one wallet or account—and performing the authoritative balance read and corresponding mutation within that same consistency boundary. Top-ups, reservations, captures, settlements, releases, expirations, and reversals should be explicit state transitions. Cached or asynchronously projected balances may support display use cases, but they should not authorize spending unless their consistency contract makes them safe for that decision.

The short answer: serialize wallet decisions and read from the same consistency boundary

A wallet system usually has two different reading requirements:

  1. Decision reads determine whether an operation may proceed, such as approving a new reservation or capture.
  2. Display reads show a customer or operator the wallet’s current and pending amounts.

The first category is correctness-sensitive. The system must not approve two competing operations from the same stale view of available funds. A safe transaction flow commonly looks like this:

  1. Begin a transaction or enter the wallet’s serialized command path.
  2. Read authoritative wallet state.
  3. Validate the requested transition against the current state and applicable invariants.
  4. Write the ledger operation and update any transactional balance state.
  5. Record the operation identifier or idempotency result.
  6. Commit the complete change atomically.

Consider a wallet with 100 units available. Two concurrent requests each try to reserve 70. If both requests independently read 100 and then write their results without conflict detection, both may be approved even though the combined reservations exceed the available amount.

A wallet-level serialization mechanism changes the sequence. The first request reads 100, reserves 70, and leaves 30 available. The second request must then evaluate against 30, conflict and retry against the new state, or otherwise be rejected. The exact behavior depends on the chosen database and processing architecture, but the key principle is consistent: the read that authorizes a mutation cannot be separated from the mechanism that protects that decision from competing writes.

This does not mean one database setting solves every failure mode. Transaction isolation, idempotency, lifecycle validation, event ordering, and processor reconciliation solve different problems and should be designed together.

Define the balances and invariants before choosing a concurrency mechanism

“Balance” is not sufficiently precise for implementation. Teams should define what each amount means, which entries contribute to it, and whether it is authoritative or derived.

TermTypical meaningImportant design question
Posted balanceFunds represented by completed or posted ledger entriesWhich top-ups, captures, refunds, and reversals qualify as posted?
Pending amountOperations that have started but are not finalDoes pending money affect spendability, display only, or both?
Reserved amountFunds held for an approved future capture or settlementWhen does a reservation expire, and can it be partially captured?
Available balanceAmount currently eligible for a new spending decisionWhich posted, reserved, pending, disputed, or restricted entries contribute to the calculation?

There is no universal formula for available balance. One implementation might calculate it as posted funds minus active reservations and other restrictions. Another might treat pending top-ups as visible but not spendable. The formula must follow the wallet’s accounting and product rules rather than an assumed industry default.

The system’s invariants should then be stated in machine-testable terms. Depending on the product, those rules may include:

  • A reservation cannot reduce available funds below an allowed floor.
  • The cumulative captured amount cannot exceed the reservation’s capturable amount.
  • A top-up, capture, or reversal operation cannot be applied twice under the same business identifier.
  • A reservation cannot be simultaneously released and captured beyond its remaining amount.
  • Every materialized balance can be traced back to authoritative ledger operations.
  • Negative balances are either prohibited or allowed only through explicitly defined transitions.

These rules should also clarify units, currency handling, rounding, and whether wallets can contain multiple currencies. If multiple wallets or accounts participate in one operation, the serialization boundary may need to extend beyond a single wallet or use a transfer protocol that preserves the required cross-account invariants.

Keep the ledger authoritative and model every money movement explicitly

An append-oriented ledger is a common foundation because it preserves the history needed to explain how a wallet reached its current state. Instead of overwriting a single balance with each request, the system records explicit business operations and derives current amounts from those records, either directly or through maintained projections.

Useful operation types may include:

  • Top-up initiated, posted, failed, or reversed
  • Reservation created, adjusted, released, or expired
  • Partial or full capture
  • Settlement recorded
  • Refund or reversal
  • Administrative correction through a traceable compensating entry

The precise operation set depends on the wallet and processor model. For example, some systems treat capture and settlement as separate stages, while others receive a processor event that combines their practical effect. What matters is that distinct lifecycle events are not collapsed into an ambiguous “update balance” command.

Each ledger operation should carry enough identity and context to establish what happened. Common fields include a unique operation ID, wallet ID, operation type, amount and currency, business reference, event time, recording time, prior operation reference, and resulting status. Double-entry accounting may be appropriate when the product needs explicit movement between accounts, but it is not the only possible ledger model.

Snapshots and materialized balance rows can avoid replaying the entire ledger for every request. They remain derived state unless the architecture explicitly makes their update part of the authoritative transaction. A transactional balance row, for example, may be updated atomically with the new ledger entry and used for subsequent decisions. An asynchronous analytics projection should instead be treated as a read model that can lag.

Repair should be deterministic. If a projection becomes inconsistent, the system should be able to rebuild or verify it from authoritative records. Historical entries should generally not be silently rewritten to force the balance to match an external total. Corrections are better represented as traceable operations under the applicable accounting model.

Choose where competing wallet mutations are serialized

The wallet or account is a common serialization boundary because operations against unrelated wallets can proceed independently. However, the right boundary follows the invariant. A transfer between two wallets, a shared spending pool, or a merchant-level limit may require coordination across more than one record or partition.

Several concurrency patterns can protect the boundary:

PatternOften suitable whenMain implementation considerations
Serializable transactionsThe datastore provides suitable semantics and transactions remain boundedSerialization failures require safe retries; database behavior must be tested under the actual access pattern
Row-level lockingWallet state is represented by a lockable authoritative rowKeep transactions short, use consistent lock ordering, and plan for waits and deadlocks
Optimistic concurrencyConflicts are relatively uncommon and version checks are reliableFailed compare-and-set operations must re-read, revalidate, and retry within a bounded policy
Single-writer or partitioned commandsCommands can be routed consistently by wallet or account keyPartition ownership, failover, ordering, backlog, and hot-wallet behavior become operational concerns

A serializable database transaction can detect or prevent certain conflicting executions, but its guarantees depend on the datastore and transaction scope. The application still needs to validate business transitions, recognize duplicates, handle retries, and reconcile external outcomes.

Row-level locking can be straightforward when one row represents the decision state. The transaction might lock the wallet row, read its current version and amounts, validate the request, append the ledger operation, update the balance row, and commit. Transactions should avoid unrelated network calls while holding the lock because external latency can increase contention and complicate failure handling.

Optimistic concurrency attaches a version to wallet state. A command reads version 41 and attempts to update only if the version is still 41. If another command has already produced version 42, the update fails. The losing command must fetch current state and rerun the full business validation; merely retrying the stale write is insufficient.

A single-writer design routes all commands for a wallet to one ordered processor. This can make command ordering explicit, but it does not remove the need for durable operation identities, state validation, recovery procedures, or protection against duplicate message delivery.

Choose among these patterns based on:

  • Expected contention, including unusually active or shared wallets
  • Required decision latency and acceptable retry behavior
  • Datastore transaction and isolation semantics
  • Cross-wallet or cross-partition invariants
  • Failover and message-redelivery behavior
  • The team’s ability to operate locks, retries, partitions, and lag monitoring

Whichever pattern is selected, test it against the actual schema and query plan. Isolation labels alone do not establish that every application-level race has been addressed.

Separate authorization reads from customer-facing display reads

A read used to approve spending should observe authoritative state that is sufficiently fresh to participate in the protected decision. In many designs, the balance is read from the primary transactional store after acquiring a lock, inside a serializable transaction, during a successful version check, or within the wallet’s ordered command processor.

Caches, read replicas, and asynchronous projections have a different role. They may be appropriate for account screens, dashboards, statements, and analytics when the interface communicates their freshness and distinguishes posted amounts from pending or reserved amounts.

A practical read-path policy might classify requests as follows:

  • Reserve or authorize: Read and mutate inside the wallet’s protected consistency boundary.
  • Capture or release: Load the authoritative reservation state and validate the transition atomically.
  • Immediate post-action confirmation: Use the committed result or an authoritative read if read-your-writes behavior is required.
  • General wallet display: A projection may be acceptable if lag is monitored and pending states are shown accurately.
  • Reporting and analytics: An asynchronous replica may be suitable if the report states its cutoff or processing time.

A cached value should not become an authorization value merely because it is faster to retrieve. If a system does permit authorization from a reservation-backed or bounded-staleness model, that model needs an explicit mechanism that prevents the same funds from being committed elsewhere. A time-to-live setting by itself does not provide that protection.

Display APIs should also expose enough state to avoid misleading users. Showing “100” without indicating that 70 is reserved may be technically derived from posted entries but operationally confusing. Depending on the product, a response may need separate posted, reserved, pending, and available fields, along with an update timestamp or processing status.

Monitor projection lag as an operational signal. When lag exceeds the accepted display contract, the service can mark the balance as updating, route to an authoritative source where practical, or temporarily limit affected views. The appropriate degraded behavior should be designed rather than discovered during an incident.

Make retries and duplicate events safe without relying on vague exactly-once claims

Retries are unavoidable when clients time out, workers restart, queues redeliver messages, or processors repeat notifications. The wallet must distinguish a repeated attempt at the same business operation from a genuinely new operation.

An idempotency key or unique operation ID is a common mechanism. The key should be scoped to the relevant actor and operation, stored under a uniqueness constraint, and linked to the resulting ledger record and response. Where the datastore permits it, the idempotency record and wallet mutation should commit atomically.

For example, if a client retries a top-up request after losing the response, the server can look up the original operation and return its existing status rather than crediting the wallet again. The same principle applies to duplicate settlement webhooks and redelivered capture commands.

The design should define what happens when the same key arrives with different parameters. Silently treating a different amount as equivalent can conceal an integration error. A safer contract commonly rejects the conflicting request and returns the identity or status of the original operation.

Idempotency does not replace concurrency control. Two different operation IDs can still compete for the same available funds. Nor does it automatically extend across a database, queue, and external payment processor. Each failure boundary needs a defined protocol, such as transactional persistence followed by an outbox, an inbox for processed events, or a retriable state machine with durable operation status.

Safe retries should be bounded and observable. Record conflicts, serialization failures, deadlocks, duplicate deliveries, and operations left in intermediate states. Automated retry is appropriate only when the operation can be re-evaluated safely against current authoritative state.

Resolve races across reservation, settlement, expiry, and reversal lifecycles

Reservation and settlement behavior is best represented as a state machine with validated transitions, not as independent balance arithmetic. Each transition should evaluate the current reservation state and remaining capturable amount inside the same protected boundary used for the ledger mutation.

Important race conditions include:

  • Settlement after expiry: Decide whether the processor’s late event is accepted, rejected, recorded as an exception, or handled through a compensating flow. The answer depends on the external contract and wallet model.
  • Partial capture: Track the cumulative captured amount and remaining reservable amount. A later capture must validate against what remains, not the original reservation total.
  • Over-capture attempt: Reject or route the excess according to explicit policy rather than allowing the balance to drift silently.
  • Concurrent capture and release: Serialize both transitions against the same reservation so only a valid ordering can commit.
  • Top-up reversal after spending: Record the reversal explicitly and apply the wallet’s defined negative-balance, recovery, or restriction policy.
  • Out-of-order events: Use operation references, processor sequence information where available, and current-state validation rather than assuming delivery order equals business order.

External settlement creates a second source of operational truth: the internal ledger records what the wallet believes occurred, while the processor records what it accepted or settled. Reconciliation should compare the two using stable operation references, amounts, currencies, statuses, and relevant processing dates.

Discrepancies should enter an exception process. Depending on the case, resolution may involve retrying an integration step, obtaining a missing processor record, rejecting a duplicate, or posting a traceable compensating entry. Quietly changing historical balances makes future investigation and reconstruction harder.

Correctness should be verified with invariant-focused tests rather than only happy-path API tests. Useful validation includes:

  • Launching many simultaneous reservations against the same constrained balance
  • Racing capture, release, and expiry for one reservation
  • Replaying top-up and settlement events with duplicate and conflicting identifiers
  • Delivering processor events late and out of order
  • Injecting failures before and after ledger, idempotency, outbox, and projection commits
  • Rebuilding projections and comparing them with authoritative records
  • Monitoring unresolved operations, projection lag, duplicate rates, and reconciliation differences

The final architecture should make it possible to explain every displayed and decision-making balance from recorded operations. Correctness comes from explicit invariants, an effective serialization boundary, authoritative decision reads, atomic state transitions, duplicate handling, reconciliation, and sustained operational testing—not from any one database feature in isolation.

Next Step

This wallet architecture guide is separate from Token Forge Cloud’s AI model serving products. If your organization is also evaluating enterprise AI infrastructure, contact Token Forge Cloud to discuss API access, private deployment, and LLM inference cost control.

Contact us