docket#
distributed background task system for zig 0.16.
a zig port of chrisguidry/docket
(PyPI: pydocket), which is the original work of
Chris Guidry. credit for the design, the
API shape, the Lua scripts, and the entire conceptual model goes to him —
this repo is just a translation. everything good here came from pydocket;
mistakes and inadequacies in the translation are mine.
schedules immediate and future work onto a redis-backed queue, with workers that consume the queue and execute tasks under explicit io discipline.
status: v0 in progress — skeleton compiles, composite ops not yet wired. will graduate as prefect-server adopts it for background services.
quick sketch#
const docket = @import("docket");
const GreetParams = struct { greeting: []const u8, name: []const u8 };
fn greet(io: std.Io, ctx: *docket.TaskContext, p: GreetParams) !void {
try io.sleep(.{ .nanoseconds = std.time.ns_per_s }, .awake);
std.debug.print("{s}, {s}!\n", .{ p.greeting, p.name });
}
pub fn main() !void {
var threaded = std.Io.Threaded.init(allocator, .{});
const io = threaded.io();
var d = try docket.Docket.init(allocator, io, .{ .url = "redis://localhost:6379/0" });
defer d.deinit();
try d.register("greet", greet, GreetParams, .{});
_ = try d.add("greet", GreetParams{ .greeting = "Hello", .name = "Jane" }, .{});
var w = try docket.Worker.init(allocator, io, &d, .{ .name = "w1", .concurrency = 4 });
defer w.deinit();
try w.runForever();
}
scope#
v0 (in progress):
- task registration with comptime-erased Params (JSON over the wire)
add(name, params, .{ .when_micros = ... })— immediate + futurePerpetualtasks (interval-based auto-rescheduling)- worker
runForeverwith concurrency limit, heartbeat, clean Io-cancellation shutdown - multiple workers consuming the same queue (horizontal scale)
- ack on success / re-park on failure (simplest retry semantics)
- redis backend via tangled.org/zzstoatzz.io/redis
v0.2:
- embedded redis-compatible in-process server (in-memory store + liblua via FFI)
via
memory://URLs, for testing without a real redis
deferred:
- cron expressions
- explicit retry policies (Retry, ExponentialRetry)
- replace, cancel-by-key, strikes
- result storage
- DI machinery (Depends, Shared, TaskLogger, Cooldown/Debounce/RateLimit/ConcurrencyLimit)
- agenda batch helper
design notes#
- task signature: every user task is
fn(Io, *TaskContext, Params) !void. three positional args, fixed. io flows explicitly. - task config struct, not annotations: cross-cutting concerns
(perpetual, future retry, future timeout) are registration-time options on a
config struct — no python-style
Annotated[T, ...]DI. - one io throughout: docket takes
ioat init and threads it through scheduling, scripts, worker loops, and into user tasks. zlay-style discipline. - backend = composite ops: each atomic op (schedule, promote_due, ack_or_repark, heartbeat) is a Lua script. redis backend EVALs them remotely; the future embedded backend evaluates them locally via liblua against an in-memory store. one source of truth for atomicity.
build#
zig build # build static lib
zig build test # run unit tests
requires zig 0.16. depends on tangled.org/zzstoatzz.io/redis and
codeberg.org/r4gus/uuid-zig@0.5.0.