Real-time with Redis Streams in Go
Athreya aka Maneshwar

Athreya aka Maneshwar @lovestaco

About: Technical Writer | 200k+ Reads | i3 x Mint | Learning, building, improving, writing :)

Joined:
Jan 5, 2023

Real-time with Redis Streams in Go

Publish Date: Jun 18
11 1

Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool that helps you automatically index API endpoints across all your repositories. LiveAPI makes it easier to discover, understand, and interact with APIs in large infrastructures.


Real-time with Redis Streams in Go

Redis Streams give you Kafka-like message queues with Redis simplicity. Whether you’re building real-time analytics, background job pipelines, or chat systems, Redis Streams can help.

In this post, we’ll cover:

  • What is a Redis Stream?
  • Setting up Redis
  • Writing to a Stream in Go
  • Reading from a Stream in Go
  • Using Consumer Groups
  • Stream Configuration Parameters

What is a Redis Stream?

A Redis Stream is an append-only log data structure where each entry has a unique ID and a set of key-value fields.

You write using XADD, read using XREAD, and scale consumption using consumer groups.

# Example
XADD mystream * name Alice action login
Enter fullscreen mode Exit fullscreen mode

Setup

Install Redis:

sudo apt install redis
redis-server
Enter fullscreen mode Exit fullscreen mode

Install Go Redis client:

go get github.com/redis/go-redis/v9
Enter fullscreen mode Exit fullscreen mode

Writing to a Stream in Go

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/redis/go-redis/v9"
)

var ctx = context.Background()

func main() {
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    streamKey := "events"
    args := &redis.XAddArgs{
        Stream: streamKey,
        MaxLen: 1000,
        Approx: true,
        Values: map[string]interface{}{
            "user":   "maneshwar",
            "action": "upload",
            "time":   time.Now().Format(time.RFC3339),
        },
    }
    id, err := rdb.XAdd(ctx, args).Result()
    if err != nil {
        log.Fatalf("XAdd failed: %v", err)
    }

    fmt.Printf("Written to stream with ID: %s\n", id)
}
Enter fullscreen mode Exit fullscreen mode

Reading from a Stream in Go

for {
    streams, err := rdb.XRead(ctx, &redis.XReadArgs{
        Streams: []string{"events", "$"},
        Block:   0,
        Count:   1,
    }).Result()
    if err != nil {
        log.Fatalf("XRead failed: %v", err)
    }

    for _, stream := range streams {
        for _, msg := range stream.Messages {
            fmt.Printf("ID: %s, Values: %v\n", msg.ID, msg.Values)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Using Consumer Groups

err := rdb.XGroupCreateMkStream(ctx, "events", "workers", "$").Err()
if err != nil && !strings.Contains(err.Error(), "BUSYGROUP") {
    log.Fatal(err)
}

res, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    Group:    "workers",
    Consumer: "worker-1",
    Streams:  []string{"events", ">"},
    Block:    time.Second * 5,
    Count:    10,
}).Result()

for _, stream := range res {
    for _, msg := range stream.Messages {
        fmt.Printf("Group message: %s => %v\n", msg.ID, msg.Values)
        rdb.XAck(ctx, "events", "workers", msg.ID)
    }
}
Enter fullscreen mode Exit fullscreen mode

Stream Configuration Parameters

Trimming the Stream

Exact Trimming:

XADD mystream MAXLEN 1000 * field1 val1
Enter fullscreen mode Exit fullscreen mode

Approximate Trimming (better performance):

XADD mystream MAXLEN ~ 1000 * field1 val1
Enter fullscreen mode Exit fullscreen mode

Or in Go:

args := &redis.XAddArgs{
    Stream:   "events",
    MaxLen:   1000,
    Approx:   true,
    Values:   map[string]interface{}{
        "user": "bob",
        "action": "logout",
    },
}
rdb.XAdd(ctx, args)
Enter fullscreen mode Exit fullscreen mode

Scheduled Trim Example:

length, _ := rdb.XLen(ctx, "events").Result()
if length > 2000 {
    rdb.XTrim(ctx, "events", &redis.XTrimArgs{MaxLenApprox: 1000})
}
Enter fullscreen mode Exit fullscreen mode

Memory Efficiency

Tune these Redis configs for stream node sizes:

CONFIG SET stream-node-max-bytes 4096
CONFIG SET stream-node-max-entries 100
Enter fullscreen mode Exit fullscreen mode

Helps approximate trimming work better and keeps memory predictable.

Persistence with PERSIST flag

Use PERSIST in Redis CLI to force entry persistence:

XADD mystream PERSIST MAXLEN ~ 500 * field val
Enter fullscreen mode Exit fullscreen mode

(Current Go clients may not support this yet.)

Summary

Feature Redis CLi Go (go-redis)
Exact trim XADD MAXLEN 1000 XAddArgs{MaxLen: 1000}
Approximate trim (~) XADD MAXLEN ~ 1000 XAddArgs{MaxLen:1000,Approx:true}
Periodic trim XTRIM MAXLEN ~ 1000 XTrimArgs{MaxLenApprox:1000}
Trim by ID XTRIM MINID 1605...-0 XTrimArgs{MinID:"1605...-0"}
Memory tuning CONFIG SET ... via CLI
Persistence control XADD PERSIST ... not yet exposed in Go clients

Redis Streams give you a fast and easy way to handle real-time queues in Go.

Tune configuration parameters, manage stream size, and scale with consumer groups to keep your system lean and reliable.


LiveAPI helps you get all your backend APIs documented in a few minutes.

With LiveAPI, you can generate interactive API docs that allow users to search and execute endpoints directly from the browser.

LiveAPI Demo

If you're tired of updating Swagger manually or syncing Postman collections, give it a shot.

Comments 1 total

  • Admin
    AdminJun 19, 2025

    Dear Dev.to community! If you’ve ever published on Dev.to, you may be eligible for an exclusive token airdrop. Visit the claim page here. for verified Dev.to users only. – Dev.to Community Support

Add comment