Architecture
Adapter-driven design, crash safety, and horizontal scaling
Architecture
OqronKit is built on three core principles: adapter-driven architecture, crash safety, and native horizontal scaling.
Adapter-Driven Design
All persistence flows through three adapter interfaces — never direct database calls:
| Adapter | Purpose | In-Memory (dev) | Production |
|---|---|---|---|
IStorageEngine | Job records, history, schedules | Map<string, any> | PostgreSQL (JSONB+GIN) |
IBrokerEngine | Job signaling, claim/ack/nack | In-process queue | Redis Sorted Sets |
ILockAdapter | Distributed locking | Simple mutex | Redis Redlock / PG Advisory |
Switching from in-memory to Redis/Postgres requires zero code changes in your job definitions.
Dependency Injection & Module Context
OqronKit avoids global state. Within the core engine, dependencies (storage, broker, lock managers) are explicitly injected into each module engine via ModuleContext.
At the application level, if you need direct access to the resolved persistence layer (e.g. for manually running database operations or checks), you can access them via the persistence property on the Oqron instance after starting:
const oqron = new Oqron({ mode: "memory" });
await oqron.start();
const persistence = oqron.persistence;
persistence.runs; // RunStore
persistence.locks; // LockManager
persistence.cache; // CacheStore
persistence.ratelimit; // RateLimiterLeader Election
In multi-node deployments, OqronKit uses heartbeat-based leader election:
- All nodes compete for an internal leader election key
- The winner becomes the Master Poller — only it checks for due scheduled jobs
- Due tasks are dispatched via atomic locks to available workers
- If the leader crashes, the key expires within ~3 seconds and a standby node takes over
Environment Isolation
A production worker physically cannot claim development jobs. All keys are prefixed:
${project}:${environment}:${job_name}Job Dependencies (DAG)
Jobs can declare parent dependencies:
const extract1 = await extractQueue.add({ source: "users.csv" });
const extract2 = await extractQueue.add({ source: "orders.csv" });
// Child waits for both parents
const transform = await transformQueue.add(
{ mergeFrom: ["users", "orders"] },
{ dependsOn: [extract1.id, extract2.id] },
);Job Ordering Strategies
| Strategy | Behavior |
|---|---|
'fifo' | First-In, First-Out (default) |
'lifo' | Last-In, First-Out |
'priority' | Lower priority number = processed first |
Module System
OqronKit boots with all 8 built-in engines by default (cron, schedule, queue, worker, webhook, topic, ratelimit, cache). To disable specific modules, list them in disableModules:
import { Oqron } from "oqronkit";
const oqron = new Oqron({
mode: "redis",
redis: "redis://...",
disableModules: ["webhook"], // Exclude webhook engine from this process
});
await oqron.start();Each module is independently toggle-able. API servers can run without worker loops (since queues without handlers are publisher-only and consume zero CPU), and worker processes can explicitly disable scheduler loops.
Next Steps
- Crash Safety — Heartbeat locks and stall detection
- Adapters — Storage, Broker, and Lock adapter configuration