# Memory and eviction

> Cap HotKV's memory with maxmemory, choose an eviction policy, handle OOM errors, and inspect memory with MEMORY and INFO.

URL: https://docs.hotkv.com/operate/memory-and-eviction/


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).

## Setting a memory limit

```
maxmemory 4gb
maxmemory-policy allkeys-lru
```

or in `hotkv.toml`:

```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`](https://docs.hotkv.com/reference/configuration/index.md#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.

### The replication backlog

A primary keeps its recent writes in the backlog so that a replica that
reconnects can continue where it left off (see [Replication](https://docs.hotkv.com/operate/replication/index.md)).
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.

## Eviction policies

`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.

## What happens at the limit

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`](#client-memory) |
| `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](https://docs.hotkv.com/operate/monitoring/index.md#prometheus-metrics) exports
`hotkv_used_memory_bytes` and `hotkv_evicted_keys` for alerting.

## Lazy freeing

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.

## Client memory

`maxmemory` limits the data. [`maxmemory-clients`](https://docs.hotkv.com/reference/configuration/index.md#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
```

### What counts as a client's memory

The figure for each client is its `tot-mem` in [CLIENT LIST](https://docs.hotkv.com/commands/client-list/index.md)
and [CLIENT INFO](https://docs.hotkv.com/commands/client-info/index.md). 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](https://docs.hotkv.com/develop/client-side-caching/index.md)
  `BCAST` tracking;
- the fieldsets it prepared with [HIMPORT](https://docs.hotkv.com/commands/himport/index.md);
- 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.

### What is evicted

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](https://docs.hotkv.com/commands/client-no-evict/index.md) `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 to look

| 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](https://docs.hotkv.com/commands/memory-stats/index.md): `clients.normal`, `clients.slaves` | The same two totals. |
| [CLIENT LIST](https://docs.hotkv.com/commands/client-list/index.md): `tot-mem` | Each client. |

## Inspecting memory

### MEMORY USAGE

[`MEMORY USAGE key [SAMPLES count]`](https://docs.hotkv.com/commands/memory-usage/index.md) 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

[`MEMORY STATS`](https://docs.hotkv.com/commands/memory-stats/index.md) 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

[`MEMORY DOCTOR`](https://docs.hotkv.com/commands/memory-doctor/index.md) 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`](https://docs.hotkv.com/commands/memory-purge/index.md) asks the process to return freed
memory to the operating system. `MEMORY MALLOC-STATS` returns allocator
statistics as text.

### INFO memory

`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](https://docs.hotkv.com/operate/monitoring/index.md).

> **Tip**
> If `used_memory_rss` is far above `used_memory` after you deleted a lot of
> data, run `MEMORY PURGE` before concluding the server needs more RAM.

## Next steps

- [Monitoring](https://docs.hotkv.com/operate/monitoring/index.md)
- [Keys and expiration](https://docs.hotkv.com/develop/keys-and-expiration/index.md)
- [Configuration reference](https://docs.hotkv.com/reference/configuration/index.md#maxmemory)
- [Client-side caching](https://docs.hotkv.com/develop/client-side-caching/index.md): invalidations count in a client's memory.
- [Memory on the target of a reshard](https://docs.hotkv.com/clustering/scaling/index.md#memory-on-the-target): `maxmemory` applies to each node of a cluster.