Repository navigation
v0.4.0
Composable HTTP release. Same v0.3.0 reactor underneath, one unified entry point (srv.serve(handler, num_workers=N)), and thread-per-core scaling that hits a measured 257,461 req/s at 4 workers on Linux EPYC: 4.4x linear, 3.6x nginx (1 worker), 7x Go net/http.
Headline: multicore reactor
var srv = HttpServer.bind(SocketAddr.localhost(8080))
srv.serve(router^, num_workers=default_worker_count())One call. Same function as the single-threaded reactor, one extra argument. num_workers=1 (the default) keeps the v0.3.x hot path; num_workers >= 2 binds N SO_REUSEPORT listeners on N pthread workers via flare.runtime.scheduler.Scheduler, with optional per-core pinning on Linux (pin_cores=True by default, no-op on macOS). Shared-nothing per-connection ownership, no locks on the hot path.
Linux multicore benchmark (AWS EPYC 7R32, wrk -t8 -c256 -d30s, 5-run median, stdev <= 3%)
| Server | Req/s (median) | p50 | p99 | vs Go net/http |
vs 1-thread flare |
|---|---|---|---|---|---|
flare_mc (4 workers, pinned) |
257,461 | 0.97 ms | 1.58 ms | 7.03x | 4.38x |
| nginx (1 worker) | 70,592 | 3.53 ms | 4.23 ms | 1.93x | 1.20x |
| flare (single-threaded) | 58,731 | — | 4.64 ms | 1.60x | 1.00x |
Go net/http (GOMAXPROCS=1) |
36,617 | — | — | 1.00x | 0.62x |
Near-linear scaling: each of the four pinned workers runs its own un-contended reactor. p99 tail latency collapses from 4.64 ms (single-thread flare under 256 concurrent connections) to 1.58 ms (multicore), and the flare-vs-Go gap widens from ~2x (single-thread) to 7x (4 workers) because Go net/http + netpoll overhead grows faster with concurrency on a slower EPYC core than flare's SO_REUSEPORT sharding does.
macOS loopback saturates at ~140K req/s regardless of worker count because wrk and the server share one client-side CPU; use Linux for multicore numbers.
Single-thread performance (carried over from v0.3.0)
- Linux (AWS EPYC 7R32): on par with single-worker nginx, about 1.96x Go
net/http(GOMAXPROCS=1). - macOS (Apple M-series): about 1.10x Go
net/http(GOMAXPROCS=1).
Same reactor, same kqueue/epoll code path, no throughput regression. Intra-platform ratios are the invariant across Mojo nightlies.
Handlers, routing, state
Handlertrait with a blanket impl, so every existingdef(Request) raises -> Responsehandler is already aHandler.Routerwithget/post/put/patch/delete, path params (/users/:id), wildcard tails (/files/*), auto 405 withAllow:+ 404.App[S: Copyable, H: Handler]+State[T]view for handing application state to middleware layers.- Middleware is a
Handlerthat wraps anotherHandler.[Inner: Handler]generic structs, so composition monomorphises into one direct call chain at compile time. Seeexamples/18_middleware.mojofor a five-layer production pipeline (RequestID->Logger->Timing->Recover->RequireAuth->Router). req.param(name),req.has_param(name),req.has_params(). Path params live on a lazily-allocated pointer so handlers that never route pay zeroDictallocation per request.
Compile-time server wiring
HttpServer.serve[H: Handler & Copyable](handler, num_workers, pin_cores): unified entry point, monomorphised against the concrete handler type.HttpServer.serve_comptime[handler, config]takes both the handler andServerConfigas comptime parameters and runs every invariant (max_body_size >= max_header_size,max_keepalive_requests >= 1,idle_timeout_ms >= 0, etc.) throughconstrained[...]. Misconfigured servers fail the build, not the first request.FnHandlerCT[F]zero-size trait adapter for binding a function into the handler surface at compile time.
What's in
- 460 tests, 18 runnable examples, 16 fuzz harnesses, over 1M aggregate fuzz runs, zero known crashes
- New examples:
15_router,16_state,17_multicore,18_middleware - New test tasks:
test-handler,test-router,test-server-handler,test-app-state,test-serve-comptime,test-thread-ffi,test-reuseport,test-scheduler,test-server-multicore - New fuzz harnesses:
fuzz_router_paths(200K runs),fuzz_scheduler_shutdown(10K runs)
What's deferred
- Typed extractors (
Path[T],Json[T],Query[T],Header[T]), comptime route trie, pre-encoded literal responses, SIMD-width-parametric header scanner: v0.4.1 - HTTP/2 (
h2over TLS, RFC 9113 + 7541): v0.4.2 - Streaming bodies (
Body+ChunkSourcetraits): v0.4.3+
Install
[dependencies]
flare = { git = "https://github.com/ehsanmok/flare.git", tag = "v0.4.0" }Pinned to Mojo 0.26.3.0.dev2026042005 + json v0.1.3.