Redis Redis Stream Created: 14 Jan 2026 Updated: 14 Jan 2026

A Deep Dive into StreamAddAsync

In the world of high-performance distributed systems, Redis Streams have emerged as a powerhouse for handling append-only logs, event sourcing, and reliable message queuing. While traditional Redis Pub/Sub is excellent for "fire-and-forget" scenarios, Streams offer the persistence and consumer-group features required for modern microservices.

In this article, we will break down the primary method for producing data to a stream using the StackExchange.Redis library: StreamAddAsync.

What is StreamAddAsync?

The StreamAddAsync method is the asynchronous .NET wrapper for the Redis XADD command. It allows you to append a new entry to a stream. Unlike a simple Key-Value pair, a stream entry consists of an ID and a collection of field-value pairs, making it structurally similar to a row in a table or a JSON object.

Detailed Parameter Breakdown

Understanding the method signature is key to optimizing your Redis implementation. Let’s look at the parameters as defined in the IDatabaseAsync interface:

ParameterTypePurpose
keyRedisKeyThe name of the stream. If it doesn't exist, Redis creates it automatically.
streamPairsNameValueEntry[]The payload. Each NameValueEntry represents a field and its value (e.g., "OrderId": "123").
messageIdRedisValue?The unique ID for the entry. Use null for Redis to auto-generate a timestamp-based ID.
maxLengthlong?The maximum number of items allowed in the stream. Used for automatic eviction.
useApproximateMaxLengthboolPerformance Boost: When true, Redis trims the stream "approximately" to save CPU cycles.
limitlong?Limits the number of items deleted during a trim operation (used with StreamTrimMode.MinId).
trimModeStreamTrimModeStrategy for deleting old data: MaxLen (by count) or MinId (by ID threshold).
flagsCommandFlagsAdvanced options like FireAndForget or DemandMaster.

Professional Implementation in ASP.NET Core

To implement this effectively, you should encapsulate the logic within a service and leverage dependency injection. Below is a robust example of a Producer service.

1. The Producer Service

using StackExchange.Redis;

public class EventPublisher : IEventPublisher
{
private readonly IDatabase _db;
private const string StreamName = "telemetry:sensors";

public EventPublisher(IConnectionMultiplexer redis)
{
_db = redis.GetDatabase();
}

public async Task<string> PublishSensorDataAsync(int sensorId, double value)
{
// Define the message payload
var data = new NameValueEntry[]
{
new("sensor_id", sensorId),
new("value", value.ToString()),
new("captured_at", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds())
};

// Efficiently add to stream with approximate trimming
// This keeps the stream size around 5000 items without high CPU overhead
RedisValue resultId = await _db.StreamAddAsync(
key: StreamName,
streamPairs: data,
maxLength: 5000,
useApproximateMaxLength: true,
trimMode: StreamTrimMode.MaxLen
);

return resultId.ToString();
}
}

Why Performance Matters: Approximate Trimming

One of the most powerful features of StreamAddAsync is the useApproximateMaxLength parameter.

When you set a maxLength of 1000 and useApproximateMaxLength is false, Redis must perform an $O(N)$ operation to ensure exactly 1000 items remain every time you add a message. By setting it to true, Redis uses a "near-constant time" approach by removing internal macro-nodes only when it is efficient to do so. This is critical for high-throughput systems where every millisecond counts.

Note: Even with approximate trimming, the stream size will stay very close to your limit, but it might occasionally be slightly larger.

Best Practices for Producers

  1. Key Naming: Use a consistent naming convention, such as domain:subdomain:action (e.g., orders:payments:captured).
  2. Idempotency: If you are retrying failed XADD operations, consider including a unique business-level ID in your payload to help consumers detect duplicates.
  3. Memory Management: Always use maxLength and StreamTrimMode.MaxLen. Streams live in RAM; without a capping strategy, you risk exhausting your Redis memory.

Redis Streams offer a robust foundation for building resilient, scalable .NET applications. By mastering StreamAddAsync, you ensure your data is stored efficiently and safely for downstream consumers.

Share this lesson: