taskito 0.2.2__tar.gz → 0.2.3__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.
- {taskito-0.2.2 → taskito-0.2.3}/Cargo.lock +45 -2
- {taskito-0.2.2 → taskito-0.2.3}/Cargo.toml +1 -0
- {taskito-0.2.2 → taskito-0.2.3}/PKG-INFO +11 -5
- {taskito-0.2.2 → taskito-0.2.3}/README.md +7 -4
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/Cargo.toml +6 -1
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/job.rs +2 -2
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/lib.rs +6 -3
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/periodic.rs +3 -5
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/resilience/circuit_breaker.rs +3 -3
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/resilience/dlq.rs +3 -3
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/resilience/rate_limiter.rs +9 -6
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/resilience/retry.rs +2 -2
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/scheduler.rs +182 -118
- taskito-0.2.3/crates/taskito-core/src/storage/mod.rs +299 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/models.rs +8 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/circuit_breakers.rs +40 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/dead_letter.rs +143 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/jobs.rs +739 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/logs.rs +89 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/metrics.rs +115 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/mod.rs +330 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/periodic.rs +49 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/rate_limits.rs +85 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/trait_impl.rs +207 -0
- taskito-0.2.3/crates/taskito-core/src/storage/postgres/workers.rs +79 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/schema.rs +4 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/dead_letter.rs +45 -18
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/jobs.rs +166 -92
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/logs.rs +5 -5
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/metrics.rs +5 -5
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/mod.rs +95 -101
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/periodic.rs +2 -7
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/rate_limits.rs +3 -8
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/tests.rs +22 -9
- taskito-0.2.3/crates/taskito-core/src/storage/sqlite/trait_impl.rs +207 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/workers.rs +4 -5
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/traits.rs +5 -3
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-python/Cargo.toml +5 -1
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-python/src/py_config.rs +1 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-python/src/py_queue.rs +77 -22
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-python/src/py_worker.rs +33 -34
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/__init__.py +6 -1
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/_taskito.pyi +3 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/app.py +62 -10
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/canvas.py +1 -1
- taskito-0.2.3/py_src/taskito/contrib/django/__init__.py +25 -0
- taskito-0.2.3/py_src/taskito/contrib/django/admin.py +176 -0
- taskito-0.2.3/py_src/taskito/contrib/django/apps.py +24 -0
- taskito-0.2.3/py_src/taskito/contrib/django/management/commands/__init__.py +0 -0
- taskito-0.2.3/py_src/taskito/contrib/django/management/commands/taskito_dashboard.py +27 -0
- taskito-0.2.3/py_src/taskito/contrib/django/management/commands/taskito_info.py +62 -0
- taskito-0.2.3/py_src/taskito/contrib/django/management/commands/taskito_worker.py +29 -0
- taskito-0.2.3/py_src/taskito/contrib/django/settings.py +38 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/contrib/otel.py +9 -3
- taskito-0.2.3/py_src/taskito/py.typed +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/result.py +1 -3
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/testing.py +3 -0
- {taskito-0.2.2 → taskito-0.2.3}/pyproject.toml +7 -1
- taskito-0.2.2/crates/taskito-core/src/storage/mod.rs +0 -6
- taskito-0.2.2/crates/taskito-core/src/storage/sqlite/trait_impl.rs +0 -68
- {taskito-0.2.2 → taskito-0.2.3}/LICENSE +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/error.rs +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/resilience/mod.rs +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-core/src/storage/sqlite/circuit_breakers.rs +1 -1
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-python/src/lib.rs +4 -4
- {taskito-0.2.2 → taskito-0.2.3}/crates/taskito-python/src/py_job.rs +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/cli.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/context.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/contrib/__init__.py +0 -0
- taskito-0.2.2/py_src/taskito/py.typed → taskito-0.2.3/py_src/taskito/contrib/django/management/__init__.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/contrib/fastapi.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/dashboard.py +6 -6
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/exceptions.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/middleware.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/mixins.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/serializers.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/task.py +0 -0
- {taskito-0.2.2 → taskito-0.2.3}/py_src/taskito/templates/dashboard.html +0 -0
|
@@ -35,6 +35,12 @@ version = "3.20.2"
|
|
|
35
35
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
36
|
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
|
37
37
|
|
|
38
|
+
[[package]]
|
|
39
|
+
name = "byteorder"
|
|
40
|
+
version = "1.5.0"
|
|
41
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
+
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
43
|
+
|
|
38
44
|
[[package]]
|
|
39
45
|
name = "bytes"
|
|
40
46
|
version = "1.11.1"
|
|
@@ -152,9 +158,13 @@ version = "2.3.6"
|
|
|
152
158
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
153
159
|
checksum = "d9b6c2fc184a6fb6ebcf5f9a5e3bbfa84d8fd268cdfcce4ed508979a6259494d"
|
|
154
160
|
dependencies = [
|
|
161
|
+
"bitflags",
|
|
162
|
+
"byteorder",
|
|
155
163
|
"diesel_derives",
|
|
156
164
|
"downcast-rs",
|
|
165
|
+
"itoa",
|
|
157
166
|
"libsqlite3-sys",
|
|
167
|
+
"pq-sys",
|
|
158
168
|
"r2d2",
|
|
159
169
|
"sqlite-wasm-rs",
|
|
160
170
|
"time",
|
|
@@ -482,6 +492,18 @@ version = "1.21.3"
|
|
|
482
492
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
483
493
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
484
494
|
|
|
495
|
+
[[package]]
|
|
496
|
+
name = "openssl-sys"
|
|
497
|
+
version = "0.9.111"
|
|
498
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
499
|
+
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
|
|
500
|
+
dependencies = [
|
|
501
|
+
"cc",
|
|
502
|
+
"libc",
|
|
503
|
+
"pkg-config",
|
|
504
|
+
"vcpkg",
|
|
505
|
+
]
|
|
506
|
+
|
|
485
507
|
[[package]]
|
|
486
508
|
name = "parking_lot"
|
|
487
509
|
version = "0.12.5"
|
|
@@ -538,6 +560,26 @@ dependencies = [
|
|
|
538
560
|
"zerocopy",
|
|
539
561
|
]
|
|
540
562
|
|
|
563
|
+
[[package]]
|
|
564
|
+
name = "pq-src"
|
|
565
|
+
version = "0.3.11+libpq-18.3"
|
|
566
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
567
|
+
checksum = "6fb5bfbe0c3445d371dd8acf7d6c912ece8baf2f61f7c571af85a1d7687c2d0f"
|
|
568
|
+
dependencies = [
|
|
569
|
+
"cc",
|
|
570
|
+
"openssl-sys",
|
|
571
|
+
]
|
|
572
|
+
|
|
573
|
+
[[package]]
|
|
574
|
+
name = "pq-sys"
|
|
575
|
+
version = "0.6.3"
|
|
576
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
577
|
+
checksum = "f6cc05d7ea95200187117196eee9edd0644424911821aeb28a18ce60ea0b8793"
|
|
578
|
+
dependencies = [
|
|
579
|
+
"pq-src",
|
|
580
|
+
"vcpkg",
|
|
581
|
+
]
|
|
582
|
+
|
|
541
583
|
[[package]]
|
|
542
584
|
name = "prettyplease"
|
|
543
585
|
version = "0.2.37"
|
|
@@ -847,7 +889,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
|
847
889
|
|
|
848
890
|
[[package]]
|
|
849
891
|
name = "taskito-core"
|
|
850
|
-
version = "0.2.
|
|
892
|
+
version = "0.2.3"
|
|
851
893
|
dependencies = [
|
|
852
894
|
"chrono",
|
|
853
895
|
"cron",
|
|
@@ -855,6 +897,7 @@ dependencies = [
|
|
|
855
897
|
"diesel",
|
|
856
898
|
"libsqlite3-sys",
|
|
857
899
|
"log",
|
|
900
|
+
"pq-sys",
|
|
858
901
|
"rand",
|
|
859
902
|
"serde",
|
|
860
903
|
"serde_json",
|
|
@@ -866,7 +909,7 @@ dependencies = [
|
|
|
866
909
|
|
|
867
910
|
[[package]]
|
|
868
911
|
name = "taskito-python"
|
|
869
|
-
version = "0.2.
|
|
912
|
+
version = "0.2.3"
|
|
870
913
|
dependencies = [
|
|
871
914
|
"crossbeam-channel",
|
|
872
915
|
"pyo3",
|
|
@@ -5,6 +5,7 @@ resolver = "2"
|
|
|
5
5
|
[workspace.dependencies]
|
|
6
6
|
diesel = { version = "2.2", features = ["sqlite", "r2d2", "returning_clauses_for_sqlite_3_35"] }
|
|
7
7
|
libsqlite3-sys = { version = "0.30", features = ["bundled"] }
|
|
8
|
+
pq-sys = { version = "0.6", features = ["bundled"] }
|
|
8
9
|
uuid = { version = "1", features = ["v7"] }
|
|
9
10
|
tokio = { version = "1", features = ["full"] }
|
|
10
11
|
serde = { version = "1", features = ["derive"] }
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taskito
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Requires-Dist: cloudpickle>=3.0
|
|
5
5
|
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
6
6
|
Requires-Dist: pytest-asyncio>=0.21 ; extra == 'dev'
|
|
7
7
|
Requires-Dist: ruff>=0.8 ; extra == 'dev'
|
|
8
8
|
Requires-Dist: mypy>=1.13 ; extra == 'dev'
|
|
9
|
+
Requires-Dist: django>=3.2 ; extra == 'django'
|
|
9
10
|
Requires-Dist: zensical ; extra == 'docs'
|
|
10
11
|
Requires-Dist: fastapi>=0.100.0 ; extra == 'fastapi'
|
|
11
12
|
Requires-Dist: pydantic>=2.0 ; extra == 'fastapi'
|
|
12
13
|
Requires-Dist: opentelemetry-api ; extra == 'otel'
|
|
13
14
|
Requires-Dist: opentelemetry-sdk ; extra == 'otel'
|
|
14
15
|
Provides-Extra: dev
|
|
16
|
+
Provides-Extra: django
|
|
15
17
|
Provides-Extra: docs
|
|
16
18
|
Provides-Extra: fastapi
|
|
17
19
|
Provides-Extra: otel
|
|
20
|
+
Provides-Extra: postgres
|
|
18
21
|
License-File: LICENSE
|
|
19
22
|
Summary: Rust-powered task queue for Python. No broker required.
|
|
20
23
|
Requires-Python: >=3.9
|
|
@@ -26,10 +29,11 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
|
26
29
|
[](https://pypi.org/project/taskito/)
|
|
27
30
|
[](https://github.com/pratyush618/taskito/blob/master/LICENSE)
|
|
28
31
|
|
|
29
|
-
A Rust-powered task queue for Python. No broker required — just SQLite.
|
|
32
|
+
A Rust-powered task queue for Python. No broker required — just SQLite or Postgres.
|
|
30
33
|
|
|
31
34
|
```
|
|
32
|
-
pip install taskito
|
|
35
|
+
pip install taskito # SQLite (default)
|
|
36
|
+
pip install taskito[postgres] # with Postgres backend
|
|
33
37
|
```
|
|
34
38
|
|
|
35
39
|
## Quickstart
|
|
@@ -63,7 +67,7 @@ print(job.result(timeout=10)) # 5
|
|
|
63
67
|
|
|
64
68
|
## Why taskito?
|
|
65
69
|
|
|
66
|
-
Most Python task queues require a separate broker (Redis, RabbitMQ) even for single-machine workloads. taskito embeds everything — storage, scheduling, and worker management — into a single `pip install` with no external dependencies beyond Python itself.
|
|
70
|
+
Most Python task queues require a separate broker (Redis, RabbitMQ) even for single-machine workloads. taskito embeds everything — storage, scheduling, and worker management — into a single `pip install` with no external dependencies beyond Python itself. For distributed setups, an optional Postgres backend enables multi-machine workers with the same API.
|
|
67
71
|
|
|
68
72
|
The heavy lifting runs in Rust: a Tokio async scheduler, OS thread worker pool with crossbeam channels, and Diesel ORM over SQLite in WAL mode. Python's GIL is only held during task execution.
|
|
69
73
|
|
|
@@ -93,6 +97,7 @@ The heavy lifting runs in Rust: a Tokio async scheduler, OS thread worker pool w
|
|
|
93
97
|
- **Async support** — `await job.aresult()`, `await queue.astats()`
|
|
94
98
|
- **Web dashboard** — `taskito dashboard --app myapp:queue` serves a built-in monitoring UI
|
|
95
99
|
- **FastAPI integration** — `TaskitoRouter` for instant REST API over the queue
|
|
100
|
+
- **Postgres backend** — optional multi-machine storage via PostgreSQL
|
|
96
101
|
- **CLI** — `taskito worker`, `taskito info --watch`, `taskito dashboard`
|
|
97
102
|
|
|
98
103
|
## Examples
|
|
@@ -149,7 +154,7 @@ chord([download.s(u) for u in urls], merge.s()).apply()
|
|
|
149
154
|
### Periodic Tasks
|
|
150
155
|
|
|
151
156
|
```python
|
|
152
|
-
@queue.periodic(cron="0 */6 * * *")
|
|
157
|
+
@queue.periodic(cron="0 0 */6 * * *")
|
|
153
158
|
def cleanup_temp_files():
|
|
154
159
|
...
|
|
155
160
|
```
|
|
@@ -278,6 +283,7 @@ Coming from Celery? See the **[Migration Guide](https://taskito-sepia.vercel.app
|
|
|
278
283
|
| Per-task middleware | **Yes** | No | No | Yes | No |
|
|
279
284
|
| Cancel running tasks | **Yes** | Yes | No | No | No |
|
|
280
285
|
| Custom serializers | **Yes** | Yes | No | No | No |
|
|
286
|
+
| Postgres backend | **Yes** | Yes | No | No | No |
|
|
281
287
|
| Setup | **`pip install`** | Broker + backend | Redis | Broker | Redis |
|
|
282
288
|
|
|
283
289
|
## License
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
[](https://pypi.org/project/taskito/)
|
|
5
5
|
[](https://github.com/pratyush618/taskito/blob/master/LICENSE)
|
|
6
6
|
|
|
7
|
-
A Rust-powered task queue for Python. No broker required — just SQLite.
|
|
7
|
+
A Rust-powered task queue for Python. No broker required — just SQLite or Postgres.
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
pip install taskito
|
|
10
|
+
pip install taskito # SQLite (default)
|
|
11
|
+
pip install taskito[postgres] # with Postgres backend
|
|
11
12
|
```
|
|
12
13
|
|
|
13
14
|
## Quickstart
|
|
@@ -41,7 +42,7 @@ print(job.result(timeout=10)) # 5
|
|
|
41
42
|
|
|
42
43
|
## Why taskito?
|
|
43
44
|
|
|
44
|
-
Most Python task queues require a separate broker (Redis, RabbitMQ) even for single-machine workloads. taskito embeds everything — storage, scheduling, and worker management — into a single `pip install` with no external dependencies beyond Python itself.
|
|
45
|
+
Most Python task queues require a separate broker (Redis, RabbitMQ) even for single-machine workloads. taskito embeds everything — storage, scheduling, and worker management — into a single `pip install` with no external dependencies beyond Python itself. For distributed setups, an optional Postgres backend enables multi-machine workers with the same API.
|
|
45
46
|
|
|
46
47
|
The heavy lifting runs in Rust: a Tokio async scheduler, OS thread worker pool with crossbeam channels, and Diesel ORM over SQLite in WAL mode. Python's GIL is only held during task execution.
|
|
47
48
|
|
|
@@ -71,6 +72,7 @@ The heavy lifting runs in Rust: a Tokio async scheduler, OS thread worker pool w
|
|
|
71
72
|
- **Async support** — `await job.aresult()`, `await queue.astats()`
|
|
72
73
|
- **Web dashboard** — `taskito dashboard --app myapp:queue` serves a built-in monitoring UI
|
|
73
74
|
- **FastAPI integration** — `TaskitoRouter` for instant REST API over the queue
|
|
75
|
+
- **Postgres backend** — optional multi-machine storage via PostgreSQL
|
|
74
76
|
- **CLI** — `taskito worker`, `taskito info --watch`, `taskito dashboard`
|
|
75
77
|
|
|
76
78
|
## Examples
|
|
@@ -127,7 +129,7 @@ chord([download.s(u) for u in urls], merge.s()).apply()
|
|
|
127
129
|
### Periodic Tasks
|
|
128
130
|
|
|
129
131
|
```python
|
|
130
|
-
@queue.periodic(cron="0 */6 * * *")
|
|
132
|
+
@queue.periodic(cron="0 0 */6 * * *")
|
|
131
133
|
def cleanup_temp_files():
|
|
132
134
|
...
|
|
133
135
|
```
|
|
@@ -256,6 +258,7 @@ Coming from Celery? See the **[Migration Guide](https://taskito-sepia.vercel.app
|
|
|
256
258
|
| Per-task middleware | **Yes** | No | No | Yes | No |
|
|
257
259
|
| Cancel running tasks | **Yes** | Yes | No | No | No |
|
|
258
260
|
| Custom serializers | **Yes** | Yes | No | No | No |
|
|
261
|
+
| Postgres backend | **Yes** | Yes | No | No | No |
|
|
259
262
|
| Setup | **`pip install`** | Broker + backend | Redis | Broker | Redis |
|
|
260
263
|
|
|
261
264
|
## License
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "taskito-core"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
|
|
6
|
+
[features]
|
|
7
|
+
default = []
|
|
8
|
+
postgres = ["diesel/postgres", "pq-sys"]
|
|
9
|
+
|
|
6
10
|
[dependencies]
|
|
7
11
|
diesel = { workspace = true }
|
|
8
12
|
libsqlite3-sys = { workspace = true }
|
|
13
|
+
pq-sys = { workspace = true, optional = true }
|
|
9
14
|
uuid = { workspace = true }
|
|
10
15
|
tokio = { workspace = true }
|
|
11
16
|
serde = { workspace = true }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use serde::{Deserialize, Serialize};
|
|
2
|
-
use std::time::{SystemTime, UNIX_EPOCH};
|
|
2
|
+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
3
3
|
use uuid::Uuid;
|
|
4
4
|
|
|
5
5
|
use crate::storage::models::JobRow;
|
|
@@ -142,6 +142,6 @@ impl NewJob {
|
|
|
142
142
|
pub fn now_millis() -> i64 {
|
|
143
143
|
SystemTime::now()
|
|
144
144
|
.duration_since(UNIX_EPOCH)
|
|
145
|
-
.
|
|
145
|
+
.unwrap_or(Duration::ZERO)
|
|
146
146
|
.as_millis() as i64
|
|
147
147
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
pub mod job;
|
|
2
1
|
pub mod error;
|
|
3
|
-
pub mod
|
|
4
|
-
pub mod scheduler;
|
|
2
|
+
pub mod job;
|
|
5
3
|
pub mod periodic;
|
|
6
4
|
pub mod resilience;
|
|
5
|
+
pub mod scheduler;
|
|
6
|
+
pub mod storage;
|
|
7
7
|
|
|
8
8
|
// Primary public API — the types most consumers need.
|
|
9
9
|
pub use error::QueueError;
|
|
10
10
|
pub use job::{Job, JobStatus, NewJob};
|
|
11
11
|
pub use scheduler::{Scheduler, SchedulerConfig};
|
|
12
|
+
#[cfg(feature = "postgres")]
|
|
13
|
+
pub use storage::postgres::PostgresStorage;
|
|
12
14
|
pub use storage::sqlite::SqliteStorage;
|
|
13
15
|
pub use storage::Storage;
|
|
16
|
+
pub use storage::StorageBackend;
|
|
@@ -8,12 +8,10 @@ use crate::error::{QueueError, Result};
|
|
|
8
8
|
/// Compute the next run time (in UNIX milliseconds) for a cron expression,
|
|
9
9
|
/// starting from `after_ms` (also UNIX milliseconds).
|
|
10
10
|
pub fn next_cron_time(cron_expr: &str, after_ms: i64) -> Result<i64> {
|
|
11
|
-
let schedule = Schedule::from_str(cron_expr)
|
|
12
|
-
QueueError::Config(format!("invalid cron expression '{cron_expr}': {e}"))
|
|
13
|
-
})?;
|
|
11
|
+
let schedule = Schedule::from_str(cron_expr)
|
|
12
|
+
.map_err(|e| QueueError::Config(format!("invalid cron expression '{cron_expr}': {e}")))?;
|
|
14
13
|
|
|
15
|
-
let after_dt = chrono::DateTime::from_timestamp_millis(after_ms)
|
|
16
|
-
.unwrap_or_else(Utc::now);
|
|
14
|
+
let after_dt = chrono::DateTime::from_timestamp_millis(after_ms).unwrap_or_else(Utc::now);
|
|
17
15
|
|
|
18
16
|
let next = schedule
|
|
19
17
|
.after(&after_dt)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use crate::error::Result;
|
|
2
2
|
use crate::job::now_millis;
|
|
3
3
|
use crate::storage::models::CircuitBreakerRow;
|
|
4
|
-
use crate::storage::
|
|
4
|
+
use crate::storage::{Storage, StorageBackend};
|
|
5
5
|
|
|
6
6
|
/// Circuit breaker states.
|
|
7
7
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
@@ -40,11 +40,11 @@ pub struct CircuitBreakerConfig {
|
|
|
40
40
|
|
|
41
41
|
/// Circuit breaker manager backed by SQLite.
|
|
42
42
|
pub struct CircuitBreaker {
|
|
43
|
-
storage:
|
|
43
|
+
storage: StorageBackend,
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
impl CircuitBreaker {
|
|
47
|
-
pub fn new(storage:
|
|
47
|
+
pub fn new(storage: StorageBackend) -> Self {
|
|
48
48
|
Self { storage }
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
use crate::error::Result;
|
|
2
2
|
use crate::job::Job;
|
|
3
|
-
use crate::storage::
|
|
3
|
+
use crate::storage::{DeadJob, Storage, StorageBackend};
|
|
4
4
|
|
|
5
5
|
/// Dead letter queue manager.
|
|
6
6
|
pub struct DeadLetterQueue {
|
|
7
|
-
storage:
|
|
7
|
+
storage: StorageBackend,
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
impl DeadLetterQueue {
|
|
11
|
-
pub fn new(storage:
|
|
11
|
+
pub fn new(storage: StorageBackend) -> Self {
|
|
12
12
|
Self { storage }
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
use crate::error::Result;
|
|
2
|
-
use crate::storage::
|
|
2
|
+
use crate::storage::{Storage, StorageBackend};
|
|
3
3
|
|
|
4
4
|
/// Token bucket rate limiter backed by SQLite for persistence.
|
|
5
5
|
pub struct RateLimiter {
|
|
6
|
-
storage:
|
|
6
|
+
storage: StorageBackend,
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/// Parsed rate limit configuration (e.g., "100/m" → 100 tokens, refill 1.667/s).
|
|
@@ -37,7 +37,7 @@ impl RateLimitConfig {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
impl RateLimiter {
|
|
40
|
-
pub fn new(storage:
|
|
40
|
+
pub fn new(storage: StorageBackend) -> Self {
|
|
41
41
|
Self { storage }
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -71,7 +71,8 @@ mod tests {
|
|
|
71
71
|
|
|
72
72
|
#[test]
|
|
73
73
|
fn test_token_bucket() {
|
|
74
|
-
let storage =
|
|
74
|
+
let storage =
|
|
75
|
+
StorageBackend::Sqlite(crate::storage::sqlite::SqliteStorage::in_memory().unwrap());
|
|
75
76
|
let limiter = RateLimiter::new(storage);
|
|
76
77
|
let config = RateLimitConfig {
|
|
77
78
|
max_tokens: 3.0,
|
|
@@ -89,13 +90,15 @@ mod tests {
|
|
|
89
90
|
|
|
90
91
|
#[test]
|
|
91
92
|
fn test_concurrent_token_acquisition() {
|
|
92
|
-
use std::sync::Arc;
|
|
93
93
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
94
|
+
use std::sync::Arc;
|
|
94
95
|
use std::sync::Barrier;
|
|
95
96
|
|
|
96
97
|
let dir = tempfile::tempdir().unwrap();
|
|
97
98
|
let db_path = dir.path().join("rate_limit_test.db");
|
|
98
|
-
let storage =
|
|
99
|
+
let storage = StorageBackend::Sqlite(
|
|
100
|
+
crate::storage::sqlite::SqliteStorage::new(db_path.to_str().unwrap()).unwrap(),
|
|
101
|
+
);
|
|
99
102
|
let limiter = Arc::new(RateLimiter::new(storage));
|
|
100
103
|
let config = RateLimitConfig {
|
|
101
104
|
max_tokens: 10.0,
|