Skip to main content

A reliable distributed scheduler with pluggable storage backends

Project description

A reliable distributed scheduler with pluggable storage backends for Async Python.

  • Free software: MIT license

Installation

Minimal installation (just SQLite persistence):

pip install pyncette

Full installation (all the backends and Prometheus metrics exporter):

pip install pyncette[all]

You can also install the in-development version with:

pip install https://github.com/tibordp/pyncette/archive/master.zip

Documentation

https://pyncette.readthedocs.io

Usage example

Simple in-memory scheduler (does not persist state)

from pyncette import Pyncette, Context

app = Pyncette()

@app.task(schedule='* * * * *')
async def foo(context: Context):
    print('This will run every minute')

if __name__ == '__main__':
    app.main()

Persistent distributed cron using Redis (coordinates execution with parallel instances and survives restarts)

from pyncette import Pyncette, Context
from pyncette.redis import redis_repository

app = Pyncette(repository_factory=redis_repository, redis_url='redis://localhost')

@app.task(schedule='* * * * * */10')
async def foo(context: Context):
    print('This will run every 10 seconds')

if __name__ == '__main__':
    app.main()

See the examples directory for more examples of usage.

Use cases

Pyncette is designed for reliable (at-least-once or at-most-once) execution of recurring tasks (think cronjobs) whose lifecycles are managed dynamically, but can work effectively for non-reccuring tasks too.

Example use cases:

  • You want to perform a database backup every day at noon

  • You want a report to be generated daily for your 10M users at the time of their choosing

  • You want currency conversion rates to be refreshed every 10 seconds

  • You want to allow your users to schedule non-recurring emails to be sent at an arbitrary time in the future

Pyncette might not be a good fit if:

  • You want your tasks to be scheduled to run (ideally) once as soon as possible. It is doable, but you will be better served by a general purpose reliable queue like RabbitMQ or Amazon SQS.

  • You need tasks to execute at sub-second intervals with low jitter. Pyncette coordinates execution on a per task-instance basis and this corrdination can add overhead and jitter.

Supported backends

Pyncette comes with an implementation for the following backends (used for persistence and coordination) out-of-the-box:

  • SQLite (included)

  • Redis (pip install pyncette[redis])

  • PostgreSQL (pip install pyncette[postgres])

  • MySQL 8.0+ (pip install pyncette[mysql])

  • Amazon DynamoDB (pip install pyncette[dynamodb])

Pyncette imposes few requirements on the underlying datastores, so it can be extended to support other databases or custom storage formats / integrations with existing systems. For best results, the backend needs to provide:

  • Some sort of serialization mechanism, e.g. traditional transactions, atomic stored procedures or compare-and-swap

  • Efficient range queries over a secondary index, which can be eventually consistent

Development

To run integration tests you will need Redis, PostgreSQL, MySQL and Localstack (for DynamoDB) running locally.

To run the all tests run:

tox

Alternatively, there is a Docker Compose environment that will set up all the backends so that integration tests can run seamlessly:

docker-compose up -d
docker-compose run --rm shell
tox

To run just the unit tests (excluding integration tests):

tox -e py310  # or your Python version of choice

Note, to combine the coverage data from all the tox environments run:

Windows

set PYTEST_ADDOPTS=--cov-append
tox

Other

PYTEST_ADDOPTS=--cov-append tox

Changelog

0.10.1 (2023-05-09)

  • Include missing lua files in the built wheel

0.10.0 (2023-05-08)

  • Drop support for Python 3.7

  • Add support for Python 3.11

  • Modernize Python package structure and linters

  • Fix a few bugs and type annotations

0.8.1 (2021-04-08)

  • Improve performance for calculation of the next execution time

  • Add ability for repositories to pass a pagination token

  • Add add_to_context() to inject static data to context

  • Clean up documentation and add additional examples

0.8.0 (2021-04-05)

  • Added Amazon DynamoDB backend

  • Added MySQL backend

  • Added support for partitioned dynamic tasks

0.7.0 (2021-03-31)

  • Added support for automatic and cooperative lease heartbeating

  • PostgreSQL backend can now skip automatic table creation

  • Improved signal handling

  • CI: Add Codecov integration

  • Devenv: Run integration tests in Docker Compose

0.6.1 (2020-04-02)

  • Optimize the task querying on Postgres backend

  • Fix: ensure that there are no name colissions between concrete instances of different dynamic tasks

  • Improve fairness of polling tasks under high contention.

0.6.0 (2020-03-31)

  • Added PostgreSQL backend

  • Added Sqlite backend and made it the default (replacing InMemoryRepository)

  • Refactored test suite to cover all conformance/integration tests on all backends

  • Refactored Redis backend, simplifying the Lua scripts and improving exceptional case handling (e.g. tasks disappearing between query and poll)

  • Main loop only sleeps for the rest of remaining poll_interval before next tick instead of the full amount

  • General bug fixes, documentation changes, clean up

0.5.0 (2020-03-27)

  • Fixes bug where a locked dynamic task could be executed again on next tick.

  • poll_task is now reentrant with regards to locking. If the lease passed in matches the lease on the task, it behaves as though it were unlocked.

0.4.0 (2020-02-16)

  • Middleware support and optional metrics via Prometheus

  • Improved the graceful shutdown behavior

  • Task instance and application context are now available in the task context

  • Breaking change: dynamic task parameters are now accessed via context.args[‘name’] instead of context.name

  • Improved examples, documentation and packaging

0.2.0 (2020-01-08)

  • Timezone support

  • More efficient poling when Redis backend is used

0.1.1 (2020-01-08)

  • First release that actually works.

0.0.0 (2019-12-31)

  • First release on PyPI.

Project details


Download files

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

Source Distribution

pyncette-0.10.1.tar.gz (32.9 kB view details)

Uploaded Source

Built Distribution

pyncette-0.10.1-py3-none-any.whl (37.2 kB view details)

Uploaded Python 3

File details

Details for the file pyncette-0.10.1.tar.gz.

File metadata

  • Download URL: pyncette-0.10.1.tar.gz
  • Upload date:
  • Size: 32.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.11

File hashes

Hashes for pyncette-0.10.1.tar.gz
Algorithm Hash digest
SHA256 0b1a65c1f30e6620f8c8abc8d0b6017db1a18b77322fe833d29cbe2f151753b4
MD5 a49aaa3562364bb5f702abceb08917da
BLAKE2b-256 153f798712e5acddecb06010e8489781266b1c5f87911783bfef19f18f350457

See more details on using hashes here.

File details

Details for the file pyncette-0.10.1-py3-none-any.whl.

File metadata

  • Download URL: pyncette-0.10.1-py3-none-any.whl
  • Upload date:
  • Size: 37.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.11

File hashes

Hashes for pyncette-0.10.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e303b918f026d991b802afeac548a4393def4e0628dedaaa70d1df24d6ffa6b3
MD5 740f3cb1a05361172266303c797c0065
BLAKE2b-256 7fb2ec5f228feef9d564f12f98e0cec7b5fa1b0c42d4783f7ea26d25023b6359

See more details on using hashes here.

Supported by

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