Skip to main content

A reliable, lightweight PostgreSQL job queue. Uses FOR UPDATE SKIP LOCKED for atomic, concurrent task distribution without external dependencies.

Project description

slonq

slonq (from the Slavic slon, meaning elephant, + q for queue) is a reliable, lightweight PostgreSQL job queue.

This is the Python package for slonq, providing high-performance bindings to the Rust core.

Built on top of FOR UPDATE SKIP LOCKED, it provides atomic, concurrent task distribution without the need for an external message broker. It is designed for small to medium-scale projects that already use PostgreSQL and require an ‘at-least-once’ delivery guarantee without adding infrastructure complexity.

Features

  • Atomic concurrency: Utilises Postgres SKIP LOCKED to ensure multiple workers can dequeue tasks simultaneously without collisions.
  • Idempotency support: Built-in support for idempotency keys to prevent duplicate job insertion.
  • Delayed jobs: Schedule tasks to become visible at a specific time in the future.
  • Lease mechanism: Jobs are ‘leased’ to workers; if a worker crashes, the lease expires and the job becomes visible for retry.
  • High performance: Core logic implemented in Rust with Python bindings via PyO3.

Installation

pip install slonq

Quick start

The following example demonstrates the full lifecycle of a job, including enqueuing, dequeuing, heartbeat (touching), and final acknowledgement using asyncio.

import asyncio
from slonq import PgQueue

async def main():
    # 1. Initialise the connection to Postgres
    queue = await PgQueue.connect("postgres://postgres@localhost:5432/db")

    # 2. Enqueue a job with an idempotency key and a 5-minute (300s) lease
    await queue.enqueue(
        "unique-request-id-123", 
        {"type": "process_video", "path": "/uploads/vid.mp4"}, 
        300
    )

    # 3. Dequeue a batch of jobs for 'worker-01'
    # This atomically leases up to 5 jobs for 3 attempts each
    jobs = await queue.dequeue("worker-01", 5, 3)

    for job in jobs:
        lease = job.lease_key()
        if not lease:
            continue

        # 4. Extend the lease (Heartbeat)
        # If the task is taking longer than expected, 'touch' it to prevent others from picking it up
        await queue.touch(lease, 60)

        # 5. Success vs failure logic
        success = True  # Replace with actual processing logic

        if success:
            # 6. Acknowledge (mark as done)
            await queue.ack(lease)
        else:
            # 7. Negatively acknowledge (return to queue with a 10s delay)
            await queue.nack(lease, 3, delay_seconds=10.0)
    
    # 8. Batch Acknowledgement
    # If you have a list of processed jobs, you can ack them all at once
    # await queue.ack_batch(list_of_leases)

if __name__ == "__main__":
    asyncio.run(main())

How it works

slonq manages job states through a visibility-based lease system:

  1. Enqueue: A job is inserted with a visible_at timestamp.
  2. Dequeue: A worker selects a batch of jobs where visible_at <= now(). Using FOR UPDATE SKIP LOCKED, Postgres ensures no two workers grab the same job. The visible_at is then moved forward by the lease_timeout, effectively ‘locking’ the job for that worker.
  3. Heartbeat: If a job is long-running, the worker can call touch() to extend the lease.
  4. Ack/Nack:
    • Ack: Successfully processed jobs are marked as done.
    • Nack: If a worker fails, it can negatively acknowledge the job to make it visible for retry immediately (or with a delay).
  5. Recovery: If a worker crashes, the visible_at time eventually passes, and the job naturally becomes available for another worker to attempt.

Operational considerations

  • Database schema: You must run the provided migration SQL to create the necessary table and indices.
  • Visibility: Because slonq relies on now(), ensure your application servers and database server have synchronised clocks.
  • At-least-once delivery: slonq guarantees that a job will be delivered to at least one worker. Ensure your worker logic is idempotent.

Database schema

slonq requires a specific table structure and a custom ENUM type to manage job states. You can apply the following migration to your PostgreSQL instance:

-- Required ONLY for PostgreSQL versions prior to 13 to support gen_random_uuid()
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- Define the job lifecycle states
CREATE TYPE job_status AS ENUM ('pending', 'in_progress', 'done', 'failed');

CREATE TABLE jobs
(
   id                    BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
   idempotency_key       TEXT UNIQUE NOT NULL,
   status                job_status  NOT NULL DEFAULT 'pending',
   payload               JSONB       NOT NULL,

   -- 'Next eligible time' logic:
   -- Pending: When the job becomes available for its first attempt.
   -- In_progress: When the current lease is set to expire.
   visible_at            TIMESTAMPTZ NOT NULL DEFAULT now(),

   -- Tracks attempts to manage retry logic and dead-lettering
   attempt_count         INT         NOT NULL DEFAULT 0,

   -- Per-job lease duration (in seconds)
   lease_timeout_seconds INT         NOT NULL DEFAULT 60 CHECK (lease_timeout_seconds > 0),

   -- Unique lease identifier (regenerated on each dequeue)
   lease_id              UUID NULL,
   leased_by             TEXT NULL,

   created_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
   updated_at            TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Index for the dequeue operation (FOR UPDATE SKIP LOCKED)
CREATE INDEX idx_jobs_dequeue
   ON jobs (visible_at) WHERE status IN ('pending', 'in_progress');

License

This project is licensed under the MIT License.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64

slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl (1.2 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ i686

slonq-0.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

slonq-0.0.0-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl (1.2 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ i686

slonq-0.0.0-cp38-abi3-macosx_11_0_arm64.whl (981.3 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

slonq-0.0.0-cp38-abi3-macosx_10_12_x86_64.whl (1.0 MB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 1.1 MB
  • Tags: PyPy, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e02aca620998cde8bd77152273075d81723472dd90bfe506f240c32c612f9ce8
MD5 ebd72bf96c055e6bee67ce6f5a534bcc
BLAKE2b-256 6ddaf9855f57f18105be910b4baee63762023e46455c7307716b1bbb31094e52

See more details on using hashes here.

File details

Details for the file slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

  • Download URL: slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: PyPy, manylinux: glibc 2.17+ i686
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for slonq-0.0.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 6ecb2acad4c2f24b39124abb71c53eda1fcc27a9a394116b978cbaa0cf85d491
MD5 2785be715d37d8db3552e7a3cc299713
BLAKE2b-256 9ce170589ac636c4ad2f3195b14a047bd19813acb0f44dcc4681a33baa8b82ad

See more details on using hashes here.

File details

Details for the file slonq-0.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: slonq-0.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 1.1 MB
  • Tags: CPython 3.8+, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for slonq-0.0.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5cd0e75e145d81cd41ec77ffce1510279d4f8c82548dd287cbba4f4df8d24a2b
MD5 98318d50322d55643de6d662d7040b2a
BLAKE2b-256 41b8a59e826abcb11dd7da2786653493997e5e0a00d30f20484dd06631403356

See more details on using hashes here.

File details

Details for the file slonq-0.0.0-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

  • Download URL: slonq-0.0.0-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.8+, manylinux: glibc 2.17+ i686
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for slonq-0.0.0-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 c4d39ee0af2f45e68b4356416b1753058bd39c784d8203057a7cead6892e7335
MD5 1d9c3ea73add29b1fd84da3f9d1ca885
BLAKE2b-256 4d54613cf42bb66aa64fd45b825b2a67f9b49d28e9002d7dc023b48ea73e0f35

See more details on using hashes here.

File details

Details for the file slonq-0.0.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: slonq-0.0.0-cp38-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 981.3 kB
  • Tags: CPython 3.8+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for slonq-0.0.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6c760d48802fcc02d7e8328e689e8eca1f1361e97371746e6d870299d5fc54d5
MD5 cc338b83c7c641ee5c0f759e8c95e264
BLAKE2b-256 b8f8ff111220e60c6909dc8b35fed98a6b2dc833b12e24e25960b738ac6bae55

See more details on using hashes here.

File details

Details for the file slonq-0.0.0-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: slonq-0.0.0-cp38-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 1.0 MB
  • Tags: CPython 3.8+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for slonq-0.0.0-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d6afa8f4aa00e133a5355f962fb487be8473aafe8f1d6685a591b6a58652d5e5
MD5 5f47a5b9b388303c938cad24af46b343
BLAKE2b-256 1531bbdfa3bc19a772e8971fdb28a9d8b90d2696f58bc3a53f11f5282de66bc8

See more details on using hashes here.

Supported by

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