Component Deep Dive: src/scheduler.rs

The scheduler module currently implements a minimalist thread-pool abstraction that will eventually coordinate background work such as WAL flushing, compaction, and asynchronous IO. Even in its prototype form, it demonstrates how tasks are queued and executed across worker threads.

Source Layout

src/scheduler.rs
34  struct ThreadPool {
        workers: Vec<Worker>,
        sender: Option<mpsc::Sender<Job>>,
     }
39  type Job = Box<dyn FnOnce() + Send + 'static>;
41  struct Worker { thread: Option<thread::JoinHandle<()>> }

45  impl ThreadPool {
        fn new(size: usize) -> Self { … }
        fn execute<F>(&self, f: F)
     }

82  impl Drop for ThreadPool { … } // graceful shutdown

Architecture

┌────────────────────────────────────── ThreadPool ──────────────────────────────────────┐
│ workers: Vec<Worker>       sender: Option<mpsc::Sender<Job>>                           │
│                                                                                       │
│            ┌───────────────┐       ┌───────────────┐        ┌───────────────┐          │
│            │  Worker[0]    │       │  Worker[1]    │  ...   │ Worker[size-1]│          │
│            │  thread: Join │       │  thread: Join │        │  thread: Join │          │
│            └──────┬────────┘       └──────┬────────┘        └──────┬────────┘          │
│                   │                       │                         │                  │
│                   ▼ mpsc::Receiver<Job> (shared via Arc<Mutex<_>>)   ▼                  │
│                               │                                        │               │
│                Jobs queued via ThreadPool::execute(job)                │               │
└────────────────────────────────────────────────────────────────────────────────────────┘

Construction (ThreadPool::new)

fn new(size)
   ├─ create mpsc::channel<Job>() → (sender, receiver)
   ├─ wrap receiver in Arc<Mutex<_>> to share between workers
   ├─ spawn `size` worker threads:
         loop {
             job = receiver.lock().unwrap().recv()
             match job {
                Ok(job) => job(),
                Err(_) => break (channel closed)
             }
         }
   └─ store JoinHandle in Worker.thread

Workers run indefinitely until the sender is dropped, at which point recv() yields Err, breaking the loop and allowing threads to exit gracefully.

Task Submission (ThreadPool::execute)

fn execute<F>(f: F)
   where F: FnOnce() + Send + 'static

   └─ sender.send(Box::new(f)).unwrap()

Jobs are heap-allocated via Box<dyn FnOnce()> and sent through the MPSC channel for workers to pick up.

ASCII Timeline

ThreadPool::execute(task A) ──────► sender
ThreadPool::execute(task B) ──────► sender
                                      │
                                      ▼
                               mpsc::Receiver (shared)
                                      │
        Worker 0 thread ── recv() ────┤──► executes task A
        Worker 1 thread ── recv() ────┘
                                      │
        Worker 1 thread ── recv() ───────► executes task B

Shutdown (Drop impl)

impl Drop for ThreadPool
   ├─ sender.take() → drop sender end → closes channel
   ├─ iterate workers:
         if thread handle exists:
             thread.join().unwrap()

Dropping the pool cleanly waits for in-flight tasks to complete and avoids detached threads continuing after shutdown.

Future Role in the System

While the module is currently unused, comments outline future responsibilities:

  • Reserve thread subsets for specific subsystems (WAL, operations, compaction).
  • Possibly perform CPU affinity or workload partitioning (deferred due to hypervisor scheduling concerns on shared cloud hosts).
  • Provide a scheduler capable of handling connection stickiness to preserve operation ordering.

Enhancements to Consider

  • Task Priorities: Different queues for latency-sensitive operations vs. background maintenance.
  • Dynamic Sizing: Adjust number of workers based on system load.
  • Work Stealing: Reduce contention when many tasks are submitted simultaneously.
  • Metrics Hooks: Track queue depth, task latency, and worker utilization.

Even as a prototype, the current structure provides a reliable building block: a scoped thread pool with graceful shutdown semantics.