zig port of github.com/chrisguidry/docket
0

Configure Feed

Select the types of activity you want to include in your feed.

Zig 93.5%
Lua 6.5%
13 1 1

Clone this repository

https://tangled.org/zzstoatzz.io/docket https://tangled.org/did:plc:xt7oc2ck4z27ysad2coiropw
git@tangled.org:zzstoatzz.io/docket git@tangled.org:did:plc:xt7oc2ck4z27ysad2coiropw

For self-hosted knots, clone URLs may differ based on your setup.



README.md

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 + future
  • Perpetual tasks (interval-based auto-rescheduling)
  • worker runForever with 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 io at 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.