localqueue 0.1.0__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.
- localqueue-0.1.0/.gitignore +20 -0
- localqueue-0.1.0/CHANGELOG.md +22 -0
- localqueue-0.1.0/LICENSE +21 -0
- localqueue-0.1.0/PKG-INFO +356 -0
- localqueue-0.1.0/README.md +325 -0
- localqueue-0.1.0/docs/api.md +344 -0
- localqueue-0.1.0/docs/index.md +206 -0
- localqueue-0.1.0/docs/operational-maturity.md +96 -0
- localqueue-0.1.0/docs/queues.md +307 -0
- localqueue-0.1.0/docs/release.md +107 -0
- localqueue-0.1.0/docs/retries.md +249 -0
- localqueue-0.1.0/examples/email_worker.py +10 -0
- localqueue-0.1.0/examples/enqueue_email.py +21 -0
- localqueue-0.1.0/localqueue/__init__.py +67 -0
- localqueue-0.1.0/localqueue/cli.py +744 -0
- localqueue-0.1.0/localqueue/queue.py +261 -0
- localqueue-0.1.0/localqueue/retry/__init__.py +39 -0
- localqueue-0.1.0/localqueue/retry/store.py +182 -0
- localqueue-0.1.0/localqueue/retry/tenacity.py +685 -0
- localqueue-0.1.0/localqueue/store.py +1197 -0
- localqueue-0.1.0/localqueue/worker.py +192 -0
- localqueue-0.1.0/pyproject.toml +80 -0
- localqueue-0.1.0/tests/test_cli.py +988 -0
- localqueue-0.1.0/tests/test_queue.py +907 -0
- localqueue-0.1.0/tests/test_retry.py +589 -0
- localqueue-0.1.0/uv.lock +579 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
site/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv
|
|
12
|
+
/talk.md
|
|
13
|
+
/.coverage
|
|
14
|
+
/.cache
|
|
15
|
+
/.codex
|
|
16
|
+
/localqueue_queue
|
|
17
|
+
/localqueue_queue.sqlite3
|
|
18
|
+
/localqueue_retries
|
|
19
|
+
/localqueue_retries.sqlite3
|
|
20
|
+
feedback.md
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Add persistent retry wrappers for sync and async Tenacity retryers.
|
|
6
|
+
- Add SQLite-backed durable local queues with leases, delayed delivery,
|
|
7
|
+
acknowledgements, release, dead-letter records, and requeue from dead-letter
|
|
8
|
+
storage. LMDB remains available as an optional backend.
|
|
9
|
+
- Add CLI commands for config, queue add/pop/ack/release/dead-letter/stats,
|
|
10
|
+
inspect, dead-letter listing, dead-letter requeue, and continuous processing.
|
|
11
|
+
- Add `queue exec` for processing messages with external commands that receive
|
|
12
|
+
the message value as JSON on stdin.
|
|
13
|
+
- Add worker identity metadata with `--worker-id` and `leased_by` for inflight
|
|
14
|
+
message inspection.
|
|
15
|
+
- Document local-worker operational boundaries, at-least-once delivery, and
|
|
16
|
+
idempotency guidance.
|
|
17
|
+
- Add focused examples for enqueueing and processing email jobs locally.
|
|
18
|
+
- Add MIT license metadata for package distribution.
|
|
19
|
+
- Add `dead_letter_on_failure` as the preferred worker failure policy option,
|
|
20
|
+
keeping `dead_letter_on_exhaustion` as a compatibility alias.
|
|
21
|
+
- Enable SQLite WAL journal mode and normal synchronous mode for improved
|
|
22
|
+
concurrency in `SQLiteAttemptStore`.
|
localqueue-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bruno Portis
|
|
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,356 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: localqueue
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Durable local queues for Python, with persistent retry state powered by Tenacity.
|
|
5
|
+
Author: Bruno Portis
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: tenacity>=9.1.4
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Requires-Dist: lmdb>=2.2.0; extra == 'all'
|
|
20
|
+
Requires-Dist: pyyaml>=6.0.3; extra == 'all'
|
|
21
|
+
Requires-Dist: rich>=15.0.0; extra == 'all'
|
|
22
|
+
Requires-Dist: typer>=0.16.0; extra == 'all'
|
|
23
|
+
Provides-Extra: cli
|
|
24
|
+
Requires-Dist: pyyaml>=6.0.3; extra == 'cli'
|
|
25
|
+
Requires-Dist: rich>=15.0.0; extra == 'cli'
|
|
26
|
+
Requires-Dist: typer>=0.16.0; extra == 'cli'
|
|
27
|
+
Provides-Extra: lmdb
|
|
28
|
+
Requires-Dist: lmdb>=2.2.0; extra == 'lmdb'
|
|
29
|
+
Provides-Extra: sqlite
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# `localqueue`
|
|
33
|
+
|
|
34
|
+
[](https://github.com/brunoportis/localqueue/actions/workflows/tests.yml)
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
Durable local queues for Python workers, with persistent retry state powered by
|
|
38
|
+
Tenacity.
|
|
39
|
+
|
|
40
|
+
`localqueue` provides two small building blocks for reliable job processing in
|
|
41
|
+
scripts, CLIs, cron jobs, and small worker processes:
|
|
42
|
+
|
|
43
|
+
- a SQLite-backed queue with at-least-once delivery, leases, delayed delivery,
|
|
44
|
+
acknowledgements, release, and dead-letter records
|
|
45
|
+
- `localqueue.retry`: a Tenacity-backed retry adapter that persists retry
|
|
46
|
+
budgets across process restarts
|
|
47
|
+
|
|
48
|
+
Use the queue when one machine needs to persist jobs and process them later. Use
|
|
49
|
+
the retry layer directly when another system already delivers work and you only
|
|
50
|
+
need durable retry state.
|
|
51
|
+
|
|
52
|
+
## Why this exists
|
|
53
|
+
|
|
54
|
+
Python has good retry tools and in-memory queues, but many worker scripts need a
|
|
55
|
+
small durable local queue without bringing in Celery, Redis, RabbitMQ, SQS, or
|
|
56
|
+
another broker. This project focuses on that local worker shape:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
enqueue job -> lease message -> run handler with retry -> ack or dead-letter
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Tenacity already provides the right retry model:
|
|
63
|
+
|
|
64
|
+
- `Retrying` and `AsyncRetrying`
|
|
65
|
+
- configurable `stop`, `wait`, and `retry` strategies
|
|
66
|
+
- callback hooks such as `before`, `after`, and `before_sleep`
|
|
67
|
+
- decorator wrappers with `retry_with`
|
|
68
|
+
|
|
69
|
+
This library keeps that model and uses it as the retry engine for queue workers
|
|
70
|
+
and lower-level retry wrappers.
|
|
71
|
+
|
|
72
|
+
`localqueue` is not distributed coordination. The default store is a local
|
|
73
|
+
SQLite file, so it is best suited to workers running on the same host or against
|
|
74
|
+
the same local filesystem. If the workload needs multi-host scheduling, high
|
|
75
|
+
write throughput, broker-managed retention, stream processing, or hard
|
|
76
|
+
cross-service ordering guarantees, use a broker or database designed for that
|
|
77
|
+
operating model.
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install localqueue
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`localqueue` requires Python 3.11 or newer.
|
|
86
|
+
|
|
87
|
+
Install the optional CLI dependencies with:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install "localqueue[cli]"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Install `localqueue[lmdb]` when you want the optional LMDB queue or retry
|
|
94
|
+
stores.
|
|
95
|
+
|
|
96
|
+
## CLI
|
|
97
|
+
|
|
98
|
+
The CLI reads YAML configuration from
|
|
99
|
+
`~/.config/localqueue/config.yaml`. `XDG_CONFIG_HOME` is respected when set.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
localqueue config init --store-path ./localqueue_queue.sqlite3
|
|
103
|
+
localqueue config show
|
|
104
|
+
localqueue config set retry_store_path ./localqueue_retries.sqlite3
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Example config:
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
store_path: ./localqueue_queue.sqlite3
|
|
111
|
+
retry_store_path: ./localqueue_retries.sqlite3
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`store_path` is the SQLite queue file. `retry_store_path` is the SQLite file
|
|
115
|
+
used by `queue process` to persist retry attempts.
|
|
116
|
+
|
|
117
|
+
The CLI starts with queue management commands. Values are JSON by default.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
localqueue queue add emails --value '{"to":"user@example.com"}'
|
|
121
|
+
echo '{"to":"user@example.com"}' | localqueue queue add emails
|
|
122
|
+
localqueue queue size emails
|
|
123
|
+
localqueue queue stats emails
|
|
124
|
+
localqueue queue inspect emails <message-id>
|
|
125
|
+
localqueue queue dead emails
|
|
126
|
+
localqueue queue requeue-dead emails <message-id>
|
|
127
|
+
localqueue queue pop emails --worker-id worker-1
|
|
128
|
+
localqueue queue ack emails <message-id>
|
|
129
|
+
localqueue queue exec emails -- python scripts/send_email.py
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Use `--raw` when the queue value should be stored as a string:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
localqueue queue add emails --value user@example.com --raw
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Queue values are stored as JSON in the queue store, so values must be
|
|
139
|
+
JSON-serializable.
|
|
140
|
+
|
|
141
|
+
To process queued messages, pass an importable handler in `module:function`
|
|
142
|
+
format. The handler receives the message value as its first argument. Successful
|
|
143
|
+
handlers ack the message. Failing handlers release the message unless the
|
|
144
|
+
persistent retry budget is exhausted, in which case the message is moved to
|
|
145
|
+
dead-letter storage by default.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
localqueue queue process emails myapp.workers:send_email --max-tries 5
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Use `queue exec` when the handler is an external command. The message value is
|
|
152
|
+
written to the command's stdin as JSON. Exit code `0` acknowledges the message;
|
|
153
|
+
any other exit code is treated as a processing failure and follows the same
|
|
154
|
+
retry, release, and dead-letter rules as `queue process`.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
localqueue queue exec emails -- python scripts/send_email.py
|
|
158
|
+
localqueue queue exec webhooks -- curl -X POST https://example.com/hook -d @-
|
|
159
|
+
localqueue queue exec emails -- sh -c 'jq -r .to | xargs -I{} curl https://example.com/{}'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Use `--forever` for a long-running worker. When interrupted with `SIGINT` or
|
|
163
|
+
`SIGTERM`, the CLI finishes the current message before stopping.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
localqueue queue process emails myapp.workers:send_email \
|
|
167
|
+
--forever \
|
|
168
|
+
--block \
|
|
169
|
+
--worker-id worker-1 \
|
|
170
|
+
--max-tries 5
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`--worker-id` is recorded on leased messages as `leased_by`, which makes
|
|
174
|
+
`queue inspect` useful when multiple workers consume the same queue.
|
|
175
|
+
|
|
176
|
+
## Quickstart
|
|
177
|
+
|
|
178
|
+
Run this from the repository root after installing the optional CLI
|
|
179
|
+
dependencies. It enqueues one email job, processes it with the example handler,
|
|
180
|
+
and leaves no external services running.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
localqueue queue add emails \
|
|
184
|
+
--store-path /tmp/localqueue-demo \
|
|
185
|
+
--value '{"to":"user@example.com"}'
|
|
186
|
+
|
|
187
|
+
localqueue queue process emails examples.email_worker:send_email \
|
|
188
|
+
--store-path /tmp/localqueue-demo \
|
|
189
|
+
--retry-store-path /tmp/localqueue-demo-retries.sqlite3 \
|
|
190
|
+
--worker-id worker-1 \
|
|
191
|
+
--max-tries 3
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Inspect worker ownership while a message is leased:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
localqueue queue inspect emails <message-id> \
|
|
198
|
+
--store-path /tmp/localqueue-demo
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Queue worker
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from localqueue import PersistentQueue, PersistentWorkerConfig, persistent_worker
|
|
205
|
+
from tenacity import retry_if_exception_type, stop_after_attempt, wait_exponential
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
queue = PersistentQueue("emails", store_path="./localqueue_queue.sqlite3")
|
|
209
|
+
queue.put({"to": "user@example.com"})
|
|
210
|
+
|
|
211
|
+
worker_config = PersistentWorkerConfig(
|
|
212
|
+
max_tries=5,
|
|
213
|
+
wait=wait_exponential(multiplier=1, min=1, max=30),
|
|
214
|
+
retry=retry_if_exception_type(ConnectionError),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@persistent_worker(queue, config=worker_config)
|
|
219
|
+
def send_email_job(job: dict[str, str]) -> None:
|
|
220
|
+
send_email(job["to"])
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
send_email_job()
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The worker leases one message, runs the handler with persistent retry state, and
|
|
227
|
+
acknowledges the message on success. If processing keeps failing until the retry
|
|
228
|
+
budget is exhausted, the message is moved to dead-letter storage by default.
|
|
229
|
+
Worker handlers receive `message.value` as their first argument.
|
|
230
|
+
Pass `dead_letter_on_failure=False` to `PersistentWorkerConfig` or
|
|
231
|
+
`persistent_worker()` when final handler failures should release the message
|
|
232
|
+
instead.
|
|
233
|
+
|
|
234
|
+
Use `persistent_async_worker()` for async handlers.
|
|
235
|
+
|
|
236
|
+
## Manual queue control
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from localqueue import PersistentQueue
|
|
240
|
+
|
|
241
|
+
queue = PersistentQueue("emails", store_path="./localqueue_queue.sqlite3")
|
|
242
|
+
|
|
243
|
+
queue.put({"to": "user@example.com"})
|
|
244
|
+
|
|
245
|
+
message = queue.get_message()
|
|
246
|
+
try:
|
|
247
|
+
send_email(message.value["to"])
|
|
248
|
+
except RetryLater as exc:
|
|
249
|
+
queue.release(message, delay=30, error=exc)
|
|
250
|
+
except Exception as exc:
|
|
251
|
+
queue.dead_letter(message, error=exc)
|
|
252
|
+
raise
|
|
253
|
+
else:
|
|
254
|
+
queue.ack(message)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`localqueue` uses at-least-once delivery:
|
|
258
|
+
|
|
259
|
+
- `put()` persists before returning
|
|
260
|
+
- `get_message()` leases a message and moves it to `inflight`
|
|
261
|
+
- `ack()` removes the message permanently
|
|
262
|
+
- `release()` makes it available again, optionally after a delay
|
|
263
|
+
- expired leases are returned to the ready queue
|
|
264
|
+
- `dead_letter()` moves a message out of normal delivery
|
|
265
|
+
- `requeue_dead()` returns a dead-letter message to ready delivery
|
|
266
|
+
|
|
267
|
+
Handlers must be safe to run more than once. A worker can crash after an
|
|
268
|
+
external side effect succeeds but before `ack()` is persisted, and the message
|
|
269
|
+
will be delivered again after the lease expires. Store an idempotency key in the
|
|
270
|
+
payload, pass it to external APIs when possible, or check your own database
|
|
271
|
+
before repeating non-idempotent side effects.
|
|
272
|
+
|
|
273
|
+
When a worker fails, `last_error` and `failed_at` are stored on the message so
|
|
274
|
+
CLI failures and redelivered messages show why processing failed.
|
|
275
|
+
Use `queue stats` when you need ready, delayed, inflight, dead-letter, and total
|
|
276
|
+
counts instead of only ready messages. Use `queue inspect` to inspect one
|
|
277
|
+
message by id, `queue dead` to list dead-letter messages, and
|
|
278
|
+
`queue requeue-dead` to retry one after the failure cause is fixed.
|
|
279
|
+
|
|
280
|
+
## Operational boundaries
|
|
281
|
+
|
|
282
|
+
The default SQLite backend is intentionally simple. It is practical for local
|
|
283
|
+
worker processes, small teams, development tools, and operational scripts, but
|
|
284
|
+
it is not a drop-in replacement for a distributed queue.
|
|
285
|
+
|
|
286
|
+
- Run producers and consumers where they can safely share the same SQLite file.
|
|
287
|
+
- Use small JSON payloads; store large files externally and enqueue references.
|
|
288
|
+
- Treat ordering as best effort once multiple producers or consumers are active.
|
|
289
|
+
- Keep handlers idempotent because delivery is at least once.
|
|
290
|
+
- Monitor `queue stats`, `queue dead`, and store file growth for long-running
|
|
291
|
+
deployments.
|
|
292
|
+
- Back up the SQLite files if queued work or retry state matters after host
|
|
293
|
+
loss.
|
|
294
|
+
- Move to Postgres, Redis, SQS, RabbitMQ, Kafka, or a similar system when you
|
|
295
|
+
need multi-host coordination, high concurrency, retention controls, metrics,
|
|
296
|
+
or managed operations.
|
|
297
|
+
|
|
298
|
+
The [Operational maturity](docs/operational-maturity.md) checklist tracks the
|
|
299
|
+
remaining hardening work before describing this as a mature production queue
|
|
300
|
+
system.
|
|
301
|
+
|
|
302
|
+
## Persistent retry layer
|
|
303
|
+
|
|
304
|
+
Use `localqueue.retry` directly when the job source is not `localqueue`.
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from localqueue.retry import key_from_argument, persistent_retry
|
|
308
|
+
from tenacity import retry_if_exception_type, stop_after_attempt, wait_fixed
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@persistent_retry(
|
|
312
|
+
key_fn=key_from_argument("email_id"),
|
|
313
|
+
stop=stop_after_attempt(3),
|
|
314
|
+
wait=wait_fixed(1),
|
|
315
|
+
retry=retry_if_exception_type(ConnectionError),
|
|
316
|
+
)
|
|
317
|
+
def send_email(email_id: str, address: str) -> None:
|
|
318
|
+
deliver(address)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Every persistent retry needs an explicit key. Use one of these forms:
|
|
322
|
+
|
|
323
|
+
- `key="email:42"` for a retryer bound to one logical job
|
|
324
|
+
- `key_fn=key_from_argument("email_id")` for scalar IDs
|
|
325
|
+
- `key_fn=key_from_attr("task", "id", prefix="process-video")` for object attributes
|
|
326
|
+
- `key_fn=idempotency_key_from_id("task", prefix="process-video")` as shorthand for `task.id`
|
|
327
|
+
|
|
328
|
+
When a retry budget is exhausted, later calls with the same key raise
|
|
329
|
+
`PersistentRetryExhausted` before the wrapped function is called again.
|
|
330
|
+
|
|
331
|
+
## Stores
|
|
332
|
+
|
|
333
|
+
The default queue store is SQLite at `./localqueue_queue.sqlite3`.
|
|
334
|
+
|
|
335
|
+
The default retry store is SQLite at `./localqueue_retries.sqlite3`.
|
|
336
|
+
The CLI `retry_store_path` setting also uses SQLite. In the Python retry API,
|
|
337
|
+
`PersistentRetrying(store_path=...)` selects an optional LMDB attempt-store
|
|
338
|
+
directory; pass `store=SQLiteAttemptStore("retries.sqlite3")` when you want a
|
|
339
|
+
SQLite file explicitly.
|
|
340
|
+
|
|
341
|
+
LMDB remains available as an optional backend through `localqueue[lmdb]`
|
|
342
|
+
and explicit `LMDBQueueStore` or `LMDBAttemptStore` usage.
|
|
343
|
+
|
|
344
|
+
For tests, use `MemoryQueueStore` and `MemoryAttemptStore`.
|
|
345
|
+
|
|
346
|
+
## Documentation
|
|
347
|
+
|
|
348
|
+
- [Persistent queues](docs/queues.md): message lifecycle, workers, leases, delay, and dead-letter behavior.
|
|
349
|
+
- [Persistent retries](docs/retries.md): decorators, low-level retryers, keys, stores, and exhaustion behavior.
|
|
350
|
+
- [API reference](docs/api.md): exported classes, functions, and protocols.
|
|
351
|
+
- [Operational maturity](docs/operational-maturity.md): documented limits and checklist for future production-hardening work.
|
|
352
|
+
- [Release checklist](docs/release.md): manual versioning, build, smoke test, and publish steps.
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
`localqueue` is distributed under the MIT license. See [LICENSE](LICENSE).
|