in-memory redis
redis in-memory testing
Zig 100%

README.md

burner-redis #

an embedded, in-process redis-compatible store for zig, ported from the engine in prefectlabs/burner-redis (rust). zig callers use the store directly: there is no server and no wire protocol. the design, command set and semantics are the rust original's.

install #

requires zig 0.17.

zig fetch --save https://tangled.org/zzstoatzz.io/burner-redis/archive/v0.0.5.tar.gz

then in build.zig:

const burner = b.dependency("burner_redis_zig", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("burner_redis", burner.module("burner_redis"));

use #

store — keys, strings, hashes, sets, sorted sets, lists, streams
const burner = @import("burner_redis");

var store = burner.Store.init(allocator, io);
defer store.deinit();

_ = try store.zadd("queue", &.{.{ .score = 1, .member = "task" }}, .{});
const score = try store.zscore("queue", "task"); // ?f64

one method per redis command. which commands, and where they answer differently from redis-server, is in docs/redis-compatibility.md.

scripts — lua 5.1 with redis.call, as EVAL runs it
var lua = burner.scripting.LuaEngine.init(allocator, &store);
defer lua.deinit();
const reply = try lua.eval(arena, "return redis.call('ZCARD', KEYS[1])", &.{"queue"}, &.{});

a script runs with nothing else in between, as in redis. eval leaves its scratch allocations to the allocator it is given: pass an arena.

snapshots — save the keyspace to one file and load it back
try store.save("burner.brdb");
const found = try store.load("burner.brdb"); // false when there is no file

a damaged file is refused with an error and the store left as it was.

pub/sub — channels and patterns, delivered to callbacks

subscribe, psubscribe and publish on Store, in the process only.

design #

  • embedded, not served — callers that would otherwise run a redis next to themselves get its data model as a library, with no socket and no serialization.
  • one lock for the whole store — redis's single-threaded model, so a *Store can be shared between threads and a script holds the lock from its first line to its last.
  • the same lua a real redis runs — a consumer can send identical scripts to redis-server or to this store and get the same effects.
  • the wall clock — deadlines, stream ids and idle times read std.Io's clock, as redis reads its own.

develop #

zig build test                          # unit tests

property tests #

the store is checked against a real redis-server: generated histories of commands and of docket's lua scripts are sent to both, and every reply compared. the harness runs on libhegel through hegel-zig, and lives on the pbt/discovery branch so that zig build test needs neither.

git switch pbt/discovery
zig build pbt -Dhegel -Doptimize=ReleaseSafe   # needs redis-server, and cargo the first time

a run prints what it exercised by rule, and counts the divergences from redis it already knows by name. the method is written up in hegel-zig's docs.

docs #

docs/ holds the store's layout, its redis compatibility, and its benchmarks against the rust original.

used by #

  • docket — its memory backend, running the same lua scripts it sends to redis

license #

MIT