Changelog
1.1.0 (2025-10-20)
Features
Added MongoDB backend support (pip install pyncette[mongodb])
Uses Motor (async MongoDB driver) for efficient async operations
Implements optimistic locking using version-based CAS (Compare-And-Swap)
Compatible with MongoDB 4.2+
Supports all Pyncette features including dynamic tasks and partitioning
Added force parameter to schedule_task to control behavior when updating dynamic task instances
Default (force=False): Fails with TaskLockedException if task is locked, preserves sooner execution time to prevent schedule postponement
With force=True: Unconditionally overwrites task state including locks and schedule
Added new task state query API for inspecting both static and dynamic tasks
get_task(task, instance_name=None) - Query the state of any task (static or dynamic)
For static tasks: get_task(my_task)
For dynamic tasks: get_task(template, "instance") or get_task(concrete_instance)
list_tasks(parent_task, limit=None, continuation_token=None) - List all instances of a dynamic task with pagination
Returns TaskState objects containing task, scheduled_at, locked_until, and locked_by
Implemented across all backends: PostgreSQL, MySQL, SQLite, DynamoDB, Redis, MongoDB
Bug Fixes
Fixed race condition in schedule_task where calling it while a task was executing could clear locks and cause duplicate execution
Fixed schedule starvation issue where repeated schedule_task calls would indefinitely postpone task execution
Switched MySQL backend to READ COMMITTED isolation level to eliminate gap lock deadlocks and improve concurrency
Optimized Postgres and MySQL register_task to use upsert (INSERT ON CONFLICT/INSERT ON DUPLICATE KEY UPDATE) for force mode
API Changes
Added TaskLockedException exception type for clearer error handling when tasks are locked
Updated register_task across all backends to accept force parameter
Added new get_task_state() method to Repository interface for querying individual task state
Added new list_task_states() method to Repository interface for paginated listing of dynamic task instances
Added new public API methods to PyncetteContext:
get_task(task, instance_name=None) - Query state of static or dynamic tasks
list_tasks(parent_task, limit=None, continuation_token=None) - List dynamic task instances with pagination
Added new model classes: TaskState (task state snapshot) and ListTasksResponse (paginated results)
1.0.0 (2025-10-17)
Breaking Changes
Dropped Python 3.8 support - Minimum Python version is now 3.9
Migrated from setup.py to modern PEP 621 pyproject.toml with hatchling build backend
Replaced tox with uv for dependency management and testing across all workflows
Updated GitHub Actions workflows to use native uv commands (uv sync, uv run, uv build)
Migrated documentation from Sphinx/reStructuredText to MkDocs Material/Markdown
Set up automatic API documentation generation with mkdocstrings
Replaced mypy with ty for type checking
Consolidated linting/formatting to use Ruff (replacing black, isort, pyupgrade)
Modernized pre-commit hooks configuration
Updated Docker development environment to install uv
Bug Fixes
Fixed Python 3.14 compatibility: Converted all SQLite SQL queries to use consistent named parameter style (:name) instead of mixing PostgreSQL-style ($1), qmark (?), and named parameters
Fixed latent bug in poll_task where lease comparison failed due to UUID vs string type mismatch
Fixed bug in poll_dynamic_task where optimistic locking was not working due to incorrect parameter binding
Documentation
Converted all documentation files from .rst to .md format
Updated all development instructions to use uv commands
Added relevant PyPI keywords for better discoverability
0.11.0 (2024-11-25)
Add support for Python 3.12 and 3.13
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)
Back to top