taskito 0.9.0__tar.gz → 0.10.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.9.0 → taskito-0.10.0}/Cargo.lock +3 -3
- {taskito-0.9.0 → taskito-0.10.0}/PKG-INFO +6 -6
- {taskito-0.9.0 → taskito-0.10.0}/README.md +1 -1
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/Cargo.toml +1 -1
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/scheduler/mod.rs +69 -8
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/scheduler/result_handler.rs +10 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/Cargo.toml +1 -1
- taskito-0.10.0/crates/taskito-python/src/prefork/dispatch.rs +65 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/__init__.py +1 -1
- taskito-0.10.0/py_src/taskito/dashboard.py +404 -0
- taskito-0.10.0/py_src/taskito/templates/dashboard.html +233 -0
- {taskito-0.9.0 → taskito-0.10.0}/pyproject.toml +5 -5
- taskito-0.9.0/crates/taskito-python/src/prefork/dispatch.rs +0 -32
- taskito-0.9.0/py_src/taskito/dashboard.py +0 -387
- taskito-0.9.0/py_src/taskito/templates/dashboard.css +0 -368
- taskito-0.9.0/py_src/taskito/templates/dashboard.html +0 -40
- taskito-0.9.0/py_src/taskito/templates/js/actions.js +0 -34
- taskito-0.9.0/py_src/taskito/templates/js/app.js +0 -93
- taskito-0.9.0/py_src/taskito/templates/js/chart.js +0 -150
- taskito-0.9.0/py_src/taskito/templates/js/components.js +0 -58
- taskito-0.9.0/py_src/taskito/templates/js/utils.js +0 -19
- taskito-0.9.0/py_src/taskito/templates/js/views.js +0 -348
- {taskito-0.9.0 → taskito-0.10.0}/Cargo.toml +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/LICENSE +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/error.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/job.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/lib.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/periodic.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/resilience/circuit_breaker.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/resilience/dlq.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/resilience/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/resilience/rate_limiter.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/resilience/retry.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/scheduler/maintenance.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/scheduler/poller.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/diesel_common/jobs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/diesel_common/locks.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/diesel_common/logs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/diesel_common/metrics.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/diesel_common/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/models.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/archival.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/circuit_breakers.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/dead_letter.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/jobs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/locks.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/logs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/metrics.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/periodic.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/queue_state.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/rate_limits.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/trait_impl.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/postgres/workers.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/archival.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/circuit_breakers.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/dead_letter.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/jobs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/locks.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/logs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/metrics.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/periodic.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/queue_state.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/rate_limits.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/trait_impl.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/redis_backend/workers.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/schema.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/archival.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/circuit_breakers.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/dead_letter.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/jobs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/locks.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/logs.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/metrics.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/periodic.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/queue_state.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/rate_limits.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/tests.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/trait_impl.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/sqlite/workers.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/storage/traits.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/src/worker.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/tests/rust/storage_tests.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-core/tests/rust.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/async_worker.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/lib.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/prefork/child.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/prefork/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/prefork/protocol.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/py_config.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/py_job.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/py_queue/inspection.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/py_queue/mod.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/py_queue/worker.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/crates/taskito-python/src/py_worker.rs +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/_taskito.pyi +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/app.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/async_support/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/async_support/context.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/async_support/executor.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/async_support/helpers.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/async_support/locks.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/async_support/mixins.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/async_support/result.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/canvas.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/cli.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/context.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/admin.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/apps.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/management/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/management/commands/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/management/commands/taskito_dashboard.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/management/commands/taskito_info.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/management/commands/taskito_worker.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/django/settings.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/fastapi.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/flask.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/otel.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/prometheus.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/contrib/sentry.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/events.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/exceptions.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/health.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/inject.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/built_in.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/converters.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/errors.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/interceptor.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/metrics.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/reconstruct.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/registry.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/strategy.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/interception/walker.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/locks.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/middleware.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/mixins.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/prefork/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/prefork/__main__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/prefork/child.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/built_in.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handler.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handlers/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handlers/boto3_client.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handlers/file.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handlers/gcs_client.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handlers/httpx_client.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handlers/logger.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/handlers/requests_session.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/metrics.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/no_proxy.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/reconstruct.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/registry.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/schema.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/proxies/signing.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/py.typed +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/__init__.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/definition.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/frozen.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/graph.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/health.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/pool.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/runtime.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/thread_local.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/resources/toml_config.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/result.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/scaler.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/serializers.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/task.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/testing.py +0 -0
- {taskito-0.9.0 → taskito-0.10.0}/py_src/taskito/webhooks.py +0 -0
|
@@ -1217,7 +1217,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
|
1217
1217
|
|
|
1218
1218
|
[[package]]
|
|
1219
1219
|
name = "taskito-async"
|
|
1220
|
-
version = "0.
|
|
1220
|
+
version = "0.10.0"
|
|
1221
1221
|
dependencies = [
|
|
1222
1222
|
"async-trait",
|
|
1223
1223
|
"crossbeam-channel",
|
|
@@ -1229,7 +1229,7 @@ dependencies = [
|
|
|
1229
1229
|
|
|
1230
1230
|
[[package]]
|
|
1231
1231
|
name = "taskito-core"
|
|
1232
|
-
version = "0.
|
|
1232
|
+
version = "0.10.0"
|
|
1233
1233
|
dependencies = [
|
|
1234
1234
|
"async-trait",
|
|
1235
1235
|
"chrono",
|
|
@@ -1253,7 +1253,7 @@ dependencies = [
|
|
|
1253
1253
|
|
|
1254
1254
|
[[package]]
|
|
1255
1255
|
name = "taskito-python"
|
|
1256
|
-
version = "0.
|
|
1256
|
+
version = "0.10.0"
|
|
1257
1257
|
dependencies = [
|
|
1258
1258
|
"async-trait",
|
|
1259
1259
|
"base64",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taskito
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -49,17 +49,17 @@ License-File: LICENSE
|
|
|
49
49
|
Summary: Rust-powered task queue for Python. No broker required.
|
|
50
50
|
Requires-Python: >=3.10
|
|
51
51
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
52
|
-
Project-URL: Changelog, https://github.com/
|
|
52
|
+
Project-URL: Changelog, https://github.com/ByteVeda/taskito/blob/master/docs/changelog.md
|
|
53
53
|
Project-URL: Documentation, https://taskito.grigori.in
|
|
54
|
-
Project-URL: Homepage, https://github.com/
|
|
55
|
-
Project-URL: Issues, https://github.com/
|
|
56
|
-
Project-URL: Repository, https://github.com/
|
|
54
|
+
Project-URL: Homepage, https://github.com/ByteVeda/taskito
|
|
55
|
+
Project-URL: Issues, https://github.com/ByteVeda/taskito/issues
|
|
56
|
+
Project-URL: Repository, https://github.com/ByteVeda/taskito
|
|
57
57
|
|
|
58
58
|
# taskito
|
|
59
59
|
|
|
60
60
|
[](https://pypi.org/project/taskito/)
|
|
61
61
|
[](https://pypi.org/project/taskito/)
|
|
62
|
-
[](https://github.com/
|
|
62
|
+
[](https://github.com/ByteVeda/taskito/blob/master/LICENSE)
|
|
63
63
|
|
|
64
64
|
A Rust-powered task queue for Python. No broker required — just SQLite or Postgres.
|
|
65
65
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/taskito/)
|
|
4
4
|
[](https://pypi.org/project/taskito/)
|
|
5
|
-
[](https://github.com/
|
|
5
|
+
[](https://github.com/ByteVeda/taskito/blob/master/LICENSE)
|
|
6
6
|
|
|
7
7
|
A Rust-powered task queue for Python. No broker required — just SQLite or Postgres.
|
|
8
8
|
|
|
@@ -22,6 +22,9 @@ pub use crate::job::Job;
|
|
|
22
22
|
pub struct SchedulerConfig {
|
|
23
23
|
/// Interval between scheduler poll cycles.
|
|
24
24
|
pub poll_interval: Duration,
|
|
25
|
+
/// Priority boost per second of wait time. `None` disables aging.
|
|
26
|
+
/// Example: `Some(1)` boosts priority by 1 per second of wait.
|
|
27
|
+
pub aging_factor: Option<i64>,
|
|
25
28
|
/// Reap stale jobs every N iterations.
|
|
26
29
|
pub reap_interval: u32,
|
|
27
30
|
/// Check periodic tasks every N iterations.
|
|
@@ -36,6 +39,7 @@ impl Default for SchedulerConfig {
|
|
|
36
39
|
fn default() -> Self {
|
|
37
40
|
Self {
|
|
38
41
|
poll_interval: Duration::from_millis(50),
|
|
42
|
+
aging_factor: None,
|
|
39
43
|
reap_interval: 100,
|
|
40
44
|
periodic_check_interval: 60,
|
|
41
45
|
cleanup_interval: 1200,
|
|
@@ -116,6 +120,34 @@ pub struct QueueConfig {
|
|
|
116
120
|
pub max_concurrent: Option<i32>,
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
/// In-memory cache of average task execution duration.
|
|
124
|
+
/// Updated on every `handle_result()` — no DB queries needed.
|
|
125
|
+
struct TaskDurationCache {
|
|
126
|
+
durations: HashMap<String, (u64, i64)>, // (count, sum_ns)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
impl TaskDurationCache {
|
|
130
|
+
fn new() -> Self {
|
|
131
|
+
Self {
|
|
132
|
+
durations: HashMap::new(),
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fn record(&mut self, task_name: &str, wall_time_ns: i64) {
|
|
137
|
+
let entry = self.durations.entry(task_name.to_string()).or_default();
|
|
138
|
+
entry.0 += 1;
|
|
139
|
+
entry.1 = entry.1.saturating_add(wall_time_ns);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#[allow(dead_code)]
|
|
143
|
+
fn avg_ns(&self, task_name: &str) -> Option<i64> {
|
|
144
|
+
self.durations
|
|
145
|
+
.get(task_name)
|
|
146
|
+
.filter(|(count, _)| *count > 0)
|
|
147
|
+
.map(|(count, sum)| sum / *count as i64)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
119
151
|
/// The central scheduler that coordinates job dispatch, retries, rate limiting, and circuit breakers.
|
|
120
152
|
pub struct Scheduler {
|
|
121
153
|
storage: StorageBackend,
|
|
@@ -129,6 +161,7 @@ pub struct Scheduler {
|
|
|
129
161
|
shutdown: Arc<Notify>,
|
|
130
162
|
paused_cache: Mutex<(HashSet<String>, Instant)>,
|
|
131
163
|
namespace: Option<String>,
|
|
164
|
+
duration_cache: Mutex<TaskDurationCache>,
|
|
132
165
|
}
|
|
133
166
|
|
|
134
167
|
/// Counters for tick-based scheduling of periodic maintenance tasks.
|
|
@@ -162,6 +195,7 @@ impl Scheduler {
|
|
|
162
195
|
shutdown: Arc::new(Notify::new()),
|
|
163
196
|
paused_cache: Mutex::new((HashSet::new(), Instant::now())),
|
|
164
197
|
namespace,
|
|
198
|
+
duration_cache: Mutex::new(TaskDurationCache::new()),
|
|
165
199
|
}
|
|
166
200
|
}
|
|
167
201
|
|
|
@@ -192,24 +226,44 @@ impl Scheduler {
|
|
|
192
226
|
|
|
193
227
|
/// Run the scheduler loop. Polls for ready jobs and dispatches them
|
|
194
228
|
/// to the worker pool via the provided channel.
|
|
229
|
+
///
|
|
230
|
+
/// Uses adaptive polling: starts at `poll_interval`, backs off
|
|
231
|
+
/// exponentially (up to 1s) when no jobs are found, resets immediately
|
|
232
|
+
/// when a job is dispatched.
|
|
195
233
|
pub async fn run(&self, job_tx: tokio::sync::mpsc::Sender<Job>) {
|
|
196
234
|
let mut counters = TickCounters::default();
|
|
235
|
+
let base_interval = self.config.poll_interval;
|
|
236
|
+
let max_interval = Duration::from_millis(200);
|
|
237
|
+
let mut current_interval = base_interval;
|
|
197
238
|
|
|
198
239
|
loop {
|
|
199
240
|
tokio::select! {
|
|
200
241
|
_ = self.shutdown.notified() => break,
|
|
201
|
-
_ = tokio::time::sleep(
|
|
242
|
+
_ = tokio::time::sleep(current_interval) => {}
|
|
202
243
|
}
|
|
203
244
|
|
|
204
|
-
self.tick(&job_tx, &mut counters);
|
|
245
|
+
let had_work = self.tick(&job_tx, &mut counters);
|
|
246
|
+
if had_work {
|
|
247
|
+
current_interval = base_interval;
|
|
248
|
+
} else {
|
|
249
|
+
current_interval = (current_interval * 2).min(max_interval);
|
|
250
|
+
}
|
|
205
251
|
}
|
|
206
252
|
}
|
|
207
253
|
|
|
208
254
|
/// Execute one iteration of the scheduler loop.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
255
|
+
/// Returns true if any work was done (job dispatched or periodic task enqueued),
|
|
256
|
+
/// which resets the adaptive poll interval.
|
|
257
|
+
fn tick(&self, job_tx: &tokio::sync::mpsc::Sender<Job>, counters: &mut TickCounters) -> bool {
|
|
258
|
+
let dispatched = match self.try_dispatch(job_tx) {
|
|
259
|
+
Ok(d) => d,
|
|
260
|
+
Err(e) => {
|
|
261
|
+
error!("scheduler error: {e}");
|
|
262
|
+
false
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
let mut had_maintenance = false;
|
|
213
267
|
|
|
214
268
|
counters.reap += 1;
|
|
215
269
|
counters.periodic += 1;
|
|
@@ -225,8 +279,13 @@ impl Scheduler {
|
|
|
225
279
|
.periodic
|
|
226
280
|
.is_multiple_of(self.config.periodic_check_interval)
|
|
227
281
|
{
|
|
228
|
-
|
|
229
|
-
|
|
282
|
+
match self.check_periodic() {
|
|
283
|
+
Ok(()) => {
|
|
284
|
+
// Periodic tasks may have been enqueued — reset polling
|
|
285
|
+
// so the next tick picks them up quickly.
|
|
286
|
+
had_maintenance = true;
|
|
287
|
+
}
|
|
288
|
+
Err(e) => error!("periodic check error: {e}"),
|
|
230
289
|
}
|
|
231
290
|
}
|
|
232
291
|
|
|
@@ -238,6 +297,8 @@ impl Scheduler {
|
|
|
238
297
|
error!("auto-cleanup error: {e}");
|
|
239
298
|
}
|
|
240
299
|
}
|
|
300
|
+
|
|
301
|
+
dispatched || had_maintenance
|
|
241
302
|
}
|
|
242
303
|
}
|
|
243
304
|
|
|
@@ -36,6 +36,11 @@ impl Scheduler {
|
|
|
36
36
|
error!("circuit breaker error for {task_name}: {e}");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// Update in-memory duration cache for smart scheduling
|
|
40
|
+
if let Ok(mut cache) = self.duration_cache.lock() {
|
|
41
|
+
cache.record(task_name, wall_time_ns);
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
Ok(ResultOutcome::Success {
|
|
40
45
|
job_id,
|
|
41
46
|
task_name: task_name.clone(),
|
|
@@ -71,6 +76,11 @@ impl Scheduler {
|
|
|
71
76
|
log::error!("circuit breaker error for {task_name}: {e}");
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
// Update in-memory duration cache for smart scheduling
|
|
80
|
+
if let Ok(mut cache) = self.duration_cache.lock() {
|
|
81
|
+
cache.record(&task_name, wall_time_ns);
|
|
82
|
+
}
|
|
83
|
+
|
|
74
84
|
// Look up the job to get the queue name for middleware context
|
|
75
85
|
let queue = self
|
|
76
86
|
.storage
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//! Job dispatch strategies for distributing work across child processes.
|
|
2
|
+
|
|
3
|
+
/// Selects the child with the fewest in-flight jobs (least-loaded).
|
|
4
|
+
/// Falls back to round-robin if all children have equal load.
|
|
5
|
+
pub fn least_loaded(in_flight_counts: &[u32]) -> usize {
|
|
6
|
+
in_flight_counts
|
|
7
|
+
.iter()
|
|
8
|
+
.enumerate()
|
|
9
|
+
.min_by_key(|(_, &count)| count)
|
|
10
|
+
.map(|(idx, _)| idx)
|
|
11
|
+
.unwrap_or(0)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// Selects the child with the lowest weighted load.
|
|
15
|
+
///
|
|
16
|
+
/// Score = in_flight_count * avg_task_duration_ns. A worker with 1 slow job
|
|
17
|
+
/// scores higher than one with 3 fast jobs, enabling better load distribution
|
|
18
|
+
/// across heterogeneous workloads.
|
|
19
|
+
///
|
|
20
|
+
/// Falls back to `least_loaded` when `avg_duration_ns` is 0.
|
|
21
|
+
#[allow(dead_code)]
|
|
22
|
+
pub fn weighted_least_loaded(in_flight_counts: &[u32], avg_duration_ns: i64) -> usize {
|
|
23
|
+
if avg_duration_ns <= 0 {
|
|
24
|
+
return least_loaded(in_flight_counts);
|
|
25
|
+
}
|
|
26
|
+
in_flight_counts
|
|
27
|
+
.iter()
|
|
28
|
+
.enumerate()
|
|
29
|
+
.min_by_key(|(_, &count)| count as i64 * avg_duration_ns)
|
|
30
|
+
.map(|(idx, _)| idx)
|
|
31
|
+
.unwrap_or(0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[cfg(test)]
|
|
35
|
+
mod tests {
|
|
36
|
+
use super::*;
|
|
37
|
+
|
|
38
|
+
#[test]
|
|
39
|
+
fn test_least_loaded_picks_idle() {
|
|
40
|
+
assert_eq!(least_loaded(&[3, 0, 2]), 1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#[test]
|
|
44
|
+
fn test_least_loaded_picks_first_on_tie() {
|
|
45
|
+
assert_eq!(least_loaded(&[1, 1, 1]), 0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#[test]
|
|
49
|
+
fn test_least_loaded_single() {
|
|
50
|
+
assert_eq!(least_loaded(&[5]), 0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#[test]
|
|
54
|
+
fn test_weighted_picks_lowest_score() {
|
|
55
|
+
// Worker 0: 2 in-flight * 100ns = 200
|
|
56
|
+
// Worker 1: 1 in-flight * 100ns = 100 ← pick this
|
|
57
|
+
// Worker 2: 3 in-flight * 100ns = 300
|
|
58
|
+
assert_eq!(weighted_least_loaded(&[2, 1, 3], 100), 1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#[test]
|
|
62
|
+
fn test_weighted_falls_back_on_zero_duration() {
|
|
63
|
+
assert_eq!(weighted_least_loaded(&[3, 0, 2], 0), 1); // same as least_loaded
|
|
64
|
+
}
|
|
65
|
+
}
|