Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ zig-out/
*.rdb
*.aof
.vscode
.zig-global-cache/
.zig-global-cache/
zig-pkg/
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ WORKDIR /build
COPY build.zig build.zig.zon ./
COPY src src/

RUN zig build -Doptimize=ReleaseFast -Dtarget="$(uname -m)-linux-musl"
RUN zig build -Doptimize=ReleaseSafe -Dtarget="$(uname -m)-linux-musl"

##############################################################################
# Stage: create data dir with correct ownership for distroless
Expand Down
78 changes: 29 additions & 49 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// build.zig
// This is the build script for our Zig Redis project.
// To build the project, run `zig build` in your terminal.
// To run the server, execute `zig build run`.

const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSafe });

const zio = b.dependency("zio", .{ .target = target, .optimize = optimize });
const zio_mod = zio.module("zio");

// --- Main executable ---
const exe = b.addExecutable(.{
.name = "zedis",
.root_module = b.createModule(.{
Expand All @@ -17,86 +16,68 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
}),
});

// This makes the standard library available to our project.
exe.root_module.addImport("zio", zio_mod);
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);

if (b.args) |args| {
run_cmd.addArgs(args);
}

const run_step = b.step("run", "Run the application");
const run_step = b.step("run", "Run the server");
run_step.dependOn(&run_cmd.step);

// For ZLS - builds but doesn't install anything
// ZLS check
const check_exe = b.addExecutable(.{ .name = "check", .root_module = exe.root_module });
const check_step = b.step("check", "check for build errors");
const check_step = b.step("check", "Check for build errors (ZLS)");
check_step.dependOn(&check_exe.step);

// Test steps - enhanced test runner system
// --- Tests ---
const test_mod = b.createModule(.{
.root_source_file = b.path("src/unit_tests.zig"),
.target = target,
.optimize = optimize,
});
test_mod.addImport("zio", zio_mod);

const unit_tests = b.addTest(.{
.name = "test-unit",
.root_module = b.createModule(.{
.root_source_file = b.path("src/unit_tests.zig"),
.target = target,
.optimize = optimize,
}),
.root_module = test_mod,
.filters = b.args orelse &.{},
});

const run_unit_tests = b.addRunArtifact(unit_tests);
run_unit_tests.setEnvironmentVariable("ZIG_EXE", b.graph.zig_exe);
if (b.args != null) run_unit_tests.has_side_effects = true;

// Don't cache test results if running with specific args (filters, etc.)
if (b.args != null) {
run_unit_tests.has_side_effects = true;
}

// Main test commands
const test_step = b.step("test", "Run all unit tests");
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);

const test_unit_step = b.step("test:unit", "Run unit tests only");
test_unit_step.dependOn(&run_unit_tests.step);

const test_build_step = b.step("test:build", "Build tests without running");
test_build_step.dependOn(&b.addInstallArtifact(unit_tests, .{}).step);

// Format checking
const fmt_step = b.step("test:fmt", "Check code formatting");
const run_fmt = b.addFmt(.{ .paths = &.{"src"}, .check = true });
fmt_step.dependOn(&run_fmt.step);

// Integration testing (for future expansion)
const test_integration_step = b.step("test:integration", "Run integration tests");
// For now, just depend on unit tests - can be expanded later
test_integration_step.dependOn(&run_unit_tests.step);

// All tests (unit + format + integration)
const test_all_step = b.step("test:all", "Run all tests including formatting checks");
const test_all_step = b.step("test:all", "Run tests and formatting checks");
test_all_step.dependOn(&run_unit_tests.step);
test_all_step.dependOn(fmt_step);

// Benchmark steps
// Micro-benchmarks (component-level performance)
// --- Benchmarks ---
const bench_micro_exe = b.addExecutable(.{
.name = "bench-micro",
.root_module = b.createModule(.{
.root_source_file = b.path("src/bench_micro.zig"),
.target = target,
.optimize = .ReleaseFast, // Always use optimized builds for benchmarks
.optimize = .ReleaseFast,
}),
});
bench_micro_exe.root_module.addImport("zio", zio_mod);

const run_bench_micro = b.addRunArtifact(bench_micro_exe);

const bench_micro_step = b.step("benchmark:micro", "Run micro-benchmarks (component-level)");
const bench_micro_step = b.step("bench:micro", "Run micro-benchmarks");
bench_micro_step.dependOn(&run_bench_micro.step);

// Load tests (integration benchmarks with real server)
const bench_load_exe = b.addExecutable(.{
.name = "benchmark-load",
.root_module = b.createModule(.{
Expand All @@ -105,13 +86,12 @@ pub fn build(b: *std.Build) void {
.optimize = .ReleaseFast,
}),
});
bench_load_exe.root_module.addImport("zio", zio_mod);

const run_bench_load = b.addRunArtifact(bench_load_exe);

const bench_load_step = b.step("benchmark:load", "Run load tests (requires running server)");
const bench_load_step = b.step("bench:load", "Run load tests (requires running server)");
bench_load_step.dependOn(&run_bench_load.step);

// Run all benchmarks (micro only, as load tests require manual server start)
const bench_all_step = b.step("benchmark", "Run all benchmarks (micro-benchmarks only)");
const bench_all_step = b.step("bench", "Run micro-benchmarks");
bench_all_step.dependOn(&run_bench_micro.step);
}
7 changes: 6 additions & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{},
.dependencies = .{
.zio = .{
.url = "git+https://github.com/lalinsky/zio?ref=v0.12.0#9f622147033da64b5d51402a1f06fe3e640f4f5c",
.hash = "zio-0.12.0-xHbVVD79GwD7y4FMkB5DroWGWxCELLiOPM3G-tuLGa8C",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
Expand Down
12 changes: 6 additions & 6 deletions src/bench_load.zig
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const std = @import("std");
const zio = @import("zio");
const bench_load = @import("benchmarks/bench_load.zig");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var threaded: std.Io.Threaded = .init(allocator, .{ .environ = .empty });
const io = threaded.io();
const rt = try zio.Runtime.init(allocator, .{});
defer rt.deinit();
const io = rt.io();

var stdout_writer = std.Io.File.stdout().writer(io, &.{});
const stdout = &stdout_writer.interface;
Expand Down
4 changes: 2 additions & 2 deletions src/bench_micro.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const std = @import("std");
const bench_store = @import("benchmarks/bench_store.zig");
const bench_commands = @import("benchmarks/bench_commands.zig");

pub fn main() !void {
const allocator = std.heap.smp_allocator;
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

std.debug.print("\n", .{});
std.debug.print("=" ** 100 ++ "\n", .{});
Expand Down
12 changes: 2 additions & 10 deletions src/kv_allocator.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const testing = std.testing;
const Config = @import("config.zig");
const Store = @import("store.zig").Store;
const Clock = @import("clock.zig");
Expand Down Expand Up @@ -99,8 +100,6 @@ pub fn getMemoryBudget(self: *KeyValueAllocator) usize {
}

test "KeyValueAllocator tracks alloc free and resize" {
const testing = std.testing;

var kv = try KeyValueAllocator.init(testing.allocator, 1024, .noeviction);
defer kv.deinit();

Expand All @@ -119,8 +118,6 @@ test "KeyValueAllocator tracks alloc free and resize" {
}

test "KeyValueAllocator returns out of memory without eviction" {
const testing = std.testing;

var kv = try KeyValueAllocator.init(testing.allocator, 2048, .noeviction);
defer kv.deinit();

Expand Down Expand Up @@ -153,8 +150,6 @@ test "KeyValueAllocator returns out of memory without eviction" {
}

test "KeyValueAllocator evicts under allkeys_lru pressure" {
const testing = std.testing;

var kv = try KeyValueAllocator.init(testing.allocator, 4096, .allkeys_lru);
defer kv.deinit();

Expand Down Expand Up @@ -190,8 +185,6 @@ test "KeyValueAllocator evicts under allkeys_lru pressure" {
}

test "KeyValueAllocator survives repeated budgeted overwrite pressure" {
const testing = std.testing;

const key_count = 256;
const key_len = "evict-key:0000".len;
const value_len = 1024;
Expand Down Expand Up @@ -242,8 +235,7 @@ test "KeyValueAllocator works with smp allocator parent" {
var kv = try KeyValueAllocator.init(base_allocator, 288 * 1024, .allkeys_lru);
defer kv.deinit();

var threaded: std.Io.Threaded = .init_single_threaded;
const io = threaded.io();
const io = testing.io;
var clock = Clock.init(io, 0);
var store = try Store.init(kv.allocator(), io, &clock, .{
.initial_capacity = 256,
Expand Down
11 changes: 5 additions & 6 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const zio = @import("zio");
const Server = @import("server.zig");
const Config = @import("config.zig");

Expand All @@ -7,13 +8,11 @@ const log = std.log.scoped(.main);
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

var threaded: std.Io.Threaded = .init(allocator, .{
.async_limit = .unlimited,
.concurrent_limit = .unlimited,
.environ = .empty,
const rt = try zio.Runtime.init(allocator, .{
.executors = .auto,
});
defer threaded.deinit();
const io = threaded.io();
defer rt.deinit();
const io = rt.io();

const cfg = Config.readConfig(allocator, io, init.minimal.args) catch |err| {
log.err("Failed to read config: {s}", .{@errorName(err)});
Expand Down
1 change: 0 additions & 1 deletion src/store.zig
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@ pub const Store = struct {
}

fn setString(self: *Store, key: []const u8, value: []const u8) !void {
assert(value.len > 0);
const zedis_value: ZedisValue = if (value.len <= 23)
.{ .short_string = ShortString.fromSlice(value) }
else
Expand Down
2 changes: 1 addition & 1 deletion src/testing/string.zig
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ test "SET command with integer value" {

const stored_value = store.get("key1");
try testing.expect(stored_value != null);
try testing.expectEqual(@as(i64, 42), stored_value.?.value.int);
try testing.expectEqualStrings("42", stored_value.?.value.short_string.asSlice());
}

test "GET command with existing string value" {
Expand Down
Loading