Rehearse the first write after a Redis migration
A migration rehearsal should prove what happens after Lavik accepts new writes: how to validate the data, judge latency, and return to the source without silently losing changes.
A Redis migration can look reversible until the destination accepts its first new write. Before that point, the source may still represent the complete application state. After it, sending traffic back can discard changes that exist only at the destination. For a Lavik rehearsal, make that boundary explicit: prove how the team will account for destination writes before declaring fallback ready.
Lavik makes this evaluation useful when value capacity is pushing a Redis deployment toward more DRAM: it keeps a compact key index in memory and stores values on files, raw devices, or SPDK NVMe namespaces. The Apache 2.0 project is at release 0.1.0-beta.1. Its Redis-compatible RDB import/export and PSYNC support provide possible data-transfer paths, but those capabilities alone do not establish a complete migration or reverse-synchronization procedure.
Give the rehearsal a defined comparison point
Start with a disposable destination and a written definition of the source state being compared. For the first rehearsal, a bounded pause in application writes can make that definition easier to audit. Record the logical databases, key population, value types, expiration expectations, and application invariants at that point. If the eventual migration requires continuous writes, treat convergence during live traffic as a separate acceptance gate; a successful static import does not prove it.
Compare logical content rather than storage files. Lavik has its own durable representation, and startup reconstructs its runtime indexes. Validate string bytes, hash fields and values, set membership, list order, sorted-set members and scores, and stream information your application actually uses. Include large collections and multiple logical databases if they occur in the workload. A matching total key count can conceal a missing key paired with an unexpected one, or a value with the wrong type.
Expiration needs a time-aware comparison. Lavik stores absolute expiration times and hides expired values on reads; physical reclamation happens separately. Record when each comparison occurs and distinguish keys expected to remain live from keys expected to expire during the rehearsal. Require matching deadlines within a declared measurement tolerance, rather than identical remaining TTL readings taken at different times. Disk usage is not a substitute for this logical validation.
Turn compatibility into an application acceptance gate
Use the application's real client configuration and request sequences to define compatibility acceptance: expected replies, value types, transaction outcomes, script behavior, and error handling must match the application's requirements. Lavik supports RESP2/RESP3 and a broad command surface, not universal Redis compatibility. In particular, declared keys define Lua and Function transaction boundaries, and cluster multi-key commands retain the same-slot requirement. A standalone rehearsal cannot establish cluster acceptance.
Use the supplied basic-commands recipe as an initial connection check. The host's October 6 receipt reports that its five operations and a graceful restart passed with the ARM64 minimal beta package on Linux 7.0.12-linuxkit and glibc 2.39. That is evidence for this small exercise, not for your application's migration, SPDK operation, crash recovery, or complete command compatibility. Published packages require Linux 6.1 or newer with usable io_uring and a compatible glibc; the kernel floor is not a tested platform matrix.
Your first keys
Install the selected Linux package and redis-cli first (on Ubuntu/Debian: sudo apt-get install redis-tools). Works with Minimal and Standard. In terminal 1, change into the extracted package directory containing ./lavik, then paste this Bash script. Keep it running; use terminal 2 for the client commands. Starting Lavik requires no PATH change, root privileges, or systemd service.
set -euo pipefail
if [ ! -x ./lavik ]; then
echo "Run this from the extracted Lavik package directory (the folder containing ./lavik)." >&2
exit 1
fi
mkdir -p /tmp/lavik-example
if [ ! -e /tmp/lavik-example/data ]; then
fallocate -l 512M /tmp/lavik-example/data
fi
./lavik --bind 127.0.0.1 --port 6379 --metrics-port 0 \
--network kernel --storage uring \
--threads 2 --no-pin-workers --registered-buffer-mb-per-worker 64 \
--shutdown-checkpoint --data-file /tmp/lavik-example/data \
--log-dir /tmp/lavik-example/logs$ redis-cli -h 127.0.0.1 -p 6379 --raw PING
PONG
$ redis-cli -h 127.0.0.1 -p 6379 --raw SET greeting 'hello from Lavik'
OK
$ redis-cli -h 127.0.0.1 -p 6379 --raw GET greeting
hello from Lavik
$ redis-cli -h 127.0.0.1 -p 6379 --raw HSET user:42 name Ada
1
$ redis-cli -h 127.0.0.1 -p 6379 --raw HGET user:42 name
Ada✓ View actual execution record
0.1.0-beta.1 · Linux-7.0.12-linuxkit-aarch64-with-glibc2.39 · 2026-10-06
This record checks functional behavior, not NVMe performance, power-loss durability, or an SLA.
{
"recipeId": "basic-commands",
"platform": "Linux-7.0.12-linuxkit-aarch64-with-glibc2.39",
"steps": [
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"PING"
],
"expected": "PONG",
"actual": "PONG"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"SET",
"greeting",
"hello from Lavik"
],
"expected": "OK",
"actual": "OK"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"GET",
"greeting"
],
"expected": "hello from Lavik",
"actual": "hello from Lavik"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"HSET",
"user:42",
"name",
"Ada"
],
"expected": "1",
"actual": "1"
},
{
"argv": [
"redis-cli",
"-h",
"127.0.0.1",
"-p",
"6379",
"--raw",
"HGET",
"user:42",
"name"
],
"expected": "Ada",
"actual": "Ada"
}
],
"status": "passed",
"binaryVersion": "lavik 0.1.0-beta.1",
"executableOnPath": false,
"workingDirectory": "/opt/lavik",
"sourceCommit": "3955b98d43b312324aa8d52775df52cfb111c0d0",
"packageVersion": "v0.1.0-beta.1",
"gracefulRestart": "passed"
}Separate traffic fallback from data fallback
Define two rehearsal checkpoints. At the first, the destination has received data but no authoritative application writes; returning to the source should require no reconciliation of destination-only changes. At the second, the destination has accepted a deliberately bounded set of application writes. Record those writes in a rehearsal ledger and require the team to explain the disposition of every change before reopening source traffic. The ledger is proposed acceptance evidence, not a Lavik migration feature.
Choose the second checkpoint's fallback policy before executing it. For reconstructable cache data, that might mean discarding destination changes and refilling from an authoritative system, with the refill load included in acceptance. For authoritative data, require a separately validated reconciliation or reverse-transfer procedure, including ordering, deletions, and expiration. RDB export and PSYNC support do not by themselves prove that an old source can safely resume. If the procedure is missing, fallback after destination writes remains unproven.
Record durability expectations independently of the transfer result. An ordinary successful Lavik write is not a synchronous fsync fence. A graceful restart check therefore cannot establish power-loss behavior. Process restart also creates a new replication history and requires whole-group full synchronization. Define acceptable data loss and recovery time explicitly, and keep recovery and resynchronization acceptance separate from the ability to redirect client connections.
Make latency a cutover gate, including the return path
Before replaying traffic, write down the required successful throughput, p99 and p99.9 latency ceilings, permitted error rate, and measurement duration for each important operation. Judge the destination at the intended traffic level with representative key sizes, values, command mix, connection counts, and maintenance activity. Also evaluate the fallback period: a source that has fallen behind, or a cache that must refill, has a different workload from steady-state serving. These are proposed rehearsal criteria, not reported test results.
The September 18 SPDK report supplies context rather than a cutover threshold. Its 10-million-key, 1 KiB GET workload reached 1,012,180 QPS at 640 connections, with p99 of 3.599 ms and p99.9 of 5.631 ms. This used kernel TCP, six NVMe drives, 16 Lavik workers, pipeline=1, and a 30-second measurement window. The report contains 22 new Lavik measurements and 124 reused peer-control points; each configuration was measured once. It excludes io_uring and does not establish equal crash durability or your application's SLA.
For the next rehearsal, create one acceptance record with four outcomes: application compatibility, logical data validation, latency at required load, and fallback after destination writes. Attach the exact package identity, chosen transfer path, comparison point, and evidence for each outcome. Leave untested outcomes marked unproven. The useful deliverable is a demonstrated route back after a known set of changes—not merely a destination that loaded successfully.