Skip to content

Commit b785750

Browse files
committed
refactor: deduplicate Event::Request fields, add mpsc benchmark
Remove duplicate method/path/trusted_ip from Event::Request - access via request.* instead. Add mpsc benchmark directory for comparison.
1 parent ad8ab30 commit b785750

File tree

4 files changed

+97
-21
lines changed

4 files changed

+97
-21
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Benchmark: mpsc-logging
2+
3+
> **Note:** Run benchmarks only with this branch checked out: `git checkout bench-mpsc-logging`
4+
5+
Comparing mpsc channel vs broadcast channel for logging.
6+
7+
## Setup
8+
- Event enum is owned (no Clone needed for mpsc)
9+
- emit() sends to std::sync::mpsc::Sender (non-blocking)
10+
- Handlers run in dedicated threads with blocking recv()
11+
- No clone on receive (mpsc moves ownership)
12+
13+
## Run
14+
```bash
15+
nu run.nu # run and display
16+
nu run.nu --save # run and save to results.nuon
17+
18+
# Manual testing against a running server
19+
oha -z 5s -c 50 http://127.0.0.1:3001/
20+
```
21+
22+
## Results
23+
See `results.nuon`
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[[format, requests_per_sec, avg_latency_ms, dropped, connections, duration]; [jsonl, 24373.74, 2.04, 0, 50, "5s"], [human, 25352.79, 1.96, 0, 50, "5s"]]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env nu
2+
3+
# Benchmark script for broadcast-based logging
4+
5+
def main [
6+
--duration (-d): string = "5s" # Duration per test
7+
--connections (-c): int = 50 # Concurrent connections
8+
--save (-s) # Save results to results.nuon
9+
] {
10+
let script_dir = ($env.FILE_PWD? | default ".")
11+
let root = $"($script_dir)/../.."
12+
13+
print "Building release..."
14+
cargo build --release --quiet
15+
16+
mut results = []
17+
18+
for format in ["jsonl", "human"] {
19+
let port = 9990 + ($results | length)
20+
let logfile = $"/tmp/http-nu-bench-($port).log"
21+
print $"Testing ($format) on :($port)..."
22+
23+
# Start server, capture output to check for lag
24+
bash -c $"($root)/target/release/http-nu --log-format ($format) :($port) '{|req| {body: \"hello\"}}' > ($logfile) 2>&1 &"
25+
sleep 1sec
26+
27+
# Run benchmark
28+
let oha_out = (oha -z $duration -c $connections $"http://127.0.0.1:($port)/" | complete).stdout
29+
30+
# Kill server
31+
pkill -f $"http-nu.*:($port)"
32+
sleep 500ms
33+
34+
# Parse benchmark results
35+
let rps = ($oha_out | parse -r 'Requests/sec:\s+([\d.]+)' | get 0?.capture0? | default "0" | into float)
36+
let avg = ($oha_out | parse -r 'Average:\s+([\d.]+)\s+ms' | get 0?.capture0? | default "0" | into float)
37+
38+
# Check for lag (jsonl: "lagged" in JSON, human: "logging lagged" on stderr)
39+
let dropped = if $format == "jsonl" {
40+
let lagged = (open $logfile | lines | where {|l| $l =~ '"message":"lagged"'} | each {|l| $l | from json | get dropped})
41+
if ($lagged | is-empty) { 0 } else { $lagged | math sum }
42+
} else {
43+
let count = (grep -c "logging lagged" $logfile | complete | get stdout | str trim)
44+
if ($count | is-empty) { 0 } else { $count | into int }
45+
}
46+
47+
$results = ($results | append {
48+
format: $format
49+
requests_per_sec: ($rps | math round -p 2)
50+
avg_latency_ms: ($avg | math round -p 2)
51+
dropped: $dropped
52+
connections: $connections
53+
duration: $duration
54+
})
55+
56+
rm -f $logfile
57+
}
58+
59+
if $save {
60+
$results | to nuon | save -f $"($script_dir)/results.nuon"
61+
print $"Saved to ($script_dir)/results.nuon"
62+
}
63+
64+
$results
65+
}

src/logging.rs

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ pub enum Event {
5555
// Access events
5656
Request {
5757
request_id: scru128::Scru128Id,
58-
method: String,
59-
path: String,
60-
trusted_ip: Option<String>,
6158
request: Box<RequestData>,
6259
},
6360
Response {
@@ -111,13 +108,9 @@ fn emit(event: Event) {
111108
// --- Public emit functions ---
112109

113110
pub fn log_request(request_id: scru128::Scru128Id, request: &Request) {
114-
let data = RequestData::from(request);
115111
emit(Event::Request {
116112
request_id,
117-
method: data.method.clone(),
118-
path: data.path.clone(),
119-
trusted_ip: data.trusted_ip.clone(),
120-
request: Box::new(data),
113+
request: Box::new(RequestData::from(request)),
121114
});
122115
}
123116

@@ -218,18 +211,15 @@ pub fn run_jsonl_handler(rx: broadcast::Receiver<Event>) {
218211
let json = match event {
219212
Event::Request {
220213
request_id,
221-
method,
222-
path,
223-
trusted_ip,
224214
request,
225215
} => {
226216
serde_json::json!({
227217
"stamp": stamp,
228218
"message": "request",
229219
"request_id": request_id.to_string(),
230-
"method": method,
231-
"path": path,
232-
"trusted_ip": trusted_ip,
220+
"method": &request.method,
221+
"path": &request.path,
222+
"trusted_ip": &request.trusted_ip,
233223
"request": request,
234224
})
235225
}
@@ -414,10 +404,7 @@ pub fn run_human_handler(rx: broadcast::Receiver<Event>) {
414404
}
415405
Event::Request {
416406
request_id,
417-
method,
418-
path,
419-
trusted_ip,
420-
..
407+
request,
421408
} => {
422409
let now = std::time::Instant::now();
423410
if now.duration_since(last_shown) < min_interval {
@@ -437,9 +424,9 @@ pub fn run_human_handler(rx: broadcast::Receiver<Event>) {
437424

438425
let state = RequestState {
439426
pb: pb.clone(),
440-
method,
441-
path,
442-
trusted_ip,
427+
method: request.method.clone(),
428+
path: request.path.clone(),
429+
trusted_ip: request.trusted_ip.clone(),
443430
status: None,
444431
latency_ms: None,
445432
};

0 commit comments

Comments
 (0)