Skip to content
Open
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
4 changes: 4 additions & 0 deletions frameworks/zix-ws/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.zig-cache
zig-out
zig-package
vendor
15 changes: 4 additions & 11 deletions frameworks/zix-ws/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ARG RETRY=6
ARG TARGETARCH
ARG RETRY_DELAY=3
ARG ZIG_VERSION=0.16.0
ARG ZIX_VERSION=0.4.x-rc3
ARG ZIX_VERSION=0.5.x-rc1
RUN apk add --no-cache ca-certificates curl git tar xz

RUN set -eu; \
Expand All @@ -19,15 +19,8 @@ RUN set -eu; \
mv "/opt/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}" /opt/zig
ENV PATH="/opt/zig:${PATH}"

# Vendor zix X.Y.Z, separate layer so source-only rebuilds skip the fetch.
# The Http1 raw engine work this image needs (large-body drain plus the per-worker
# response cache used by the /json endpoint) must be present on the X.Y.Z branch.
# Four ordered attempts before giving up: curl the archive tarball from github then
# codeberg, then a shallow git clone from github then codeberg. The github archive
# redirects to codeload.github.com (which the benchmark runner may not resolve), so
# curl can fall through to the codeberg tarball, and git clone talks to github.com
# and codeberg.org directly as the deeper fallback. RETRY and RETRY_DELAY bound
# every attempt.
# Vendor zix ${ZIX_VERSION} in its own layer so source-only rebuilds skip the fetch. Tries curl
# tarball then git clone, github before codeberg. RETRY / RETRY_DELAY bound every attempt.
RUN set -eu; \
fetch() { \
rm -rf /src/vendor/zix; mkdir -p /src/vendor/zix; \
Expand Down Expand Up @@ -61,7 +54,7 @@ RUN set -eu; \
amd64) ZIG_TARGET=x86_64-linux-musl; ZIG_CPU=x86_64_v3 ;; \
arm64) ZIG_TARGET=aarch64-linux-musl; ZIG_CPU=baseline ;; \
esac; \
zig build -Dtarget="${ZIG_TARGET}" -Dcpu="${ZIG_CPU}" --release=fast
zig build -Dtarget="${ZIG_TARGET}" -Dcpu="${ZIG_CPU}+aes+pclmul+adx" --release=fast

FROM alpine:3.20
COPY --from=build /src/zig-out/bin/zix-ws /zix-ws
Expand Down
6 changes: 3 additions & 3 deletions frameworks/zix-ws/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"display_name": "zix-ws",
"language": "Zig",
"type": "engine",
"engine": "zix",
"description": "Zig WebSocket echo server on the zix.Http1 raw engine (no std.http). Shared-nothing by design: each worker owns its own SO_REUSEPORT multishot accept, io_uring completion ring, and connections, with no shared state or locking across cores. The upgrade and echo are engine-owned: frames are read from a shared per-worker provided-buffer ring (an idle connection holds no buffer), and a pipelined burst is coalesced into one write.",
"repo": "https://codeberg.org/prothegee/zix",
"engine": "zix uring",
"description": "Zig WebSocket echo server on the zix.Http1 raw engine (no std.http). Shared-nothing: each worker owns its SO_REUSEPORT accept, io_uring loop, and connections. The upgrade and echo are engine-owned: frames are read from a per-worker buffer ring (an idle connection holds no buffer), and a pipelined burst is coalesced into one write.",
"repo": "https://github.com/prothegee/zix",
"enabled": true,
"tests": [
"echo-ws",
Expand Down
25 changes: 9 additions & 16 deletions frameworks/zix-ws/src/main.zig
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
//! HttpArena: zix-ws
//!
//! zix HttpArena WebSocket entry point.
//!
//! Intent: demonstrate the engine-owned WebSocket path of zix.Http1 (EPOLL
//! dispatch model) against the HttpArena echo and echo-pipeline suites.
//!
//! Design choices:
//! - GET /ws upgrades, then zix.Http1.WebSocket.serve drives the echo loop
//! inside the engine: frames are echoed on readiness and a pipelined burst is
//! coalesced into a single write.
//! - No response cache: echo is per-connection, not a broadcast fanout, so there
//! is nothing to precompute or share across connections.
//! zix.Http1 engine-owned WebSocket (.URING) against the HttpArena echo and echo-pipeline suites.
//! GET /ws upgrades, then WebSocket.serve drives the echo loop inside the engine (frames echoed on
//! readiness, a pipelined burst coalesced into one write). No response cache: echo is per-connection,
//! nothing to precompute or share.

const std = @import("std");
const zix = @import("zix");
Expand All @@ -22,12 +15,12 @@ const LISTEN_IP: []const u8 = "::";
const DISPATCH_MODEL: zix.Http1.DispatchModel = .URING;
const KERNEL_BACKLOG: u31 = 16 * 1024;

/// Per-machine tuning profile (ADR-041 increment 5): .lean for the 12-thread /
/// 32 GB dev box, .throughput for the 64-core / 251 GB competition box. Only the
/// HTTP handshake recv buffer differs (the WS frame buffer is already 32 KiB).
/// Select .throughput for the 64-core deployment.
/// Per-machine tuning profile (ADR-041 incr 5): .lean for the 12-thread dev box, .throughput for
/// the 64-core box. Under .URING the engine now honors ws_recv_buf (sizes conn.buf and the unmask
/// scratch, independent of max_recv_buf), so a deep pipelined echo burst accumulates in one
/// WS_RECV_BUF (32 KiB) buffer. max_recv_buf only sizes the brief HTTP handshake recv.
const Profile = enum { lean, throughput };
const PROFILE: Profile = .lean;
const PROFILE: Profile = .throughput;

const MAX_RECV_BUF: usize = switch (PROFILE) {
.lean => 4 * 1024,
Expand Down