OkRedis is a zero-allocation client for Redis 6
OkRedis aims to offer an interface with great ergonomics without compromising on performance or flexibility.
OkRedis is currently in alpha. The main features are mostly complete,
but a lot of polishing is still required. Zig version 0.10.1
is
supported. Once async
is available in a stable release, OkRedis will
pivot to a new Zig version.
Everything mentioned in the docs is already implemented and you can just
zig run example.zig
to quickly see what it can do. Remember OkRedis only
supports Redis 6 and above.
The client has two main interfaces to send commands: send
and sendAlloc
.
Following Zig's mantra of making dynamic allocations explicit, only sendAlloc
can allocate dynamic memory, and only does so by using a user-provided allocator.
The way this is achieved is by making good use of RESP3's typed responses and Zig's metaprogramming facilities. The library uses compile-time reflection to specialize down to the parser level, allowing OkRedis to decode whenever possible a reply directly into a function frame, without any intermediate dynamic allocation. If you want more information about Zig's comptime:
- Official documentation
- What is Zig's Comptime? (blog post written by me)
By using sendAlloc
you can decode replies with arbrirary shape at the cost of
occasionally performing dynamic allocations. The interface takes an allocator
as input, so the user can setup custom allocation schemes such as
arenas.
const std = @import("std");
const okredis = @import("./src/okredis.zig");
const SET = okredis.commands.strings.SET;
const OrErr = okredis.types.OrErr;
const Client = okredis.Client;
pub fn main() !void {
const addr = try std.net.Address.parseIp4("127.0.0.1", 6379);
var connection = try std.net.tcpConnectToAddress(addr);
var client: Client = undefined;
try client.init(connection);
defer client.close();
// Basic interface
try client.send(void, .{ "SET", "key", "42" });
const reply = try client.send(i64, .{ "GET", "key" });
if (reply != 42) @panic("out of towels");
// Command builder interface
const cmd = SET.init("key", "43", .NoExpire, .IfAlreadyExisting);
const otherReply = try client.send(OrErr(void), cmd);
switch (otherReply) {
.Nil => @panic("command should not have returned nil"),
.Err => @panic("command should not have returned an error"),
.Ok => std.debug.print("success!", .{}),
}
}
The reference documentation is available here.
If you are a Lua script or Redis module author, you might be interested in
reading the final sections of COMMANDS.md
and REPLIES.md
.
Take a look at the final section of REPLIES.md
.
- Implement remaining command builders
- Better connection management (ipv6, unixsockets, ...)
- Streamline design of Zig errors
- Refine support for async/await and think about connection pooling
- Refine the Redis traits
- Pub/Sub