Offload functions to worker threads with shared memory primitives for Node.js.
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs(serve): README section + changeset for moroutine/serve

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+74
+22
.changeset/server-threads.md
··· 1 + --- 2 + 'moroutine': minor 3 + --- 4 + 5 + New subpath `moroutine/serve`: fd-fanout server primitive 6 + 7 + Scale an ordinary Node `http.Server` across worker threads by passing raw socket fds from the main thread to workers. Each worker runs its own server; main just accepts connections and routes them. Includes `leastConns()` (default) and `roundRobin()` balance strategies, graceful shutdown via `server.close()`, and composition with any framework that exposes an `http.Server` (express, fastify, etc.). POSIX-only. 8 + 9 + ```ts 10 + // worker 11 + export const runServer = mo(import.meta, async (...args: ListenArgs): Promise<void> => { 12 + const srv = createServer((req, res) => { 13 + res.end('hi'); 14 + }); 15 + await listen(srv, ...args); 16 + }); 17 + 18 + // main 19 + await using run = workers(4); 20 + using threads = serverThreads(run.workers, server); 21 + await run(threads.map(([w, args]) => assign(w, runServer(...args)))); 22 + ```
+52
README.md
··· 366 366 console.log(pos.load()); // { x: 1, y: 1 } 367 367 ``` 368 368 369 + ## Server Threads 370 + 371 + Scale a Node HTTP server across worker threads by fanning out socket fds. Main accepts TCP connections; workers each run their own `http.Server` and handle requests. Imported from the `moroutine/serve` subpath. 372 + 373 + ```ts 374 + // app-server.ts 375 + import { createServer } from 'node:http'; 376 + import { mo } from 'moroutine'; 377 + import { listen, type ListenArgs } from 'moroutine/serve'; 378 + 379 + export const runServer = mo(import.meta, async (...args: ListenArgs): Promise<void> => { 380 + const srv = createServer((req, res) => { 381 + res.writeHead(200); 382 + res.end('hello'); 383 + }); 384 + await listen(srv, ...args); 385 + }); 386 + ``` 387 + 388 + ```ts 389 + // main.ts 390 + import { createServer } from 'node:net'; 391 + import { workers, assign } from 'moroutine'; 392 + import { serverThreads } from 'moroutine/serve'; 393 + import { runServer } from './app-server.ts'; 394 + 395 + const server = createServer(); 396 + server.listen(3000); 397 + process.on('SIGINT', () => server.close()); 398 + 399 + { 400 + await using run = workers(4); 401 + using threads = serverThreads(run.workers, server); 402 + await run(threads.map(([w, args]) => assign(w, runServer(...args)))); 403 + } 404 + ``` 405 + 406 + Connection routing defaults to `leastConns()`; pass `{ balance: roundRobin() }` for simple fan-out. TLS termination runs on workers (`https.createServer` / `tls.createServer` inside your moroutine); the main-thread listener stays raw TCP. POSIX-only (Linux, macOS). Frameworks that expose an underlying `http.Server` compose without glue: 407 + 408 + ```ts 409 + // express 410 + const srv = createServer(express()); 411 + await listen(srv, ...args); 412 + 413 + // fastify 414 + const app = fastify(); 415 + await app.ready(); 416 + await listen(app.server, ...args); 417 + ``` 418 + 419 + Graceful shutdown: call `server.close()` on the main listener; the cascade closes worker channels, drains in-flight requests (up to `drainTimeout`, default 30s), and resolves the moroutines. Combine with `await using run = workers(...)` for full pool teardown. 420 + 369 421 ## Streaming 370 422 371 423 ### Streaming Moroutines