Skip to main content

cairnq (Python)

SQLite-first, cross-language, storage-centered durable task runtime. The Python SDK. API and worker processes coordinate only through a shared SQLite file.

from cairnq import CairnQ, Worker

# Worker side — a handler always receives (ctx, payload).
worker = Worker.sqlite("tasks.db")

@worker.task                          # registered under the function name, "create_summary"
async def create_summary(ctx, payload):
    await ctx.progress(0.2, "reading")
    return {"summary": await llm.summarize(payload["text"])}

worker.serve()                        # blocking entry point; Ctrl-C closes cleanly

# API side (in your server) — submit returns immediately.
tasks = CairnQ.sqlite("tasks.db")
task = await tasks.submit("create_summary", {"text": text}, key=f"summary:{aid}")

@worker.task defaults the task name to the function's name. Pass a string for a dotted/namespaced name: @worker.task("summary.create").

Synchronous call (submit + wait):

from cairnq import TaskFailed, TaskTimeout

try:
    result = await tasks.call("create_summary", {"text": text}, wait_timeout_ms=10_000)
except TaskFailed as e:
    log(e.code, e.message, e.retryable)   # envelope fields, no e.error["code"] digging
except TaskTimeout as e:
    # The task keeps running — resume the wait instead of submitting again.
    result = await tasks.wait(e.task_id, timeout_ms=60_000)
    # …or tasks.wait_by_key(key), from a process that never held the id.

Inspect a task by id/key without memorizing status strings:

task = await tasks.get_by_key(key)
if task and task.succeeded:        # also .failed / .canceled / .running / .queued / .is_terminal
    use(task.result)

Optionally define a task once and share the symbol across both ends — the name lives in one place (no string drift), and call() is typed as the task's result:

from cairnq import TaskDef

summarize = TaskDef[dict, dict]("summarize")

@worker.task(summarize)            # registered under summarize.name
async def handle(ctx, payload): ...

result = await tasks.call(summarize, {"text": text})

Opt-in: every API still accepts a plain name string (cross-language callers use it).

Running it in production

worker = Worker.sqlite(
    "tasks.db",
    concurrency=4,            # handler calls at once; use max_in_flight_bytes to bound memory
    retry_backoff_ms=1_000,   # window doubles per attempt, capped at retry_backoff_max_ms (30s),
                              # jittered over its upper half; 0 disables
    on_error=lambda exc, info: log.warning("worker survived %s: %s", info, exc),
)

# Nothing else deletes rows, so give the client a retention policy — it sweeps
# terminal tasks in bounded batches for as long as the handle is open. A
# per-status mapping keeps each status on its own clock (statuses left out are
# never swept): spent results go in minutes, failures stay for diagnosis.
tasks = CairnQ.sqlite(
    "tasks.db",
    retention=Retention(older_than_ms={"succeeded": 300_000, "failed": 7 * 24 * 3600_000}),
)

A sync handler (def, not async def) is dispatched to a thread, so the usual shape around a blocking GPU or HTTP call keeps the worker's event loop — and with it every lease this worker holds — alive:

@worker.task("score")
def score(ctx, payload):
    return {"score": model.forward(payload["image"])}  # blocking, off the loop

A handler that does real side effects should bail out when it loses its lease — the task is already running on another worker and nothing it writes is recorded:

@worker.task("long.job")
async def long_job(ctx, payload):
    for chunk in chunks:
        if ctx.lost_lease or await ctx.canceled():
            return
        await process(chunk)

Multi-host

Same code, Postgres instead of the file — CairnQ.postgres(dsn) / Worker.postgres(dsn). Install with pip install cairnq[postgres].

The protocol (schema + canonical SQL) lives in ../cairnq-protocol and is shared verbatim with the TypeScript SDK. See ../cairnq-protocol/PROTOCOL.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cairnq-0.8.0.tar.gz (135.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cairnq-0.8.0-py3-none-any.whl (113.0 kB view details)

Uploaded Python 3

File details

Details for the file cairnq-0.8.0.tar.gz.

File metadata

  • Download URL: cairnq-0.8.0.tar.gz
  • Upload date:
  • Size: 135.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cairnq-0.8.0.tar.gz
Algorithm Hash digest
SHA256 babb9251b642d203cd31915dc6f1dd679432e508ae2fae54d12d6797e980799e
MD5 c1ce6aa6c864fb1806cf438c5f16457e
BLAKE2b-256 3322168b332cdce5de04610e76aaa5d4a4ad7d1bd0737f43502a1b379d033b34

See more details on using hashes here.

Provenance

The following attestation bundles were made for cairnq-0.8.0.tar.gz:

Publisher: publish.yml on Jannchie/cairnq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cairnq-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: cairnq-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 113.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cairnq-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5314f87b1752e145c451b90549fede4dad00e77ef79018250f5d76f645661ab8
MD5 659490cb752f70c29329df41a3ad393e
BLAKE2b-256 a8481c678bfd8ca0c1a520cb86cb74577565531a729cdc2e6f6b1a8f7c409328

See more details on using hashes here.

Provenance

The following attestation bundles were made for cairnq-0.8.0-py3-none-any.whl:

Publisher: publish.yml on Jannchie/cairnq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

This release

0.8.0 This release

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page