---
title: "Upgrading Signals to Valkey 9.1: 10% less memory, 28% less write time | Snowplow Signals"
description: "We replayed one Signals workload on Valkey 8.0 through 9.1. Memory per stored attribute fell 10% and server time per write fell 28%."
source: https://signals.snowplow.io/blog/valkey-9
---

[Blog](https://signals.snowplow.io/blog) / Engineering 

# Upgrading Signals to Valkey 9.1: 10% less memory, 28% less write time

ST Signals team Sep 30, 2026 · 4 min read 

## Why per-key overhead matters for live customer profiles

Acting on live customer behavior means keeping a profile current for every user, fresh as each event arrives, and ready to be read in milliseconds. A Signals profile is stored as a set of smaller values: counters, recently viewed items, unique category counts, each held in its own structure. Each structure carries a fixed overhead, which in some cases is larger than the value it stores.

As an example, a counter attribute holding the number 3 costs about 181 bytes on Valkey 8.0\. Across millions of users it is that overhead, not the data, that dictates how much a deployment can afford to keep before it has to scale. Valkey 8.1 and 9.0 each reduce this number in a different way. We ran Signals against every version from 8.0 to 9.1, replaying the same recorded stream of events into each, and measured the memory the resulting profiles needed and the time Valkey spent on each write.

## Background: Snowplow Signals

![](https://signals.snowplow.io/images/blog/valkey-9/architecture.png)

Signals calculates customer profiles on top of Snowplow clickstream event data. It turns raw behavioral events, such as page views, clicks and purchases, into attributes that describe each customer as they browse: products viewed in the last hour, current cart value, favorite categories.

Signals processes this data through several engines. They differ in how they process events (streaming or batch) and how they make decisions (rules, ML models or AI). All of them read and write the same Valkey database, which runs on ElastiCache in the customer’s cloud account for AWS. As a result, Valkey’s memory footprint and write speed directly shape how Signals performs and what it costs to run.

## Methodology

We ran Signals with a real e-commerce configuration, the same one customers use to track shoppers browsing, filling carts and purchasing. For the benchmark we used a recorded stream of shopper activity, thousands of events, against each Valkey version from 8.0 to 9.1\. Every benchmark started empty and received exactly the same events in the same order, so the only difference was the Valkey version.

* **Versions:** the official Valkey 8.0, 8.1, 9.0 and 9.1 images.
* **Configuration:** the e-commerce scenario, unchanged across versions, with attribute groups and Interventions defined.
* **Workload:** one recorded stream of e-commerce journeys, replayed to every version.
* **What we measured:** the memory held once each run finished, broken down by attribute type, and the time Valkey spent executing each command it received.

We then focused on two questions: how much memory Valkey used to hold the resulting profiles, and how long it spent executing each write.

## Memory results

![Memory held per stored attribute, in bytes, Valkey 8.0 against 9.1. The profiles store overall goes from 241.7 to 217.6 bytes per key, a 10% reduction. Counter and sum attributes fall 14.5%, mean attributes 9.2%, category counts 8.6%, approximate distinct counts 8.3% and unique lists 7.3%.](https://signals.snowplow.io/images/blog/valkey-9/memory.png)

A Signals profile is dozens of small values stored separately from each other, so any one of them can be updated as events arrive and read back when a decision needs it: how often this shopper views a product, what they last put in the cart, which categories they keep returning to. Each value is stored under an identifier naming the shopper, the attribute and the time window it covers, and that identifier is often larger than the value it holds. On Valkey 8.0, the average stored attribute cost 241.7 bytes. On 9.1, it costs 217.6, a **reduction of 10%**.

Most of that arrives in Valkey 8.1, whose redesigned hashtable removes a fixed overhead every stored value carried regardless of its contents. It is worth about 18 bytes each, which is why every attribute type in the chart improves by a similar absolute amount.

Counter and sum attributes save more, because a second change lands in 9.1\. Storing very small values had become slightly more expensive in 8.1, and 9.1 reverses that, worth another 8 bytes per value. Counters and sums are common aggregations in Signals profiles.

## Write performance

![Time Valkey spends executing each write, in microseconds per command, Valkey 8.0 against 9.1. The attribute write path overall goes from 1.22 to 0.88 microseconds per command, a 28% reduction. The largest falls are the dedup check (sismember) at 52% and trimming windowed attributes (zremrangebyrank) at 50%. The TTL update on every key (expire) is unchanged.](https://signals.snowplow.io/images/blog/valkey-9/write-time.png)

Every event Signals processes touches Valkey dozens of times. A single shopper action updates the attributes it affects, refreshes their windowed buckets, adjusts value TTLs and guards against duplicate processing. On Valkey 8.0 those commands averaged 1.22 microseconds inside the server. On 9.1, they averaged 0.88, a reduction of 28%.

Almost all of that arrives in Valkey 9.0, which improved how it handles pipelined commands. Signals never sends a command to Valkey on its own, so every interaction is already a pipeline. Moving only as far as Valkey 8.1 would not move these numbers. Its [hashtable rewrite](https://valkey.io/blog/new-hash-table/) is what produced the improvements on the memory side, and on the write path it makes counter and sum updates slightly slower, which 9.0 fixes.

Across every command Signals issues, including the TTL updates that did not change, total Valkey time per event fell by 21% in 9.1\. The absolute figures depend on hardware, but the reduction held across measurement runs.

## What it means for Signals customers

Signals stores attribute and profile information the same way regardless of use case. An e-commerce cart, a content affinity score, a churn risk and a session-level recommendation all carry much the same per-value overhead. At scale, that overhead decides how much a deployment can handle before performance becomes a costly tradeoff.

With all of that in mind, upgrading to Valkey 9.1 is a clear-cut choice:

* **Lower infrastructure cost.** Ten percent less memory per stored attribute means fewer nodes to hold the same set of profiles. For deployments that sit close to their memory ceiling, this relieves the pressure and keeps costs predictable.
* **More headroom for peaks.** Signals usage scales with customer demand. Less time spent in Valkey per event gives more room to hold performance SLAs through peaks.

## Try it on your own events

To see what Signals builds from your own events, [start free](https://signals.snowplow.io/start). The free tier runs on the real engine, needs no card, and shows an attribute updating against your own traffic in the first session.

Start on the free tier

Add the SDK and watch an attribute updating against your own traffic in the first session. The free tier runs on the real engine: no card, no sales call.

[Start free](https://signals.snowplow.io/start) 

Share this post

In this post

* [Why per-key overhead matters for live customer profiles](#why-per-key-overhead-matters-for-live-customer-profiles)
* [Background: Snowplow Signals](#background-snowplow-signals)
* [Methodology](#methodology)
* [Memory results](#memory-results)
* [Write performance](#write-performance)
* [What it means for Signals customers](#what-it-means-for-signals-customers)
* [Try it on your own events](#try-it-on-your-own-events)

Start on the free tier

Add the SDK and watch an attribute updating against your own traffic in the first session. The free tier runs on the real engine: no card, no sales call.

[Start free](https://signals.snowplow.io/start)
