Browse Source

[Zig/Httpz] Remove Mustache and Optimize Date Updates (#9678)

* [Zig/Httpz] calculate date on another thread

* [Zig/Httpz] fix build error

* [Zig/Httpz] fix build error 2

* [Zig/Httpz] fix build error 3

* [Zig/Httpz] fix #4

* [Zig/Httpz] proper escape

* [Zig/Htttpz] update run.sh

* [Zig/Httpz] update dockerfile

* [Zig/Httpz] update dockerfile 2

* [Zig/Httpz] update dockerfile 3

* [Zig/Httpz] update dockerfile 4

* [Zig/Httpz] update dockerfile 5

* update dockerfile 6

* update dockerfile 7

* update doclerfile 8

* update dockerfile 9
Kayden 5 months ago
parent
commit
83ee3c9370

+ 0 - 2
frameworks/Zig/httpz/build.zig

@@ -16,12 +16,10 @@ pub fn build(b: *std.Build) !void {
     const httpz_module = b.dependency("httpz", dep_opts).module("httpz");
     const pg_module = b.dependency("pg", dep_opts).module("pg");
     const datetimez_module = b.dependency("datetimez", dep_opts).module("zig-datetime");
-    const mustache_module = b.dependency("mustache", dep_opts).module("mustache");
 
     exe.root_module.addImport("httpz", httpz_module);
     exe.root_module.addImport("pg", pg_module);
     exe.root_module.addImport("datetimez", datetimez_module);
-    exe.root_module.addImport("mustache", mustache_module);
 
     // This declares intent for the executable to be installed into the
     // standard location when the user invokes the "install" step (the default

+ 0 - 4
frameworks/Zig/httpz/build.zig.zon

@@ -12,8 +12,4 @@
         .url = "git+https://github.com/frmdstryr/zig-datetime#70aebf28fb3e137cd84123a9349d157a74708721",
         .hash = "122077215ce36e125a490e59ec1748ffd4f6ba00d4d14f7308978e5360711d72d77f",
     },
-    .mustache = .{
-            .url = "git+https://github.com/batiati/mustache-zig#ae5ecc1522da983dc39bb0d8b27f5d1b1d7956e3",
-            .hash = "1220ac9e3316ce71ad9cd66c7f215462bf5c187828b50bb3d386549bf6af004e3bb0",
-        },
 } }

+ 14 - 9
frameworks/Zig/httpz/httpz.dockerfile

@@ -1,6 +1,4 @@
-FROM fedora:40
-
-WORKDIR /httpz
+FROM debian:12.9
 
 ENV PG_USER=benchmarkdbuser
 ENV PG_PASS=benchmarkdbpass
@@ -8,16 +6,23 @@ ENV PG_DB=hello_world
 ENV PG_HOST=tfb-database
 ENV PG_PORT=5432
 
+WORKDIR /app
+
 COPY src src
 COPY build.zig.zon build.zig.zon
 COPY build.zig build.zig
-COPY run.sh run.sh
 
-RUN dnf install -y zig
-RUN zig version
-RUN zig build -Doptimize=ReleaseFast 
-RUN cp /httpz/zig-out/bin/httpz /usr/local/bin
+ARG ZIG_VER=0.13.0
+
+RUN apt-get update && apt-get install -y curl xz-utils ca-certificates
+
+RUN curl https://ziglang.org/download/${ZIG_VER}/zig-linux-$(uname -m)-${ZIG_VER}.tar.xz -o zig-linux.tar.xz && \
+    tar xf zig-linux.tar.xz && \
+    mv zig-linux-$(uname -m)-${ZIG_VER}/ /opt/zig
+
+RUN /opt/zig/zig build -Doptimize=ReleaseFast
 
 EXPOSE 3000
+RUN ls 
 
-CMD ["sh", "run.sh"]
+CMD ["zig-out/bin/httpz"]

+ 0 - 3
frameworks/Zig/httpz/run.sh

@@ -1,3 +0,0 @@
-echo "Waiting for Httpz framework to start..."
-
-httpz 3000

+ 32 - 68
frameworks/Zig/httpz/src/endpoints.zig

@@ -2,16 +2,12 @@ const std = @import("std");
 const httpz = @import("httpz");
 const pg = @import("pg");
 const datetimez = @import("datetimez");
-const mustache = @import("mustache");
 
-const Thread = std.Thread;
-const Mutex = Thread.Mutex;
-const template = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>{{#fortunes}}<tr><td>{{id}}</td><td>{{message}}</td></tr>{{/fortunes}}</table></body></html>";
+pub var date_str: []u8 = "";
 
 pub const Global = struct {
     pool: *pg.Pool,
     rand: *std.rand.Random,
-    mutex: std.Thread.Mutex = .{},
 };
 
 const World = struct {
@@ -40,9 +36,7 @@ pub fn json(_: *Global, _: *httpz.Request, res: *httpz.Response) !void {
 pub fn db(global: *Global, _: *httpz.Request, res: *httpz.Response) !void {
     try setHeaders(res.arena, res);
 
-    global.mutex.lock();
     const random_number = 1 + (global.rand.uintAtMostBiased(u32, 9999));
-    global.mutex.unlock();
 
     const world = getWorld(global.pool, random_number) catch |err| {
         std.debug.print("Error querying database: {}\n", .{err});
@@ -76,31 +70,37 @@ fn getWorld(pool: *pg.Pool, random_number: u32) !World {
 fn setHeaders(allocator: std.mem.Allocator, res: *httpz.Response) !void {
     res.header("Server", "Httpz");
 
-    const now = datetimez.datetime.Date.now();
-    const time = datetimez.datetime.Time.now();
+    //const now = datetimez.datetime.Date.now();
+    //const time = datetimez.datetime.Time.now();
 
     // Wed, 17 Apr 2013 12:00:00 GMT
     // Return date in ISO format YYYY-MM-DD
-    const TB_DATE_FMT = "{s:0>3}, {d:0>2} {s:0>3} {d:0>4} {d:0>2}:{d:0>2}:{d:0>2} GMT";
-    const now_str = try std.fmt.allocPrint(allocator, TB_DATE_FMT, .{ now.weekdayName()[0..3], now.day, now.monthName()[0..3], now.year, time.hour, time.minute, time.second });
+    //const TB_DATE_FMT = "{s:0>3}, {d:0>2} {s:0>3} {d:0>4} {d:0>2}:{d:0>2}:{d:0>2} GMT";
+    //const now_str = try std.fmt.allocPrint(allocator, TB_DATE_FMT, .{ now.weekdayName()[0..3], now.day, now.monthName()[0..3], now.year, time.hour, time.minute, time.second });
 
     //defer allocator.free(now_str);
 
-    res.header("Date", now_str);
+    res.header("Date", try allocator.dupe(u8, date_str));
 }
 
 fn getFortunesHtml(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const u8 {
     const fortunes = try getFortunes(allocator, pool);
 
-    const raw = try mustache.allocRenderText(allocator, template, .{ .fortunes = fortunes });
+    var sb = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);
 
-    // std.debug.print("mustache output {s}\n", .{raw});
+    const writer = sb.writer(allocator);
+    try sb.appendSlice(allocator, "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
 
-    const html = try deescapeHtml(allocator, raw);
+    for (fortunes) |ft| {
+        try writer.print("<tr><td>{d}</td><td>{s}</td></tr>", .{
+            ft.id,
+            try escape_html(allocator, ft.message),
+        });
+    }
 
-    // std.debug.print("html output {s}\n", .{html});
+    try sb.appendSlice(allocator, "</table></body></html>");
 
-    return html;
+    return sb.toOwnedSlice(allocator);
 }
 
 fn getFortunes(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const Fortune {
@@ -110,18 +110,18 @@ fn getFortunes(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const Fortune {
     var rows = try conn.query("SELECT id, message FROM Fortune", .{});
     defer rows.deinit();
 
-    var fortunes = std.ArrayList(Fortune).init(allocator);
-    defer fortunes.deinit();
+    var fortunes = try std.ArrayListUnmanaged(Fortune).initCapacity(allocator, 0);
+    defer fortunes.deinit(allocator);
 
     while (try rows.next()) |row| {
         const current_fortune = Fortune{ .id = row.get(i32, 0), .message = row.get([]const u8, 1) };
-        try fortunes.append(current_fortune);
+        try fortunes.append(allocator, current_fortune);
     }
 
     const zero_fortune = Fortune{ .id = 0, .message = "Additional fortune added at request time." };
-    try fortunes.append(zero_fortune);
+    try fortunes.append(allocator, zero_fortune);
 
-    const fortunes_slice = try fortunes.toOwnedSlice();
+    const fortunes_slice = try fortunes.toOwnedSlice(allocator);
     std.mem.sort(Fortune, fortunes_slice, {}, cmpFortuneByMessage);
 
     return fortunes_slice;
@@ -131,53 +131,17 @@ fn cmpFortuneByMessage(_: void, a: Fortune, b: Fortune) bool {
     return std.mem.order(u8, a.message, b.message).compare(std.math.CompareOperator.lt);
 }
 
-fn deescapeHtml(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
-    var output = std.ArrayList(u8).init(allocator);
-    defer output.deinit();
-
-    var i: usize = 0;
-    while (i < input.len) {
-        if (std.mem.startsWith(u8, input[i..], "&#32;")) {
-            try output.append(' ');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#34;")) {
-            try output.append('"');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#38;")) {
-            try output.append('&');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#39;")) {
-            try output.append('\'');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#40;")) {
-            try output.append('(');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#41;")) {
-            try output.append(')');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#43;")) {
-            try output.append('+');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#44;")) {
-            try output.append(',');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#46;")) {
-            try output.append('.');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#47;")) {
-            try output.append('/');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#58;")) {
-            try output.append(':');
-            i += 5;
-        } else if (std.mem.startsWith(u8, input[i..], "&#59;")) {
-            try output.append(';');
-            i += 5;
-        } else {
-            try output.append(input[i]);
-            i += 1;
+fn escape_html(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
+    var output = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);
+    defer output.deinit(allocator);
+
+    for (input) |char| {
+        switch (char) {
+            '<' => try output.appendSlice(allocator, "&lt;"),
+            '>' => try output.appendSlice(allocator, "&gt;"),
+            else => try output.append(allocator, char),
         }
     }
 
-    return output.toOwnedSlice();
+    return output.toOwnedSlice(allocator);
 }

+ 19 - 22
frameworks/Zig/httpz/src/main.zig

@@ -20,6 +20,24 @@ pub fn main() !void {
     var pg_pool = try pool.initPool(allocator);
     defer pg_pool.deinit();
 
+    const date_thread = try std.Thread.spawn(.{}, struct {
+        fn update() !void {
+            const ally = std.heap.page_allocator;
+            while (true) {
+                const now = datetimez.datetime.Date.now();
+                const time = datetimez.datetime.Time.now();
+
+                // Wed, 17 Apr 2013 12:00:00 GMT
+                // Return date in ISO format YYYY-MM-DD
+                const TB_DATE_FMT = "{s:0>3}, {d:0>2} {s:0>3} {d:0>4} {d:0>2}:{d:0>2}:{d:0>2} GMT";
+                endpoints.date_str = try std.fmt.allocPrint(ally, TB_DATE_FMT, .{ now.weekdayName()[0..3], now.day, now.monthName()[0..3], now.year, time.hour, time.minute, time.second });
+                std.time.sleep(std.time.ns_per_ms * 980);
+            }
+        }
+    }.update, .{});
+
+    date_thread.detach();
+
     var prng = std.rand.DefaultPrng.init(@as(u64, @bitCast(std.time.milliTimestamp())));
 
     var rand = prng.random();
@@ -29,10 +47,7 @@ pub fn main() !void {
         .rand = &rand,
     };
 
-    const args = try std.process.argsAlloc(allocator);
-
-    const port: u16 = if (args.len > 1) try std.fmt.parseInt(u16, args[1], 0) else 3000;
-
+    const port: u16 = 3000;
     const workers = @as(u16, @intCast(16 * cpu_count));
 
     server = try httpz.ServerApp(*endpoints.Global).init(allocator, .{
@@ -55,10 +70,6 @@ pub fn main() !void {
             // static buffers. For example, if response headers don't fit in in
             // $response.header_buffer_size, a buffer will be pulled from here.
             // This is per-worker.
-            .large_buffer_count = 16,
-
-            // The size of each large buffer.
-            .large_buffer_size = 65536,
 
             // Size of bytes retained for the connection arena between use. This will
             // result in up to `count * min_conn * retain_allocated_bytes` of memory usage.
@@ -77,22 +88,8 @@ pub fn main() !void {
             // This applies back pressure to the above workers and ensures that, under load
             // pending requests get precedence over processing new requests.
             .backlog = 2048,
-
-            // Size of the static buffer to give each thread. Memory usage will be
-            // `count * buffer_size`. If you're making heavy use of either `req.arena` or
-            // `res.arena`, this is likely the single easiest way to gain performance.
-            .buffer_size = 8192,
         },
         .request = .{
-            // Maximum request body size that we'll process. We can allocate up
-            // to this much memory per request for the body. Internally, we might
-            // keep this memory around for a number of requests as an optimization.
-            .max_body_size = 1_048_576,
-
-            // This memory is allocated upfront. The request header _must_ fit into
-            // this space, else the request will be rejected.
-            .buffer_size = 4_096,
-
             // Maximum number of headers to accept.
             // Additional headers will be silently ignored.
             .max_header_count = 32,