Skip to main content

Workraft

A simple, lightweight, database-only, worker library in Python

Workraft is a simple, lightweight, database-only worker library in Python with a MySQL database as the single source of truth.

Workraft adresses some of the pain points of using Celery, namely its incapability to handle long-running tasks and the fact that you need a message broker and that also that sometimes the workers aren't doing the tasks as you would expect them to.

All Workraft needs is a running MySQL database, which means you could in theory scale Workraft both vertically (get more database resources) and horizontally (get more databases). But so far, I've not tried scaling it that way.

Workraft is not the best in sitations where you need extreme precision and sub-second latency/wait times before a task is fetched and processed.

But if it's OK for you that your workers take at least 1 second to fetch your task AND you want a clear overview of your tasks and workers (using a database GUI for example), then Workraft is ideal for you.

Installation

Run

pip install workraft

Getting started

First, you need a running MySQL database. Then, for one time, you need to setup all the tables and events. For that, first you need to create a .env file and add some variables in there:

WK_DB_HOST="127.0.0.1"
WK_DB_PORT=3306
WK_DB_USER="root"
WK_DB_PASS="workraft"
WK_DB_NAME="workraft"

(Adjust to your settings of course)

Then, run:

python3 -m workraft setup_database_tables

This command will take the connection parameters from your .env file - but it's not strictly required. You can also pass it those as parameters. Here are the args of the setup_database_tables function:

def setup_database_tables(
    db_host: str = "127.0.0.1",
    db_port: int = 3306,
    db_user: str = "root",
    db_name: str = "workraft",
    db_password: str | None = None,
    read_from_env: bool = True,
    drop_tables: bool = False,
):
...

E.g.:

python3 -m workraft setup_database_tables --read_from_env=False --db_password=test --drop_tables=True

Then, to use workers, implement your worker code:

import asyncio
import random
import time
from multiprocessing import Pool

from loguru import logger
from workraft.core import Workraft
from workraft.db import get_db_config


workraft = Workraft()

global_counter = 0


@workraft.setup_handler()
def setup_handler():
    global global_counter
    global_counter = 1000
    logger.info("Setting up the worker!")


@workraft.task("simple_task")
def simple_task(a: int, b: int, c: int) -> int:
    global global_counter
    global_counter += 1
    time.sleep(1)
    logger.info(global_counter)
    # raise ValueError("Random error!")
    return a + b + c


@workraft.postrun_handler()
def postrun_handler(task_id, task_name, result, status):
    logger.info(
        f"Postrun handler called for {task_id} and {task_name}! Got result: {result} and status {status}"
    )


def get_random_number():
    logger.info("Getting random number...")
    time.sleep(random.randint(5, 10))
    return random.randint(1, 100)


@workraft.task("complex_task_1")
def parallel_task():
    num_processes = 8
    n_random_numbers = 20
    with Pool(processes=num_processes) as pool:
        pool.starmap(
            get_random_number,
            [() for _ in range(n_random_numbers)],
        )


async def main():
    n_tasks = 1

    for _ in range(n_tasks):
        a = random.randint(1, 100)
        b = random.randint(1, 100)
        c = random.randint(1, 100)

        workraft.send_task_sync(
            "simple_task",
            [a, b],
            task_kwargs={"c": c},
            retry_on_failure=True,
            db_config=get_db_config(),
        )
        # But you could also just directly input the data into the database


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

To run a worker then, you would run:

python3 -m workraft peon --workraft_path=example.workraft --worker-id=test1

If you then execute example.py, you will add a task into the queue and then see as the worker processes that task.

Configuration

If you have a workraft.config.json file, those settings will be used when setting up the tables as well as other, worker-related settings:

{
  "DB_PEON_HEARTBEAT_INTERVAL": 5,
  "DB_POLLING_INTERVAL": 5,
  "DB_SETUP_BACKOFF_MULTIPLIER_SECONDS": 30,
  "DB_SETUP_BACKOFF_MAX_SECONDS": 3600,
  "DB_SETUP_RUN_SELF_CORRECT_TASK_INTERVAL": 10,
  "DB_SETUP_RUN_REOPEN_FAILED_TASK_INTERVAL": 10,
  "DB_SETUP_WAIT_TIME_BEFORE_WORKER_DECLARED_DEAD": 60,
  "DB_SETUP_CHECK_DEAD_WORKER_INTERVAL": 10
}

DB_PEON_HEARTBEAT_INTERVAL: This is the interval at which the peon sends a heartbeat to the database. DB_POLLING_INTERVAL: This is the interval at which the peon polls the database for new tasks. DB_SETUP_BACKOFF_MULTIPLIER_SECONDS: This is the multiplier for the exponential backoff algorithm. DB_SETUP_BACKOFF_MAX_SECONDS: This is the maximum backoff time for the exponential backoff algorithm. DB_SETUP_RUN_SELF_CORRECT_TASK_INTERVAL: This is the interval at which the database runs the self-correct task. DB_SETUP_RUN_REOPEN_FAILED_TASK_INTERVAL: This is the interval at which the database reopens failed tasks. DB_SETUP_WAIT_TIME_BEFORE_WORKER_DECLARED_DEAD: This is the time the database waits before declaring a worker dead. DB_SETUP_CHECK_DEAD_WORKER_INTERVAL: This is the interval at which the database checks for dead workers.

The configs with DB_SETUP_ in the beginning are only used during the setup of the database. In other words, they are only used once. The first two are using during runtime.

Metadata

Release files for workraft 0.5.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for workraft 0.5.5
File Size Uploaded
workraft-0.5.5.tar.gz 51.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for workraft 0.5.5
File Interpreter ABI Platform
workraft-0.5.5-py3-none-any.whl Python 3 none any Details

Total release size: 68.3 kB

Release files / workraft-0.5.5.tar.gz

Download URL workraft-0.5.5.tar.gz
Size 51.1 kB
Tags Source
SHA-256 checksum
How to use checksums
625e0440ea9a4df3e747a7411611647ba49586f095153b79e96ca59b99c6a946
BLAKE2b-256 checksum
How to use checksums
265055189e397c909faf0ad511b5b6acf094f6e1d1a103a64c1a3fe235f93be1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-httpx/0.27.0

Release files / workraft-0.5.5-py3-none-any.whl

Download URL workraft-0.5.5-py3-none-any.whl
Size 17.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
583a86ad7fb7cc0d75a0cce41ea8ec3e5d335147f0b811d479aeeeb1793da3bc
BLAKE2b-256 checksum
How to use checksums
437c6bdf748c3d899aa4d96ca1e34328c57e857581082f5a54609cc28c22d36d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-httpx/0.27.0

Release history Release notifications | RSS feed

This release

0.5.5 This release

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.7

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page