Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
087335d
♻️ refactor: replace diskcache with aiosqlite, make all bench routes …
renardeinside Mar 31, 2026
b80d061
✨ feat: implement ASGI lifespan protocol for startup/shutdown hooks
renardeinside Mar 31, 2026
262d4a2
♻️ refactor: deduplicate ASGI version constants into asgi/mod.rs
renardeinside Mar 31, 2026
75c5925
🚀 perf: optimize ASGI data path (P3/P4/P5/P6/P12/P13)
renardeinside Mar 31, 2026
4bd227a
🚀 perf: inline coroutine driving to eliminate create_task overhead
renardeinside Apr 1, 2026
75866b7
🐛 fix: deliver Future results in continuation and preserve contextvar…
renardeinside Apr 1, 2026
00c8925
increase drainer
renardeinside Apr 1, 2026
3d0686a
♻️ refactor: migrate to asyncio protocol with Rust HTTP primitives
renardeinside Apr 1, 2026
e38f3f0
🐛 fix: correct telemetry gauges, request_total lifecycle, and histogr…
renardeinside Apr 2, 2026
326b05e
🐛 fix: apply uvloop event loop policy in oneshot worker
renardeinside Apr 2, 2026
ae2e41d
🐛 fix: prevent active_requests counter leak on connection close
renardeinside Apr 2, 2026
a58498d
✨ feat: implement HTTP protocol compliance and performance from uvi-c…
renardeinside Apr 2, 2026
5efd1dc
🐛 fix: RAII request slot guard, pause_writing via multiple-pymethods,…
renardeinside Apr 2, 2026
4fd7bb9
🎨 style: remove stale comments, dead code, and old metric names
renardeinside Apr 2, 2026
c6b1bcf
🚀 perf: optimize continuation resume with inline fast path
renardeinside Apr 2, 2026
8bb2629
🐛 fix: add SQLite busy_timeout to eliminate write contention errors
renardeinside Apr 2, 2026
0accff5
✨ feat: add WebSocket support via wsproto bridge
renardeinside Apr 7, 2026
226384c
✅ test: add thorough WebSocket integration tests
renardeinside Apr 7, 2026
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
56 changes: 14 additions & 42 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ which = "7"
# instead of silent interpreter corruption in the multi-worker fork model.
# NOTE: "extension-module" is NOT set here — it is set only in crates/apx
# (the cdylib). Library crates need to link against libpython for `cargo test`.
pyo3 = { version = "0.28.2", features = ["macros"] }
pyo3 = { version = "0.28.2", features = ["macros", "multiple-pymethods"] }
pyo3-async-runtimes = { version = "0.28.0", features = ["tokio-runtime"] }
socket2 = "0.6.2"

Expand All @@ -142,6 +142,13 @@ http = "1"
bytes = "1"
http-body-util = "0.1"

# Framework: oneshot protocol primitives
httparse = "1.10"
httpdate = "1"
matchit = "0.8"
percent-encoding = "2"
uuid = { version = "1", features = ["v4"] }

[workspace.lints.rust]
unsafe_code = "deny"
warnings = "deny"
Expand Down
84 changes: 77 additions & 7 deletions crates/core/src/tracing_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,19 +181,22 @@ impl opentelemetry_sdk::logs::LogProcessor for TimestampProcessor {

/// Histogram boundaries for duration metrics recorded in seconds.
///
/// Aligned with OpenTelemetry HTTP semantic conventions for
/// `http.server.request.duration`.
/// Extends the OpenTelemetry HTTP semantic convention boundaries with
/// sub-millisecond resolution (100µs–2.5ms) so fast endpoints like health
/// probes get meaningful percentile estimates instead of landing in the
/// catch-all `[0, 5ms)` bucket.
const DURATION_SECONDS_BOUNDARIES: &[f64] = &[
0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0,
0.000_1, 0.000_25, 0.000_5, 0.001, 0.002_5, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5,
0.75, 1.0, 2.5, 5.0, 7.5, 10.0,
];

/// Histogram boundaries for duration metrics recorded in microseconds.
///
/// Covers sub-millisecond dispatch latencies (body collect, crossbeam send,
/// ASGI parse) up to 100ms outliers.
/// Covers sub-microsecond dispatch phases (scope build, send parse) through
/// 100ms handler latencies with ≤2.5× gaps between adjacent boundaries.
const DURATION_MICROSECONDS_BOUNDARIES: &[f64] = &[
1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1_000.0, 2_500.0, 5_000.0, 10_000.0, 50_000.0,
100_000.0,
1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1_000.0, 2_500.0, 5_000.0, 10_000.0, 25_000.0,
50_000.0, 100_000.0,
];

/// SDK View that assigns appropriate histogram bucket boundaries based on unit.
Expand Down Expand Up @@ -414,3 +417,70 @@ fn init_tracing_with_otel(

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

fn is_strictly_ascending(boundaries: &[f64]) -> bool {
boundaries.windows(2).all(|w| w[0] < w[1])
}

#[test]
fn seconds_boundaries_are_strictly_ascending() {
assert!(
is_strictly_ascending(DURATION_SECONDS_BOUNDARIES),
"seconds boundaries must be sorted with no duplicates"
);
}

#[test]
fn microseconds_boundaries_are_strictly_ascending() {
assert!(
is_strictly_ascending(DURATION_MICROSECONDS_BOUNDARIES),
"microseconds boundaries must be sorted with no duplicates"
);
}

#[test]
fn seconds_boundaries_resolve_sub_millisecond() {
let sub_ms_boundaries: Vec<f64> = DURATION_SECONDS_BOUNDARIES
.iter()
.copied()
.filter(|&b| b < 0.001)
.collect();
assert!(
sub_ms_boundaries.len() >= 3,
"expected ≥3 boundaries below 1ms for sub-millisecond resolution, \
got {sub_ms_boundaries:?}"
);
}

#[test]
fn microseconds_boundaries_max_gap_ratio() {
for w in DURATION_MICROSECONDS_BOUNDARIES.windows(2) {
let ratio = w[1] / w[0];
assert!(
ratio <= 5.1,
"gap between {:.0} and {:.0} is {:.1}×, exceeds 5× maximum",
w[0],
w[1],
ratio
);
}
}

#[test]
fn seconds_boundaries_max_gap_ratio() {
for w in DURATION_SECONDS_BOUNDARIES.windows(2) {
let ratio = w[1] / w[0];
assert!(
ratio <= 5.1,
"gap between {} and {} is {:.1}×, exceeds 5× maximum",
w[0],
w[1],
ratio
);
}
}
}
19 changes: 5 additions & 14 deletions crates/framework/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ jemalloc = ["dep:tikv-jemallocator"]
[dependencies]
mimalloc = { version = "0.1", optional = true, default-features = false }
pyo3.workspace = true
pyo3-async-runtimes.workspace = true
tokio.workspace = true
socket2.workspace = true
apx-common.workspace = true
tracing.workspace = true
tracing-opentelemetry.workspace = true
Expand All @@ -39,27 +37,20 @@ rmp-serde.workspace = true
tempfile.workspace = true
http.workspace = true
bytes.workspace = true
http-body = "1"
http-body-util.workspace = true
sysinfo.workspace = true
which.workspace = true
notify.workspace = true
futures-core.workspace = true
futures-util.workspace = true
crossbeam-channel.workspace = true
crossbeam-queue.workspace = true
tokio-stream.workspace = true
hyper = { version = "1", features = ["http1", "http2", "server"] }
hyper-util = { version = "0.1", features = ["tokio", "server-auto"] }
hyper-tungstenite = { workspace = true }
uuid = { version = "1", features = ["v4"] }
httparse.workspace = true
httpdate.workspace = true
matchit.workspace = true
percent-encoding.workspace = true
uuid.workspace = true

[target.'cfg(not(target_env = "msvc"))'.dependencies]
tikv-jemallocator = { version = "0.6", optional = true }

[dev-dependencies]
tempfile.workspace = true
reqwest = { workspace = true }
tokio-tungstenite = { workspace = true }
opentelemetry_sdk = { workspace = true, features = ["testing"] }
tracing-subscriber = { workspace = true }
Loading