Skip to content

Ask the HotKV docs

Answers come only from these docs and can be wrong; check the linked pages before you rely on them.

Memory and eviction

Last updated

HotKV keeps all data in memory. By default it uses as much as the data needs. Set maxmemory to cap it, and maxmemory-policy to decide what happens at the cap: refuse new data, or evict existing keys to make room. That choice depends on whether HotKV is your cache (evict) or your system of record (refuse).

maxmemory 4gb
maxmemory-policy allkeys-lru

or in hotkv.toml:

[memory]
maxmemory = "4gb"
maxmemory_policy = "allkeys-lru"

maxmemory 0 (the default) means no limit. Sizes take the Redis units: k, m and g are powers of 1000, kb, mb and gb powers of 1024. Both settings can be changed at runtime with CONFIG SET and take effect at once:

127.0.0.1:6379> CONFIG SET maxmemory 2gb
OK
127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lfu
OK

The limit applies to the memory the data uses and to the replication backlog, up to repl-backlog-size, as in Redis. What replicas that lag hold beyond the backlog, and the AOF buffers, do not count (INFO memory shows them as mem_not_counted_for_evict). Leave headroom between maxmemory and the machine’s RAM for those buffers, client output buffers, and the operating system.

A primary keeps its recent writes in the backlog so that a replica that reconnects can continue where it left off (see Replication). The backlog fills as the primary takes writes, and it counts toward maxmemory as it fills. mem_replication_backlog in INFO memory shows how much it holds. Here a primary with a 128 MB limit and one replica took twenty values of 1 MB each:

127.0.0.1:6379> INFO memory
...
used_memory:34352784
maxmemory:134217728
mem_replication_backlog:8000464
mem_not_counted_for_evict:45

used_memory holds the 20 MB of data, the 8 MB of backlog so far and the server’s own base. The backlog is 64 MiB unless you set repl-backlog-size (or replication.backlog-size). When maxmemory is set at start and no backlog size is given, the default is a sixteenth of maxmemory if that is less than 64 MiB:

$ hotkv --maxmemory 128mb
127.0.0.1:6379> CONFIG GET repl-backlog-size
1) "repl-backlog-size"
2) "8388608"

A size you give is always kept as you gave it, and a maxmemory set later with CONFIG SET leaves the backlog size as it is. CONFIG SET repl-backlog-size resizes the backlog at once. If you set maxmemory on a primary, size it for the data plus the backlog.

maxmemory-policy takes the eight policies Redis has:

Policy Which keys may be evicted How they are chosen
noeviction (default) None Commands that would add data get an OOM error
allkeys-lru Any key Least recently used first
allkeys-lfu Any key Least frequently used first
allkeys-random Any key At random
volatile-lru Keys with a TTL Least recently used first
volatile-lfu Keys with a TTL Least frequently used first
volatile-random Keys with a TTL At random
volatile-ttl Keys with a TTL Nearest expiry first

Some guidance for choosing one:

  • Cache of data that lives elsewhere: allkeys-lru, or allkeys-lfu when a small set of keys is read far more than the rest.
  • Mix of cache entries and data you must keep: give the cache entries a TTL and use a volatile-* policy, so keys without a TTL are never evicted.
  • Primary data store: noeviction, so a full server refuses writes instead of silently dropping data.

LRU, LFU and TTL choices are approximate, as in Redis: HotKV samples keys and evicts the best candidates among them. maxmemory-samples (default 5, from 1 to 64) sets how many are sampled; more samples pick closer to the exact order at a little more CPU per eviction. For the LFU policies, lfu-log-factor (default 10) sets how slowly a key’s frequency counter rises and lfu-decay-time (default 1) the minutes it takes to decay by one. OBJECT FREQ key shows a key’s counter while an LFU policy is selected.

Before running a command while memory is over maxmemory, HotKV evicts keys under the policy until memory is back under the limit. Evicted keys are deleted as if by DEL: they fire evicted keyspace notifications (when enabled), and replicas and the AOF receive the deletes.

When nothing can be evicted, because the policy is noeviction or no key matches it (a volatile-* policy with no keys that have a TTL), commands that can add data are refused:

127.0.0.1:6379> SET session:42 "..."
(error) OOM command not allowed when used memory > 'maxmemory'.

Commands that only read or remove data (GET, DEL, UNLINK, EXPIRE, LPOP, FLUSHDB and the like) still run, so you can always free memory. Inside MULTI, such a command gets the OOM error when it is queued, and EXEC replies EXECABORT; a transaction queued earlier whose EXEC finds memory still full is aborted the same way. In a script, a write that needs memory is refused with the same error.

Two cases where HotKV does not evict:

  • Replicas. A replica never evicts on its own and does not refuse writes from its primary for memory: the primary’s evictions reach it as deletes. Give replicas the same maxmemory as their primary, and enough RAM.
  • While clients are paused with CLIENT PAUSE, the data is left as it is.

To see how often you hit the limit, watch these INFO fields:

Field Section Meaning
used_memory, used_memory_human memory Memory in use
maxmemory, maxmemory_policy memory The limit and the policy in force
evicted_keys stats Keys evicted since start (or CONFIG RESETSTAT)
evicted_clients stats Clients closed by maxmemory-clients
total_eviction_exceeded_time stats Total milliseconds spent over maxmemory
current_eviction_exceeded_time stats Milliseconds the current period over maxmemory has lasted

The Prometheus endpoint exports hotkv_used_memory_bytes and hotkv_evicted_keys for alerting.

HotKV has no lazy-freeing settings. The lazyfree-* directives from redis.conf are accepted with a warning and have no effect. UNLINK deletes keys the same way DEL does, and FLUSHALL and FLUSHDB accept ASYNC and SYNC for compatibility and flush at once with either.

Other Redis memory settings that are accepted and ignored include maxmemory-eviction-tenacity, replica-ignore-maxmemory (replicas never evict, see above) and the activedefrag family.

maxmemory limits the data. maxmemory-clients limits what the client connections themselves hold: input waiting to run, queued transactions, subscriptions and replies that are waiting to be sent. A few clients that stop reading, or that queue large commands, can otherwise use more memory than the data does.

maxmemory-clients 200mb

A number is bytes (with the usual units), and N% is that share of maxmemory (no limit while maxmemory is 0). 0, the default, turns it off. A limit under 128kb is raised to 128kb, so that no setting cuts every client off; CONFIG GET still shows the value you gave. CONFIG SET applies it at once, and so does a new maxmemory for a percentage: clients are evicted then if they hold more than the limit allows.

127.0.0.1:6379> CONFIG SET maxmemory-clients 200kb
OK
127.0.0.1:6379> CONFIG GET maxmemory-clients
1) "maxmemory-clients"
2) "204800"
127.0.0.1:6379> CONFIG SET maxmemory-clients 10%
OK

The figure for each client is its tot-mem in CLIENT LIST and CLIENT INFO. It is the sum of:

  • the connection itself, a small fixed amount;
  • its input that has not run yet (qbuf), counted with the room the buffer has beyond the input (qbuf-free);
  • the commands queued by MULTI (multi-mem);
  • the keys it watches;
  • its subscriptions, to channels, patterns and shard channels;
  • the prefixes of its client-side caching BCAST tracking;
  • the fieldsets it prepared with HIMPORT;
  • the replies and pushes waiting to be sent to it (omem).

When a client says that an argument will be large, the room for the whole argument counts from that moment: a client that announces a value larger than the limit is evicted before its data arrives.

When the clients together hold more than the limit, HotKV closes them, the largest first, one after the other, until the rest fit. Each one is logged (Client evicted for its memory, with its CLIENT LIST line, its size and the limit) and counted in evicted_clients in INFO stats (CONFIG RESETSTAT clears it). What the client had not been sent is dropped. The memory is checked as a client grows and when its command has run, so a command with a huge argument is evicted before it runs. A client that has been evicted, or that holds more than the limit and is about to be, is not listed by CLIENT LIST.

CLIENT NO-EVICT ON exempts a connection. Its memory still counts, so when it is the one that holds too much, the clients that can be evicted are closed in its place. A replica’s link, the connection of an atomic slot migration and another cluster node’s own connection are never evicted and not counted.

Where What it shows
INFO memory: mem_clients_normal, mem_clients_slaves What the clients and subscribers hold (the figure the limit judges), and what the replicas’ links hold.
INFO stats: evicted_clients Clients closed by the limit.
INFO clients: client_recent_max_input_buffer, client_recent_max_output_buffer The largest input and output buffers of any client.
MEMORY STATS: clients.normal, clients.slaves The same two totals.
CLIENT LIST: tot-mem Each client.

MEMORY USAGE key [SAMPLES count] estimates the bytes a key and its value use. For a collection, the first 5 elements are measured and the rest assumed to be the same size, as in Redis; SAMPLES 0 measures every element. A missing key returns nil:

127.0.0.1:6379> MEMORY USAGE no-such-key
(nil)

MEMORY STATS returns a breakdown with Redis’s field names, including peak.allocated, total.allocated, startup.allocated, replication.backlog, clients.normal, aof.buffer, lua.caches, functions.caches, overhead.total, keys.count, dataset.bytes, dataset.percentage, peak.percentage and fragmentation.

MEMORY DOCTOR returns a short report in plain text. On a server using less than 5 MB it says there is nothing to report. Otherwise it points out:

  • the process holding much more memory than the data uses (fragmentation after a peak or many deletes), with a suggestion to run MEMORY PURGE
  • a peak well above current use
  • memory in use above 90% of maxmemory, and whether writes will then be refused or keys evicted
  • a client with more than 32 MB of unread replies (find it by omem in CLIENT LIST)
  • a replication backlog that takes a large share of memory

MEMORY PURGE asks the process to return freed memory to the operating system. MEMORY MALLOC-STATS returns allocator statistics as text.

INFO memory reports used_memory, used_memory_rss, used_memory_peak, used_memory_dataset, maxmemory, maxmemory_policy, mem_fragmentation_ratio and the rest of Redis’s memory fields, so dashboards built for Redis find them where they expect. See Monitoring.