From a9c32967fde0291c5419b245d79c1459ee2d3474 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Thu, 21 May 2026 17:51:22 -0300 Subject: [PATCH 1/2] Use lalinsky/zio v0.12.0 Fix tests --- .gitignore | 3 +- build.zig | 76 ++++++++++++++++-------------------------- build.zig.zon | 7 +++- src/bench_load.zig | 12 +++---- src/bench_micro.zig | 4 +-- src/kv_allocator.zig | 12 ++----- src/main.zig | 11 +++--- src/store.zig | 1 - src/testing/string.zig | 2 +- 9 files changed, 52 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index 5afa3f6..e3e868f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ zig-out/ *.rdb *.aof .vscode -.zig-global-cache/ \ No newline at end of file +.zig-global-cache/ +zig-pkg/ \ No newline at end of file diff --git a/build.zig b/build.zig index f4c722f..ff13ba9 100644 --- a/build.zig +++ b/build.zig @@ -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 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(.{ @@ -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(.{ @@ -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); } diff --git a/build.zig.zon b/build.zig.zon index 11bd7ec..34fa3be 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -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", diff --git a/src/bench_load.zig b/src/bench_load.zig index cac602a..9d9bbe7 100644 --- a/src/bench_load.zig +++ b/src/bench_load.zig @@ -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; diff --git a/src/bench_micro.zig b/src/bench_micro.zig index c0ac610..b39834a 100644 --- a/src/bench_micro.zig +++ b/src/bench_micro.zig @@ -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", .{}); diff --git a/src/kv_allocator.zig b/src/kv_allocator.zig index 6405d68..15618c8 100644 --- a/src/kv_allocator.zig +++ b/src/kv_allocator.zig @@ -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"); @@ -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(); @@ -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(); @@ -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(); @@ -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; @@ -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, diff --git a/src/main.zig b/src/main.zig index 8a1aba8..f93d3a4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const zio = @import("zio"); const Server = @import("server.zig"); const Config = @import("config.zig"); @@ -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)}); diff --git a/src/store.zig b/src/store.zig index adc2a4f..5b48945 100644 --- a/src/store.zig +++ b/src/store.zig @@ -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 diff --git a/src/testing/string.zig b/src/testing/string.zig index 71bde35..062fc4b 100644 --- a/src/testing/string.zig +++ b/src/testing/string.zig @@ -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" { From 3c2dc912ff3ca2d58f15cda47faf866e0b7e14b6 Mon Sep 17 00:00:00 2001 From: Charles Fonseca Date: Thu, 21 May 2026 18:01:20 -0300 Subject: [PATCH 2/2] fix: change optimization level to ReleaseSafe for consistent builds --- Dockerfile | 2 +- build.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9de569b..386a731 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/build.zig b/build.zig index ff13ba9..70de7fc 100644 --- a/build.zig +++ b/build.zig @@ -2,7 +2,7 @@ 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");