3.2 KiB
3.2 KiB
CPU and resource limiting
-
Tokio worker threads
-
Decide thread policy:
- Option A: set TOKIO_WORKER_THREADS in the environment for deployments.
- Option B: build a custom runtime with tokio::runtime::Builder::new_multi_thread().worker_threads(n).
-
Document your default policy (e.g., 50% of physical cores).
-
-
Concurrency guard for CPU-heavy tasks
- Create a global tokio::sync::Semaphore with N permits (N = allowed concurrent heavy tasks).
- Acquire a permit before invoking heavy module operations; release automatically on drop.
- Expose the semaphore in app state so handlers/jobs can share it.
-
HTTP backpressure and rate limiting (if using API)
- Add tower::limit::ConcurrencyLimitLayer to cap in-flight requests.
- Add tower::limit::RateLimitLayer or request-size/timeouts as needed.
- Optionally add tower::timeout::TimeoutLayer to bound handler latency.
-
Stronger isolation (optional, later)
- Evaluate running certain modules as separate processes for strict CPU caps.
- Use cgroups v2 (Linux) or Job Objects (Windows) to bound CPU/memory per process.
- Reuse the same JSON interface over IPC (e.g., stdio or a local socket).
Build and run
-
Build all crates
- Run: cargo build --workspace
-
Build each plugin as cdylib
- Example: cd crates/modules/summarizer && cargo build --release
-
Stage plugin libraries for the host to find
-
Create a modules directory the daemon will read, e.g. target/modules
-
Copy the built artifact into that directory:
- Linux: copy target/release/libsummarizer.so -> target/modules/libsummarizer.so
- macOS: copy target/release/libsummarizer.dylib -> target/modules/libsummarizer.dylib
- Windows: copy target/release/summarizer.dll -> target/modules/summarizer.dll
-
Alternatively set OWLY_MODULES_DIR to your chosen directory.
-
-
Run the daemon
- cargo run -p owly-news
- Optionally set:
- OWLY_MODULES_DIR=/absolute/path/to/modules
- TOKIO_WORKER_THREADS=N
Wire into the API
-
Share ModuleHost in app state
- Create a struct AppState { host: Arc, cpu_sem: Arc , ... }.
- Add AppState to Axum with .with_state(state).
-
In a handler (example: POST /summarize)
- Parse payload as JSON.
- Acquire a permit from cpu_sem before heavy work.
- host.get("summarizer").await? to lazily load the module.
- Call module.invoke_json("summarize", payload_value)?.
- Map success to 200 with JSON; map errors to appropriate status codes.
-
Error handling and observability
- Use thiserror/anyhow to classify operational vs. client errors.
- Add tracing spans around module loading and invocation; include module name and op.
- Return structured error JSON when module reports an error.
-
Configuration
- Decide env vars and defaults: OWLY_MODULES_DIR, TOKIO_WORKER_THREADS, concurrency permits, rate limits.
- Optionally add a config file (toml) and load via figment or config crate.
-
Health and lifecycle
-
Add a /health route that checks:
- Tokio is responsive.
- Optional: preflight-check that required modules are present (or skip to keep lazy).
-
Graceful shutdown: listen for SIGINT/SIGTERM and drain in-flight requests before exit.
-