All eyes on AI: 2026 predictions – The shifts that will shape your stack.

Read now

Blog

Idempotency patterns for LLM apps with Redis

February 11, 202610 minute read
James Tessier
James Tessier

You've probably hit this before: a network timeout fires during a large language model (LLM) API call, your app retries, and now you're not sure if you just paid for that response twice. If you retry a paid LLM call, you can end up paying twice, and output tokens are often priced higher than input tokens depending on the model.

Idempotency helps address this. An operation is idempotent if running it multiple times produces the same result as running it once. For LLM apps, this property influences whether your retry logic creates duplicate API charges or handles network failures without breaking your budget.

This guide covers idempotency patterns for LLM operations, practical implementations using Redis atomic commands, and semantic caching strategies that recognize duplicate queries even when users phrase them differently.

Why idempotency matters for LLM apps

LLM API calls add up. For many hosted LLMs, a request with roughly 1,000 input tokens and a few hundred output tokens costs only a few cents or less, depending on the model and provider. Retry that request three times without idempotency guarantees, and you've potentially tripled what should have been one operation.

The math tends to get worse at scale. Say your retrieval-augmented generation (RAG) app handles 1,000 daily queries averaging a few cents each. With a 5% retry rate due to timeouts, you're making around 50 duplicate calls daily. That's potentially a few dollars per day wasted, scaling to hundreds annually. Enterprise workloads with tens of thousands of daily queries can multiply the problem significantly.

Cost isn't the only concern. Operational experience with LLM systems shows failures can stem from multiple layers: infrastructure, configuration, inference engines, or operations. Each of these often requires some form of retry logic. Without idempotent operations, those retries risk duplicate charges, inconsistent state, or cascading failures across your system.

RAG workflows face their own challenges. Without content hashing during ingestion, the same document can be processed multiple times, potentially creating duplicate embeddings that may clutter your index, degrade retrieval quality, and inflate storage costs.

How idempotency works in APIs & Redis

By HTTP semantics defined in RFC 9110, methods like GET, HEAD, PUT, and DELETE are defined to be idempotent. The intended resource state should be the same after one or multiple identical requests, although servers can still produce side effects such as logging. OPTIONS and TRACE are also semantically idempotent. POST tends to be the problem child because it can create new resources with each request, which makes retries risky.

The Idempotency-Key header offers a common approach to this problem and is used by major payment APIs. Stripe, for example, has adopted it. In production, idempotency keys help prevent double charges during network glitches, though client apps are still responsible for generating the keys and implementing retry logic (such as exponential backoff).

Redis command classification

Understanding which Redis commands behave idempotently helps you design retry logic that minimizes unintended side effects. The distinction comes down to whether running a command twice with the same arguments produces the same data state as running it once. Redis commands can be grouped based on these properties.

Idempotent commands

For a given key and identical arguments, some Redis commands behave idempotently in terms of resulting data state. SET and SADD can typically be retried safely when using the same value or members. Executing them repeatedly with the same data results in the same database state:

SET mykey "value" # State: mykey = "value"

SET mykey "value" # State: mykey = "value" (unchanged)

HSET overwrites specified hash fields with the same value, and SADD ignores members that already exist in the set. Note that while the data state remains consistent, return values may differ between first and subsequent calls (for example, SADD returns the count of new members added, which will be zero on retries).

Non-idempotent commands

INCR increments the integer value by one each time it runs. Three retries mean your counter increases by three, not one. LPUSH inserts elements at the head of the list, and RPUSH inserts at the tail. Neither prevents duplicates, which can create ordering challenges when retries insert the same element multiple times.

ZINCRBY presents similar considerations for sorted sets, incrementing a member's score with each execution. These commands work well for accumulation use cases like counters or leaderboards where each operation should have additive effects. The key is recognizing when your use case calls for idempotent alternatives.

This classification shapes how you design retry logic. Commands that behave idempotently with identical arguments can generally be retried safely, though practical systems typically recommend bounded retries rather than retrying indefinitely. Commands like INCR or LPUSH change state with each call and usually benefit from application-level safeguards: unique request identifiers, deduplication tables, or transactional guarantees.

Redis patterns for idempotent LLM operations

Redis offers several patterns for implementing idempotency in LLM workflows. The core idea is straightforward: before executing an expensive operation, check whether you've already processed that request.

SET NX for idempotency keys

A common approach uses the SET command with NX option, which provides atomic check-and-set behavior:

SET idempotency:request-123 "PENDING" NX EX 3600

The NX option only sets the key if it doesn't already exist, while EX sets expiration in seconds. An OK response means the key was set (new request); a nil reply means it already existed (likely a duplicate). This single command handles the check-and-lock operation atomically, helping to avoid race conditions between checking and setting.

When using SET with additional options like NX, EX, or XX, ensure your retry logic uses the same options and values to maintain consistent behavior across attempts.

Lua scripts for complex operations

When you need multi-step logic (checking a key, returning a cached result if found, or setting a new value with specific time-to-live) Lua scripts execute atomically in Redis. All commands in the script run as a single, uninterrupted operation on a given shard, which helps avoid race conditions between those operations. This becomes useful when your idempotency logic needs to handle multiple states: new request, currently processing, or completed with cached result.

Applying idempotency to LLM workflows

These patterns apply at different layers of a typical LLM application. Understanding where to add idempotency helps you protect against duplicate costs without overcomplicating your architecture.

Document ingestion

RAG pipelines often benefit from idempotency at the ingestion layer to help reduce duplicate embeddings. The approach typically involves hashing document content and using that hash as an idempotency key. Before generating embeddings, check if the content hash already exists. If so, skip processing. This can reduce duplicate work when the same document gets submitted multiple times, whether through retries or batch processing overlap.

Content hashing tends to work best when you have consistent document identification. The same document can still be embedded multiple times if it appears in different collections or with different document IDs.

Query-time operations

For user queries, request-level idempotency helps reduce duplicate LLM calls during retries. The pattern involves generating a unique request ID for each query, claiming that ID with SET NX before calling the LLM, then storing the response for potential retry hits. If a retry comes in with the same request ID, you return the cached response instead of making another API call.

LangChain integration

LangChain includes a Redis-based cache integration that checks for cached LLM responses before calling the model. When a matching entry is found, it returns the stored response instead of issuing a new API request. Typically, the cache key is derived from the prompt plus key model parameters, so identical requests should hit the same cache entry.

For semantic caching, Redis uses Redis Query Engine (included in Redis Stack and available in Redis Cloud) to handle vector search over embeddings.

Semantic caching with Redis LangCache

Exact-match caching works well for programmatic queries, but it often falls short when humans are involved. Users don't phrase questions consistently. "What's the weather?" and "How's the weather today?" would both miss an exact-match cache despite requesting identical information.

Semantic caching addresses this by converting queries into vector embeddings and using similarity matching. When a query arrives, the system generates an embedding vector representing its semantic meaning, then compares it against cached query embeddings using vector search. If the similarity score exceeds a configured threshold, the cached response returns immediately. Otherwise, the system calls the LLM, caches the new response with its embedding, and returns the result.

In Redis's own tests documented in the LLMOps Guide, semantic caching reduced latency from approximately 1.67 seconds to 0.052 seconds per cache hit—a 96.9% latency reduction in that scenario.

Redis LangCache

Redis LangCache is a managed semantic caching service that handles the vector search and similarity matching for you. In Redis's reported benchmarks, it can deliver up to 15x faster responses for cache hits and reduce LLM API costs by up to 70% by skipping unnecessary LLM calls. You can integrate LangCache through a REST API or software development kit (SDK), often requiring only minimal changes to your existing LLM call paths.

Threshold tuning benefits from empirical testing with your specific queries. Too permissive and you risk returning cached answers for genuinely different questions; too restrictive and you miss valid cache opportunities.

Exact-match vs semantic caching

Exact-match caching tends to work well for deterministic prompts where wording rarely varies, like system prompts or templated queries. Semantic caching is often better suited for user-facing apps where question phrasing varies naturally. Redis Query Engine powers the vector search capabilities behind semantic caching, available in Redis Stack for self-managed deployments and natively in Redis Cloud.

When to invest in idempotency patterns

Not every LLM operation needs idempotency safeguards. The decision depends on the cost of duplicates, your retry frequency, and the complexity of your workflows.

High-priority scenarios

Certain scenarios benefit from idempotency patterns from day one because the cost of duplicates is too high to ignore.

  • Financial operations like token purchases, API billing, and credit deductions create real monetary loss when duplicated. For many models, output tokens are priced significantly higher than input tokens, so duplicate API calls multiply costs rapidly. Without idempotency, a network timeout during a $50 batch inference job could charge you $100 or more when retries succeed multiple times.
  • High concurrency scenarios with request rates typically exceeding 10 per second to the same logical resource often benefit from idempotency. At higher rates, the window for race conditions shrinks to milliseconds. Two requests can both check for an existing key, both find nothing, and both proceed to execute.
  • Microservices coordination across LLM inference, vector storage, and result caching pipelines spanning multiple services works best with idempotent design. When a pipeline fails mid-way (say, after embeddings are stored but before the LLM response is cached), retries without idempotency can create orphaned embeddings and inconsistent state that's difficult to reconcile.
  • Expensive operations with individual API calls costing more than $0.10 where duplicates waste budget typically benefit from idempotency patterns.

If any of these scenarios describe your workload, consider implementing idempotency safeguards before scaling to production.

Start with Redis basics

For simpler workloads where retry frequency stays low, Redis' atomic SET NX pattern handles most idempotency needs without additional infrastructure. As your workload grows, you can layer on Lua scripts for multi-step logic or add semantic caching for user-facing queries where phrasing varies.

The implementation cost tradeoff

Redis-based idempotency typically involves tracking infrastructure, distributed lock management, and thorough testing. With pipelining, Redis can deliver around 1 million requests per second on an average Linux system, making performance overhead negligible for most workloads. The development complexity is often the real cost.

Making your LLM stack reliable

Idempotency helps transform unreliable distributed systems into more predictable infrastructure. For LLM apps where every API call has a price tag, this property is foundational.

Redis provides the building blocks: atomic SET NX operations for idempotency keys, low-latency checks that typically complete in well under a millisecond, and semantic caching to reduce duplicate LLM calls when users phrase queries differently. Document ingestion uses content-based hashing, retrieval employs differentiated time-to-live (TTL) strategies, and idempotency keys allow safe retries without duplicate charges.

Try Redis free to test these patterns with your workload, or talk to our team about optimizing your AI infrastructure for reliability and cost efficiency.

Get started with Redis today

Speak to a Redis expert and learn more about enterprise-grade Redis today.