localqueue 0.4.0__tar.gz → 0.4.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 (56) hide show
  1. {localqueue-0.4.0 → localqueue-0.4.1}/CHANGELOG.md +10 -0
  2. {localqueue-0.4.0 → localqueue-0.4.1}/PKG-INFO +14 -3
  3. {localqueue-0.4.0 → localqueue-0.4.1}/README.md +13 -2
  4. {localqueue-0.4.0 → localqueue-0.4.1}/docs/index.md +15 -5
  5. {localqueue-0.4.0 → localqueue-0.4.1}/docs/queues.md +19 -4
  6. {localqueue-0.4.0 → localqueue-0.4.1}/docs/retries.md +21 -8
  7. localqueue-0.4.1/docs/use-cases/local-outbox.md +83 -0
  8. localqueue-0.4.1/docs/use-cases/operator-recovery.md +85 -0
  9. localqueue-0.4.1/docs/use-cases/persistent-retries.md +74 -0
  10. localqueue-0.4.1/docs/use-cases.md +54 -0
  11. {localqueue-0.4.0 → localqueue-0.4.1}/pyproject.toml +1 -1
  12. {localqueue-0.4.0 → localqueue-0.4.1}/uv.lock +1 -1
  13. localqueue-0.4.0/docs/use-cases.md +0 -174
  14. {localqueue-0.4.0 → localqueue-0.4.1}/.gitignore +0 -0
  15. {localqueue-0.4.0 → localqueue-0.4.1}/LICENSE +0 -0
  16. {localqueue-0.4.0 → localqueue-0.4.1}/docs/api.md +0 -0
  17. {localqueue-0.4.0 → localqueue-0.4.1}/docs/compare.md +0 -0
  18. {localqueue-0.4.0 → localqueue-0.4.1}/docs/develop.md +0 -0
  19. {localqueue-0.4.0 → localqueue-0.4.1}/docs/operational-maturity.md +0 -0
  20. {localqueue-0.4.0 → localqueue-0.4.1}/docs/release.md +0 -0
  21. {localqueue-0.4.0 → localqueue-0.4.1}/docs/stability.md +0 -0
  22. {localqueue-0.4.0 → localqueue-0.4.1}/examples/email_worker.py +0 -0
  23. {localqueue-0.4.0 → localqueue-0.4.1}/examples/enqueue_email.py +0 -0
  24. {localqueue-0.4.0 → localqueue-0.4.1}/examples/process_webhook.sh +0 -0
  25. {localqueue-0.4.0 → localqueue-0.4.1}/examples/retry_demo.py +0 -0
  26. {localqueue-0.4.0 → localqueue-0.4.1}/examples/sqlite_concurrency_benchmark.py +0 -0
  27. {localqueue-0.4.0 → localqueue-0.4.1}/examples/sqlite_process_harness.py +0 -0
  28. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/__init__.py +0 -0
  29. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/cli.py +0 -0
  30. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/cli_commands.py +0 -0
  31. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/cli_support.py +0 -0
  32. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/cli_worker_commands.py +0 -0
  33. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/failure.py +0 -0
  34. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/paths.py +0 -0
  35. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/queue.py +0 -0
  36. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/__init__.py +0 -0
  37. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/stores/__init__.py +0 -0
  38. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/stores/_shared.py +0 -0
  39. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/stores/base.py +0 -0
  40. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/stores/lmdb.py +0 -0
  41. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/stores/memory.py +0 -0
  42. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/stores/sqlite.py +0 -0
  43. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/retry/tenacity.py +0 -0
  44. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/services/__init__.py +0 -0
  45. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/services/queue_worker.py +0 -0
  46. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/stores/__init__.py +0 -0
  47. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/stores/_shared.py +0 -0
  48. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/stores/base.py +0 -0
  49. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/stores/lmdb.py +0 -0
  50. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/stores/memory.py +0 -0
  51. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/stores/sqlite.py +0 -0
  52. {localqueue-0.4.0 → localqueue-0.4.1}/localqueue/worker.py +0 -0
  53. {localqueue-0.4.0 → localqueue-0.4.1}/tests/test_cli.py +0 -0
  54. {localqueue-0.4.0 → localqueue-0.4.1}/tests/test_process_harness.py +0 -0
  55. {localqueue-0.4.0 → localqueue-0.4.1}/tests/test_queue.py +0 -0
  56. {localqueue-0.4.0 → localqueue-0.4.1}/tests/test_retry.py +0 -0
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.1](https://github.com/brunoportis/localqueue/compare/v0.4.0...v0.4.1) (2026-04-25)
4
+
5
+
6
+ ### Documentation
7
+
8
+ * reposition use cases and guides ([8e98379](https://github.com/brunoportis/localqueue/commit/8e98379c29091b414ad437facbdb4d7a7c45db4c))
9
+ * reposition use cases and guides ([280288b](https://github.com/brunoportis/localqueue/commit/280288b02f877d78d2df067df67ce14a800cbfe2))
10
+ * simplify queue examples ([ed488b2](https://github.com/brunoportis/localqueue/commit/ed488b2ab210369331fbcda3568760f0da24618b))
11
+ * split use cases into dedicated pages ([5496826](https://github.com/brunoportis/localqueue/commit/54968266b0733b009147c56761ce9fe29889d5e9))
12
+
3
13
  ## [0.4.0](https://github.com/brunoportis/localqueue/compare/v0.3.4...v0.4.0) (2026-04-24)
4
14
 
5
15
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: localqueue
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Durable local queues for Python, with persistent retry state powered by Tenacity.
5
5
  Project-URL: Homepage, https://brunoportis.github.io/localqueue/
6
6
  Project-URL: Documentation, https://brunoportis.github.io/localqueue/
@@ -40,9 +40,17 @@ Description-Content-Type: text/markdown
40
40
  [![PyPI version](https://img.shields.io/pypi/v/localqueue.svg)](https://pypi.org/project/localqueue/)
41
41
  ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
42
42
 
43
- `localqueue` is a small durable queue for one machine. It stores work on the local filesystem by default and keeps retry state with Tenacity.
43
+ `localqueue` is a small durable queue for one machine. It stores work on the
44
+ local filesystem by default and keeps retry state with Tenacity.
44
45
 
45
- Use it for scripts, CLI tools, cron jobs, and small Python workers that share one local store. Use `localqueue.retry` when another system already delivers work and you only need retry state that survives restarts.
46
+ Use it when the work can stay local and you want one of these outcomes:
47
+
48
+ - accept work now and perform the side effect later on the same machine
49
+ - inspect failed jobs and replay them from the terminal
50
+ - keep retry budgets across process restarts without adding a broker
51
+
52
+ It fits scripts, CLI tools, cron jobs, and small Python workers that share one
53
+ local store.
46
54
 
47
55
  ```bash
48
56
  localqueue queue exec emails -- python scripts/send_email.py
@@ -61,6 +69,9 @@ def send_email(job: dict[str, str]) -> None:
61
69
 
62
70
  Full docs live at [brunoportis.github.io/localqueue](https://brunoportis.github.io/localqueue/). The source docs are in [`docs/`](docs/).
63
71
 
72
+ Start with [`docs/use-cases.md`](docs/use-cases.md) if you want the shortest
73
+ path to deciding whether the project fits your workflow.
74
+
64
75
  For development and release workflow details, see [`docs/develop.md`](docs/develop.md).
65
76
 
66
77
  ## Install
@@ -5,9 +5,17 @@
5
5
  [![PyPI version](https://img.shields.io/pypi/v/localqueue.svg)](https://pypi.org/project/localqueue/)
6
6
  ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
7
7
 
8
- `localqueue` is a small durable queue for one machine. It stores work on the local filesystem by default and keeps retry state with Tenacity.
8
+ `localqueue` is a small durable queue for one machine. It stores work on the
9
+ local filesystem by default and keeps retry state with Tenacity.
9
10
 
10
- Use it for scripts, CLI tools, cron jobs, and small Python workers that share one local store. Use `localqueue.retry` when another system already delivers work and you only need retry state that survives restarts.
11
+ Use it when the work can stay local and you want one of these outcomes:
12
+
13
+ - accept work now and perform the side effect later on the same machine
14
+ - inspect failed jobs and replay them from the terminal
15
+ - keep retry budgets across process restarts without adding a broker
16
+
17
+ It fits scripts, CLI tools, cron jobs, and small Python workers that share one
18
+ local store.
11
19
 
12
20
  ```bash
13
21
  localqueue queue exec emails -- python scripts/send_email.py
@@ -26,6 +34,9 @@ def send_email(job: dict[str, str]) -> None:
26
34
 
27
35
  Full docs live at [brunoportis.github.io/localqueue](https://brunoportis.github.io/localqueue/). The source docs are in [`docs/`](docs/).
28
36
 
37
+ Start with [`docs/use-cases.md`](docs/use-cases.md) if you want the shortest
38
+ path to deciding whether the project fits your workflow.
39
+
29
40
  For development and release workflow details, see [`docs/develop.md`](docs/develop.md).
30
41
 
31
42
  ## Install
@@ -4,19 +4,30 @@ icon: lucide/inbox
4
4
 
5
5
  # Overview
6
6
 
7
- `localqueue` is a small durable queue for one machine. It stores work on the local filesystem by default and keeps retry state with Tenacity.
7
+ `localqueue` is a small durable queue for one machine. It stores work on the
8
+ local filesystem by default and keeps retry state with Tenacity.
8
9
 
9
- It fits scripts, CLI tools, cron jobs, development helpers, and small workers that can safely share one local store. It is not a distributed broker and does not provide multi-host coordination.
10
+ It fits scripts, CLI tools, cron jobs, development helpers, and small workers
11
+ that can safely share one local store. It is not a distributed broker and does
12
+ not provide multi-host coordination.
10
13
 
11
14
  !!! note
12
15
  The default model is local-file storage, at-least-once delivery, and best-effort ordering under concurrency.
13
16
 
17
+ ## Best-fit workflows
18
+
19
+ `localqueue` is strongest in three situations:
20
+
21
+ - local outbox workflows where the caller should return before the side effect happens
22
+ - terminal-driven recovery where operators need dead-letter inspection and replay
23
+ - persistent retry budgets for work that already arrives from somewhere else
24
+
14
25
  ## CLI
15
26
 
16
27
  Use the CLI when you want to enqueue, inspect, watch, and recover jobs from the terminal.
17
28
 
18
29
  ```bash
19
- localqueue queue add emails --value '{"to":"user@example.com"}'
30
+ echo '{"to":"user@example.com"}' | localqueue queue add emails
20
31
  localqueue queue exec emails -- python scripts/send_email.py
21
32
  localqueue queue stats emails --watch --interval 1
22
33
  localqueue queue dead emails --summary
@@ -42,14 +53,13 @@ def send_email(job: dict[str, str]) -> None:
42
53
  deliver(job["to"])
43
54
  ```
44
55
 
45
- `localqueue.retry` stays available when another system already delivers work and you only need retry state.
46
-
47
56
  See [Persistent retries](retries.md) for the retry API and store options.
48
57
 
49
58
  ## When to use
50
59
 
51
60
  - small Python workers on one machine
52
61
  - scripts that need durable work after restarts
62
+ - local outbox workflows for emails, webhooks, uploads, or reports
53
63
  - terminal-driven queues for jobs you want to inspect and requeue
54
64
  - retry state that must survive process restarts
55
65
 
@@ -4,14 +4,28 @@ icon: lucide/inbox
4
4
 
5
5
  # Persistent Queues
6
6
 
7
- `localqueue` is the main workflow API in this project. It is a small queue
8
- abstraction backed by SQLite by default and designed for durable, at-least-once
9
- delivery of Python values.
7
+ `localqueue` queues are for local workflows where you want to accept work now,
8
+ run it later on the same machine, and keep enough state to inspect and recover
9
+ failures from the terminal.
10
+
11
+ This is the main workflow API in the project. It is a small queue abstraction
12
+ backed by SQLite by default and designed for durable, at-least-once delivery of
13
+ Python values.
10
14
 
11
15
  It is local infrastructure: producers and consumers should run where they can
12
16
  share the same queue store safely. It is not a distributed broker and does not
13
17
  provide multi-host coordination.
14
18
 
19
+ Best-fit queue workflows:
20
+
21
+ - local outbox patterns for email, webhooks, uploads, or report generation
22
+ - CLI-driven workers that should survive restarts
23
+ - jobs that may need dead-letter inspection and replay
24
+
25
+ Start somewhere else if the queue itself needs to be a distributed system
26
+ boundary. `localqueue` keeps delivery local, recovery local, and operations
27
+ simple.
28
+
15
29
  If you have independent workloads, give them separate queue names instead of
16
30
  loading everything into one queue. Each queue keeps its own stats, dead
17
31
  letters, retention settings, and worker identity history.
@@ -30,7 +44,8 @@ queue = PersistentQueue(
30
44
  )
31
45
  ```
32
46
 
33
- The queue name identifies an independent stream inside the store. Queue names cannot be empty and cannot contain `:`.
47
+ The queue name identifies an independent stream inside the store. Queue names
48
+ cannot be empty and cannot contain `:`.
34
49
  Queue-level retry defaults are merged into worker retry kwargs before explicit
35
50
  worker overrides, so the same policy can be reused across several workers that
36
51
  consume the same queue.
@@ -4,14 +4,27 @@ icon: lucide/rotate-cw
4
4
 
5
5
  # Persistent Retries
6
6
 
7
- `localqueue` is the durable retry layer used by queue workers and by code
8
- that already has its own delivery mechanism. It wraps Tenacity's `Retrying` and
9
- `AsyncRetrying` classes. You still configure `stop`, `wait`, `retry`, callbacks,
10
- and `retry_with()` the same way; the wrapper adds a durable attempt record before
11
- each stop decision.
12
-
13
- If you need a full job lifecycle with ack, release, leases, and dead-letter
14
- records, start with [Persistent queues](queues.md).
7
+ `localqueue.retry` is for code that already receives the work but still needs a
8
+ retry budget that survives process restarts.
9
+
10
+ This is the durable retry layer used by queue workers and by code that already
11
+ has its own delivery mechanism. It wraps Tenacity's `Retrying` and
12
+ `AsyncRetrying` classes. You still configure `stop`, `wait`, `retry`,
13
+ callbacks, and `retry_with()` the same way; the wrapper adds a durable attempt
14
+ record before each stop decision.
15
+
16
+ Best-fit retry workflows:
17
+
18
+ - cron jobs that call fragile external services
19
+ - HTTP handlers or consumers that need durable retry budgets per logical job
20
+ - scripts where retry state must outlive the current process
21
+
22
+ Use this layer when the missing piece is persistent attempt tracking. If you
23
+ need ack, release, leases, dead-letter records, or queue inspection, start with
24
+ [Persistent queues](queues.md) instead.
25
+
26
+ The main design constraint is stable identity: each logical job needs a stable
27
+ retry key so the same work does not retry forever under a new process.
15
28
 
16
29
  ## Decorator API
17
30
 
@@ -0,0 +1,83 @@
1
+ ---
2
+ icon: lucide/mail
3
+ ---
4
+
5
+ # Local Outbox
6
+
7
+ Use this pattern when the caller should return now and the external side effect
8
+ can happen a few seconds later on the same machine.
9
+
10
+ This is one of the strongest `localqueue` stories: accept work quickly, store
11
+ it durably, and let a local worker perform the slow or fragile side effect
12
+ later.
13
+
14
+ ## Best fit
15
+
16
+ - email delivery from a web app or admin script
17
+ - webhook fan-out from a local integration process
18
+ - uploads, report generation, or notifications that should not block the caller
19
+
20
+ ## Why `localqueue` fits
21
+
22
+ - no broker, Redis, or extra service to operate
23
+ - queue state survives process restarts
24
+ - the worker can be a Python function or an external command
25
+
26
+ ## Boundaries
27
+
28
+ - this is a single-host pattern, not a distributed outbox
29
+ - delivery is at least once, so handlers should be idempotent
30
+
31
+ ## Setup
32
+
33
+ Create a scratch directory and a small worker script once:
34
+
35
+ ```bash
36
+ mkdir -p /tmp/localqueue-use-cases
37
+ cat > /tmp/localqueue-use-cases/email_worker.py <<'PY'
38
+ from __future__ import annotations
39
+
40
+ import json
41
+ import sys
42
+
43
+
44
+ payload = json.load(sys.stdin)
45
+ address = payload["to"]
46
+ if payload.get("fail"):
47
+ raise ConnectionError(f"could not deliver email to {address}")
48
+ print(f"sent email to {address}")
49
+ PY
50
+ ```
51
+
52
+ ## Minimal flow
53
+
54
+ Enqueue one email job:
55
+
56
+ ```bash
57
+ echo '{"to":"user@example.com"}' | localqueue queue add emails
58
+ ```
59
+
60
+ Inspect the queue before processing:
61
+
62
+ ```bash
63
+ localqueue queue stats emails
64
+ ```
65
+
66
+ Process one queued message with the worker script:
67
+
68
+ ```bash
69
+ localqueue queue exec emails \
70
+ -- python /tmp/localqueue-use-cases/email_worker.py
71
+ ```
72
+
73
+ Check that the queue is empty again:
74
+
75
+ ```bash
76
+ localqueue queue stats emails
77
+ ```
78
+
79
+ ## When to choose something else
80
+
81
+ Use a broker or managed queue instead when producers and consumers run on
82
+ different machines, strict global ordering matters, or the queue is part of a
83
+ distributed system boundary.
@@ -0,0 +1,85 @@
1
+ ---
2
+ icon: lucide/life-buoy
3
+ ---
4
+
5
+ # Operator Recovery
6
+
7
+ Use this pattern when jobs can fail in recoverable ways and operators need to
8
+ inspect, dead-letter, and replay them from the terminal.
9
+
10
+ This is where `localqueue` feels more useful than a pile of shell scripts:
11
+ failures stay visible, recovery is explicit, and replay does not require a
12
+ custom one-off script.
13
+
14
+ ## Best fit
15
+
16
+ - webhook delivery with occasional upstream outages
17
+ - notification pipelines that need human inspection on failure
18
+ - CLI-driven maintenance jobs where operators need a dead-letter queue
19
+
20
+ ## Why `localqueue` fits
21
+
22
+ - failed jobs can move to dead-letter after a small retry budget
23
+ - operators can inspect summary and full records locally
24
+ - replay is one command instead of ad hoc recovery code
25
+
26
+ ## Boundaries
27
+
28
+ - recovery is local to one machine and one shared store
29
+ - this does not replace broker-level operations across multiple services
30
+
31
+ ## Setup
32
+
33
+ Create a scratch directory and a small worker script once:
34
+
35
+ ```bash
36
+ mkdir -p /tmp/localqueue-use-cases
37
+ cat > /tmp/localqueue-use-cases/email_worker.py <<'PY'
38
+ from __future__ import annotations
39
+
40
+ import json
41
+ import sys
42
+
43
+
44
+ payload = json.load(sys.stdin)
45
+ address = payload["to"]
46
+ if payload.get("fail"):
47
+ raise ConnectionError(f"could not deliver email to {address}")
48
+ print(f"sent email to {address}")
49
+ PY
50
+ ```
51
+
52
+ ## Minimal flow
53
+
54
+ Create a failing job:
55
+
56
+ ```bash
57
+ echo '{"to":"broken@example.com","fail":true}' | localqueue queue add webhooks
58
+ ```
59
+
60
+ Process it with a small retry budget so it reaches dead-letter quickly:
61
+
62
+ ```bash
63
+ localqueue queue exec webhooks \
64
+ --max-tries 2 \
65
+ -- python /tmp/localqueue-use-cases/email_worker.py
66
+ ```
67
+
68
+ Inspect the dead-letter summary and full record:
69
+
70
+ ```bash
71
+ localqueue queue dead webhooks --summary
72
+
73
+ localqueue queue dead webhooks
74
+ ```
75
+
76
+ Replay everything in the dead-letter queue:
77
+
78
+ ```bash
79
+ localqueue queue requeue-dead webhooks --all
80
+ ```
81
+
82
+ ## When to choose something else
83
+
84
+ Use a different system when recovery, retention, and observability must work as
85
+ shared platform capabilities across multiple hosts or services.
@@ -0,0 +1,74 @@
1
+ ---
2
+ icon: lucide/rotate-cw
3
+ ---
4
+
5
+ # Persistent Retries
6
+
7
+ Use this pattern when another part of your system already delivers the work and
8
+ the missing piece is a retry budget that survives process restarts.
9
+
10
+ This page is about persistent attempt tracking, not a full queue lifecycle. If
11
+ you need ack, release, leases, or dead-letter handling, start with
12
+ `PersistentQueue` instead.
13
+
14
+ ## Best fit
15
+
16
+ - cron jobs that call an external API
17
+ - HTTP handlers that already receive the work but need durable retry budgets
18
+ - consumers from another queueing system that want persistent retry state
19
+
20
+ ## Why `localqueue` fits
21
+
22
+ - retry budgets survive process restarts
23
+ - a stable key prevents the same logical job from being retried forever
24
+ - exhausted jobs can be pruned later from the CLI
25
+
26
+ ## Boundaries
27
+
28
+ - this is retry state, not a full message lifecycle
29
+ - each logical job needs a stable retry key
30
+
31
+ ## Minimal flow
32
+
33
+ Run this once in an environment where `localqueue` is importable.
34
+
35
+ ```bash
36
+ python - <<'PY'
37
+ from localqueue.retry import PersistentRetryExhausted, persistent_retry
38
+
39
+
40
+ @persistent_retry(
41
+ key="invoice:1001",
42
+ max_tries=3,
43
+ )
44
+ def flaky() -> None:
45
+ print("calling remote API")
46
+ raise ConnectionError("temporary upstream failure")
47
+
48
+
49
+ try:
50
+ flaky()
51
+ except PersistentRetryExhausted as exc:
52
+ print(f"exhausted: {exc.key} after {exc.attempts} attempts")
53
+ except Exception as exc:
54
+ print(f"failed this run: {exc}")
55
+ PY
56
+ ```
57
+
58
+ Run the same command again. The retry state is reused because the key is
59
+ stable and the attempt store is on disk. After the budget is exhausted,
60
+ `PersistentRetryExhausted` is raised before the wrapped function runs again.
61
+
62
+ Inspect or prune exhausted retry records later:
63
+
64
+ ```bash
65
+ localqueue retry prune \
66
+ --dry-run \
67
+ --older-than 0
68
+ ```
69
+
70
+ ## When to choose something else
71
+
72
+ Use a queue instead when work must be stored for later delivery, acknowledged
73
+ after success, released for another attempt, or moved to dead-letter on final
74
+ failure.
@@ -0,0 +1,54 @@
1
+ ---
2
+ icon: lucide/briefcase
3
+ ---
4
+
5
+ # Use Cases
6
+
7
+ `localqueue` shines when the work can stay on one machine and the value is in
8
+ durable retries, local recovery, and simple terminal operations.
9
+
10
+ The pages in this section focus on workflows that are already well supported by
11
+ the current queue, worker, dead-letter, and persistent-retry APIs.
12
+
13
+ By default, `localqueue` stores queue and retry state under the usual XDG data
14
+ location for your user.
15
+
16
+ ## The three strongest workflows
17
+
18
+ ### Local outbox
19
+
20
+ Accept work now, run the external side effect later on the same machine.
21
+
22
+ This is the best fit when the caller should return quickly and the worker can
23
+ consume the job from a shared local store.
24
+
25
+ [Read the local outbox guide](use-cases/local-outbox.md)
26
+
27
+ ### Operator recovery
28
+
29
+ Inspect failed jobs, review dead-letter records, and replay them from the
30
+ terminal after the underlying issue is fixed.
31
+
32
+ This is the best fit when a human operator needs a local recovery loop instead
33
+ of ad hoc shell scripts.
34
+
35
+ [Read the operator recovery guide](use-cases/operator-recovery.md)
36
+
37
+ ### Persistent retries
38
+
39
+ Keep retry budgets across restarts when another part of the system already
40
+ delivers the work.
41
+
42
+ This is the best fit when you need durable attempt tracking but not a queue
43
+ lifecycle.
44
+
45
+ [Read the persistent retries guide](use-cases/persistent-retries.md)
46
+
47
+ ## When these use cases do not fit
48
+
49
+ Use a broker or managed queue instead when:
50
+
51
+ - producers and consumers run on different machines
52
+ - strict global ordering matters
53
+ - very high write concurrency is expected
54
+ - queue retention, fan-out, or cross-service coordination is a first-class need
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "localqueue"
3
- version = "0.4.0"
3
+ version = "0.4.1"
4
4
  description = "Durable local queues for Python, with persistent retry state powered by Tenacity."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -200,7 +200,7 @@ wheels = [
200
200
 
201
201
  [[package]]
202
202
  name = "localqueue"
203
- version = "0.4.0"
203
+ version = "0.4.1"
204
204
  source = { editable = "." }
205
205
  dependencies = [
206
206
  { name = "tenacity" },
@@ -1,174 +0,0 @@
1
- ---
2
- icon: lucide/briefcase
3
- ---
4
-
5
- # Use Cases
6
-
7
- These examples are meant to be copied and run from any directory.
8
- They keep state under `/tmp/localqueue-use-cases` so they do not interfere with
9
- your normal localqueue data.
10
-
11
- ## Command style
12
-
13
- The examples below use the direct CLI form:
14
-
15
- ```bash
16
- localqueue queue exec ...
17
- ```
18
-
19
- Install `localqueue[cli]` first if the `localqueue` command is not available.
20
- If you prefer `pipx run` or `uvx`, translate the command locally after you
21
- understand the workflow.
22
-
23
- ## Setup
24
-
25
- Create a scratch directory and a small worker script once:
26
-
27
- ```bash
28
- mkdir -p /tmp/localqueue-use-cases
29
- cat > /tmp/localqueue-use-cases/email_worker.py <<'PY'
30
- from __future__ import annotations
31
-
32
- import json
33
- import sys
34
-
35
-
36
- payload = json.load(sys.stdin)
37
- address = payload["to"]
38
- if payload.get("fail"):
39
- raise ConnectionError(f"could not deliver email to {address}")
40
- print(f"sent email to {address}")
41
- PY
42
- ```
43
-
44
- ## 1. Local email spooler
45
-
46
- This is the simplest shape for `localqueue`: one process enqueues work and
47
- another process handles it later on the same machine.
48
-
49
- Enqueue one email job:
50
-
51
- ```bash
52
- localqueue queue add emails \
53
- --store-path /tmp/localqueue-use-cases/emails.sqlite3 \
54
- --value '{"to":"user@example.com"}'
55
- ```
56
-
57
- Inspect the queue before processing:
58
-
59
- ```bash
60
- localqueue queue stats emails \
61
- --store-path /tmp/localqueue-use-cases/emails.sqlite3
62
- ```
63
-
64
- Process one queued message with the worker script:
65
-
66
- ```bash
67
- localqueue queue exec emails \
68
- --store-path /tmp/localqueue-use-cases/emails.sqlite3 \
69
- -- python /tmp/localqueue-use-cases/email_worker.py
70
- ```
71
-
72
- Check that the queue is empty again:
73
-
74
- ```bash
75
- localqueue queue stats emails \
76
- --store-path /tmp/localqueue-use-cases/emails.sqlite3
77
- ```
78
-
79
- Use this pattern when your app should accept work quickly and let a local
80
- worker perform the external side effect later.
81
-
82
- ## 2. Dead-letter and recover a failed command
83
-
84
- This recipe shows a practical operator loop: enqueue a job that fails, inspect
85
- the dead-letter list, then requeue it after fixing the payload.
86
-
87
- Create a failing job:
88
-
89
- ```bash
90
- localqueue queue add emails \
91
- --store-path /tmp/localqueue-use-cases/failing-emails.sqlite3 \
92
- --value '{"to":"broken@example.com","fail":true}'
93
- ```
94
-
95
- Process it with a small retry budget so it reaches dead-letter quickly:
96
-
97
- ```bash
98
- localqueue queue exec emails \
99
- --store-path /tmp/localqueue-use-cases/failing-emails.sqlite3 \
100
- --retry-store-path /tmp/localqueue-use-cases/failing-emails-retries.sqlite3 \
101
- --max-tries 2 \
102
- -- python /tmp/localqueue-use-cases/email_worker.py
103
- ```
104
-
105
- Inspect the dead-letter summary and full record:
106
-
107
- ```bash
108
- localqueue queue dead emails \
109
- --store-path /tmp/localqueue-use-cases/failing-emails.sqlite3 \
110
- --summary
111
-
112
- localqueue queue dead emails \
113
- --store-path /tmp/localqueue-use-cases/failing-emails.sqlite3
114
- ```
115
-
116
- If you want to replay everything in the dead-letter queue:
117
-
118
- ```bash
119
- localqueue queue requeue-dead emails \
120
- --store-path /tmp/localqueue-use-cases/failing-emails.sqlite3 \
121
- --all
122
- ```
123
-
124
- This is a good fit for webhook delivery, notification pipelines, and other
125
- single-host jobs where operators need to inspect and replay failed work from
126
- the terminal.
127
-
128
- ## 3. Persistent retries without a queue
129
-
130
- Sometimes another system already delivers the job and all you need is retry
131
- state that survives restarts. In that case, use `localqueue.retry` directly.
132
-
133
- Run this once in an environment where `localqueue` is importable.
134
-
135
- ```bash
136
- python - <<'PY'
137
- from localqueue.retry import PersistentRetryExhausted, persistent_retry
138
-
139
-
140
- @persistent_retry(
141
- key="invoice:1001",
142
- store_path="/tmp/localqueue-use-cases/retries.sqlite3",
143
- max_tries=3,
144
- )
145
- def flaky() -> None:
146
- print("calling remote API")
147
- raise ConnectionError("temporary upstream failure")
148
-
149
-
150
- try:
151
- flaky()
152
- except PersistentRetryExhausted as exc:
153
- print(f"exhausted: {exc.key} after {exc.attempts} attempts")
154
- except Exception as exc:
155
- print(f"failed this run: {exc}")
156
- PY
157
- ```
158
-
159
- Run the same command again. The retry state is reused because the key is
160
- stable and the attempt store is on disk. After the budget is exhausted,
161
- `PersistentRetryExhausted` is raised before the wrapped function runs again.
162
-
163
- You can later inspect or prune exhausted retry records:
164
-
165
- ```bash
166
- localqueue retry prune \
167
- --retry-store-path /tmp/localqueue-use-cases/retries.sqlite3 \
168
- --dry-run \
169
- --older-than 0
170
- ```
171
-
172
- Use this shape when work already arrives from somewhere else, such as a cron
173
- trigger, another queue, or an HTTP handler, and you only want durable retry
174
- budgets.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes