Ferrite / source
412 commits 17 branches MIT license

A deterministic task runtime for edge workers — cooperative scheduling, zero allocations in the hot path, and a replayable event log you can diff against production.

iris-kovac scheduler: drain microtasks before advancing the virtual clock c4f19ab 3 hours ago
scheduler.ts 148 lines4.2 KBTypeScript
41/** Cooperative scheduler. Never allocates inside `tick`. */
42export class Scheduler {
43  readonly #queue = new RingQueue<Task>(4096);
44  readonly #clock: VirtualClock;
45  #epoch = 0;
46
47  // c4f19ab — drain microtasks first, otherwise a task that
48  // resolves during tick() observes tomorrow's clock.
49  async tick(budgetMs = 8): Promise<number> {
50    await this.drainMicrotasks();
51    const deadline = this.#clock.now() + budgetMs;
52    let drained = 0;
53
54    while (this.#queue.size > 0) {
55      if (this.#clock.now() >= deadline) {
56        this.emit("budget-exhausted", { drained });
57        break;
58      }
59      const task = this.#queue.shift()!;
60      this.run(task, this.#epoch);
61      drained++;
62    }
63
64    this.#epoch++;
65    return drained;
66  }
67}

Recent history — packages/runtime

  • c4f19abiris-kovac — drain microtasks before advancing the virtual clock
  • 9de0177priya-anand — cover the starvation case found in staging shard 4
  • 40bb3c2tomas-erdely — swap linked list for a ring buffer (3.1× fanout)
  • 1f7a55eiris-kovac — re-export TaskHandle from the package root
  • 8c23d90dep-bot — bump @ferrite/codec to 2.4.1

README.md

Ferrite is the scheduling core behind Nightshift's edge platform. It runs the same code on a laptop, in CI, and on 340 points of presence — and it produces byte-identical event logs in all three.

Why determinism

If a request misbehaves in production, you shouldn't need to reproduce the weather. Ferrite records every scheduling decision into a replay log; ferrite inspect --replay reconstructs the exact interleaving locally.

Install

npm i @ferrite/runtime@2.9.0-rc.3

Design constraints

  • No allocations inside tick() — the ring buffer is sized at construction.
  • Wall-clock reads are forbidden below the VirtualClock boundary.
  • Every emitted event carries an epoch, so logs sort without timestamps.
  • Public API changes require a fixture diff in __fixtures__.