Chronos is a distributed task scheduler built from scratch using Java (for the coordinator leader and Hashed Wheel Timer queue) and TypeScript (for the task executor worker nodes). It achieves ultra-precise timing execution at scale by replacing slow database polling loops with memory-efficient time-wheel algorithms.
In large systems, you often need to run tasks at specific times—for example, sending subscription renewal emails at midnight, executing weekly bank reports, or running cleaning scripts every 10 minutes.
Normally, a scheduler checks a database every second to see if a job is due (SELECT * FROM jobs WHERE trigger_time <= NOW()). When there are millions of tasks, this constant database querying crashes the database.
Chronos solves this using a two-part architecture:
-
The Hashed Wheel Timer (Java Coordinator): Think of this as a clock face with 512 ticks. Instead of checking a database, Chronos reads upcoming jobs and maps them onto specific slots on the clock wheel based on their trigger times. When the "second hand" ticks to a slot, Chronos dispatches all tasks in that slot instantly (
$O(1)$ efficiency). - Distributed Worker Nodes (TypeScript): Independent worker processes connect to the coordinator over TCP. When a task fires, the coordinator load-balances it, streaming the payload to a registered worker over a raw network connection.
- Fault Tolerance: If a worker node crashes mid-execution, the coordinator detects the connection drop, pulls the task back, and reschedules it for execution on another healthy worker (guaranteeing at-least-once task execution).
chronos/
├── worker/ <-- TypeScript task execution agent
│ ├── src/
│ │ └── worker.ts <-- TCP socket listener & frame decoder
│ ├── package.json
│ └── tsconfig.json
│
├── scheduler/ <-- Java task coordinator / scheduler
│ ├── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── chronos/
│ │ ├── SchedulerServer.java <-- TCP socket gateway & demo task pool
│ │ ├── TaskJob.java <-- Task entity inside the wheel
│ │ └── HashedWheelTimer.java <-- O(1) circular tick queue
│ └── pom.xml <-- Maven compilation properties
│
├── package.json <-- Monorepo workspaces coordinator
└── README.md <-- You are here!
- Java JDK 17 (Ensure
javaandjavacare in your PATH) - Node.js (v20+ or Bun runtime)
- Git
Follow these steps to start the scheduler, connect a worker node, and witness the real-time execution flow:
Compile the Java coordinator source files, and start the TCP scheduler:
cd scheduler
mkdir -p target/classes
javac -d target/classes src/main/java/com/chronos/*.java
java -cp target/classes com.chronos.SchedulerServerIn a second window, start the executor worker:
cd worker
npm run devYou will see the worker successfully register in Terminal 1!
🔌 Worker node registered: /127.0.0.1:52134 (Total active: 1)
- Once both processes are active, the coordinator starts scheduling demo tasks to run 2 seconds in the future:
⏰ Scheduled task [job-id-1] to run in 2 seconds. - Watch the Hashed Wheel Timer tick. After exactly 2 seconds, the timer fires, and the task payload streams to the worker:
🔀 Dispatched task [job-id-1] to worker: /127.0.0.1:52134 - Watch Terminal 2. The worker immediately receives the payload, executes the job operations, and logs completion:
📥 [Received] Task Triggered: [job-id-1]✅ [Success] Task [job-id-1] execution complete. Duration: 642ms
- Start the Java Coordinator.
- Open two separate terminals and start two workers:
npx tsx src/worker.tsin each. - Observe the coordinator round-robin load-balancing task dispatches between both worker nodes.
- Kill one of the workers (Ctrl+C) mid-run.
- Notice that the coordinator instantly catches the socket disconnection and securely handles job re-routing.