soniq 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- soniq-0.0.1/CHANGELOG.md +172 -0
- soniq-0.0.1/LICENSE +21 -0
- soniq-0.0.1/MANIFEST.in +16 -0
- soniq-0.0.1/PKG-INFO +192 -0
- soniq-0.0.1/README.md +130 -0
- soniq-0.0.1/elephantq/__init__.py +376 -0
- soniq-0.0.1/elephantq/app.py +651 -0
- soniq-0.0.1/elephantq/backends/__init__.py +247 -0
- soniq-0.0.1/elephantq/backends/memory.py +417 -0
- soniq-0.0.1/elephantq/backends/postgres.py +799 -0
- soniq-0.0.1/elephantq/backends/sqlite.py +502 -0
- soniq-0.0.1/elephantq/cli/__init__.py +0 -0
- soniq-0.0.1/elephantq/cli/colors.py +396 -0
- soniq-0.0.1/elephantq/cli/commands/__init__.py +3 -0
- soniq-0.0.1/elephantq/cli/commands/core.py +705 -0
- soniq-0.0.1/elephantq/cli/commands/extended.py +359 -0
- soniq-0.0.1/elephantq/cli/main.py +71 -0
- soniq-0.0.1/elephantq/cli/registry.py +167 -0
- soniq-0.0.1/elephantq/core/__init__.py +0 -0
- soniq-0.0.1/elephantq/core/heartbeat.py +144 -0
- soniq-0.0.1/elephantq/core/leadership.py +61 -0
- soniq-0.0.1/elephantq/core/processor.py +266 -0
- soniq-0.0.1/elephantq/core/queue.py +70 -0
- soniq-0.0.1/elephantq/core/registry.py +206 -0
- soniq-0.0.1/elephantq/core/retry.py +86 -0
- soniq-0.0.1/elephantq/dashboard/__init__.py +36 -0
- soniq-0.0.1/elephantq/dashboard/app.py +468 -0
- soniq-0.0.1/elephantq/dashboard/fastapi_app.py +898 -0
- soniq-0.0.1/elephantq/db/__init__.py +0 -0
- soniq-0.0.1/elephantq/db/connection.py +83 -0
- soniq-0.0.1/elephantq/db/context.py +190 -0
- soniq-0.0.1/elephantq/db/helpers.py +11 -0
- soniq-0.0.1/elephantq/db/migrations/001_core_jobs.sql +53 -0
- soniq-0.0.1/elephantq/db/migrations/002_workers.sql +42 -0
- soniq-0.0.1/elephantq/db/migrations/003_scheduling.sql +40 -0
- soniq-0.0.1/elephantq/db/migrations/004_features.sql +97 -0
- soniq-0.0.1/elephantq/db/migrations.py +256 -0
- soniq-0.0.1/elephantq/discovery.py +93 -0
- soniq-0.0.1/elephantq/errors.py +73 -0
- soniq-0.0.1/elephantq/features/__init__.py +26 -0
- soniq-0.0.1/elephantq/features/dead_letter.py +747 -0
- soniq-0.0.1/elephantq/features/flags.py +11 -0
- soniq-0.0.1/elephantq/features/logging.py +699 -0
- soniq-0.0.1/elephantq/features/managers.py +268 -0
- soniq-0.0.1/elephantq/features/metrics.py +575 -0
- soniq-0.0.1/elephantq/features/recurring.py +1011 -0
- soniq-0.0.1/elephantq/features/scheduling.py +404 -0
- soniq-0.0.1/elephantq/features/signing.py +197 -0
- soniq-0.0.1/elephantq/features/timeout_processor.py +660 -0
- soniq-0.0.1/elephantq/features/webhooks.py +775 -0
- soniq-0.0.1/elephantq/job.py +61 -0
- soniq-0.0.1/elephantq/py.typed +0 -0
- soniq-0.0.1/elephantq/settings.py +402 -0
- soniq-0.0.1/elephantq/utils/__init__.py +1 -0
- soniq-0.0.1/elephantq/utils/hashing.py +51 -0
- soniq-0.0.1/elephantq/utils/signals.py +156 -0
- soniq-0.0.1/elephantq/worker.py +288 -0
- soniq-0.0.1/pyproject.toml +122 -0
- soniq-0.0.1/setup.cfg +4 -0
- soniq-0.0.1/soniq.egg-info/PKG-INFO +192 -0
- soniq-0.0.1/soniq.egg-info/SOURCES.txt +63 -0
- soniq-0.0.1/soniq.egg-info/dependency_links.txt +1 -0
- soniq-0.0.1/soniq.egg-info/entry_points.txt +2 -0
- soniq-0.0.1/soniq.egg-info/requires.txt +43 -0
- soniq-0.0.1/soniq.egg-info/top_level.txt +1 -0
soniq-0.0.1/CHANGELOG.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to ElephantQ will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
- `StorageBackend` protocol gained a required `reschedule_job(job_id, *, delay_seconds, attempts, reason=None)` method, used by the new `Snooze` return type. All in-tree backends (Postgres, SQLite, Memory) implement it. Any third-party backend must add an implementation; otherwise `isinstance(backend, StorageBackend)` checks fail and handlers that return `Snooze` will raise `AttributeError`.
|
|
12
|
+
- Advisory-lock leader election (see below) requires Postgres in session-pooling mode. **Transaction-pooling PgBouncer deployments must either switch to session-pooling or disable the feature** - `pg_try_advisory_lock` releases between statements under transaction-pooling, making the lock unsafe. Single-writer deployments (SQLite, Memory, or a single Postgres worker) are unaffected.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `Snooze(seconds, reason=None)` return type (`elephantq.Snooze`). Returning it from a job handler re-schedules the job without consuming a retry slot. Useful for rate-limited APIs (HTTP 429) and webhook backpressure. Capped by the new `snooze_max_seconds` setting (default `86400`).
|
|
16
|
+
- `retry_jitter` parameter on `@elephantq.job` (default `True`). Applies full-jitter to the exponential backoff when `retry_backoff=True`, sampling uniformly in `[computed/2, computed]` to prevent thundering-herd retries after batch failures. Set `retry_jitter=False` for deterministic timing.
|
|
17
|
+
- `elephantq/core/leadership.py`: `advisory_key(name)` (blake2b-derived, cross-process stable) and `with_advisory_lock(backend, name)` async context manager. Backends without native support fall through to always-leader mode.
|
|
18
|
+
- `PostgresBackend.with_advisory_lock(name)`: session-scoped `pg_try_advisory_lock` on a dedicated connection for the lifetime of the context.
|
|
19
|
+
- `examples/snooze_on_rate_limit.py`: HTTP 429 demo wiring `Snooze` to `Retry-After`.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Exponential backoff (`retry_backoff=True`) now applies full-jitter by default. Jobs that depended on deterministic retry timing will see delays uniformly sampled in `[computed/2, computed]`. Set `retry_jitter=False` on the `@elephantq.job` decorator to restore the previous behavior.
|
|
23
|
+
- `Worker._maybe_cleanup` (pruner + stale-worker rescuer) and `EnhancedRecurringScheduler._scheduler_loop` now run under an advisory-lock leader guard. Multi-worker deployments stop duplicating maintenance work N times per tick. Single-worker deployments see no change. The per-job optimistic lock in `_claim_and_advance_run` remains the correctness floor for recurring job claims.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- Removed orphan `asyncio.create_task(db_handler.setup_database())` call in `LoggingConfig.setup_enterprise_logging`. The target was a no-op, so the task never did anything; dropping it also eliminates a warning about an un-awaited coroutine when logging is configured synchronously.
|
|
27
|
+
|
|
28
|
+
### Added (tests)
|
|
29
|
+
- `tests/unit/test_no_orphan_tasks.py`: AST-walk guard against new orphan `asyncio.create_task` / `asyncio.ensure_future` calls.
|
|
30
|
+
- `tests/integration/test_dead_letter_recursion.py`: bounded-time regression coverage for the `_rows_affected` recursion fix.
|
|
31
|
+
- `tests/integration/test_schedule_builder_race.py`: 50-way concurrent-enqueue test pinning the `JobScheduleBuilder` dedup_key contract.
|
|
32
|
+
- `tests/unit/test_no_depends_on.py`: expanded to `hasattr`, direct-import `ImportError`, and `__all__` assertions so the removed `depends_on` API cannot silently return.
|
|
33
|
+
- `tests/unit/test_retry_backoff.py`: full-jitter bounds, RNG injection, and `retry_max_delay` cap under jitter.
|
|
34
|
+
- `tests/unit/test_advisory_key.py` + `tests/integration/test_leader_election.py`: key stability across processes and exclusive-leader semantics under concurrent holders.
|
|
35
|
+
- `tests/unit/test_snooze.py` + `tests/integration/test_snooze_postgres.py`: Snooze requeue path, attempts roll-back, cap enforcement, and end-to-end Postgres flow.
|
|
36
|
+
### Fixed
|
|
37
|
+
- Removed orphan `asyncio.create_task(db_handler.setup_database())` call in `LoggingConfig.setup_enterprise_logging`. The target was a no-op, so the task never did anything; dropping it also eliminates a warning about an un-awaited coroutine when logging is configured synchronously.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- `tests/unit/test_no_orphan_tasks.py`: AST-walk guard that fails if any new bare expression-statement `asyncio.create_task(...)` / `asyncio.ensure_future(...)` call is introduced in the package. Existing call sites store or track their tasks and remain allowed.
|
|
41
|
+
- `tests/integration/test_dead_letter_recursion.py`: bounded-time regression coverage for the `_rows_affected` recursion fix across the `delete`, `bulk_delete`, and `move` paths of `DeadLetterManager`.
|
|
42
|
+
- `tests/integration/test_schedule_builder_race.py`: pins the documented `JobScheduleBuilder` dedup_key contract under 50-way concurrent `enqueue()` - exactly one row, all callers receive the winning job id.
|
|
43
|
+
- `tests/unit/test_no_depends_on.py`: additional assertions (`hasattr`, direct-import `ImportError`, `__all__`) to ensure the already-removed `depends_on` API cannot be silently reintroduced.
|
|
44
|
+
|
|
45
|
+
## [0.3.0] - 2026-03-27
|
|
46
|
+
|
|
47
|
+
### Architecture
|
|
48
|
+
- **Pluggable storage backends** — `StorageBackend` Protocol with PostgreSQL, SQLite, and Memory implementations
|
|
49
|
+
- **Auto-detection** — backend selected automatically from `database_url` (`.db` → SQLite, `postgresql://` → Postgres)
|
|
50
|
+
- **Worker extraction** — `elephantq/worker.py` extracted from `client.py` (-247 lines)
|
|
51
|
+
- **Three-tier test architecture** — smoke/unit/functional/integration with proper conftest isolation
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- `PostgresBackend` — all SQL extracted from inline code into dedicated backend
|
|
55
|
+
- `SQLiteBackend` — zero-setup local development (`pip install elephantq[sqlite]`)
|
|
56
|
+
- `MemoryBackend` — in-memory backend for unit tests (zero external deps)
|
|
57
|
+
- `@elephantq.periodic(cron="...", every_minutes=N)` — first-class decorator for recurring jobs
|
|
58
|
+
- `JobContext` — runtime metadata injection for running jobs (`ctx: JobContext`)
|
|
59
|
+
- `queueing_lock` parameter for flexible job deduplication
|
|
60
|
+
- `elephantq.reset()` — test fixture cleanup via backend
|
|
61
|
+
- Module discovery: auto `sys.path` fix, multi-module support, batch error reporting
|
|
62
|
+
- Clean import paths: `from elephantq import every, cron` (no more `elephantq.features.X`)
|
|
63
|
+
- `docs/backends.md`, `docs/agents.md`
|
|
64
|
+
|
|
65
|
+
### Removed
|
|
66
|
+
- `depends_on()` — experimental, unimplemented in worker
|
|
67
|
+
- `EnterpriseFeatures` / `enterprise` aliases
|
|
68
|
+
- All legacy/backward-compat code and comments
|
|
69
|
+
- Legacy Fernet decryption path
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
- Multi-scheduler safety via optimistic locking (`_claim_and_advance_run`)
|
|
73
|
+
- Getting-started docs lead with SQLite (zero-setup) instead of requiring PostgreSQL
|
|
74
|
+
- CI split into unit+sqlite (no Postgres) and integration (real Postgres) jobs
|
|
75
|
+
|
|
76
|
+
## [0.2.0] - 2026-03-24
|
|
77
|
+
|
|
78
|
+
### Breaking Changes
|
|
79
|
+
- Default `job_timeout` changed from `None` (no timeout) to `300` seconds. Jobs exceeding 5 minutes are now treated as failures. Override per-job with `@elephantq.job(timeout=None)` or globally with `ELEPHANTQ_JOB_TIMEOUT=0`.
|
|
80
|
+
- All timestamp columns normalized to `TIMESTAMP WITH TIME ZONE` in the base migration files.
|
|
81
|
+
- Removed `EnterpriseFeatures` and `enterprise` aliases from `elephantq.features`.
|
|
82
|
+
|
|
83
|
+
### Fixed
|
|
84
|
+
- Fixed fragile error classification that silently dead-lettered retryable jobs when error messages contained "argument" or "parameter"
|
|
85
|
+
- Fixed broken `examples/recurring_jobs.py` (used non-existent API)
|
|
86
|
+
- Fixed broken `examples/transactional_enqueue.py` (passed unsupported arg to `setup()`)
|
|
87
|
+
- Fixed LISTEN connection leak in worker loop (connection acquired outside try block)
|
|
88
|
+
- Fixed `ELEPHANTQ_SKIP_UPDATE_LOCK` env var now only honored in debug/testing mode
|
|
89
|
+
- Replaced 11 dead `docs.elephantq.dev` URLs with GitHub doc links
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
- Default 300-second job execution timeout with per-job override via `@elephantq.job(timeout=N)`
|
|
93
|
+
- `py.typed` marker for PEP 561 type checker support
|
|
94
|
+
- `init` callback on instance connection pool for UTC timezone initialization
|
|
95
|
+
- End-to-end crash recovery tests
|
|
96
|
+
- Concurrent dequeue race condition tests
|
|
97
|
+
- Timeout enforcement tests
|
|
98
|
+
- Connection pool exhaustion tests
|
|
99
|
+
- Missing job handler tests
|
|
100
|
+
- Example import smoke tests in CI
|
|
101
|
+
- Coverage reporting in CI
|
|
102
|
+
- mypy configuration and CI integration
|
|
103
|
+
- Dead URL regression guard in CI
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
- `list_jobs` default limit harmonized to 100 across all API entry points
|
|
107
|
+
- Simplified `features/features.py` imports (35 aliased imports replaced with deferred module imports)
|
|
108
|
+
- Per-query `SET timezone = 'UTC'` removed from processor (now handled by pool init callback)
|
|
109
|
+
|
|
110
|
+
### Documentation
|
|
111
|
+
- Added job timeout documentation to retries.md and getting-started.md
|
|
112
|
+
- Rewrote stuck-job-recovery.md to document automatic heartbeat-based recovery
|
|
113
|
+
- Added `__all__` exports to `features/recurring.py`
|
|
114
|
+
|
|
115
|
+
## [Unreleased]
|
|
116
|
+
|
|
117
|
+
### Fixed
|
|
118
|
+
- Fixed infinite recursion in `dead_letter.py` `_rows_affected()` that crashed all dead letter operations
|
|
119
|
+
- Fixed fire-and-forget `create_task()` calls in recurring.py that caused silent data loss
|
|
120
|
+
- Fixed non-atomic state update in recurring job execution (in-memory updated before DB write)
|
|
121
|
+
- Fixed `datetime.now()` without timezone in health.py
|
|
122
|
+
- Fixed logging handler holding asyncio lock during database I/O
|
|
123
|
+
- Removed unnecessary `async` from `high_priority()`, `background()`, `urgent()` convenience functions
|
|
124
|
+
- Added experimental warning to `depends_on()` (dependency enforcement not yet implemented in worker)
|
|
125
|
+
- Capped webhook response body reads at 4KB to prevent OOM
|
|
126
|
+
- Added backpressure to webhook delivery queue (maxsize=1000)
|
|
127
|
+
|
|
128
|
+
### Changed
|
|
129
|
+
- Moved `_rows_affected()` to shared `elephantq/db/helpers.py` (deduplicated from 3 files)
|
|
130
|
+
- Moved `croniter`, `aiohttp`, `structlog`, `cryptography` from core deps to optional extras
|
|
131
|
+
- Core install now only requires `asyncpg`, `pydantic`, `pydantic-settings`
|
|
132
|
+
- Refactored `process_jobs` and `process_jobs_with_registry` to share common logic
|
|
133
|
+
- Replaced O(n) metrics lookup with O(1) dict index
|
|
134
|
+
- Increased PBKDF2 iterations from 100k to 310k (NIST 2023 recommendation)
|
|
135
|
+
- Moved dependencies table DDL from inline runtime creation to migration 004
|
|
136
|
+
|
|
137
|
+
### Added
|
|
138
|
+
- Migration 004: composite index on (queue, status, priority, scheduled_at) for the hottest query
|
|
139
|
+
- Migration 004: `elephantq_job_dependencies` table (previously created inline)
|
|
140
|
+
- GitHub Actions CI workflows for tests, linting, and PyPI publishing
|
|
141
|
+
- At-least-once delivery semantics documented in getting-started, retries, production, and transactional-enqueue docs
|
|
142
|
+
- Idempotency guidance added to docs
|
|
143
|
+
- CHANGELOG.md
|
|
144
|
+
|
|
145
|
+
### Documentation
|
|
146
|
+
- Fixed false "exactly-once delivery" claim in transactional-enqueue docs
|
|
147
|
+
- Marked `depends_on()` as experimental in docs/features.md and docs/scheduling.md
|
|
148
|
+
- Added delivery semantics and idempotency sections across docs
|
|
149
|
+
|
|
150
|
+
## [0.1.1] - 2025-05-01
|
|
151
|
+
|
|
152
|
+
### Fixed
|
|
153
|
+
- Fixed SQL injection vectors and TOCTOU race conditions
|
|
154
|
+
- Fixed timezone bugs and memory leaks
|
|
155
|
+
- Fixed zombie job handling and secret key leak
|
|
156
|
+
- Replaced hardcoded PBKDF2 salt with random salt
|
|
157
|
+
|
|
158
|
+
## [0.1.0] - 2025-04-15
|
|
159
|
+
|
|
160
|
+
### Added
|
|
161
|
+
- Initial release
|
|
162
|
+
- PostgreSQL-backed async job queue with SKIP LOCKED
|
|
163
|
+
- Transactional enqueue support
|
|
164
|
+
- Retry engine with fixed, exponential, and per-attempt delays
|
|
165
|
+
- Dead letter queue management
|
|
166
|
+
- Worker heartbeat and stale worker recovery
|
|
167
|
+
- Job scheduling with fluent builder API
|
|
168
|
+
- Recurring jobs with cron and interval support
|
|
169
|
+
- Webhook notifications for job lifecycle events
|
|
170
|
+
- CLI for setup, worker management, and job inspection
|
|
171
|
+
- Optional FastAPI dashboard
|
|
172
|
+
- Health checks and metrics collection
|
soniq-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhinav Saxena
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
soniq-0.0.1/MANIFEST.in
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Include migration SQL files
|
|
2
|
+
recursive-include elephantq/db/migrations *.sql
|
|
3
|
+
|
|
4
|
+
# Include license and readme
|
|
5
|
+
include LICENSE
|
|
6
|
+
include README.md
|
|
7
|
+
include CHANGELOG.md
|
|
8
|
+
|
|
9
|
+
# Exclude test files, examples, and deployment configs
|
|
10
|
+
exclude tests/
|
|
11
|
+
exclude examples/
|
|
12
|
+
exclude deployment/
|
|
13
|
+
exclude jobs/
|
|
14
|
+
exclude quickstart_test/
|
|
15
|
+
exclude run_tests.py
|
|
16
|
+
exclude release.py
|
soniq-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: soniq
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: ElephantQ - PostgreSQL-only async job queue - built for developer happiness.
|
|
5
|
+
Author-email: Abhinav Saxena <abhinav@apiclabs.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/abhinavs/elephantq
|
|
8
|
+
Project-URL: Repository, https://github.com/abhinavs/elephantq
|
|
9
|
+
Project-URL: Documentation, https://github.com/abhinavs/elephantq/tree/main/docs
|
|
10
|
+
Project-URL: Changelog, https://github.com/abhinavs/elephantq/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/abhinavs/elephantq/issues
|
|
12
|
+
Keywords: async,job,queue,postgresql,task,redis-alternative,developer-experience,background-jobs
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: asyncpg<0.32.0,>=0.30.0
|
|
27
|
+
Requires-Dist: pydantic<3.0.0,>=2.7.1
|
|
28
|
+
Requires-Dist: pydantic-settings<3.0.0,>=2.0.0
|
|
29
|
+
Provides-Extra: scheduling
|
|
30
|
+
Requires-Dist: croniter>=1.4.0; extra == "scheduling"
|
|
31
|
+
Provides-Extra: sqlite
|
|
32
|
+
Requires-Dist: aiosqlite>=0.19.0; extra == "sqlite"
|
|
33
|
+
Provides-Extra: webhooks
|
|
34
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "webhooks"
|
|
35
|
+
Requires-Dist: cryptography>=3.4.0; extra == "webhooks"
|
|
36
|
+
Provides-Extra: logging
|
|
37
|
+
Requires-Dist: structlog>=22.0.0; extra == "logging"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
42
|
+
Requires-Dist: httpx>=0.24.0; extra == "dev"
|
|
43
|
+
Requires-Dist: black==25.1.0; extra == "dev"
|
|
44
|
+
Requires-Dist: flake8==7.1.0; extra == "dev"
|
|
45
|
+
Requires-Dist: isort==6.0.1; extra == "dev"
|
|
46
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
47
|
+
Requires-Dist: pre-commit>=3.4.0; extra == "dev"
|
|
48
|
+
Requires-Dist: croniter>=1.4.0; extra == "dev"
|
|
49
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "dev"
|
|
50
|
+
Requires-Dist: aiosqlite>=0.19.0; extra == "dev"
|
|
51
|
+
Requires-Dist: structlog>=22.0.0; extra == "dev"
|
|
52
|
+
Requires-Dist: cryptography>=3.4.0; extra == "dev"
|
|
53
|
+
Provides-Extra: dashboard
|
|
54
|
+
Requires-Dist: fastapi>=0.100.0; extra == "dashboard"
|
|
55
|
+
Requires-Dist: uvicorn>=0.20.0; extra == "dashboard"
|
|
56
|
+
Provides-Extra: monitoring
|
|
57
|
+
Requires-Dist: prometheus-client>=0.15.0; extra == "monitoring"
|
|
58
|
+
Requires-Dist: psutil>=5.9.0; extra == "monitoring"
|
|
59
|
+
Provides-Extra: full
|
|
60
|
+
Requires-Dist: elephantq[dashboard,logging,monitoring,scheduling,sqlite,webhooks]; extra == "full"
|
|
61
|
+
Dynamic: license-file
|
|
62
|
+
|
|
63
|
+
# ElephantQ
|
|
64
|
+
|
|
65
|
+
Background jobs for Python. Powered by the Postgres you already have.
|
|
66
|
+
|
|
67
|
+
[](https://pypi.org/project/elephantq/)
|
|
68
|
+
[](https://pypi.org/project/elephantq/)
|
|
69
|
+
[](https://github.com/abhinavs/elephantq/blob/main/LICENSE)
|
|
70
|
+
[](https://github.com/abhinavs/elephantq/actions)
|
|
71
|
+
|
|
72
|
+
## Quickstart
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install elephantq
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# jobs.py
|
|
80
|
+
from elephantq import ElephantQ
|
|
81
|
+
|
|
82
|
+
app = ElephantQ(database_url="postgresql://localhost/myapp")
|
|
83
|
+
|
|
84
|
+
@app.job(max_retries=3)
|
|
85
|
+
async def send_welcome(to: str):
|
|
86
|
+
print(f"Sending welcome email to {to}")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# enqueue from anywhere in your app
|
|
91
|
+
await app.enqueue(send_welcome, to="dev@example.com")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# set up tables and start processing
|
|
96
|
+
elephantq setup
|
|
97
|
+
elephantq start --concurrency 4
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Four steps. Define a job, enqueue it, set up the database, start a worker.
|
|
101
|
+
|
|
102
|
+
> **Local dev without Postgres?** Use SQLite: `ElephantQ(database_url='local.db')`.
|
|
103
|
+
> For production, always use PostgreSQL.
|
|
104
|
+
|
|
105
|
+
## Transactional enqueue
|
|
106
|
+
|
|
107
|
+
Enqueue a job inside your database transaction. If the transaction rolls back, the job never existed.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
async with pool.acquire() as conn:
|
|
111
|
+
async with conn.transaction():
|
|
112
|
+
await conn.execute("INSERT INTO orders ...")
|
|
113
|
+
await app.enqueue(send_invoice, connection=conn, order_id=order_id)
|
|
114
|
+
# Both commit together, or neither does
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
No Redis queue can do this. Your job and your data land in the same commit. If something fails halfway through, both roll back. No stale jobs, no ghost tasks, no cleanup scripts.
|
|
118
|
+
|
|
119
|
+
## Why ElephantQ
|
|
120
|
+
|
|
121
|
+
Most Python job queues force you to run Redis or RabbitMQ alongside your database. That's another service to deploy, monitor, back up, and debug when things go wrong at 3am.
|
|
122
|
+
|
|
123
|
+
ElephantQ uses your existing PostgreSQL. One dependency. One place your data lives. One thing to back up.
|
|
124
|
+
|
|
125
|
+
| Feature | ElephantQ | Celery | RQ |
|
|
126
|
+
| ------------------- | --------- | -------------- | ------ |
|
|
127
|
+
| No Redis dependency | Yes | No | No |
|
|
128
|
+
| Async native | Yes | Partial | No |
|
|
129
|
+
| Transactional enq. | Yes | No | No |
|
|
130
|
+
| Setup complexity | Low | High | Medium |
|
|
131
|
+
| Built-in dashboard | Yes | No (Flower) | No |
|
|
132
|
+
| Dead-letter queue | Yes | No | No |
|
|
133
|
+
|
|
134
|
+
## Features
|
|
135
|
+
|
|
136
|
+
- **Retries with backoff** -- configurable delays, exponential backoff, per-attempt delay lists
|
|
137
|
+
- **Dead-letter queue** -- failed jobs preserved for inspection and manual retry
|
|
138
|
+
- **Job priorities** -- lower number = higher priority, processed first
|
|
139
|
+
- **Scheduled jobs** -- run at a specific time or after a delay
|
|
140
|
+
- **Recurring jobs** -- cron-based periodic tasks with `@app.periodic(cron="0 * * * *")`
|
|
141
|
+
- **Transactional enqueue** -- atomic with your database writes
|
|
142
|
+
- **Multiple queues** -- route jobs by type, run dedicated workers per queue
|
|
143
|
+
- **Middleware hooks** -- `before_job`, `after_job`, `on_error` for logging, metrics, tracing
|
|
144
|
+
- **Worker heartbeat** -- auto-detect crashed workers, requeue their jobs
|
|
145
|
+
- **Job results** -- store and retrieve return values from completed jobs
|
|
146
|
+
- **Deduplication** -- prevent duplicate jobs with `dedup_key` or `unique=True`
|
|
147
|
+
- **CLI** -- `setup`, `start`, `status`, `workers`, dead-letter management
|
|
148
|
+
- **Dashboard** -- web UI for monitoring queues, workers, and job state
|
|
149
|
+
|
|
150
|
+
## Dashboard
|
|
151
|
+
|
|
152
|
+
Monitor queues, workers, retries, and system health from a built-in web UI.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pip install elephantq[dashboard]
|
|
156
|
+
elephantq dashboard
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+

|
|
160
|
+
|
|
161
|
+
## Install
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pip install elephantq # core (Postgres backend)
|
|
165
|
+
pip install elephantq[full] # everything below
|
|
166
|
+
pip install elephantq[sqlite] # SQLite backend for local dev
|
|
167
|
+
pip install elephantq[scheduling] # cron-based recurring jobs
|
|
168
|
+
pip install elephantq[dashboard] # web dashboard
|
|
169
|
+
pip install elephantq[monitoring] # Prometheus metrics
|
|
170
|
+
pip install elephantq[webhooks] # webhook delivery + signing
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## When NOT to use ElephantQ
|
|
174
|
+
|
|
175
|
+
- **You need 10k+ jobs/sec sustained throughput.** PostgreSQL row locking has limits. Redis-backed queues like Celery or Arq are built for this.
|
|
176
|
+
- **You need cross-language consumers.** ElephantQ is Python-only. If your workers are in Go or Node, use RabbitMQ or a similar broker.
|
|
177
|
+
- **You're not using PostgreSQL.** The production backend requires PostgreSQL. If your stack is MySQL or MongoDB, this isn't for you.
|
|
178
|
+
- **You need DAG-based workflow orchestration.** ElephantQ handles individual jobs, not pipelines. Look at Prefect or Airflow.
|
|
179
|
+
|
|
180
|
+
## Documentation
|
|
181
|
+
|
|
182
|
+
- [Quickstart](docs/getting-started/quickstart.md)
|
|
183
|
+
- [FastAPI integration](docs/guides/fastapi.md)
|
|
184
|
+
- [Jobs and concepts](docs/concepts/jobs.md)
|
|
185
|
+
- [Production checklist](docs/production/checklist.md)
|
|
186
|
+
- [Deployment](docs/production/deployment.md)
|
|
187
|
+
- [CLI reference](docs/cli/commands.md)
|
|
188
|
+
- [API reference](docs/api/elephantq.md)
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
MIT
|
soniq-0.0.1/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# ElephantQ
|
|
2
|
+
|
|
3
|
+
Background jobs for Python. Powered by the Postgres you already have.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/elephantq/)
|
|
6
|
+
[](https://pypi.org/project/elephantq/)
|
|
7
|
+
[](https://github.com/abhinavs/elephantq/blob/main/LICENSE)
|
|
8
|
+
[](https://github.com/abhinavs/elephantq/actions)
|
|
9
|
+
|
|
10
|
+
## Quickstart
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install elephantq
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# jobs.py
|
|
18
|
+
from elephantq import ElephantQ
|
|
19
|
+
|
|
20
|
+
app = ElephantQ(database_url="postgresql://localhost/myapp")
|
|
21
|
+
|
|
22
|
+
@app.job(max_retries=3)
|
|
23
|
+
async def send_welcome(to: str):
|
|
24
|
+
print(f"Sending welcome email to {to}")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# enqueue from anywhere in your app
|
|
29
|
+
await app.enqueue(send_welcome, to="dev@example.com")
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# set up tables and start processing
|
|
34
|
+
elephantq setup
|
|
35
|
+
elephantq start --concurrency 4
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Four steps. Define a job, enqueue it, set up the database, start a worker.
|
|
39
|
+
|
|
40
|
+
> **Local dev without Postgres?** Use SQLite: `ElephantQ(database_url='local.db')`.
|
|
41
|
+
> For production, always use PostgreSQL.
|
|
42
|
+
|
|
43
|
+
## Transactional enqueue
|
|
44
|
+
|
|
45
|
+
Enqueue a job inside your database transaction. If the transaction rolls back, the job never existed.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
async with pool.acquire() as conn:
|
|
49
|
+
async with conn.transaction():
|
|
50
|
+
await conn.execute("INSERT INTO orders ...")
|
|
51
|
+
await app.enqueue(send_invoice, connection=conn, order_id=order_id)
|
|
52
|
+
# Both commit together, or neither does
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
No Redis queue can do this. Your job and your data land in the same commit. If something fails halfway through, both roll back. No stale jobs, no ghost tasks, no cleanup scripts.
|
|
56
|
+
|
|
57
|
+
## Why ElephantQ
|
|
58
|
+
|
|
59
|
+
Most Python job queues force you to run Redis or RabbitMQ alongside your database. That's another service to deploy, monitor, back up, and debug when things go wrong at 3am.
|
|
60
|
+
|
|
61
|
+
ElephantQ uses your existing PostgreSQL. One dependency. One place your data lives. One thing to back up.
|
|
62
|
+
|
|
63
|
+
| Feature | ElephantQ | Celery | RQ |
|
|
64
|
+
| ------------------- | --------- | -------------- | ------ |
|
|
65
|
+
| No Redis dependency | Yes | No | No |
|
|
66
|
+
| Async native | Yes | Partial | No |
|
|
67
|
+
| Transactional enq. | Yes | No | No |
|
|
68
|
+
| Setup complexity | Low | High | Medium |
|
|
69
|
+
| Built-in dashboard | Yes | No (Flower) | No |
|
|
70
|
+
| Dead-letter queue | Yes | No | No |
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
- **Retries with backoff** -- configurable delays, exponential backoff, per-attempt delay lists
|
|
75
|
+
- **Dead-letter queue** -- failed jobs preserved for inspection and manual retry
|
|
76
|
+
- **Job priorities** -- lower number = higher priority, processed first
|
|
77
|
+
- **Scheduled jobs** -- run at a specific time or after a delay
|
|
78
|
+
- **Recurring jobs** -- cron-based periodic tasks with `@app.periodic(cron="0 * * * *")`
|
|
79
|
+
- **Transactional enqueue** -- atomic with your database writes
|
|
80
|
+
- **Multiple queues** -- route jobs by type, run dedicated workers per queue
|
|
81
|
+
- **Middleware hooks** -- `before_job`, `after_job`, `on_error` for logging, metrics, tracing
|
|
82
|
+
- **Worker heartbeat** -- auto-detect crashed workers, requeue their jobs
|
|
83
|
+
- **Job results** -- store and retrieve return values from completed jobs
|
|
84
|
+
- **Deduplication** -- prevent duplicate jobs with `dedup_key` or `unique=True`
|
|
85
|
+
- **CLI** -- `setup`, `start`, `status`, `workers`, dead-letter management
|
|
86
|
+
- **Dashboard** -- web UI for monitoring queues, workers, and job state
|
|
87
|
+
|
|
88
|
+
## Dashboard
|
|
89
|
+
|
|
90
|
+
Monitor queues, workers, retries, and system health from a built-in web UI.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install elephantq[dashboard]
|
|
94
|
+
elephantq dashboard
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
|
|
99
|
+
## Install
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install elephantq # core (Postgres backend)
|
|
103
|
+
pip install elephantq[full] # everything below
|
|
104
|
+
pip install elephantq[sqlite] # SQLite backend for local dev
|
|
105
|
+
pip install elephantq[scheduling] # cron-based recurring jobs
|
|
106
|
+
pip install elephantq[dashboard] # web dashboard
|
|
107
|
+
pip install elephantq[monitoring] # Prometheus metrics
|
|
108
|
+
pip install elephantq[webhooks] # webhook delivery + signing
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## When NOT to use ElephantQ
|
|
112
|
+
|
|
113
|
+
- **You need 10k+ jobs/sec sustained throughput.** PostgreSQL row locking has limits. Redis-backed queues like Celery or Arq are built for this.
|
|
114
|
+
- **You need cross-language consumers.** ElephantQ is Python-only. If your workers are in Go or Node, use RabbitMQ or a similar broker.
|
|
115
|
+
- **You're not using PostgreSQL.** The production backend requires PostgreSQL. If your stack is MySQL or MongoDB, this isn't for you.
|
|
116
|
+
- **You need DAG-based workflow orchestration.** ElephantQ handles individual jobs, not pipelines. Look at Prefect or Airflow.
|
|
117
|
+
|
|
118
|
+
## Documentation
|
|
119
|
+
|
|
120
|
+
- [Quickstart](docs/getting-started/quickstart.md)
|
|
121
|
+
- [FastAPI integration](docs/guides/fastapi.md)
|
|
122
|
+
- [Jobs and concepts](docs/concepts/jobs.md)
|
|
123
|
+
- [Production checklist](docs/production/checklist.md)
|
|
124
|
+
- [Deployment](docs/production/deployment.md)
|
|
125
|
+
- [CLI reference](docs/cli/commands.md)
|
|
126
|
+
- [API reference](docs/api/elephantq.md)
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|