Cut costs, boost speed: learn how Azure Managed Redis and Azure Cosmos DB with cache‑aside deliver sub‑millisecond reads and RU savings—without rewriting your app.
Co-authored by James Codella - Principal Product Manager, Azure Cosmos DB, Microsoft; Andrew Liu - Principal Group Product Manager, Azure Cosmos DB, Microsoft; Philip Laussermair – Azure Managed Redis Solution Architect, Redis Inc.
Using Azure Managed Redis alongside Azure Cosmos DB is a powerful way to reduce operational costs in read-heavy applications. While Azure Cosmos DB delivers low-latency point reads, an SLA-backed 10ms at the 99th percentile of requests, each read consumes Request Units (RUs), which directly impact your billing. For workloads with frequent access to the same data, caching those reads in Azure Managed Redis can dramatically reduce RU consumption and smooth out cost spikes. This cache-aside pattern allows applications to serve hot data from memory while preserving Azure Cosmos DB as the system of record. By shifting repeated reads to Azure Managed Redis, developers can optimize for cost efficiency without sacrificing consistency, durability, or global availability.
What each service does: Azure Managed Redis is Microsoft’s first‑party, fully managed service built on Redis Enterprise. Azure Cosmos DB offers higher durable multi-region writes, high availability SLAs, and in-region single-digit millisecond latency for point operations. Co-locating app compute, Azure Managed Redis, and Azure Cosmos DB in the same region minimizes round-trips; adding Azure Managed Redis on top of Azure Cosmos DB reduces RU consumption from repeat reads and smooths tail latency spikes during peak load. Both Azure Managed Redis and Azure Cosmos DB offer great support for queries (including vector search) over JSON data to work well together fast efficient AI apps.
This pairing doesn’t require a rewrite. You can adopt a cache‑aside strategy in your data‑access layer: look in Redis first; on a miss, read from Azure Cosmos DB and populate Redis with a TTL; on writes, update Azure Cosmos DB and invalidate or refresh the corresponding cache key. Use Azure Cosmos DB ETags in cache keys to make invalidation deterministic, and use the Azure Cosmos DB Change Feed to trigger precise cache refreshes when data changes.
The Cache‑Aside (Lazy Loading) Pattern
Read path: GET key from Azure Managed Redis. If found, return. If not, issue a point read to Azure Cosmos DB, then SET/JSON.SET the value in Azure Managed Redis with a TTL and return the payload to the caller.
Write path: Persist to Azure Cosmos DB as the source of truth. Invalidate or refresh the related Redis key (for example, delete product:{id}:v{etag} or write the new version). If you subscribe to the Change Feed, an Azure Function can perform this invalidation asynchronously to keep caches hot under write bursts.
Code Example (.NET):
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
using Microsoft.Azure.StackExchangeRedis;
using Azure.Identity;
public static class CosmosDbChangeFeedFunction
{
private static RedisConnection _redisConnection;
static CosmosDbChangeFeedFunction()
{
// Initialize Redis connection using Entra ID (Azure AD) authentication
var redisHostName = Environment.GetEnvironmentVariable("RedisHostName"); // e.g., mycache.redis.cache.windows.net
var credential = new DefaultAzureCredential();
var configurationOptions = ConfigurationOptions.Parse($"{redisHostName}:10000");
_redisConnection = RedisConnection.ConnectAsync(configurationOptions.ConfigureForAzureWithTokenCredentialAsync(credential)).GetAwaiter().GetResult();
}
[FunctionName("CosmosDbChangeFeedFunction")]
public static async Task Run(
[CosmosDBTrigger(
databaseName: "my-database",
containerName: "my-container",
ConnectionStringSetting = "CosmosDBConnection",
LeaseContainerName = "leases")] IReadOnlyList<Document> input,
ILogger log)
{
var cache = _redisConnection.GetDatabase();
if (input != null && input.Count > 0)
{
foreach (var doc in input)
{
string id = doc.GetPropertyValue<string>("id");
string etag = doc.GetPropertyValue<string>("_etag");
string cacheKey = $"item:{id}:v{etag}";
string json = doc.ToString();
await cache.StringSetAsync(cacheKey, json, TimeSpan.FromMinutes(10));
log.LogInformation($"🔄 Refreshed cache for key: {cacheKey}");
}
}
}
}
Why it works: The database handles correctness and global replication; the cache handles locality and frequency. You reduce repeated reads (and RU costs) and lower p99 by serving hot keys from memory close to the compute. TTLs give you explicit control over staleness; negative caching and stale‑while‑revalidate are easy extensions when appropriate.
Design Choices That Matter
- TTLs: Choose TTLs that reflect business tolerance for staleness. Use jitter (±N%) to avoid thundering herds when many keys expire simultaneously.
- Keying and versioning: Include an ETag or version in the key, e.g., product:{id}:v{etag}. When the record changes, the ETag changes, naturally busting the old key.
- Cache stampede control: For hot keys that miss, use a short single‑flight lock so that one request refreshes the value while others wait briefly or serve stale data.
- Serialization: For portability, utilize compact JSON (RedisJSON if you want field‑level reads/writes). Keep values small to preserve cache efficiency.
- Failure semantics: Treat Azure Managed Redis as an optimization. If the cache is unavailable, the app should continue by reading from Azure Cosmos DB. Favor idempotent writes and retry‑safe operations.
Why Azure Managed Redis & Azure Cosmos DB Work Well in Practice
Local speed, global reach: Azure Cosmos DB targets a single-digit millisecond p99 for in-region point operations. Placing Azure Managed Redis in the same region enables sub-millisecond memory reads for repeated access patterns, providing optimal performance for high-frequency data access. The result is a shorter, more predictable critical path.
Active‑active at both layers: Azure Cosmos DB supports multi‑region writes, so each region can accept traffic locally. Azure Managed Redis supports active geo‑replication across up to five instances, using conflict-free replicated data types (CRDT) to converge cache state. That yields region‑local cache hits with eventual consistency at the cache tier and strong guarantees at the database tier.
Reference Architecture
Regional deployment
- Ingress: Clients reach the app via Azure Front Door (or similar) and land on Azure App Service in a VNet.
- Read path: The app queries Azure Managed Redis first. On a miss, it performs a point read/write against Azure Cosmos DB (API for NoSQL) and updates Redis with a TTL.
- Write path: Writes go to Azure Cosmos DB. The Change Feed triggers an Azure Function that invalidates or refreshes related Redis keys.
- Observability: Use Azure Monitor for logs and metrics. Monitor cache hit ratio, gets/sets, evictions, and geo‑replication health on the Azure Managed Redis side; RU/s, throttles, and p99 latency on the Azure Cosmos DB side.
Operations and SRE Considerations
- Co‑location: Keep compute, Azure Managed Redis, and the Azure Cosmos DB write/read region together to avoid unnecessary RTTs.
- Capacity planning: Size Azure Managed Redis memory for working‑set coverage and headroom for failover. Validate eviction policies (volatile‑TTL vs all‑keys) against workload behavior.
- Back‑pressure: Watch RU throttling on Azure Cosmos DB and evictions on Azure Managed Redis. High evictions or a low hit ratio indicate a working-set mismatch or TTLs that are too short.
- Testing: Load‑test with realistic key distributions and measure p50/p95/p99 on both cache hits and misses. Chaos‑test cache outages to verify graceful degradation.
- Security: Use managed identities for data plane access where supported; apply App Service access restrictions and VNet integration as appropriate; audit with Azure Monitor logs.
Putting It All Together
Adopt cache‑aside in one region, measure hit ratio and RU savings, then add Change Feed–based invalidation to keep hot keys fresh under write load. When you need global scale, enable Azure Cosmos DB multi-region writes and Azure Managed Redis active geo-replication so that every region serves users locally. You end up with fast read paths, clear consistency boundaries, and a deployment model that scales without surprises.
Next steps
- Review Azure Managed Redis documentation - https://learn.microsoft.com/azure/redis/ ,
- Learn more about Cache Aside pattern in the Azure Architecture Center - https://learn.microsoft.com/azure/architecture/patterns/cache-aside
- Start with a sample app - https://github.com/AzureManagedRedis/