rrq 0.2.5__py3-none-any.whl
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.
- rrq/__init__.py +0 -0
- rrq/client.py +159 -0
- rrq/constants.py +42 -0
- rrq/exc.py +46 -0
- rrq/job.py +133 -0
- rrq/registry.py +77 -0
- rrq/rrq.py +328 -0
- rrq/settings.py +107 -0
- rrq/store.py +568 -0
- rrq/worker.py +897 -0
- rrq-0.2.5.dist-info/METADATA +201 -0
- rrq-0.2.5.dist-info/RECORD +15 -0
- rrq-0.2.5.dist-info/WHEEL +4 -0
- rrq-0.2.5.dist-info/entry_points.txt +2 -0
- rrq-0.2.5.dist-info/licenses/LICENSE +13 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rrq
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: RRQ is a Python library for creating reliable job queues using Redis and asyncio
|
|
5
|
+
Project-URL: Homepage, https://github.com/getresq/rrq
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/getresq/rrq/issues
|
|
7
|
+
Author-email: Mazdak Rezvani <mazdak@me.com>
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
15
|
+
Classifier: Topic :: System :: Monitoring
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: click>=8.1.3
|
|
18
|
+
Requires-Dist: pydantic-settings>=2.9.1
|
|
19
|
+
Requires-Dist: pydantic>=2.11.4
|
|
20
|
+
Requires-Dist: redis[hiredis]<6,>=4.2.0
|
|
21
|
+
Requires-Dist: watchfiles>=0.19.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8.3.5; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# RRQ: Reliable Redis Queue
|
|
28
|
+
|
|
29
|
+
____ ____ ___
|
|
30
|
+
| _ \ | _ \ / _ \
|
|
31
|
+
| |_) | | |_) | | | | |
|
|
32
|
+
| _ < | _ < | |_| |
|
|
33
|
+
|_| \_\ |_| \_\ \__\_\
|
|
34
|
+
|
|
35
|
+
RRQ is a Python library for creating reliable job queues using Redis and `asyncio`, inspired by [ARQ (Async Redis Queue)](https://github.com/samuelcolvin/arq). It focuses on providing at-least-once job processing semantics with features like automatic retries, job timeouts, dead-letter queues, and graceful worker shutdown.
|
|
36
|
+
|
|
37
|
+
## Core Components
|
|
38
|
+
|
|
39
|
+
* **`RRQClient` (`client.py`)**: Used to enqueue jobs onto specific queues. Supports deferring jobs (by time delta or specific datetime), assigning custom job IDs, and enforcing job uniqueness via keys.
|
|
40
|
+
* **`RRQWorker` (`worker.py`)**: The process that polls queues, fetches jobs, executes the corresponding handler functions, and manages the job lifecycle based on success, failure, retries, or timeouts. Handles graceful shutdown via signals (SIGINT, SIGTERM).
|
|
41
|
+
* **`JobRegistry` (`registry.py`)**: A simple registry to map string function names (used when enqueuing) to the actual asynchronous handler functions the worker should execute.
|
|
42
|
+
* **`JobStore` (`store.py`)**: An abstraction layer handling all direct interactions with Redis. It manages job definitions (Hashes), queues (Sorted Sets), processing locks (Strings with TTL), unique job locks, and worker health checks.
|
|
43
|
+
* **`Job` (`job.py`)**: A Pydantic model representing a job, containing its ID, handler name, arguments, status, retry counts, timestamps, results, etc.
|
|
44
|
+
* **`JobStatus` (`job.py`)**: An Enum defining the possible states of a job (`PENDING`, `ACTIVE`, `COMPLETED`, `FAILED`, `RETRYING`).
|
|
45
|
+
* **`RRQSettings` (`settings.py`)**: A Pydantic `BaseSettings` model for configuring RRQ behavior (Redis DSN, queue names, timeouts, retry policies, concurrency, etc.). Loadable from environment variables (prefix `RRQ_`).
|
|
46
|
+
* **`constants.py`**: Defines shared constants like Redis key prefixes and default configuration values.
|
|
47
|
+
* **`exc.py`**: Defines custom exceptions, notably `RetryJob` which handlers can raise to explicitly request a retry, potentially with a custom delay.
|
|
48
|
+
|
|
49
|
+
## Key Features
|
|
50
|
+
|
|
51
|
+
* **At-Least-Once Semantics**: Uses Redis locks to ensure a job is processed by only one worker at a time. If a worker crashes or shuts down mid-processing, the lock expires, and the job *should* be re-processed (though re-queueing on unclean shutdown isn't implemented here yet - graceful shutdown *does* re-queue).
|
|
52
|
+
* **Automatic Retries with Backoff**: Jobs that fail with standard exceptions are automatically retried based on `max_retries` settings, using exponential backoff for delays.
|
|
53
|
+
* **Explicit Retries**: Handlers can raise `RetryJob` to control retry attempts and delays.
|
|
54
|
+
* **Job Timeouts**: Jobs exceeding their configured timeout (`job_timeout_seconds` or `default_job_timeout_seconds`) are terminated and moved to the DLQ.
|
|
55
|
+
* **Dead Letter Queue (DLQ)**: Jobs that fail permanently (max retries reached, fatal error, timeout) are moved to a DLQ list in Redis for inspection.
|
|
56
|
+
* **Job Uniqueness**: The `_unique_key` parameter in `enqueue` prevents duplicate jobs based on a custom key within a specified TTL.
|
|
57
|
+
* **Graceful Shutdown**: Workers listen for SIGINT/SIGTERM and attempt to finish active jobs within a grace period before exiting. Interrupted jobs are re-queued.
|
|
58
|
+
* **Worker Health Checks**: Workers periodically update a health key in Redis with a TTL, allowing monitoring systems to track active workers.
|
|
59
|
+
* **Deferred Execution**: Jobs can be scheduled to run at a future time using `_defer_by` or `_defer_until`.
|
|
60
|
+
*Note: Using deferral with a specific `_job_id` will effectively reschedule the job associated with that ID to the new time, overwriting its previous definition and score. It does not create multiple distinct scheduled jobs with the same ID.*
|
|
61
|
+
|
|
62
|
+
## Basic Usage
|
|
63
|
+
|
|
64
|
+
*(See [`rrq_example.py`](examples/rrq_example.py) in the project root for a runnable example)*
|
|
65
|
+
|
|
66
|
+
**1. Define Handlers:**
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# handlers.py
|
|
70
|
+
import asyncio
|
|
71
|
+
from rrq.exc import RetryJob
|
|
72
|
+
|
|
73
|
+
async def my_task(ctx, message: str):
|
|
74
|
+
job_id = ctx['job_id']
|
|
75
|
+
attempt = ctx['job_try']
|
|
76
|
+
print(f"Processing job {job_id} (Attempt {attempt}): {message}")
|
|
77
|
+
await asyncio.sleep(1)
|
|
78
|
+
if attempt < 3 and message == "retry_me":
|
|
79
|
+
raise RetryJob("Needs another go!")
|
|
80
|
+
print(f"Finished job {job_id}")
|
|
81
|
+
return {"result": f"Processed: {message}"}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**2. Register Handlers:**
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# main_setup.py (or wherever you initialize)
|
|
88
|
+
from rrq.registry import JobRegistry
|
|
89
|
+
from . import handlers # Assuming handlers.py is in the same directory
|
|
90
|
+
|
|
91
|
+
job_registry = JobRegistry()
|
|
92
|
+
job_registry.register("process_message", handlers.my_task)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**3. Configure Settings:**
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# config.py
|
|
99
|
+
from rrq.settings import RRQSettings
|
|
100
|
+
|
|
101
|
+
# Loads from environment variables (RRQ_REDIS_DSN, etc.) or uses defaults
|
|
102
|
+
rrq_settings = RRQSettings()
|
|
103
|
+
# Or override directly:
|
|
104
|
+
# rrq_settings = RRQSettings(redis_dsn="redis://localhost:6379/1")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**4. Enqueue Jobs:**
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
# enqueue_script.py
|
|
111
|
+
import asyncio
|
|
112
|
+
from rrq.client import RRQClient
|
|
113
|
+
from config import rrq_settings # Import your settings
|
|
114
|
+
|
|
115
|
+
async def enqueue_jobs():
|
|
116
|
+
client = RRQClient(settings=rrq_settings)
|
|
117
|
+
await client.enqueue("process_message", "Hello RRQ!")
|
|
118
|
+
await client.enqueue("process_message", "retry_me")
|
|
119
|
+
await client.close()
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
asyncio.run(enqueue_jobs())
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**5. Run a Worker:**
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
# worker_script.py
|
|
129
|
+
from rrq.worker import RRQWorker
|
|
130
|
+
from config import rrq_settings # Import your settings
|
|
131
|
+
from main_setup import job_registry # Import your registry
|
|
132
|
+
|
|
133
|
+
# Create worker instance
|
|
134
|
+
worker = RRQWorker(settings=rrq_settings, job_registry=job_registry)
|
|
135
|
+
|
|
136
|
+
# Run the worker (blocking)
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
worker.run()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
You can run multiple instances of `worker_script.py` for concurrent processing.
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
RRQ behavior is configured via the `RRQSettings` object, which loads values from environment variables prefixed with `RRQ_` by default. Key settings include:
|
|
146
|
+
|
|
147
|
+
* `RRQ_REDIS_DSN`: Connection string for Redis.
|
|
148
|
+
* `RRQ_DEFAULT_QUEUE_NAME`: Default queue name.
|
|
149
|
+
* `RRQ_DEFAULT_MAX_RETRIES`: Default retry limit.
|
|
150
|
+
* `RRQ_DEFAULT_JOB_TIMEOUT_SECONDS`: Default job timeout.
|
|
151
|
+
* `RRQ_WORKER_CONCURRENCY`: Max concurrent jobs per worker.
|
|
152
|
+
* ... and others (see `settings.py`).
|
|
153
|
+
|
|
154
|
+
## RRQ CLI
|
|
155
|
+
|
|
156
|
+
RRQ provides a command-line interface (CLI) for interacting with the job queue system. The `rrq` CLI allows you to manage workers, check system health, and get statistics about queues and jobs.
|
|
157
|
+
|
|
158
|
+
### Usage
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
rrq <command> [options]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Commands
|
|
165
|
+
|
|
166
|
+
- **`worker run`**: Run an RRQ worker process to process jobs from queues.
|
|
167
|
+
```bash
|
|
168
|
+
rrq worker run [--burst] [--detach] --settings <settings_path>
|
|
169
|
+
```
|
|
170
|
+
- `--burst`: Run in burst mode (process one job/batch then exit).
|
|
171
|
+
- `--detach`: Run the worker in the background.
|
|
172
|
+
- `--settings`: Python settings path for application worker settings (e.g., `myapp.worker_config.rrq_settings`).
|
|
173
|
+
|
|
174
|
+
- **`worker watch`**: Run an RRQ worker with auto-restart on file changes in a specified directory.
|
|
175
|
+
```bash
|
|
176
|
+
rrq worker watch [--path <directory>] --settings <settings_path>
|
|
177
|
+
```
|
|
178
|
+
- `--path`: Directory to watch for changes (default: current directory).
|
|
179
|
+
- `--settings`: Python settings path for application worker settings.
|
|
180
|
+
|
|
181
|
+
- **`check`**: Perform a health check on active RRQ workers.
|
|
182
|
+
```bash
|
|
183
|
+
rrq check --settings <settings_path>
|
|
184
|
+
```
|
|
185
|
+
- `--settings`: Python settings path for application settings.
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
### Configuration
|
|
189
|
+
|
|
190
|
+
The CLI uses the same `RRQSettings` as the library, loading configuration from environment variables prefixed with `RRQ_`. You can also specify the settings via the `--settings` option for commands.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
rrq worker run --settings myapp.worker_config.rrq_settings
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Help
|
|
197
|
+
|
|
198
|
+
For detailed help on any command, use:
|
|
199
|
+
```bash
|
|
200
|
+
rrq <command> --help
|
|
201
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
rrq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
rrq/client.py,sha256=2CXHxu-5TnTISqYfHPHNiTUKtdlwzSNWlSALC0tivvA,6778
|
|
3
|
+
rrq/constants.py,sha256=_BY0iIyztTyoaA8HU43JPG0yGtm_Fv05t-zC3B9al4Q,1574
|
|
4
|
+
rrq/exc.py,sha256=NJq3C7pUfcd47AB8kghIN8vdY0l90UrsHQEg4McBHP8,1281
|
|
5
|
+
rrq/job.py,sha256=sjABmhevzen2ufTwSbj86lPYQCW_HZLae1EneCB1ZX8,5571
|
|
6
|
+
rrq/registry.py,sha256=3wqwwIvqbG4zWRAA8UOuL7_AXOWFHYQA9Mxq6Ad63Xo,3048
|
|
7
|
+
rrq/rrq.py,sha256=b3FeT_PU9bN8OfPhC99-i_b1OgIdV6MokyKepi8OFPw,13054
|
|
8
|
+
rrq/settings.py,sha256=o9XdJ85mbCwJYDVKjVMTBaK8VFZWMGdN_Av9T3fIY7M,4397
|
|
9
|
+
rrq/store.py,sha256=6AZsbK4hnNWMueAFtd0UjPqS8TJOPNLhuSufe214VBM,24352
|
|
10
|
+
rrq/worker.py,sha256=3VybAINLiBrnseisuqZt744nFE-6-WD0CBL8mlN437o,39952
|
|
11
|
+
rrq-0.2.5.dist-info/METADATA,sha256=yDdof--bDhs02JP35naVvvOMma-TaEhaPU_fj5eEpyE,8984
|
|
12
|
+
rrq-0.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
13
|
+
rrq-0.2.5.dist-info/entry_points.txt,sha256=tpzz5voYGwoIv6ir-UBUTmkCl1HVtGLTW7An80RUCIk,36
|
|
14
|
+
rrq-0.2.5.dist-info/licenses/LICENSE,sha256=XDvu5hKdS2-_ByiSj3tiu_3zSsrXXoJsgbILGoMpKCw,554
|
|
15
|
+
rrq-0.2.5.dist-info/RECORD,,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2025 Mazdak Rezvani
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|