flashq 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.
Files changed (50) hide show
  1. flashq-0.1.0/.github/workflows/ci.yml +77 -0
  2. flashq-0.1.0/.gitignore +43 -0
  3. flashq-0.1.0/CONTRIBUTING.md +82 -0
  4. flashq-0.1.0/LICENSE +21 -0
  5. flashq-0.1.0/PKG-INFO +389 -0
  6. flashq-0.1.0/README.md +343 -0
  7. flashq-0.1.0/docs/research/CELERY-DEEP-ANALYSIS.md +474 -0
  8. flashq-0.1.0/docs/research/FINAL-REALITY-CHECK.md +309 -0
  9. flashq-0.1.0/docs/research/LAUNCH-STRATEGY.md +256 -0
  10. flashq-0.1.0/docs/research/OPPORTUNITY-ANALYSIS.md +326 -0
  11. flashq-0.1.0/docs/research/REALITY-CHECK-REPORT.md +232 -0
  12. flashq-0.1.0/docs/research/UPDATED-MARKET-ANALYSIS.md +154 -0
  13. flashq-0.1.0/examples/advanced.py +102 -0
  14. flashq-0.1.0/examples/basic.py +43 -0
  15. flashq-0.1.0/examples/fastapi_app.py +59 -0
  16. flashq-0.1.0/flashq/__init__.py +91 -0
  17. flashq-0.1.0/flashq/__main__.py +5 -0
  18. flashq-0.1.0/flashq/_version.py +3 -0
  19. flashq-0.1.0/flashq/app.py +281 -0
  20. flashq-0.1.0/flashq/backends/__init__.py +174 -0
  21. flashq-0.1.0/flashq/backends/postgres.py +363 -0
  22. flashq-0.1.0/flashq/backends/redis.py +288 -0
  23. flashq-0.1.0/flashq/backends/sqlite.py +473 -0
  24. flashq-0.1.0/flashq/canvas.py +302 -0
  25. flashq-0.1.0/flashq/cli.py +219 -0
  26. flashq-0.1.0/flashq/contrib/__init__.py +1 -0
  27. flashq-0.1.0/flashq/dashboard/__init__.py +1 -0
  28. flashq-0.1.0/flashq/dlq.py +173 -0
  29. flashq-0.1.0/flashq/enums.py +58 -0
  30. flashq-0.1.0/flashq/exceptions.py +59 -0
  31. flashq-0.1.0/flashq/middleware.py +212 -0
  32. flashq-0.1.0/flashq/models.py +146 -0
  33. flashq-0.1.0/flashq/py.typed +0 -0
  34. flashq-0.1.0/flashq/ratelimit.py +192 -0
  35. flashq-0.1.0/flashq/scheduler.py +216 -0
  36. flashq-0.1.0/flashq/serializers.py +75 -0
  37. flashq-0.1.0/flashq/task.py +259 -0
  38. flashq-0.1.0/flashq/worker.py +420 -0
  39. flashq-0.1.0/pyproject.toml +103 -0
  40. flashq-0.1.0/tests/__init__.py +0 -0
  41. flashq-0.1.0/tests/test_backends.py +149 -0
  42. flashq-0.1.0/tests/test_canvas.py +307 -0
  43. flashq-0.1.0/tests/test_cli.py +271 -0
  44. flashq-0.1.0/tests/test_core.py +510 -0
  45. flashq-0.1.0/tests/test_dlq.py +214 -0
  46. flashq-0.1.0/tests/test_middleware.py +401 -0
  47. flashq-0.1.0/tests/test_ratelimit.py +183 -0
  48. flashq-0.1.0/tests/test_scheduler.py +147 -0
  49. flashq-0.1.0/tests/test_serializers.py +61 -0
  50. flashq-0.1.0/tests/test_worker.py +258 -0
@@ -0,0 +1,77 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ${{ matrix.os }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-latest, macos-latest, windows-latest]
17
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Set up Python ${{ matrix.python-version }}
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: pip install -e ".[dev]"
29
+
30
+ - name: Lint
31
+ run: ruff check flashq/
32
+
33
+ - name: Format check
34
+ run: ruff format flashq/ --check
35
+
36
+ - name: Type check
37
+ run: mypy flashq/ --ignore-missing-imports
38
+ continue-on-error: true
39
+
40
+ - name: Test
41
+ run: pytest tests/ -v --tb=short
42
+
43
+ coverage:
44
+ runs-on: ubuntu-latest
45
+ needs: test
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+ - uses: actions/setup-python@v5
49
+ with:
50
+ python-version: "3.12"
51
+ - run: pip install -e ".[dev]"
52
+ - run: pytest tests/ --cov=flashq --cov-report=term-missing --cov-report=xml
53
+ - name: Upload coverage
54
+ uses: codecov/codecov-action@v4
55
+ with:
56
+ file: coverage.xml
57
+ continue-on-error: true
58
+
59
+ publish:
60
+ runs-on: ubuntu-latest
61
+ needs: [test, coverage]
62
+ if: startsWith(github.ref, 'refs/tags/v')
63
+ permissions:
64
+ id-token: write
65
+ steps:
66
+ - uses: actions/checkout@v4
67
+ - uses: actions/setup-python@v5
68
+ with:
69
+ python-version: "3.12"
70
+ - name: Install build tools
71
+ run: pip install build
72
+ - name: Build
73
+ run: python -m build
74
+ - name: Publish to PyPI
75
+ uses: pypa/gh-action-pypi-publish@release/v1
76
+ with:
77
+ password: ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,43 @@
1
+ # Byte-compiled
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual env
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # IDE
18
+ .vscode/
19
+ .idea/
20
+ *.swp
21
+ *.swo
22
+
23
+ # Testing
24
+ .pytest_cache/
25
+ htmlcov/
26
+ .coverage
27
+ .coverage.*
28
+
29
+ # mypy
30
+ .mypy_cache/
31
+
32
+ # ruff
33
+ .ruff_cache/
34
+
35
+ # FlashQ database files
36
+ *.db
37
+ *.db-wal
38
+ *.db-shm
39
+
40
+ # OS
41
+ .DS_Store
42
+ Thumbs.db
43
+ reddit_research.txt
@@ -0,0 +1,82 @@
1
+ # Contributing to FlashQ
2
+
3
+ Thanks for considering contributing to FlashQ! Here's how to get started.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/bysiber/flashq.git
9
+ cd flashq
10
+ python -m venv .venv
11
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
12
+ pip install -e ".[dev]"
13
+ ```
14
+
15
+ ## Running Tests
16
+
17
+ ```bash
18
+ # All tests
19
+ pytest tests/ -v
20
+
21
+ # With coverage
22
+ pytest tests/ --cov=flashq --cov-report=term-missing
23
+
24
+ # Specific test file
25
+ pytest tests/test_core.py -v
26
+ ```
27
+
28
+ ## Code Quality
29
+
30
+ ```bash
31
+ # Lint
32
+ ruff check flashq/
33
+
34
+ # Format
35
+ ruff format flashq/
36
+
37
+ # Type check
38
+ mypy flashq/ --ignore-missing-imports
39
+ ```
40
+
41
+ ## Pull Request Process
42
+
43
+ 1. Fork the repository
44
+ 2. Create a feature branch (`git checkout -b feature/my-feature`)
45
+ 3. Write tests for your changes
46
+ 4. Ensure all tests pass and linting is clean
47
+ 5. Submit a PR with a clear description
48
+
49
+ ## Architecture Overview
50
+
51
+ ```
52
+ flashq/
53
+ ├── app.py # FlashQ class, task registry
54
+ ├── task.py # Task decorator, TaskHandle
55
+ ├── models.py # TaskMessage, TaskResult dataclasses
56
+ ├── worker.py # Worker process, task execution
57
+ ├── middleware.py # Middleware system
58
+ ├── scheduler.py # Periodic/cron task scheduler
59
+ ├── backends/
60
+ │ ├── __init__.py # BaseBackend ABC
61
+ │ ├── sqlite.py # SQLite backend (default)
62
+ │ ├── postgres.py # PostgreSQL backend
63
+ │ └── redis.py # Redis backend
64
+ ├── serializers.py # JSON/Pickle serialization
65
+ ├── cli.py # Command-line interface
66
+ ├── enums.py # TaskState, TaskPriority
67
+ └── exceptions.py # Exception hierarchy
68
+ ```
69
+
70
+ ## Adding a New Backend
71
+
72
+ 1. Subclass `BaseBackend` from `flashq.backends`
73
+ 2. Implement all abstract methods
74
+ 3. Add tests in `tests/test_backend_yourname.py`
75
+ 4. Add optional dependency in `pyproject.toml`
76
+
77
+ ## Code Style
78
+
79
+ - Python 3.10+ features are welcome
80
+ - Type hints everywhere
81
+ - Docstrings for public APIs
82
+ - Keep imports sorted (ruff handles this)
flashq-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ozden
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.
flashq-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,389 @@
1
+ Metadata-Version: 2.4
2
+ Name: flashq
3
+ Version: 0.1.0
4
+ Summary: The task queue that works out of the box — no Redis, no RabbitMQ, just pip install and go.
5
+ Project-URL: Homepage, https://github.com/ozden/flashq
6
+ Project-URL: Documentation, https://flashq.readthedocs.io
7
+ Project-URL: Repository, https://github.com/ozden/flashq
8
+ Project-URL: Issues, https://github.com/ozden/flashq/issues
9
+ Author: Ozden
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: async,background-tasks,celery,job-queue,task-queue
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries
23
+ Classifier: Topic :: System :: Distributed Computing
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Provides-Extra: all
27
+ Requires-Dist: psycopg[binary,pool]>=3.1; extra == 'all'
28
+ Requires-Dist: redis>=5.0; extra == 'all'
29
+ Requires-Dist: starlette>=0.36; extra == 'all'
30
+ Requires-Dist: uvicorn>=0.27; extra == 'all'
31
+ Provides-Extra: dashboard
32
+ Requires-Dist: starlette>=0.36; extra == 'dashboard'
33
+ Requires-Dist: uvicorn>=0.27; extra == 'dashboard'
34
+ Provides-Extra: dev
35
+ Requires-Dist: mypy>=1.8; extra == 'dev'
36
+ Requires-Dist: pre-commit>=3.6; extra == 'dev'
37
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
38
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
39
+ Requires-Dist: pytest>=8.0; extra == 'dev'
40
+ Requires-Dist: ruff>=0.2; extra == 'dev'
41
+ Provides-Extra: postgres
42
+ Requires-Dist: psycopg[binary,pool]>=3.1; extra == 'postgres'
43
+ Provides-Extra: redis
44
+ Requires-Dist: redis>=5.0; extra == 'redis'
45
+ Description-Content-Type: text/markdown
46
+
47
+ # ⚡ FlashQ
48
+
49
+ **The task queue that works out of the box — no Redis, no RabbitMQ, just `pip install flashq` and go.**
50
+
51
+ [![CI](https://github.com/bysiber/flashq/actions/workflows/ci.yml/badge.svg)](https://github.com/bysiber/flashq/actions/workflows/ci.yml)
52
+ [![Python](https://img.shields.io/pypi/pyversions/flashq)](https://pypi.org/project/flashq/)
53
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
54
+ [![Tests](https://img.shields.io/badge/tests-226%20passing-brightgreen)]()
55
+
56
+ ---
57
+
58
+ ## Why FlashQ?
59
+
60
+ Every Python developer has been there: you need background tasks, you look at Celery, and suddenly you need Redis or RabbitMQ running, a separate broker config, and 200 lines of boilerplate before your first task runs.
61
+
62
+ **FlashQ changes that.** SQLite is the default backend — zero external dependencies, zero config. Your tasks persist across restarts, and you can scale to PostgreSQL or Redis when you need to.
63
+
64
+ ```python
65
+ from flashq import FlashQ
66
+
67
+ app = FlashQ() # That's it. Uses SQLite by default.
68
+
69
+ @app.task()
70
+ def send_email(to: str, subject: str) -> None:
71
+ print(f"Sending email to {to}: {subject}")
72
+
73
+ # Enqueue a task
74
+ send_email.delay(to="user@example.com", subject="Welcome!")
75
+ ```
76
+
77
+ ## Features
78
+
79
+ | Feature | FlashQ | Celery | Dramatiq | Huey | TaskIQ |
80
+ |---------|:------:|:------:|:--------:|:----:|:------:|
81
+ | Zero-config setup | ✅ | ❌ | ❌ | ⚠️ | ❌ |
82
+ | SQLite backend | ✅ | ❌ | ❌ | ✅ | ❌ |
83
+ | PostgreSQL backend | ✅ | ❌ | ❌ | ❌ | ⚠️ |
84
+ | Redis backend | ✅ | ✅ | ✅ | ✅ | ✅ |
85
+ | Async + Sync tasks | ✅ | ❌ | ❌ | ❌ | Async only |
86
+ | Type-safe `.delay()` | ✅ | ❌ | ⚠️ | ❌ | ✅ |
87
+ | Task chains/groups | ✅ | ✅ | ❌ | ❌ | ❌ |
88
+ | Middleware system | ✅ | ✅ | ✅ | ❌ | ✅ |
89
+ | Rate limiting | ✅ | ✅ | ❌ | ❌ | ❌ |
90
+ | Dead letter queue | ✅ | ❌ | ❌ | ❌ | ❌ |
91
+ | Task timeouts | ✅ | ✅ | ✅ | ❌ | ✅ |
92
+ | Periodic/cron scheduler | ✅ | ⚠️ | ❌ | ✅ | ⚠️ |
93
+ | Zero dependencies | ✅ | ❌ | ❌ | ❌ | ❌ |
94
+
95
+ ## Installation
96
+
97
+ ```bash
98
+ # Core (SQLite only — zero dependencies!)
99
+ pip install flashq
100
+
101
+ # With Redis
102
+ pip install "flashq[redis]"
103
+
104
+ # With PostgreSQL
105
+ pip install "flashq[postgres]"
106
+
107
+ # Development
108
+ pip install "flashq[dev]"
109
+ ```
110
+
111
+ ## Quick Start
112
+
113
+ ### 1. Define tasks
114
+
115
+ ```python
116
+ # tasks.py
117
+ from flashq import FlashQ
118
+
119
+ app = FlashQ()
120
+
121
+ @app.task()
122
+ def add(x: int, y: int) -> int:
123
+ return x + y
124
+
125
+ @app.task(queue="emails", max_retries=5, retry_delay=30.0)
126
+ def send_email(to: str, subject: str, body: str) -> dict:
127
+ return {"status": "sent", "to": to}
128
+
129
+ @app.task(timeout=120.0) # Kill if takes >2 min
130
+ async def process_image(url: str) -> str:
131
+ # Async tasks just work™
132
+ result = await download_and_resize(url)
133
+ return result
134
+ ```
135
+
136
+ ### 2. Enqueue tasks
137
+
138
+ ```python
139
+ from tasks import add, send_email
140
+
141
+ # Simple dispatch
142
+ handle = add.delay(2, 3)
143
+
144
+ # With options
145
+ handle = send_email.apply(
146
+ kwargs={"to": "user@example.com", "subject": "Hi", "body": "Hello!"},
147
+ countdown=60, # delay by 60 seconds
148
+ )
149
+
150
+ # Check result
151
+ result = handle.get_result()
152
+ if result and result.is_success:
153
+ print(f"Result: {result.result}")
154
+ ```
155
+
156
+ ### 3. Start the worker
157
+
158
+ ```bash
159
+ flashq worker tasks:app
160
+ ```
161
+
162
+ ```
163
+ ⚡ FlashQ Worker
164
+ ├─ name: worker-12345
165
+ ├─ backend: SQLiteBackend
166
+ ├─ queues: default
167
+ ├─ concurrency: 4
168
+ ├─ tasks: 3
169
+ │ └─ tasks.add
170
+ │ └─ tasks.send_email
171
+ │ └─ tasks.process_image
172
+ └─ Ready! Waiting for tasks...
173
+ ```
174
+
175
+ ## Task Composition
176
+
177
+ Chain tasks sequentially, run them in parallel, or combine both:
178
+
179
+ ```python
180
+ from flashq import chain, group, chord
181
+
182
+ # Chain: sequential — result of each passed to next
183
+ pipe = chain(
184
+ download.s("https://example.com/data.csv"),
185
+ parse_csv.s(),
186
+ store_results.s(table="imports"),
187
+ )
188
+ pipe.delay(app)
189
+
190
+ # Group: parallel execution
191
+ batch = group(
192
+ send_email.s(to="alice@test.com", subject="Hi"),
193
+ send_email.s(to="bob@test.com", subject="Hi"),
194
+ send_email.s(to="carol@test.com", subject="Hi"),
195
+ )
196
+ handle = batch.delay(app)
197
+ results = handle.get_results(timeout=30)
198
+
199
+ # Chord: parallel + callback when all complete
200
+ workflow = chord(
201
+ group(fetch_price.s("AAPL"), fetch_price.s("GOOG"), fetch_price.s("MSFT")),
202
+ aggregate_prices.s(),
203
+ )
204
+ workflow.delay(app)
205
+ ```
206
+
207
+ ## Middleware
208
+
209
+ Intercept task lifecycle events for logging, monitoring, or custom logic:
210
+
211
+ ```python
212
+ from flashq import FlashQ, Middleware
213
+
214
+ class MetricsMiddleware(Middleware):
215
+ def before_execute(self, message):
216
+ self.start = time.time()
217
+ return message
218
+
219
+ def after_execute(self, message, result):
220
+ duration = time.time() - self.start
221
+ statsd.timing(f"task.{message.task_name}.duration", duration)
222
+
223
+ def on_error(self, message, exc):
224
+ sentry.capture_exception(exc)
225
+ return False # Don't suppress
226
+
227
+ def on_dead(self, message, exc):
228
+ alert_ops_team(f"Task {message.task_name} permanently failed: {exc}")
229
+
230
+ app = FlashQ()
231
+ app.add_middleware(MetricsMiddleware())
232
+ ```
233
+
234
+ Built-in middlewares: `LoggingMiddleware`, `TimeoutMiddleware`, `RateLimiter`.
235
+
236
+ ## Rate Limiting
237
+
238
+ ```python
239
+ from flashq.ratelimit import RateLimiter
240
+
241
+ limiter = RateLimiter(default_rate="100/m") # 100 tasks/minute global
242
+ limiter.configure("send_email", rate="10/m") # 10 emails/minute
243
+ limiter.configure("api_call", rate="60/h") # 60 API calls/hour
244
+
245
+ app.add_middleware(limiter)
246
+ ```
247
+
248
+ ## Dead Letter Queue
249
+
250
+ Inspect and replay permanently failed tasks:
251
+
252
+ ```python
253
+ from flashq.dlq import DeadLetterQueue
254
+
255
+ dlq = DeadLetterQueue(app)
256
+ app.add_middleware(dlq.middleware()) # Auto-capture dead tasks
257
+
258
+ # Later...
259
+ for task in dlq.list():
260
+ print(f"{task.task_name}: {task.error}")
261
+
262
+ dlq.replay(task_id="abc123") # Re-enqueue with reset retries
263
+ dlq.replay_all() # Replay everything
264
+ dlq.purge() # Clear DLQ
265
+ ```
266
+
267
+ ## Periodic Tasks
268
+
269
+ ```python
270
+ from flashq import FlashQ, every, cron
271
+ from flashq.scheduler import Scheduler
272
+
273
+ app = FlashQ()
274
+
275
+ @app.task(name="cleanup")
276
+ def cleanup_old_data():
277
+ delete_old_records(days=30)
278
+
279
+ @app.task(name="daily_report")
280
+ def daily_report():
281
+ generate_and_send_report()
282
+
283
+ scheduler = Scheduler(app)
284
+ scheduler.add("cleanup", every(hours=6))
285
+ scheduler.add("daily_report", cron("0 9 * * 1-5")) # 9 AM weekdays
286
+ scheduler.start()
287
+ ```
288
+
289
+ ## Retry & Error Handling
290
+
291
+ ```python
292
+ @app.task(max_retries=5, retry_delay=30.0, retry_backoff=True)
293
+ def flaky_task():
294
+ # Retries: 30s → 60s → 120s → 240s → 480s (exponential backoff)
295
+ response = requests.get("https://unreliable-api.com")
296
+ response.raise_for_status()
297
+ ```
298
+
299
+ ```python
300
+ from flashq.exceptions import TaskRetryError
301
+
302
+ @app.task(max_retries=10)
303
+ def smart_retry():
304
+ try:
305
+ do_something()
306
+ except TemporaryError:
307
+ raise TaskRetryError(countdown=5.0) # Custom retry delay
308
+ ```
309
+
310
+ ## Backends
311
+
312
+ ### SQLite (Default — Zero Config)
313
+
314
+ ```python
315
+ app = FlashQ() # Creates flashq.db in current dir
316
+ app = FlashQ(backend=SQLiteBackend(path="/var/lib/flashq/tasks.db"))
317
+ app = FlashQ(backend=SQLiteBackend(path=":memory:")) # For testing
318
+ ```
319
+
320
+ ### PostgreSQL
321
+
322
+ Uses `LISTEN/NOTIFY` for instant task delivery + `FOR UPDATE SKIP LOCKED` for atomic dequeue.
323
+
324
+ ```python
325
+ from flashq.backends.postgres import PostgresBackend
326
+ app = FlashQ(backend=PostgresBackend("postgresql://localhost/mydb"))
327
+ ```
328
+
329
+ ### Redis
330
+
331
+ Uses sorted sets for scheduling and Lua scripts for atomic operations.
332
+
333
+ ```python
334
+ from flashq.backends.redis import RedisBackend
335
+ app = FlashQ(backend=RedisBackend("redis://localhost:6379/0"))
336
+ ```
337
+
338
+ ## CLI
339
+
340
+ ```bash
341
+ flashq worker myapp:app # Start worker
342
+ flashq worker myapp:app -q emails,sms # Specific queues
343
+ flashq worker myapp:app -c 16 # 16 concurrent threads
344
+ flashq info myapp:app # Queue stats
345
+ flashq purge myapp:app -f # Purge queue
346
+ ```
347
+
348
+ ## Architecture
349
+
350
+ ```
351
+ Your App → FlashQ → Backend (SQLite/PG/Redis) → Worker(s)
352
+
353
+ Middleware Stack
354
+ Rate Limiter
355
+ Scheduler
356
+ Dead Letter Queue
357
+ ```
358
+
359
+ FlashQ uses a clean, modular architecture:
360
+ - **Backend**: Pluggable storage (SQLite, PostgreSQL, Redis)
361
+ - **Worker**: Thread pool executor with graceful shutdown
362
+ - **Middleware**: Intercepts every stage of task lifecycle
363
+ - **Scheduler**: Interval and cron-based periodic dispatch
364
+ - **Canvas**: Task composition (chain, group, chord)
365
+
366
+ ## Roadmap
367
+
368
+ - [x] Core engine with SQLite backend
369
+ - [x] PostgreSQL backend (LISTEN/NOTIFY)
370
+ - [x] Redis backend (Lua scripts)
371
+ - [x] Task timeouts (non-blocking)
372
+ - [x] Middleware system
373
+ - [x] Rate limiting (token bucket)
374
+ - [x] Dead letter queue
375
+ - [x] Task chains, groups, chords
376
+ - [x] Periodic/cron scheduler
377
+ - [x] CLI (worker, info, purge)
378
+ - [x] 226 tests, 95% core coverage
379
+ - [ ] Web dashboard
380
+ - [ ] Task result streaming
381
+ - [ ] PyPI publish
382
+
383
+ ## Contributing
384
+
385
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
386
+
387
+ ## License
388
+
389
+ MIT License. See [LICENSE](LICENSE) for details.