Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

$ python3 tools/keymove.py --apply --match 'doc:*'
moved 0 keys, skipped 0, in 4.2s
14 keys refused by the target (use --replace only if you mean it):
  doc:1041: DUMP payload version or checksum are wrong
  doc:1042: DUMP payload version or checksum are wrong

That is not corruption, and --replace will not fix it. It is a RESTORE on a server whose RDB format is older than the one that produced the payload, or a value whose type belongs to a module the target does not have. Both are knowable before you write a single key, which is what this repository is for.

redis-cloud-alternative

Redis redis-py protocol license

Tooling and an honest comparison for people considering a move off Redis Cloud (or Redis Enterprise, or any managed Redis) onto a plain Redis 7.2 server — including the free instances at freebase.cloud, which is what this repo was built against.

The order of business here is deliberately backwards from most migration guides. Feasibility first, verification second, the actual copy last, because the copy is the easy part.


Contents

1. Find out if you can move the module scan, and why MODULE LIST is not enough
2. Move the keys DUMPRESTORE, and getting TTLs right
3. Prove it worked counts, type histogram, sampled deep compare
4. Roll it back manifest-driven, not FLUSHDB
What Redis Cloud does that a plain server does not the fair part
Stay where you are if… four real reasons
Running Redis on freebase.cloud connection details and MCP

1. Find out if you can move

DUMP/RESTORE moves values, not capabilities. Two things break it, and both are invisible until you try:

Module-owned key types. A key holding a JSON document has type ReJSON-RL, not string. A server without that type loaded cannot decode the payload, and there is no conversion — the document has to be re-derived from whatever wrote it. The same applies to TSDB-TYPE, MBbloom--, MBbloomCF, CMSk-TYPE, topk-TYPE, TDIS-TYPE and Redis 8's vectorset.

RDB payload version. Every DUMP payload ends with a 2-byte RDB version and an 8-byte CRC64. RESTORE checks both and errors if the version is higher than it understands (RESTORE docs). Moving from a Redis 8 source to a Redis 7.2 target can therefore fail on every single key while both servers report themselves perfectly healthy.

pip install 'redis>=5.0'
export SOURCE_REDIS_URL='rediss://default:PASSWORD@redis-12345.example.com:12345'
export TARGET_REDIS_URL='redis://HOST:6379/0'
python3 tools/module_scan.py
source          redis 7.4.1
source dbsize   184203 keys
MODULE LIST     empty or restricted
FT._LIST        idx:products, idx:orders
type sample     20000 keys scanned
  hash              11402  core type
  string             6110  core type
  ReJSON-RL          2488  JSON documents (RedisJSON / Redis 8 JSON)
source RDB ver  12
target          redis 7.2.3
target RDB ver  11

BLOCKED — do not start a key migration yet:
  - 2488 keys of type ReJSON-RL — JSON documents (RedisJSON / Redis 8 JSON)
  - 2 query-engine index(es); FT.* queries will not resolve
  - source serializes at RDB 12, target parses up to 11; RESTORE will reject every payload

Note what happened in the middle of that: MODULE LIST came back empty because plenty of providers restrict it, so the scan falls back to sampling key types, which cannot be restricted. And the RDB version was not read from INFO — it is not there. The tool writes one throwaway key with a 60-second TTL, DUMPs it, reads the version out of the payload footer, and deletes it.

Point TARGET_REDIS_URL at the server you actually intend to land on — a free Redis 7.2 instance is enough to run the scan against. Exit code 1 means blocked, 0 means clear, 2 means it could not connect.

2. Move the keys

python3 tools/keymove.py --dry-run --match 'session:*'
python3 tools/keymove.py --apply   --match 'session:*'

SCAN in batches, one pipeline per batch for DUMP plus the expiry, one pipeline to RESTORE on the far side. No KEYS, no MIGRATE (which needs the source to be able to open a connection to the target — usually impossible across two managed providers), and no blocking commands.

The part worth reading closely is expiry:

  • PEXPIRETIME (Redis 7.0+) returns the absolute expiry in milliseconds. That value is passed to RESTORE … ABSTTL, so a key with 40 seconds left at the start of the run still has about 40 seconds left when it lands, however long the transfer took. On a pre-7.0 source the tool falls back to relative PTTL and prints a warning that expiries will drift by the run duration.
  • -1 means no expiry; RESTORE wants 0 for that. Passing -1 through is an error, and passing the raw value where 0 was meant turns every session token into a permanent key. The mapping is explicit in the code and covered by the verification pass.
  • -2 means the key vanished between SCAN and DUMP. Counted as skipped, not as an error — in a live cache that number is never zero.
  • An absolute expiry already in the past is skipped rather than restored. A migration should not resurrect data the source had already given up on.

Keys that already exist on the target come back as BUSYKEY errors and are reported per key. --replace exists but is not the default, because deciding which side is authoritative is a judgement call and not a flag.

3. Prove it worked

python3 tools/keymove.py --verify --match 'session:*' --sample 200

Three layers, cheapest first:

  1. Key counts on both sides for the pattern.
  2. A full type histogram — string/hash/list/set/zset/stream counts compared side by side. Catches a partial run that a raw count would hide.
  3. A random sample of N keys deep-compared by type: strings byte for byte, hashes and sets sorted then compared, lists and streams in order, sorted sets with scores. Then TTL: presence must match, and remaining time must agree within five seconds.

Non-zero exit on any mismatch. VERIFY OK is the only output that means anything.

4. Roll it back

--apply appends every key it actually wrote to keymove-manifest.jsonl. The rollback deletes exactly those keys:

python3 tools/keymove.py --undo keymove-manifest.jsonl

Not FLUSHDB. If the target database is shared with anything else — and on a free instance it often is, because you were experimenting in it last week — that difference is the whole reason the manifest exists. Keep it until the old instance is decommissioned, not until the cutover.

The --rdb path, and when it is not available

redis-cli -u "$SOURCE_REDIS_URL" --rdb snapshot.rdb

A full RDB snapshot is faster than key-by-key for large keyspaces, but it is a replication operation: the client issues SYNC/PSYNC, which managed providers frequently restrict on shared plans. If it fails with a permission error, that is a policy decision on the provider's side, not a bug in your command. keymove.py exists because that path is not always open, and because restoring an RDB requires filesystem access to the target's data directory — which a managed target does not give you either.


What Redis Cloud does that a plain server does not

Last verified: 2026-08-18. Follow the links rather than trusting this table in six months.

Capability Redis Cloud / Enterprise Plain Redis 7.2 server
Active-Active geo-replication (CRDTs) Yes, on a Pro subscription — multi-region, conflict-free types, eventual consistency No. Async replication only, single writer
Query engine, JSON, Time Series, probabilistic types Available as database capabilities Not in 7.2. Redis Open Source 8 bundles them — a 7.2 server does not have them
Clustering / resharding Managed, with online scaling Redis Cluster exists in open source but you operate it
Backups, failover, patching Operated for you Yours
Free plan 30 MB Essentials plan, documented at redis.io n/a

Three things worth saying plainly, because comparison tables tend to bury them:

  • Active-Active is genuinely hard. CRDT-backed multi-region writes with automatic conflict resolution are not something you assemble from primitives over a weekend. Redis's own docs note the memory cost — as much as 4x the original data size — and that is the honest price of the feature, not a criticism of it.
  • The module story changed in 2025. Redis 8 returned to an OSI-approved licence (AGPLv3) and folded the query engine, JSON, time series and probabilistic structures into Redis Open Source. If your blocker is "I need FT.SEARCH and I don't want a paid plan", the answer today may be Redis 8 rather than a different vendor. It is not Redis 7.2, which is what freebase.cloud runs and what this tooling targets.
  • A free tier is not a cluster. Nothing in this repo replaces a multi-node Redis Enterprise deployment. It replaces the part of your Redis usage that is a cache, a queue, a leaderboard, or a scratchpad.

Stay where you are if…

  • You run Active-Active. There is no migration path in this repo for a multi-region CRDT database, and there is no honest way to fake one. Writing to two independent Redis servers and hoping is not the same feature.
  • FT.SEARCH, TS.* or JSON.* appear anywhere in your code. The module scan will tell you this in about four seconds. Moving the core types and leaving the module-backed ones behind gives you two sources of truth, which is strictly worse than one expensive one.
  • You are past the point where 30 MB was the constraint. If your working set is tens of gigabytes with a throughput floor, you are buying operated memory and failover, and that is a reasonable thing to buy.
  • Someone is on call for this. A managed provider with a support contract is a load-bearing part of an incident response plan. A free instance is not, and this repo will not pretend otherwise.

Migrating a cache is low risk. Migrating a system of record that happens to live in Redis is not. Know which one you have before you start.

Running Redis on freebase.cloud

Sign up on the console, create a session, pick the Redis engine. Details on the free Redis instance page.

You get Redis 7.2.3 over the real RESP2 wire protocol on port 6379 — redis-cli, redis-py, ioredis, node-redis, Lettuce and go-redis connect unmodified, which is why the tooling here is ordinary client code with no vendor SDK in it.

redis-cli -h HOST -p 6379

Core data structures, pub/sub, streams, Lua via EVAL, MULTI/EXEC, and the full expiry command set are all present. Modules are not — that is the constraint the scan in step 1 exists to surface early.

The free tier suits development, prototyping and small production workloads. No credit card, and no cluster to size.

Same instance over MCP

A second address onto the same keyspace, for AI assistants that speak the Model Context Protocol. Settings → MCP → New Token, pick your connection, copy the URL.

claude mcp add --transport http kv https://freebase.cloud/api/mcp/YOUR_TOKEN

Or commit .mcp.json with the project:

{ "mcpServers": { "kv": { "type": "http", "url": "https://freebase.cloud/api/mcp/YOUR_TOKEN" } } }

The type field is not optional in Claude Code — a url entry without it is a hard error. The token sits in the path, so there is no Authorization header to configure anywhere.

Four tools show up, prefixed with the connection name you chose:

Tool Use
kv_query run a read command and get the result back
kv_store write keys
kv_list_tables enumerate the keyspace
kv_annotate_table attach a description so the model knows what sess:* means

Step-by-step client setup: how to connect Claude to Redis. Transport is Streamable HTTP, current under MCP spec revision 2026-07-28; the older HTTP+SSE transport is deprecated.


Repository layout

tools/module_scan.py      feasibility: module types, FT indexes, RDB versions
tools/keymove.py          dry-run / apply / verify / undo
examples/preflight.sh     the three checks, in order, before any write
examples/post_cutover_smoke.mjs   six client-level assertions after the move

Sources

MIT licensed. Issues and pull requests welcome, particularly bug reports against the TTL handling — that is the part where a subtle mistake is most expensive.

freebase.cloud is an independent service and is not affiliated with Redis Ltd. or Anthropic.

About

Redis Cloud alternative — free Redis 7 instances with the standard RESP protocol, compared honestly

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages