Worker Pool Architecture
Cloudillo’s Worker Pool provides a three-tier priority thread pool for executing CPU-intensive and blocking operations. This system complements the async runtime by handling work that shouldn’t block async tasks, ensuring responsive performance even under heavy computational load.
Architecture Overview
Three-Tier Priority System
Default Configuration:
- High priority: 1 dedicated thread
- Medium priority: 2 threads (also process High)
- Low priority: 1 thread (also processes High + Medium)
- Total threads: 4
Priority Cascading
Higher priority workers process lower priority work when idle:
Benefits:
- High-priority work always has dedicated capacity
- Lower-priority work still gets done when system is idle
- Automatic load balancing
Core Components
WorkerPool Structure
The pool holds one flume sender per priority queue. Worker threads are spawned in new() and run for the process lifetime.
A job is just a boxed closure (Box<dyn FnOnce() + Send>); results are returned to the caller through a per-call oneshot channel, not stored in the job type.
Initialization
WorkerPool::new(n1, n2, n3) creates three unbounded flume channels and spawns OS threads:
n1threads listen on High only.n2threads listen on High + Medium.n3threads listen on High + Medium + Low.
Each thread shares the same receivers (via Arc), so any idle thread on a queue can pick up the next job.
Worker Loop
Every worker — regardless of how many queues it serves — runs the same loop:
The non-blocking priority pass ensures higher-priority work is always preferred; the Selector then parks the thread efficiently instead of busy-waiting.
API Usage
Each submission method takes a CPU-bound closure, sends it to a priority queue, and returns a Future that resolves with the closure’s result via a oneshot channel — so async callers offload CPU work without blocking the Tokio runtime.
| Method | Queue | Notes |
|---|---|---|
spawn(priority, f) |
chosen by Priority |
Generic entry point |
run_immed(f) |
High | “Run now” |
run(f) |
Medium | Default choice |
run_slow(f) |
Low | Background work |
try_run*(f) |
same as above | For closures returning ClResult<T>; flattens ClResult<ClResult<T>> |
If a worker panics, its oneshot sender is dropped and the awaiting future resolves to an Internal error rather than hanging.
Priority Guidelines
| Priority | Use For | Characteristics |
|---|---|---|
| High | Crypto during login, profile picture processing, real-time compression | User actively waiting, <100ms typical, <100 jobs/sec |
| Medium | Image variants for posts, file compression, data transforms | Background but needed soon, 100ms-10s, default choice |
| Low | Batch processing, cleanup, pre-generation, optimization | No user waiting, can be delayed indefinitely |
Integration Patterns
With Task Scheduler
Tasks often use the worker pool for CPU-intensive work following this pattern:
Configuration
The pool is constructed in server/src/main.rs with hardcoded thread counts:
This 1/2/1 split (4 threads total) suits mixed I/O and CPU workloads while leaving cores for the Tokio runtime. Adjusting it currently requires editing the source; there is no environment variable or runtime setting for worker counts.
See Also
- Task Scheduler - Task scheduling system that uses worker pool
- System Architecture - Overall system design
- File Storage - Image processing with worker pool
- Actions - Cryptographic operations with worker pool