liveBook · Manning

Distributed cache

As explained in Building Distributed Applications that Work

A distributed cache stores data for access by multiple processes or application instances.

A distributed cache lets separate applications or instances read the same cached data, even when another instance populated it. Redis can provide that shared cache across physical machines. For a product catalog, this avoids a database round trip on every request for information that changes infrequently. section 7.3 section 7.4.3

A /products endpoint can use a cache-first flow:

  • Register Redis with AddRedisDistributedCache() and inject IDistributedCache into the endpoint. section 7.4.3
  • Call GetStringAsync() with a product-list key. If it returns a value, deserialize the JSON and return the products. section 7.4.3
  • On a miss, query the database and store the resulting product data as a JSON string with SetStringAsync(). The next request can read it from the cache, regardless of which API instance receives it. section 7.4.3
  • After adding, removing, or changing a product, call RemoveAsync() so the next retrieval repopulates the entry rather than returning the old list. section 7.4.3
Listing 7.3 Retrieving product items from the cache
app.MapGet("/products",
    async ([FromServices] SqlConnection connection,
           [FromServices] IDistributedCache cache) =>
    {
        const string cacheKey = "Products";

        var cached = await cache.GetStringAsync(cacheKey);   #1
        if (cached is not null)    #2
        {
            var cachedProducts =
                JsonSerializer.Deserialize<ProductDto[]>(cached);

            return Results.Ok(cachedProducts);
        }

        await connection.OpenAsync();

        await using var command = new SqlCommand(@"
            USE Shop;
            SELECT Id, Title, Summary, Price
            FROM Products;", connection);

        var products = new List<ProductDto>();

        await using var reader = await command.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            products.Add(new ProductDto(
                Id: reader.GetInt32(0),
                Title: reader.GetString(1),
                Summary: reader.GetString(2),
                Price: reader.GetDecimal(3)
            ));
        }
        var serializedProducts = JsonSerializer.Serialize(products.ToArray());

        await cache.SetStringAsync(  #3
            cacheKey,
            serializedProducts,
            new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
            });

        return Results.Ok(products.ToArray());
    });

The /products endpoint listing shows the cache lookup alongside the database query it can avoid. section 7.4.3

A distributed cache can also support distributed locking: a named lock coordinates access to a shared resource across separate applications. That is a different use from caching product responses. section 7.3

How Building Distributed Applications that Work covers it

How deep it goes
Works through cache hits, misses, JSON storage, and invalidation at an API endpoint, without detailing Redis internals.
Examples
Uses a /products endpoint that reads a cached product list or queries the database and caches the result.
What it assumes
Assumes familiarity with API endpoints and database queries while explaining shared cache access and cache misses.
What you can do after
Apply it via Redis and IDistributedCache to cache product responses and invalidate them after product changes.
How it connects
Connects Redis, database queries, API scale-out, cache invalidation, and distributed locking.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Building Distributed Applications that Work. The section links point to the parts of the book it draws on.