zig-okredis
zig-okredis copied to clipboard
Zero-allocation Client for Redis 6+
OkRedis
OkRedis is a zero-allocation client for Redis 6+
Handy and Efficient
OkRedis aims to offer an interface with great ergonomics without compromising on performance or flexibility.
Project status
OkRedis is currently in alpha. The main features are mostly complete, but a lot of polishing is still required.
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.
Zero dynamic allocations, unless explicitly wanted
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.
Quickstart
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!", .{}),
}
}
Available Documentation
The reference documentation is available here.
-
Using the OkRedis client
- Connecting
- Buffering
- Evented vs blocking I/O
- Pipelining
- Transactions
- Pub/Sub
-
Sending commands
- Base interface
- Command builder interface
- Validating command syntax
- Optimized command builders
- Creating new command builders
- An afterword on command builders vs methods
-
Decoding Redis Replies
- Introduction
- The first and second rule of decoding replies
- Decoding Zig types
- Void
- Numbers
- Optionals
- Strings
- Structs
- Decoding Redis errors and nil replies as values
- Redis OK replies
- Allocating memory dynamically
- Allocating strings
- Freeing complex replies
- Allocating Redis Error messages
- Allocating structured types
- Parsing dynamic replies
- Bundled types
- Decoding types in the standard library
- Implementing decodable types
- Adding types for custom commands (Lua scripts or Redis modules)
- Adding types used by a higher-level language
Extending OkRedis
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.
Embedding OkRedis in a higher level language
Take a look at the final section of REPLIES.md.
TODOS
- 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