Skip to content

Commit 83ee3c9

Browse files
authored
[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
1 parent 80d798e commit 83ee3c9

File tree

6 files changed

+65
-108
lines changed

6 files changed

+65
-108
lines changed

frameworks/Zig/httpz/build.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ pub fn build(b: *std.Build) !void {
1616
const httpz_module = b.dependency("httpz", dep_opts).module("httpz");
1717
const pg_module = b.dependency("pg", dep_opts).module("pg");
1818
const datetimez_module = b.dependency("datetimez", dep_opts).module("zig-datetime");
19-
const mustache_module = b.dependency("mustache", dep_opts).module("mustache");
2019

2120
exe.root_module.addImport("httpz", httpz_module);
2221
exe.root_module.addImport("pg", pg_module);
2322
exe.root_module.addImport("datetimez", datetimez_module);
24-
exe.root_module.addImport("mustache", mustache_module);
2523

2624
// This declares intent for the executable to be installed into the
2725
// standard location when the user invokes the "install" step (the default

frameworks/Zig/httpz/build.zig.zon

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,4 @@
1212
.url = "git+https://github.com/frmdstryr/zig-datetime#70aebf28fb3e137cd84123a9349d157a74708721",
1313
.hash = "122077215ce36e125a490e59ec1748ffd4f6ba00d4d14f7308978e5360711d72d77f",
1414
},
15-
.mustache = .{
16-
.url = "git+https://github.com/batiati/mustache-zig#ae5ecc1522da983dc39bb0d8b27f5d1b1d7956e3",
17-
.hash = "1220ac9e3316ce71ad9cd66c7f215462bf5c187828b50bb3d386549bf6af004e3bb0",
18-
},
1915
} }

frameworks/Zig/httpz/httpz.dockerfile

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
FROM fedora:40
2-
3-
WORKDIR /httpz
1+
FROM debian:12.9
42

53
ENV PG_USER=benchmarkdbuser
64
ENV PG_PASS=benchmarkdbpass
75
ENV PG_DB=hello_world
86
ENV PG_HOST=tfb-database
97
ENV PG_PORT=5432
108

9+
WORKDIR /app
10+
1111
COPY src src
1212
COPY build.zig.zon build.zig.zon
1313
COPY build.zig build.zig
14-
COPY run.sh run.sh
1514

16-
RUN dnf install -y zig
17-
RUN zig version
18-
RUN zig build -Doptimize=ReleaseFast
19-
RUN cp /httpz/zig-out/bin/httpz /usr/local/bin
15+
ARG ZIG_VER=0.13.0
16+
17+
RUN apt-get update && apt-get install -y curl xz-utils ca-certificates
18+
19+
RUN curl https://ziglang.org/download/${ZIG_VER}/zig-linux-$(uname -m)-${ZIG_VER}.tar.xz -o zig-linux.tar.xz && \
20+
tar xf zig-linux.tar.xz && \
21+
mv zig-linux-$(uname -m)-${ZIG_VER}/ /opt/zig
22+
23+
RUN /opt/zig/zig build -Doptimize=ReleaseFast
2024

2125
EXPOSE 3000
26+
RUN ls
2227

23-
CMD ["sh", "run.sh"]
28+
CMD ["zig-out/bin/httpz"]

frameworks/Zig/httpz/run.sh

Lines changed: 0 additions & 3 deletions
This file was deleted.

frameworks/Zig/httpz/src/endpoints.zig

Lines changed: 32 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@ const std = @import("std");
22
const httpz = @import("httpz");
33
const pg = @import("pg");
44
const datetimez = @import("datetimez");
5-
const mustache = @import("mustache");
65

7-
const Thread = std.Thread;
8-
const Mutex = Thread.Mutex;
9-
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>";
6+
pub var date_str: []u8 = "";
107

118
pub const Global = struct {
129
pool: *pg.Pool,
1310
rand: *std.rand.Random,
14-
mutex: std.Thread.Mutex = .{},
1511
};
1612

1713
const World = struct {
@@ -40,9 +36,7 @@ pub fn json(_: *Global, _: *httpz.Request, res: *httpz.Response) !void {
4036
pub fn db(global: *Global, _: *httpz.Request, res: *httpz.Response) !void {
4137
try setHeaders(res.arena, res);
4238

43-
global.mutex.lock();
4439
const random_number = 1 + (global.rand.uintAtMostBiased(u32, 9999));
45-
global.mutex.unlock();
4640

4741
const world = getWorld(global.pool, random_number) catch |err| {
4842
std.debug.print("Error querying database: {}\n", .{err});
@@ -76,31 +70,37 @@ fn getWorld(pool: *pg.Pool, random_number: u32) !World {
7670
fn setHeaders(allocator: std.mem.Allocator, res: *httpz.Response) !void {
7771
res.header("Server", "Httpz");
7872

79-
const now = datetimez.datetime.Date.now();
80-
const time = datetimez.datetime.Time.now();
73+
//const now = datetimez.datetime.Date.now();
74+
//const time = datetimez.datetime.Time.now();
8175

8276
// Wed, 17 Apr 2013 12:00:00 GMT
8377
// Return date in ISO format YYYY-MM-DD
84-
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";
85-
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 });
78+
//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";
79+
//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 });
8680

8781
//defer allocator.free(now_str);
8882

89-
res.header("Date", now_str);
83+
res.header("Date", try allocator.dupe(u8, date_str));
9084
}
9185

9286
fn getFortunesHtml(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const u8 {
9387
const fortunes = try getFortunes(allocator, pool);
9488

95-
const raw = try mustache.allocRenderText(allocator, template, .{ .fortunes = fortunes });
89+
var sb = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);
9690

97-
// std.debug.print("mustache output {s}\n", .{raw});
91+
const writer = sb.writer(allocator);
92+
try sb.appendSlice(allocator, "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
9893

99-
const html = try deescapeHtml(allocator, raw);
94+
for (fortunes) |ft| {
95+
try writer.print("<tr><td>{d}</td><td>{s}</td></tr>", .{
96+
ft.id,
97+
try escape_html(allocator, ft.message),
98+
});
99+
}
100100

101-
// std.debug.print("html output {s}\n", .{html});
101+
try sb.appendSlice(allocator, "</table></body></html>");
102102

103-
return html;
103+
return sb.toOwnedSlice(allocator);
104104
}
105105

106106
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 {
110110
var rows = try conn.query("SELECT id, message FROM Fortune", .{});
111111
defer rows.deinit();
112112

113-
var fortunes = std.ArrayList(Fortune).init(allocator);
114-
defer fortunes.deinit();
113+
var fortunes = try std.ArrayListUnmanaged(Fortune).initCapacity(allocator, 0);
114+
defer fortunes.deinit(allocator);
115115

116116
while (try rows.next()) |row| {
117117
const current_fortune = Fortune{ .id = row.get(i32, 0), .message = row.get([]const u8, 1) };
118-
try fortunes.append(current_fortune);
118+
try fortunes.append(allocator, current_fortune);
119119
}
120120

121121
const zero_fortune = Fortune{ .id = 0, .message = "Additional fortune added at request time." };
122-
try fortunes.append(zero_fortune);
122+
try fortunes.append(allocator, zero_fortune);
123123

124-
const fortunes_slice = try fortunes.toOwnedSlice();
124+
const fortunes_slice = try fortunes.toOwnedSlice(allocator);
125125
std.mem.sort(Fortune, fortunes_slice, {}, cmpFortuneByMessage);
126126

127127
return fortunes_slice;
@@ -131,53 +131,17 @@ fn cmpFortuneByMessage(_: void, a: Fortune, b: Fortune) bool {
131131
return std.mem.order(u8, a.message, b.message).compare(std.math.CompareOperator.lt);
132132
}
133133

134-
fn deescapeHtml(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
135-
var output = std.ArrayList(u8).init(allocator);
136-
defer output.deinit();
137-
138-
var i: usize = 0;
139-
while (i < input.len) {
140-
if (std.mem.startsWith(u8, input[i..], "&#32;")) {
141-
try output.append(' ');
142-
i += 5;
143-
} else if (std.mem.startsWith(u8, input[i..], "&#34;")) {
144-
try output.append('"');
145-
i += 5;
146-
} else if (std.mem.startsWith(u8, input[i..], "&#38;")) {
147-
try output.append('&');
148-
i += 5;
149-
} else if (std.mem.startsWith(u8, input[i..], "&#39;")) {
150-
try output.append('\'');
151-
i += 5;
152-
} else if (std.mem.startsWith(u8, input[i..], "&#40;")) {
153-
try output.append('(');
154-
i += 5;
155-
} else if (std.mem.startsWith(u8, input[i..], "&#41;")) {
156-
try output.append(')');
157-
i += 5;
158-
} else if (std.mem.startsWith(u8, input[i..], "&#43;")) {
159-
try output.append('+');
160-
i += 5;
161-
} else if (std.mem.startsWith(u8, input[i..], "&#44;")) {
162-
try output.append(',');
163-
i += 5;
164-
} else if (std.mem.startsWith(u8, input[i..], "&#46;")) {
165-
try output.append('.');
166-
i += 5;
167-
} else if (std.mem.startsWith(u8, input[i..], "&#47;")) {
168-
try output.append('/');
169-
i += 5;
170-
} else if (std.mem.startsWith(u8, input[i..], "&#58;")) {
171-
try output.append(':');
172-
i += 5;
173-
} else if (std.mem.startsWith(u8, input[i..], "&#59;")) {
174-
try output.append(';');
175-
i += 5;
176-
} else {
177-
try output.append(input[i]);
178-
i += 1;
134+
fn escape_html(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
135+
var output = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);
136+
defer output.deinit(allocator);
137+
138+
for (input) |char| {
139+
switch (char) {
140+
'<' => try output.appendSlice(allocator, "&lt;"),
141+
'>' => try output.appendSlice(allocator, "&gt;"),
142+
else => try output.append(allocator, char),
179143
}
180144
}
181145

182-
return output.toOwnedSlice();
146+
return output.toOwnedSlice(allocator);
183147
}

frameworks/Zig/httpz/src/main.zig

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ pub fn main() !void {
2020
var pg_pool = try pool.initPool(allocator);
2121
defer pg_pool.deinit();
2222

23+
const date_thread = try std.Thread.spawn(.{}, struct {
24+
fn update() !void {
25+
const ally = std.heap.page_allocator;
26+
while (true) {
27+
const now = datetimez.datetime.Date.now();
28+
const time = datetimez.datetime.Time.now();
29+
30+
// Wed, 17 Apr 2013 12:00:00 GMT
31+
// Return date in ISO format YYYY-MM-DD
32+
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";
33+
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 });
34+
std.time.sleep(std.time.ns_per_ms * 980);
35+
}
36+
}
37+
}.update, .{});
38+
39+
date_thread.detach();
40+
2341
var prng = std.rand.DefaultPrng.init(@as(u64, @bitCast(std.time.milliTimestamp())));
2442

2543
var rand = prng.random();
@@ -29,10 +47,7 @@ pub fn main() !void {
2947
.rand = &rand,
3048
};
3149

32-
const args = try std.process.argsAlloc(allocator);
33-
34-
const port: u16 = if (args.len > 1) try std.fmt.parseInt(u16, args[1], 0) else 3000;
35-
50+
const port: u16 = 3000;
3651
const workers = @as(u16, @intCast(16 * cpu_count));
3752

3853
server = try httpz.ServerApp(*endpoints.Global).init(allocator, .{
@@ -55,10 +70,6 @@ pub fn main() !void {
5570
// static buffers. For example, if response headers don't fit in in
5671
// $response.header_buffer_size, a buffer will be pulled from here.
5772
// This is per-worker.
58-
.large_buffer_count = 16,
59-
60-
// The size of each large buffer.
61-
.large_buffer_size = 65536,
6273

6374
// Size of bytes retained for the connection arena between use. This will
6475
// result in up to `count * min_conn * retain_allocated_bytes` of memory usage.
@@ -77,22 +88,8 @@ pub fn main() !void {
7788
// This applies back pressure to the above workers and ensures that, under load
7889
// pending requests get precedence over processing new requests.
7990
.backlog = 2048,
80-
81-
// Size of the static buffer to give each thread. Memory usage will be
82-
// `count * buffer_size`. If you're making heavy use of either `req.arena` or
83-
// `res.arena`, this is likely the single easiest way to gain performance.
84-
.buffer_size = 8192,
8591
},
8692
.request = .{
87-
// Maximum request body size that we'll process. We can allocate up
88-
// to this much memory per request for the body. Internally, we might
89-
// keep this memory around for a number of requests as an optimization.
90-
.max_body_size = 1_048_576,
91-
92-
// This memory is allocated upfront. The request header _must_ fit into
93-
// this space, else the request will be rejected.
94-
.buffer_size = 4_096,
95-
9693
// Maximum number of headers to accept.
9794
// Additional headers will be silently ignored.
9895
.max_header_count = 32,

0 commit comments

Comments
 (0)