Incident Response

Redis "LOADING Redis is loading the dataset in memory": What to Do

After a restart or a replica resync, every command returns LOADING and the app reports Redis as down. Redis is actually busy reading its data back into RAM. Here is how to see how long it will take, why health checks can turn it into a restart loop, and how to make the wait shorter or invisible.

· 12 min read · AG Data Team

The Redis error "LOADING Redis is loading the dataset in memory" means the server is running but has not finished reading its data from disk, or from the master, back into RAM. Until it finishes, it rejects almost every command with this error. Nothing is broken, and the honest fix is often to wait. The work is in knowing how long, keeping health checks from killing Redis halfway through, and making the next load shorter.

$ redis-cli GET user:42
(error) LOADING Redis is loading the dataset in memory

When Redis loads, and what still works

There are two common moments:

The check itself is in server.c: while server.loading is set, any command without the CMD_LOADING flag is rejected with this error. Which commands carry the flag is defined in commands.def, and the list has a few surprises:

Valkey works the same way. Its server.c builds the same error with the server name in it, and PING has the same flags there.

How long will it take?

Because INFO works during loading, Redis tells you its own progress:

Example output (the numbers are illustrative):

$ redis-cli INFO persistence | grep -E '^(async_)?loading'
loading:1
async_loading:0
loading_start_time:1762573620
loading_total_bytes:9663676416
loading_rdb_used_mem:0
loading_loaded_bytes:4187593113
loading_loaded_perc:43.33
loading_eta_seconds:131

The INFO documentation describes loading as the flag that a load of a dump file is ongoing. The loading_* fields appear while it runs: start time, total and loaded bytes, percentage and an ETA. The ETA in server.c is a simple extrapolation from elapsed time and bytes remaining, so treat it as an estimate. It is good enough to answer "seconds or half an hour?" when someone is waiting on you.

The log shows how long previous loads took. That is the number to plan around:

# server start
* DB loaded from disk: 228.415 seconds
* DB loaded from append only file: 311.902 seconds   (AOF)
* Done loading RDB, keys loaded: ..., keys expired: ...

# replica full sync
* MASTER <-> REPLICA sync: receiving ... bytes from master to disk
* MASTER <-> REPLICA sync: Loading DB in memory

If startups keep getting slower, the dataset is growing or the disk under it is slow. Both are worth knowing before the next restart, not during it.

How health checks turn loading into an outage

This is the case that turns a few minutes of loading into hours of downtime. A liveness check that runs redis-cli PING and expects PONG fails during loading. On Kubernetes, a failed liveness probe restarts the container. The new process starts loading from the beginning, the probe fails again, and Redis never finishes. The same thing happens with any watchdog or orchestrator that restarts "unhealthy" processes.

The Kubernetes documentation has the right tool: when a startup probe is configured, liveness and readiness probes are not run until it succeeds. Give Redis a startup probe sized to its real load time, and keep the liveness check tolerant of LOADING:

startupProbe:              # gives Redis time to load; liveness waits for it
  exec:
    command: ["sh", "-c", "redis-cli INFO persistence | grep -q '^loading:0'"]
  periodSeconds: 10
  failureThreshold: 90     # up to ~15 minutes; size it to your real load time
livenessProbe:             # "alive" also while loading after a later restart
  exec:
    command: ["sh", "-c", "redis-cli PING | grep -qE 'PONG|LOADING'"]
readinessProbe:            # no traffic until the data is there
  exec:
    command: ["sh", "-c", "redis-cli PING | grep -q PONG"]

Outside Kubernetes, use the same rule in deploy scripts: wait for loading:0 before sending traffic or restarting the next node.

#!/bin/sh
# wait until Redis has finished loading, then continue
until redis-cli INFO persistence | grep -q '^loading:0'; do
  redis-cli INFO persistence | grep -E '^loading_(loaded_perc|eta_seconds)'
  sleep 5
done
echo "Redis is ready"

What clients see

Client libraries treat LOADING as a separate error type. redis-py, for example, maps the LOADING prefix to BusyLoadingError, a subclass of ConnectionError, in its response parser. That makes it easy to handle on purpose: retry with backoff for a bounded time, or fall back to the primary data source if Redis is a cache. What you do not want is a tight retry loop from every app instance that hammers the server while it loads.

Do not restart Redis because it is "stuck" in LOADING. Check loading_loaded_perc first. If the number is moving, a restart throws away the progress and starts over. Only if the percentage does not move for a long time, and the log shows no progress, is something actually wrong.

Making the next load shorter

For replicas on Redis 7.0 and newer there is one more option. With repl-diskless-load swapdb, the replica parses the new data directly from the socket while it keeps serving the current dataset. The 7.0 redis.conf notes that this does not apply when the replica cannot recognize the master as having a dataset from the same replication history, and that it needs enough memory for both copies, or you risk an OOM kill. During such a load INFO shows async_loading:1, and only commands flagged as unsafe for async loading are rejected.

# replica, Redis 7.0+: keep serving the old dataset during a full sync
repl-diskless-load swapdb
# needs memory for the old and the new dataset at the same time

Key takeaways

Redis Valkey Persistence Kubernetes Troubleshooting
Work with us

Redis restarts take too long?

We tune Redis and Valkey persistence, replication and failover so restarts and resyncs do not become outages. Tell us your dataset size and setup, and we will help you find the fastest safe path.

Get in touch More from the blog