taskito 0.5.0__tar.gz → 0.6.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.
- {taskito-0.5.0 → taskito-0.6.0}/Cargo.lock +6 -5
- {taskito-0.5.0 → taskito-0.6.0}/PKG-INFO +1 -1
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/Cargo.toml +1 -1
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/scheduler/maintenance.rs +2 -1
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/scheduler/mod.rs +47 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/scheduler/poller.rs +33 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/scheduler/result_handler.rs +31 -4
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/diesel_common/jobs.rs +13 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/mod.rs +9 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/jobs.rs +18 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/tests.rs +25 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/traits.rs +4 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/Cargo.toml +2 -1
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/async_worker.rs +1 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/py_config.rs +9 -1
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/py_queue/mod.rs +11 -1
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/py_queue/worker.rs +193 -11
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/py_worker.rs +8 -2
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/__init__.py +1 -1
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/_taskito.pyi +10 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/app.py +122 -6
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/admin.py +24 -5
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/apps.py +8 -2
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/management/commands/taskito_dashboard.py +14 -2
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/management/commands/taskito_info.py +7 -3
- taskito-0.6.0/py_src/taskito/contrib/fastapi.py +425 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/flask.py +27 -10
- taskito-0.6.0/py_src/taskito/contrib/otel.py +130 -0
- taskito-0.6.0/py_src/taskito/contrib/prometheus.py +366 -0
- taskito-0.6.0/py_src/taskito/contrib/sentry.py +103 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/events.py +5 -1
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/middleware.py +16 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/mixins.py +8 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/webhooks.py +20 -5
- {taskito-0.5.0 → taskito-0.6.0}/pyproject.toml +1 -1
- taskito-0.5.0/py_src/taskito/contrib/fastapi.py +0 -322
- taskito-0.5.0/py_src/taskito/contrib/otel.py +0 -93
- taskito-0.5.0/py_src/taskito/contrib/prometheus.py +0 -325
- taskito-0.5.0/py_src/taskito/contrib/sentry.py +0 -68
- {taskito-0.5.0 → taskito-0.6.0}/Cargo.toml +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/LICENSE +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/README.md +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/error.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/job.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/lib.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/periodic.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/resilience/circuit_breaker.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/resilience/dlq.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/resilience/mod.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/resilience/rate_limiter.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/resilience/retry.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/diesel_common/locks.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/diesel_common/logs.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/diesel_common/metrics.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/diesel_common/mod.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/models.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/archival.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/circuit_breakers.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/dead_letter.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/jobs.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/locks.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/logs.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/metrics.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/mod.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/periodic.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/queue_state.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/rate_limits.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/trait_impl.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/postgres/workers.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/archival.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/circuit_breakers.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/dead_letter.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/locks.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/logs.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/metrics.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/mod.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/periodic.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/queue_state.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/rate_limits.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/trait_impl.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/redis_backend/workers.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/schema.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/archival.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/circuit_breakers.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/dead_letter.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/jobs.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/locks.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/logs.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/metrics.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/mod.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/periodic.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/queue_state.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/rate_limits.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/trait_impl.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/storage/sqlite/workers.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/src/worker.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/tests/rust/storage_tests.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-core/tests/rust.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/lib.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/py_job.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/crates/taskito-python/src/py_queue/inspection.rs +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/async_support/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/async_support/context.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/async_support/executor.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/async_support/helpers.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/async_support/locks.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/async_support/mixins.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/async_support/result.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/canvas.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/cli.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/context.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/management/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/management/commands/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/management/commands/taskito_worker.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/contrib/django/settings.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/dashboard.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/exceptions.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/health.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/inject.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/built_in.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/converters.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/errors.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/interceptor.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/metrics.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/reconstruct.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/registry.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/strategy.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/interception/walker.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/locks.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/built_in.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handler.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handlers/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handlers/boto3_client.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handlers/file.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handlers/gcs_client.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handlers/httpx_client.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handlers/logger.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/handlers/requests_session.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/metrics.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/no_proxy.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/reconstruct.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/registry.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/schema.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/proxies/signing.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/py.typed +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/__init__.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/definition.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/frozen.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/graph.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/health.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/pool.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/runtime.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/thread_local.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/resources/toml_config.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/result.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/scaler.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/serializers.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/task.py +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/dashboard.css +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/dashboard.html +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/js/actions.js +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/js/app.js +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/js/chart.js +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/js/components.js +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/js/utils.js +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/templates/js/views.js +0 -0
- {taskito-0.5.0 → taskito-0.6.0}/py_src/taskito/testing.py +0 -0
|
@@ -69,9 +69,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
|
|
69
69
|
|
|
70
70
|
[[package]]
|
|
71
71
|
name = "cc"
|
|
72
|
-
version = "1.2.
|
|
72
|
+
version = "1.2.57"
|
|
73
73
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
74
|
-
checksum = "
|
|
74
|
+
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
|
75
75
|
dependencies = [
|
|
76
76
|
"find-msvc-tools",
|
|
77
77
|
"shlex",
|
|
@@ -1201,7 +1201,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
|
1201
1201
|
|
|
1202
1202
|
[[package]]
|
|
1203
1203
|
name = "taskito-async"
|
|
1204
|
-
version = "0.
|
|
1204
|
+
version = "0.6.0"
|
|
1205
1205
|
dependencies = [
|
|
1206
1206
|
"async-trait",
|
|
1207
1207
|
"crossbeam-channel",
|
|
@@ -1212,7 +1212,7 @@ dependencies = [
|
|
|
1212
1212
|
|
|
1213
1213
|
[[package]]
|
|
1214
1214
|
name = "taskito-core"
|
|
1215
|
-
version = "0.
|
|
1215
|
+
version = "0.6.0"
|
|
1216
1216
|
dependencies = [
|
|
1217
1217
|
"async-trait",
|
|
1218
1218
|
"chrono",
|
|
@@ -1236,11 +1236,12 @@ dependencies = [
|
|
|
1236
1236
|
|
|
1237
1237
|
[[package]]
|
|
1238
1238
|
name = "taskito-python"
|
|
1239
|
-
version = "0.
|
|
1239
|
+
version = "0.6.0"
|
|
1240
1240
|
dependencies = [
|
|
1241
1241
|
"async-trait",
|
|
1242
1242
|
"crossbeam-channel",
|
|
1243
1243
|
"pyo3",
|
|
1244
|
+
"serde_json",
|
|
1244
1245
|
"taskito-async",
|
|
1245
1246
|
"taskito-core",
|
|
1246
1247
|
"tokio",
|
|
@@ -33,7 +33,7 @@ impl Scheduler {
|
|
|
33
33
|
|
|
34
34
|
for job in stale_jobs {
|
|
35
35
|
let error = format!("job timed out after {}ms", job.timeout_ms);
|
|
36
|
-
self.handle_result(JobResult::Failure {
|
|
36
|
+
let _ = self.handle_result(JobResult::Failure {
|
|
37
37
|
job_id: job.id.clone(),
|
|
38
38
|
error,
|
|
39
39
|
retry_count: job.retry_count,
|
|
@@ -41,6 +41,7 @@ impl Scheduler {
|
|
|
41
41
|
task_name: job.task_name.clone(),
|
|
42
42
|
wall_time_ns: 0,
|
|
43
43
|
should_retry: true,
|
|
44
|
+
timed_out: true,
|
|
44
45
|
})?;
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -60,6 +60,7 @@ pub enum JobResult {
|
|
|
60
60
|
task_name: String,
|
|
61
61
|
wall_time_ns: i64,
|
|
62
62
|
should_retry: bool,
|
|
63
|
+
timed_out: bool,
|
|
63
64
|
},
|
|
64
65
|
Cancelled {
|
|
65
66
|
job_id: String,
|
|
@@ -68,12 +69,45 @@ pub enum JobResult {
|
|
|
68
69
|
},
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
/// Outcome of processing a job result, returned to the caller for
|
|
73
|
+
/// Python-side middleware hook dispatch.
|
|
74
|
+
#[derive(Debug, Clone)]
|
|
75
|
+
pub enum ResultOutcome {
|
|
76
|
+
/// Task completed successfully.
|
|
77
|
+
Success { job_id: String, task_name: String },
|
|
78
|
+
/// Task failed and will be retried.
|
|
79
|
+
Retry {
|
|
80
|
+
job_id: String,
|
|
81
|
+
task_name: String,
|
|
82
|
+
error: String,
|
|
83
|
+
retry_count: i32,
|
|
84
|
+
timed_out: bool,
|
|
85
|
+
},
|
|
86
|
+
/// Task exhausted retries and moved to the dead-letter queue.
|
|
87
|
+
DeadLettered {
|
|
88
|
+
job_id: String,
|
|
89
|
+
task_name: String,
|
|
90
|
+
error: String,
|
|
91
|
+
timed_out: bool,
|
|
92
|
+
},
|
|
93
|
+
/// Task was cancelled during execution.
|
|
94
|
+
Cancelled { job_id: String, task_name: String },
|
|
95
|
+
}
|
|
96
|
+
|
|
71
97
|
/// Per-task configuration for retry, rate limiting, and circuit breaker.
|
|
72
98
|
#[derive(Debug, Clone)]
|
|
73
99
|
pub struct TaskConfig {
|
|
74
100
|
pub retry_policy: RetryPolicy,
|
|
75
101
|
pub rate_limit: Option<crate::resilience::rate_limiter::RateLimitConfig>,
|
|
76
102
|
pub circuit_breaker: Option<CircuitBreakerConfig>,
|
|
103
|
+
pub max_concurrent: Option<i32>,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Per-queue configuration for rate limiting and concurrency caps.
|
|
107
|
+
#[derive(Debug, Clone)]
|
|
108
|
+
pub struct QueueConfig {
|
|
109
|
+
pub rate_limit: Option<crate::resilience::rate_limiter::RateLimitConfig>,
|
|
110
|
+
pub max_concurrent: Option<i32>,
|
|
77
111
|
}
|
|
78
112
|
|
|
79
113
|
/// The central scheduler that coordinates job dispatch, retries, rate limiting, and circuit breakers.
|
|
@@ -83,6 +117,7 @@ pub struct Scheduler {
|
|
|
83
117
|
dlq: DeadLetterQueue,
|
|
84
118
|
circuit_breaker: CircuitBreaker,
|
|
85
119
|
task_configs: HashMap<String, TaskConfig>,
|
|
120
|
+
queue_configs: HashMap<String, QueueConfig>,
|
|
86
121
|
queues: Vec<String>,
|
|
87
122
|
config: SchedulerConfig,
|
|
88
123
|
shutdown: Arc<Notify>,
|
|
@@ -109,6 +144,7 @@ impl Scheduler {
|
|
|
109
144
|
dlq,
|
|
110
145
|
circuit_breaker,
|
|
111
146
|
task_configs: HashMap::new(),
|
|
147
|
+
queue_configs: HashMap::new(),
|
|
112
148
|
queues,
|
|
113
149
|
config,
|
|
114
150
|
shutdown: Arc::new(Notify::new()),
|
|
@@ -124,6 +160,10 @@ impl Scheduler {
|
|
|
124
160
|
self.shutdown.clone()
|
|
125
161
|
}
|
|
126
162
|
|
|
163
|
+
pub fn register_queue_config(&mut self, queue_name: String, config: QueueConfig) {
|
|
164
|
+
self.queue_configs.insert(queue_name, config);
|
|
165
|
+
}
|
|
166
|
+
|
|
127
167
|
pub fn register_task(&mut self, task_name: String, config: TaskConfig) {
|
|
128
168
|
if let Some(ref cb_config) = config.circuit_breaker {
|
|
129
169
|
if let Err(e) = self.circuit_breaker.register(&task_name, cb_config) {
|
|
@@ -276,6 +316,7 @@ mod tests {
|
|
|
276
316
|
},
|
|
277
317
|
rate_limit: None,
|
|
278
318
|
circuit_breaker: None,
|
|
319
|
+
max_concurrent: None,
|
|
279
320
|
},
|
|
280
321
|
);
|
|
281
322
|
|
|
@@ -290,6 +331,7 @@ mod tests {
|
|
|
290
331
|
task_name: "retry_task".to_string(),
|
|
291
332
|
wall_time_ns: 500_000,
|
|
292
333
|
should_retry: true,
|
|
334
|
+
timed_out: false,
|
|
293
335
|
})
|
|
294
336
|
.unwrap();
|
|
295
337
|
|
|
@@ -312,6 +354,7 @@ mod tests {
|
|
|
312
354
|
task_name: "exhausted_task".to_string(),
|
|
313
355
|
wall_time_ns: 100,
|
|
314
356
|
should_retry: true,
|
|
357
|
+
timed_out: false,
|
|
315
358
|
})
|
|
316
359
|
.unwrap();
|
|
317
360
|
|
|
@@ -337,6 +380,7 @@ mod tests {
|
|
|
337
380
|
task_name: "no_retry_task".to_string(),
|
|
338
381
|
wall_time_ns: 100,
|
|
339
382
|
should_retry: false,
|
|
383
|
+
timed_out: false,
|
|
340
384
|
})
|
|
341
385
|
.unwrap();
|
|
342
386
|
|
|
@@ -373,6 +417,7 @@ mod tests {
|
|
|
373
417
|
refill_rate: 0.0,
|
|
374
418
|
}),
|
|
375
419
|
circuit_breaker: None,
|
|
420
|
+
max_concurrent: None,
|
|
376
421
|
},
|
|
377
422
|
);
|
|
378
423
|
|
|
@@ -414,6 +459,7 @@ mod tests {
|
|
|
414
459
|
retry_policy: RetryPolicy::default(),
|
|
415
460
|
rate_limit: None,
|
|
416
461
|
circuit_breaker: Some(cb_config),
|
|
462
|
+
max_concurrent: None,
|
|
417
463
|
},
|
|
418
464
|
);
|
|
419
465
|
|
|
@@ -452,6 +498,7 @@ mod tests {
|
|
|
452
498
|
},
|
|
453
499
|
rate_limit: None,
|
|
454
500
|
circuit_breaker: None,
|
|
501
|
+
max_concurrent: None,
|
|
455
502
|
},
|
|
456
503
|
);
|
|
457
504
|
|
|
@@ -14,6 +14,9 @@ const CIRCUIT_BREAKER_RETRY_DELAY_MS: i64 = 5000;
|
|
|
14
14
|
/// Delay before re-scheduling a rate-limited job (ms).
|
|
15
15
|
const RATE_LIMIT_RETRY_DELAY_MS: i64 = 1000;
|
|
16
16
|
|
|
17
|
+
/// Delay before re-scheduling a concurrency-limited job (ms).
|
|
18
|
+
const CONCURRENCY_RETRY_DELAY_MS: i64 = 500;
|
|
19
|
+
|
|
17
20
|
impl Scheduler {
|
|
18
21
|
pub(super) fn try_dispatch(&self, job_tx: &tokio::sync::mpsc::Sender<Job>) -> Result<bool> {
|
|
19
22
|
let now = now_millis();
|
|
@@ -53,6 +56,26 @@ impl Scheduler {
|
|
|
53
56
|
None => return Ok(false),
|
|
54
57
|
};
|
|
55
58
|
|
|
59
|
+
// Check queue-level limits
|
|
60
|
+
if let Some(qcfg) = self.queue_configs.get(&job.queue) {
|
|
61
|
+
if let Some(ref rl_config) = qcfg.rate_limit {
|
|
62
|
+
let key = format!("queue:{}", job.queue);
|
|
63
|
+
if !self.rate_limiter.try_acquire(&key, rl_config)? {
|
|
64
|
+
self.storage
|
|
65
|
+
.retry(&job.id, now + RATE_LIMIT_RETRY_DELAY_MS)?;
|
|
66
|
+
return Ok(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if let Some(max_conc) = qcfg.max_concurrent {
|
|
70
|
+
let stats = self.storage.stats_by_queue(&job.queue)?;
|
|
71
|
+
if stats.running >= max_conc as i64 {
|
|
72
|
+
self.storage
|
|
73
|
+
.retry(&job.id, now + CONCURRENCY_RETRY_DELAY_MS)?;
|
|
74
|
+
return Ok(false);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
56
79
|
// Check circuit breaker for this task
|
|
57
80
|
if let Some(config) = self.task_configs.get(&job.task_name) {
|
|
58
81
|
if config.circuit_breaker.is_some() && !self.circuit_breaker.allow(&job.task_name)? {
|
|
@@ -68,6 +91,16 @@ impl Scheduler {
|
|
|
68
91
|
return Ok(false);
|
|
69
92
|
}
|
|
70
93
|
}
|
|
94
|
+
|
|
95
|
+
// Check per-task concurrency limit
|
|
96
|
+
if let Some(max_conc) = config.max_concurrent {
|
|
97
|
+
let running = self.storage.count_running_by_task(&job.task_name)?;
|
|
98
|
+
if running >= max_conc as i64 {
|
|
99
|
+
self.storage
|
|
100
|
+
.retry(&job.id, now + CONCURRENCY_RETRY_DELAY_MS)?;
|
|
101
|
+
return Ok(false);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
71
104
|
}
|
|
72
105
|
|
|
73
106
|
// Claim exactly-once execution
|
|
@@ -3,11 +3,14 @@ use log::{error, warn};
|
|
|
3
3
|
use crate::error::Result;
|
|
4
4
|
use crate::storage::Storage;
|
|
5
5
|
|
|
6
|
-
use super::{JobResult, Scheduler};
|
|
6
|
+
use super::{JobResult, ResultOutcome, Scheduler};
|
|
7
7
|
|
|
8
8
|
impl Scheduler {
|
|
9
9
|
/// Handle a completed or failed job result from a worker.
|
|
10
|
-
|
|
10
|
+
///
|
|
11
|
+
/// Returns a [`ResultOutcome`] describing the action taken, so the
|
|
12
|
+
/// caller can dispatch Python-side middleware hooks and events.
|
|
13
|
+
pub fn handle_result(&self, result: JobResult) -> Result<ResultOutcome> {
|
|
11
14
|
match result {
|
|
12
15
|
JobResult::Success {
|
|
13
16
|
job_id,
|
|
@@ -32,6 +35,11 @@ impl Scheduler {
|
|
|
32
35
|
if let Err(e) = self.circuit_breaker.record_success(task_name) {
|
|
33
36
|
error!("circuit breaker error for {task_name}: {e}");
|
|
34
37
|
}
|
|
38
|
+
|
|
39
|
+
Ok(ResultOutcome::Success {
|
|
40
|
+
job_id,
|
|
41
|
+
task_name: task_name.clone(),
|
|
42
|
+
})
|
|
35
43
|
}
|
|
36
44
|
JobResult::Failure {
|
|
37
45
|
job_id,
|
|
@@ -41,6 +49,7 @@ impl Scheduler {
|
|
|
41
49
|
task_name,
|
|
42
50
|
wall_time_ns,
|
|
43
51
|
should_retry,
|
|
52
|
+
timed_out,
|
|
44
53
|
} => {
|
|
45
54
|
// Clear execution claim so it can be retried
|
|
46
55
|
if let Err(e) = self.storage.complete_execution(&job_id) {
|
|
@@ -68,7 +77,12 @@ impl Scheduler {
|
|
|
68
77
|
Some(job) => self.dlq.move_to_dlq(&job, &error, None)?,
|
|
69
78
|
None => warn!("job {job_id} disappeared before DLQ move"),
|
|
70
79
|
}
|
|
71
|
-
return Ok(
|
|
80
|
+
return Ok(ResultOutcome::DeadLettered {
|
|
81
|
+
job_id,
|
|
82
|
+
task_name,
|
|
83
|
+
error,
|
|
84
|
+
timed_out,
|
|
85
|
+
});
|
|
72
86
|
}
|
|
73
87
|
|
|
74
88
|
let policy = self
|
|
@@ -86,12 +100,25 @@ impl Scheduler {
|
|
|
86
100
|
if retry_count < effective_max {
|
|
87
101
|
let next_at = policy.next_retry_at(retry_count);
|
|
88
102
|
self.storage.retry(&job_id, next_at)?;
|
|
103
|
+
Ok(ResultOutcome::Retry {
|
|
104
|
+
job_id,
|
|
105
|
+
task_name,
|
|
106
|
+
error,
|
|
107
|
+
retry_count,
|
|
108
|
+
timed_out,
|
|
109
|
+
})
|
|
89
110
|
} else {
|
|
90
111
|
// Move to DLQ
|
|
91
112
|
match self.storage.get_job(&job_id)? {
|
|
92
113
|
Some(job) => self.dlq.move_to_dlq(&job, &error, None)?,
|
|
93
114
|
None => warn!("job {job_id} disappeared before DLQ move"),
|
|
94
115
|
}
|
|
116
|
+
Ok(ResultOutcome::DeadLettered {
|
|
117
|
+
job_id,
|
|
118
|
+
task_name,
|
|
119
|
+
error,
|
|
120
|
+
timed_out,
|
|
121
|
+
})
|
|
95
122
|
}
|
|
96
123
|
}
|
|
97
124
|
JobResult::Cancelled {
|
|
@@ -113,8 +140,8 @@ impl Scheduler {
|
|
|
113
140
|
{
|
|
114
141
|
error!("failed to record metric for cancelled job {job_id}: {e}");
|
|
115
142
|
}
|
|
143
|
+
Ok(ResultOutcome::Cancelled { job_id, task_name })
|
|
116
144
|
}
|
|
117
145
|
}
|
|
118
|
-
Ok(())
|
|
119
146
|
}
|
|
120
147
|
}
|
|
@@ -916,6 +916,19 @@ macro_rules! impl_diesel_job_ops {
|
|
|
916
916
|
Ok(affected as u64)
|
|
917
917
|
}
|
|
918
918
|
|
|
919
|
+
/// Count running jobs for a specific task name (for per-task concurrency limiting).
|
|
920
|
+
pub fn count_running_by_task(&self, task_name: &str) -> Result<i64> {
|
|
921
|
+
let mut conn = self.conn()?;
|
|
922
|
+
|
|
923
|
+
let count: i64 = jobs::table
|
|
924
|
+
.filter(jobs::task_name.eq(task_name))
|
|
925
|
+
.filter(jobs::status.eq(JobStatus::Running as i32))
|
|
926
|
+
.count()
|
|
927
|
+
.get_result(&mut conn)?;
|
|
928
|
+
|
|
929
|
+
Ok(count)
|
|
930
|
+
}
|
|
931
|
+
|
|
919
932
|
/// Purge job errors older than the given timestamp.
|
|
920
933
|
pub fn purge_job_errors(&self, older_than_ms: i64) -> Result<u64> {
|
|
921
934
|
let mut conn = self.conn()?;
|
|
@@ -444,6 +444,12 @@ macro_rules! impl_storage {
|
|
|
444
444
|
) -> $crate::error::Result<u64> {
|
|
445
445
|
self.purge_execution_claims(older_than_ms)
|
|
446
446
|
}
|
|
447
|
+
fn count_running_by_task(
|
|
448
|
+
&self,
|
|
449
|
+
task_name: &str,
|
|
450
|
+
) -> $crate::error::Result<i64> {
|
|
451
|
+
self.count_running_by_task(task_name)
|
|
452
|
+
}
|
|
447
453
|
fn stats_by_queue(
|
|
448
454
|
&self,
|
|
449
455
|
queue_name: &str,
|
|
@@ -792,6 +798,9 @@ impl Storage for StorageBackend {
|
|
|
792
798
|
fn purge_execution_claims(&self, older_than_ms: i64) -> Result<u64> {
|
|
793
799
|
delegate!(self, purge_execution_claims, older_than_ms)
|
|
794
800
|
}
|
|
801
|
+
fn count_running_by_task(&self, task_name: &str) -> Result<i64> {
|
|
802
|
+
delegate!(self, count_running_by_task, task_name)
|
|
803
|
+
}
|
|
795
804
|
fn stats_by_queue(&self, queue_name: &str) -> Result<QueueStats> {
|
|
796
805
|
delegate!(self, stats_by_queue, queue_name)
|
|
797
806
|
}
|
|
@@ -725,6 +725,24 @@ impl RedisStorage {
|
|
|
725
725
|
Ok(stats)
|
|
726
726
|
}
|
|
727
727
|
|
|
728
|
+
/// Count running jobs for a specific task name (for per-task concurrency limiting).
|
|
729
|
+
pub fn count_running_by_task(&self, task_name: &str) -> Result<i64> {
|
|
730
|
+
let mut conn = self.conn()?;
|
|
731
|
+
let by_task_key = self.key(&["jobs", "by_task", task_name]);
|
|
732
|
+
let job_ids: Vec<String> = conn.smembers(&by_task_key).map_err(map_err)?;
|
|
733
|
+
|
|
734
|
+
let mut count: i64 = 0;
|
|
735
|
+
for id in &job_ids {
|
|
736
|
+
if let Some(job) = self.load_job(&mut conn, id)? {
|
|
737
|
+
if job.status == JobStatus::Running {
|
|
738
|
+
count += 1;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
Ok(count)
|
|
744
|
+
}
|
|
745
|
+
|
|
728
746
|
pub fn stats_by_queue(&self, queue_name: &str) -> Result<QueueStats> {
|
|
729
747
|
let mut conn = self.conn()?;
|
|
730
748
|
let by_queue_key = self.key(&["jobs", "by_queue", queue_name]);
|
|
@@ -362,6 +362,31 @@ fn test_cascade_cancel_on_dlq() {
|
|
|
362
362
|
assert!(b.error.unwrap().contains("dependency failed"));
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
#[test]
|
|
366
|
+
fn test_count_running_by_task() {
|
|
367
|
+
let storage = test_storage();
|
|
368
|
+
storage.enqueue(make_job("task_a")).unwrap();
|
|
369
|
+
storage.enqueue(make_job("task_a")).unwrap();
|
|
370
|
+
storage.enqueue(make_job("task_b")).unwrap();
|
|
371
|
+
|
|
372
|
+
// No running jobs yet
|
|
373
|
+
assert_eq!(storage.count_running_by_task("task_a").unwrap(), 0);
|
|
374
|
+
|
|
375
|
+
let now = now_millis() + 1000;
|
|
376
|
+
// Dequeue one task_a (becomes running)
|
|
377
|
+
storage.dequeue("default", now).unwrap().unwrap();
|
|
378
|
+
|
|
379
|
+
assert_eq!(storage.count_running_by_task("task_a").unwrap(), 1);
|
|
380
|
+
assert_eq!(storage.count_running_by_task("task_b").unwrap(), 0);
|
|
381
|
+
|
|
382
|
+
// Dequeue second task_a
|
|
383
|
+
storage.dequeue("default", now).unwrap().unwrap();
|
|
384
|
+
assert_eq!(storage.count_running_by_task("task_a").unwrap(), 2);
|
|
385
|
+
|
|
386
|
+
// Nonexistent task should return 0
|
|
387
|
+
assert_eq!(storage.count_running_by_task("no_such_task").unwrap(), 0);
|
|
388
|
+
}
|
|
389
|
+
|
|
365
390
|
#[test]
|
|
366
391
|
fn test_enqueue_rejects_missing_dependency() {
|
|
367
392
|
let storage = test_storage();
|
|
@@ -161,6 +161,10 @@ pub trait Storage: Send + Sync + Clone {
|
|
|
161
161
|
fn complete_execution(&self, job_id: &str) -> Result<()>;
|
|
162
162
|
fn purge_execution_claims(&self, older_than_ms: i64) -> Result<u64>;
|
|
163
163
|
|
|
164
|
+
// ── Per-task concurrency ──────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
fn count_running_by_task(&self, task_name: &str) -> Result<i64>;
|
|
167
|
+
|
|
164
168
|
// ── Per-queue stats ──────────────────────────────────────────
|
|
165
169
|
|
|
166
170
|
fn stats_by_queue(&self, queue_name: &str) -> Result<QueueStats>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "taskito-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
|
|
6
6
|
[features]
|
|
@@ -22,3 +22,4 @@ crossbeam-channel = { workspace = true }
|
|
|
22
22
|
uuid = { workspace = true }
|
|
23
23
|
async-trait = { workspace = true }
|
|
24
24
|
taskito-async = { path = "../taskito-async", optional = true }
|
|
25
|
+
serde_json = { workspace = true }
|
|
@@ -26,13 +26,17 @@ pub struct PyTaskConfig {
|
|
|
26
26
|
pub circuit_breaker_cooldown: Option<i64>,
|
|
27
27
|
#[pyo3(get, set)]
|
|
28
28
|
pub retry_delays: Option<Vec<f64>>,
|
|
29
|
+
#[pyo3(get, set)]
|
|
30
|
+
pub max_retry_delay: Option<i64>,
|
|
31
|
+
#[pyo3(get, set)]
|
|
32
|
+
pub max_concurrent: Option<i32>,
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
#[pymethods]
|
|
32
36
|
#[allow(clippy::too_many_arguments)]
|
|
33
37
|
impl PyTaskConfig {
|
|
34
38
|
#[new]
|
|
35
|
-
#[pyo3(signature = (name, max_retries=3, retry_backoff=1.0, timeout=300, priority=0, rate_limit=None, queue="default".to_string(), circuit_breaker_threshold=None, circuit_breaker_window=None, circuit_breaker_cooldown=None, retry_delays=None))]
|
|
39
|
+
#[pyo3(signature = (name, max_retries=3, retry_backoff=1.0, timeout=300, priority=0, rate_limit=None, queue="default".to_string(), circuit_breaker_threshold=None, circuit_breaker_window=None, circuit_breaker_cooldown=None, retry_delays=None, max_retry_delay=None, max_concurrent=None))]
|
|
36
40
|
pub fn new(
|
|
37
41
|
name: String,
|
|
38
42
|
max_retries: i32,
|
|
@@ -45,6 +49,8 @@ impl PyTaskConfig {
|
|
|
45
49
|
circuit_breaker_window: Option<i64>,
|
|
46
50
|
circuit_breaker_cooldown: Option<i64>,
|
|
47
51
|
retry_delays: Option<Vec<f64>>,
|
|
52
|
+
max_retry_delay: Option<i64>,
|
|
53
|
+
max_concurrent: Option<i32>,
|
|
48
54
|
) -> Self {
|
|
49
55
|
Self {
|
|
50
56
|
name,
|
|
@@ -58,6 +64,8 @@ impl PyTaskConfig {
|
|
|
58
64
|
circuit_breaker_window,
|
|
59
65
|
circuit_breaker_cooldown,
|
|
60
66
|
retry_delays,
|
|
67
|
+
max_retry_delay,
|
|
68
|
+
max_concurrent,
|
|
61
69
|
}
|
|
62
70
|
}
|
|
63
71
|
}
|
|
@@ -33,6 +33,9 @@ pub struct PyQueue {
|
|
|
33
33
|
pub(crate) default_priority: i32,
|
|
34
34
|
pub(crate) shutdown_flag: Arc<AtomicBool>,
|
|
35
35
|
pub(crate) result_ttl_ms: Option<i64>,
|
|
36
|
+
pub(crate) scheduler_poll_interval_ms: u64,
|
|
37
|
+
pub(crate) scheduler_reap_interval: u32,
|
|
38
|
+
pub(crate) scheduler_cleanup_interval: u32,
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
#[pymethods]
|
|
@@ -43,7 +46,8 @@ pub struct PyQueue {
|
|
|
43
46
|
)]
|
|
44
47
|
impl PyQueue {
|
|
45
48
|
#[new]
|
|
46
|
-
#[pyo3(signature = (db_path=".taskito/taskito.db", workers=0, default_retry=3, default_timeout=300, default_priority=0, result_ttl=None, backend="sqlite", db_url=None, schema="taskito", pool_size=None))]
|
|
49
|
+
#[pyo3(signature = (db_path=".taskito/taskito.db", workers=0, default_retry=3, default_timeout=300, default_priority=0, result_ttl=None, backend="sqlite", db_url=None, schema="taskito", pool_size=None, scheduler_poll_interval_ms=50, scheduler_reap_interval=100, scheduler_cleanup_interval=1200))]
|
|
50
|
+
#[allow(clippy::too_many_arguments)]
|
|
47
51
|
pub fn new(
|
|
48
52
|
db_path: &str,
|
|
49
53
|
workers: usize,
|
|
@@ -55,6 +59,9 @@ impl PyQueue {
|
|
|
55
59
|
db_url: Option<&str>,
|
|
56
60
|
schema: &str,
|
|
57
61
|
pool_size: Option<u32>,
|
|
62
|
+
scheduler_poll_interval_ms: u64,
|
|
63
|
+
scheduler_reap_interval: u32,
|
|
64
|
+
scheduler_cleanup_interval: u32,
|
|
58
65
|
) -> PyResult<Self> {
|
|
59
66
|
let storage = match backend {
|
|
60
67
|
"sqlite" => {
|
|
@@ -117,6 +124,9 @@ impl PyQueue {
|
|
|
117
124
|
default_priority,
|
|
118
125
|
shutdown_flag: Arc::new(AtomicBool::new(false)),
|
|
119
126
|
result_ttl_ms: result_ttl.map(|s| s * 1000),
|
|
127
|
+
scheduler_poll_interval_ms,
|
|
128
|
+
scheduler_reap_interval,
|
|
129
|
+
scheduler_cleanup_interval,
|
|
120
130
|
})
|
|
121
131
|
}
|
|
122
132
|
|