Skip to content
Cresva
Developers
EngineeringPricing

How ACP Handles Real-Time Pricing at Scale

Dynamic pricing across thousands of storefronts means millions of price updates per hour. When an AI agent asks "what does this cost right now?", the answer needs to be accurate, fresh, and fast. This post walks through the event-driven architecture, cache invalidation strategy, and consistency guarantees behind ACP's pricing infrastructure.

The pricing challenge

In traditional e-commerce, pricing is relatively simple. A brand sets prices in their product database, their website reads from that database, and customers see the current price. Cache invalidation happens on deployment or through a CMS flush.

Agent commerce is different in three ways that make pricing significantly harder:

Fan-out. A single user query ("find me running shoes under $120") might fan out to hundreds of storefronts simultaneously. Each storefront returns current pricing for dozens of products. That is tens of thousands of price lookups per user query. Freshness expectations. Agents promise real-time accuracy. If a storefront changes a price at 2:00 PM, an agent querying at 2:01 PM must see the new price. Stale pricing erodes trust - it is the most common source of user complaints in agent commerce. Negotiation coupling. ACP supports price negotiation, which means prices are not just read-only data. An agent might negotiate a 10% discount at 1:00 PM, then query the same product at 1:05 PM. The agent expects to see the base price, not the negotiated price (negotiations are scoped to sessions). This requires careful separation between base pricing, negotiated pricing, and promotional pricing.

Architecture overview

ACP's pricing infrastructure has three layers:

[Storefronts] --push--> [Price Event Bus] --fan-out--> [Regional Price Cache]
                                                              |
                                                   [Agent Query Layer]
Layer 1: Price Event Bus. Storefronts push price changes to ACP as structured events via the product.price_updated webhook. Each event includes the product ID, old price, new price, currency, effective timestamp, and an optional expiry (for time-limited promotions). The event bus is built on a partitioned log (partitioned by storefront ID) that guarantees ordered processing per storefront. Layer 2: Regional Price Cache. Price events are consumed by cache writers that update regional price caches deployed across 6 regions (US-East, US-West, EU-West, EU-Central, APAC-East, APAC-South). Each cache is a Redis Cluster with read replicas. The cache stores the latest price for every product, along with metadata like the update timestamp and TTL for promotional prices. Layer 3: Agent Query Layer. When an agent queries products, the query layer reads prices from the nearest regional cache. Cache hits return in under 5ms. Cache misses fall through to the storefront's live API, and the response is written back to the cache asynchronously.

Cache invalidation: the hard problem

The classic computer science joke is that there are only two hard problems: cache invalidation and naming things. In pricing, cache invalidation is not a joke - it is the critical path.

ACP uses a hybrid invalidation strategy:

Push-based invalidation

The primary invalidation mechanism is push-based. When a storefront updates a price, it sends a product.price_updated event to ACP. The event bus processes the event and updates all regional caches within 500ms (p99). This is the fast path - most price changes propagate through push.

Push-based invalidation requires storefronts to be well-behaved. If a storefront changes prices in their database without sending the corresponding event, the cache becomes stale. To detect this, we run a secondary mechanism.

Pull-based reconciliation

Every 60 seconds, a background reconciliation job samples 1% of cached prices and compares them against the storefront's live API. If a discrepancy is detected, the cache is updated and the storefront receives a pricing.stale_detected notification with guidance to fix their event pipeline.

The sampling rate is adaptive. Storefronts with a history of stale pricing are sampled more frequently (up to 10% per cycle). Storefronts with a clean track record are sampled less frequently (down to 0.1% per cycle). This focuses reconciliation resources where they are most needed.

TTL-based expiry

All cached prices have a maximum TTL of 15 minutes, regardless of whether an invalidation event is received. This is the safety net - even if both push and pull mechanisms fail, a stale price will self-correct within 15 minutes. For most use cases, push invalidation means prices are fresh within seconds, but the TTL ensures bounded staleness under failure conditions.

Promotional prices with explicit expiry times use the promotion's end time as the TTL, ensuring that expired promotions are never served to agents.

Consistency guarantees

ACP provides eventual consistency for pricing with a target convergence time of under 2 seconds for push-enabled storefronts. Here is what that means in practice:

  • A price change pushed via the event bus is visible to agents in all regions within 2 seconds (p99).
  • A price change not pushed via the event bus is detected and corrected within 60-900 seconds, depending on the storefront's sampling frequency.
  • No price is ever more than 15 minutes stale, regardless of failure conditions.
  • We considered offering strong consistency (read-after-write) but rejected it for two reasons. First, it would require routing all agent queries through a single primary region, destroying the latency benefits of regional caching. Second, the practical difference between "price updated 1 second ago" and "price updated 0 seconds ago" is negligible for agent commerce use cases.

    For negotiation pricing, we do provide strong consistency within a negotiation session. Negotiated prices are stored in a separate session-scoped store (not the global cache) and are always read from the primary.

    Handling price conflicts

    When an agent receives a product card with a price, then initiates a negotiation 30 seconds later, the base price might have changed in the interim. ACP handles this with optimistic concurrency:

  • The negotiation request includes the price_version from the original product card.
  • If the base price has not changed, the negotiation proceeds normally.
  • If the base price has changed, the negotiation request returns a 409 Conflict with the updated price. The agent can then decide whether to proceed with the new base price or inform the user.
  • This prevents a class of bugs where an agent negotiates a "10% discount" on a price that has already dropped, resulting in a worse deal than the current market price.

    Performance at scale

    Here are the current production numbers for ACP's pricing infrastructure:

  • Price events processed: 2.4 million per hour across all storefronts
  • Cache hit rate: 99.7% for product queries
  • Cache update latency (push): 180ms p50, 480ms p95, 890ms p99
  • Query latency (cache hit): 3ms p50, 8ms p95, 14ms p99
  • Query latency (cache miss): 120ms p50, 280ms p95, 450ms p99
  • Reconciliation discrepancy rate: 0.02% (2 in 10,000 sampled prices are stale)
  • The 99.7% cache hit rate is critical. At 10,000 storefronts with an average of 5,000 products each, the cache holds 50 million price entries. The working set (prices actually queried by agents in a given hour) is roughly 8 million entries, which fits comfortably in each regional cache's memory allocation.

    Cost considerations

    Running a global pricing cache is not free. Here is how we keep costs manageable:

  • Tiered storage. Hot prices (queried in the last hour) are kept in Redis. Warm prices (queried in the last 24 hours) are kept in a compressed on-disk store. Cold prices (not queried in 24+ hours) are evicted from the cache entirely and served from the storefront's live API on demand.
  • Compression. Price entries are stored as packed structs (product_id, price_cents, currency_code, updated_at, ttl) at 32 bytes each, not as JSON objects. This reduces memory usage by approximately 8x compared to naive JSON storage.
  • Regional sharding. Each regional cache only stores prices relevant to that region. A storefront that only ships to the US has its prices cached only in US-East and US-West, not in EU or APAC regions.
  • What's next

    We are working on several pricing infrastructure improvements:

  • Price prediction signals. Using historical price data to give agents signals about likely future price movements ("this product has dropped 15% in the last 30 days and is likely to drop further").
  • Real-time price streaming. A WebSocket-based API that streams price changes to agents in real time, eliminating the need for polling.
  • Multi-currency resolution. Automatic currency conversion at the cache layer, so agents can request prices in the user's preferred currency without per-storefront conversion logic.

  • Questions about ACP pricing infrastructure? Reach out at developers@cresva.ai or join our GitHub discussions.