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.
Files changed (65) hide show
  1. soniq-0.0.1/CHANGELOG.md +172 -0
  2. soniq-0.0.1/LICENSE +21 -0
  3. soniq-0.0.1/MANIFEST.in +16 -0
  4. soniq-0.0.1/PKG-INFO +192 -0
  5. soniq-0.0.1/README.md +130 -0
  6. soniq-0.0.1/elephantq/__init__.py +376 -0
  7. soniq-0.0.1/elephantq/app.py +651 -0
  8. soniq-0.0.1/elephantq/backends/__init__.py +247 -0
  9. soniq-0.0.1/elephantq/backends/memory.py +417 -0
  10. soniq-0.0.1/elephantq/backends/postgres.py +799 -0
  11. soniq-0.0.1/elephantq/backends/sqlite.py +502 -0
  12. soniq-0.0.1/elephantq/cli/__init__.py +0 -0
  13. soniq-0.0.1/elephantq/cli/colors.py +396 -0
  14. soniq-0.0.1/elephantq/cli/commands/__init__.py +3 -0
  15. soniq-0.0.1/elephantq/cli/commands/core.py +705 -0
  16. soniq-0.0.1/elephantq/cli/commands/extended.py +359 -0
  17. soniq-0.0.1/elephantq/cli/main.py +71 -0
  18. soniq-0.0.1/elephantq/cli/registry.py +167 -0
  19. soniq-0.0.1/elephantq/core/__init__.py +0 -0
  20. soniq-0.0.1/elephantq/core/heartbeat.py +144 -0
  21. soniq-0.0.1/elephantq/core/leadership.py +61 -0
  22. soniq-0.0.1/elephantq/core/processor.py +266 -0
  23. soniq-0.0.1/elephantq/core/queue.py +70 -0
  24. soniq-0.0.1/elephantq/core/registry.py +206 -0
  25. soniq-0.0.1/elephantq/core/retry.py +86 -0
  26. soniq-0.0.1/elephantq/dashboard/__init__.py +36 -0
  27. soniq-0.0.1/elephantq/dashboard/app.py +468 -0
  28. soniq-0.0.1/elephantq/dashboard/fastapi_app.py +898 -0
  29. soniq-0.0.1/elephantq/db/__init__.py +0 -0
  30. soniq-0.0.1/elephantq/db/connection.py +83 -0
  31. soniq-0.0.1/elephantq/db/context.py +190 -0
  32. soniq-0.0.1/elephantq/db/helpers.py +11 -0
  33. soniq-0.0.1/elephantq/db/migrations/001_core_jobs.sql +53 -0
  34. soniq-0.0.1/elephantq/db/migrations/002_workers.sql +42 -0
  35. soniq-0.0.1/elephantq/db/migrations/003_scheduling.sql +40 -0
  36. soniq-0.0.1/elephantq/db/migrations/004_features.sql +97 -0
  37. soniq-0.0.1/elephantq/db/migrations.py +256 -0
  38. soniq-0.0.1/elephantq/discovery.py +93 -0
  39. soniq-0.0.1/elephantq/errors.py +73 -0
  40. soniq-0.0.1/elephantq/features/__init__.py +26 -0
  41. soniq-0.0.1/elephantq/features/dead_letter.py +747 -0
  42. soniq-0.0.1/elephantq/features/flags.py +11 -0
  43. soniq-0.0.1/elephantq/features/logging.py +699 -0
  44. soniq-0.0.1/elephantq/features/managers.py +268 -0
  45. soniq-0.0.1/elephantq/features/metrics.py +575 -0
  46. soniq-0.0.1/elephantq/features/recurring.py +1011 -0
  47. soniq-0.0.1/elephantq/features/scheduling.py +404 -0
  48. soniq-0.0.1/elephantq/features/signing.py +197 -0
  49. soniq-0.0.1/elephantq/features/timeout_processor.py +660 -0
  50. soniq-0.0.1/elephantq/features/webhooks.py +775 -0
  51. soniq-0.0.1/elephantq/job.py +61 -0
  52. soniq-0.0.1/elephantq/py.typed +0 -0
  53. soniq-0.0.1/elephantq/settings.py +402 -0
  54. soniq-0.0.1/elephantq/utils/__init__.py +1 -0
  55. soniq-0.0.1/elephantq/utils/hashing.py +51 -0
  56. soniq-0.0.1/elephantq/utils/signals.py +156 -0
  57. soniq-0.0.1/elephantq/worker.py +288 -0
  58. soniq-0.0.1/pyproject.toml +122 -0
  59. soniq-0.0.1/setup.cfg +4 -0
  60. soniq-0.0.1/soniq.egg-info/PKG-INFO +192 -0
  61. soniq-0.0.1/soniq.egg-info/SOURCES.txt +63 -0
  62. soniq-0.0.1/soniq.egg-info/dependency_links.txt +1 -0
  63. soniq-0.0.1/soniq.egg-info/entry_points.txt +2 -0
  64. soniq-0.0.1/soniq.egg-info/requires.txt +43 -0
  65. soniq-0.0.1/soniq.egg-info/top_level.txt +1 -0
@@ -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.
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/elephantq)](https://pypi.org/project/elephantq/)
68
+ [![Python versions](https://img.shields.io/pypi/pyversions/elephantq)](https://pypi.org/project/elephantq/)
69
+ [![License](https://img.shields.io/github/license/abhinavs/elephantq)](https://github.com/abhinavs/elephantq/blob/main/LICENSE)
70
+ [![Tests](https://img.shields.io/github/actions/workflow/status/abhinavs/elephantq/test.yml?label=tests)](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
+ ![ElephantQ Dashboard](https://raw.githubusercontent.com/abhinavs/elephantq/main/docs/assets/elephantq_dashboard.png)
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
+ [![PyPI version](https://img.shields.io/pypi/v/elephantq)](https://pypi.org/project/elephantq/)
6
+ [![Python versions](https://img.shields.io/pypi/pyversions/elephantq)](https://pypi.org/project/elephantq/)
7
+ [![License](https://img.shields.io/github/license/abhinavs/elephantq)](https://github.com/abhinavs/elephantq/blob/main/LICENSE)
8
+ [![Tests](https://img.shields.io/github/actions/workflow/status/abhinavs/elephantq/test.yml?label=tests)](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
+ ![ElephantQ Dashboard](https://raw.githubusercontent.com/abhinavs/elephantq/main/docs/assets/elephantq_dashboard.png)
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