Tech News
← Back to articles

Static Allocation with Zig

read original related products more articles

Over the past few months I've been chipping away at a small Redis-compatible key/value server called kv . The goal is to have something (mostly) production-ready, while implementing only a small subset of commands. The world doesn't necessarily need another key/value store, I'm just interested in implementing it in Zig and learning about some new (to me) techniques for systems programming.

One of those techniques is static memory allocation during initialization. The idea here is that all memory is requested and allocated from the OS at startup, and held until termination. I first heard about this while learning about TigerBeetle, and they reference it explicitly in their development style guide dubbed "TigerStyle".

All memory must be statically allocated at startup. No memory may be dynamically allocated (or freed and reallocated) after initialization. This avoids unpredictable behavior that can significantly affect performance, and avoids use-after-free. As a second-order effect, it is our experience that this also makes for more efficient, simpler designs that are more performant and easier to maintain and reason about, compared to designs that do not consider all possible memory usage patterns upfront as part of the design. TigerStyle

Although, this isn't as straightforward as it might sound at first. The first question that comes to mind might be: "How much memory do I allocate?" Of course, the answer depends on the system. If we're writing a server, how many concurrent connections do we allow? How much space is each connection allowed to work with? How much data do we expect to process at any given time? Are there limits in response size? Do we need all the data at once, or can it streamed in some fashion?

These are all questions that depend on the nature of the system and the context in which it will operate. I believe that going through the exercise of answering these questions is ultimately a good thing, as it seems to have a strong possibility of resulting in more stable systems, and forces us to understand the nature of our program at a deeper level.

On the language front, I feel like Zig is currently the best option out there for doing this with relative ease, considering its design choices around explicit memory allocation and the std.mem.Allocator interface, which allows the standard library to ship with a variety of different allocators.

Let's take a look at how we can manage static allocation in kv , considering three areas of request handling in sequence: connection handling, command parsing, and key/value storage.

A lot of this is pretty new to me, and I'm still wrestling with all these concepts. (And learning Zig!) I'm sure there are better ways of handling this stuff. I'm presenting this as one possible implementation completed as a learning exercise. I'll speak more about the trade-offs and where I think it can go further at the end of this post.

Connection Handling

The first thing we have to consider is how data comes into the system, which we'll maintain through the concept of a Connection .

... continue reading