taskito 0.8.0__tar.gz → 0.9.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.8.0 → taskito-0.9.0}/Cargo.lock +22 -3
- {taskito-0.8.0 → taskito-0.9.0}/PKG-INFO +1 -1
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/Cargo.toml +1 -1
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/mod.rs +29 -4
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/models.rs +8 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/mod.rs +18 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/workers.rs +45 -5
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/workers.rs +74 -14
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/schema.rs +4 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/mod.rs +9 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/workers.rs +45 -5
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/traits.rs +7 -1
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/tests/rust/storage_tests.rs +22 -2
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/Cargo.toml +4 -1
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/lib.rs +1 -0
- taskito-0.9.0/crates/taskito-python/src/prefork/child.rs +122 -0
- taskito-0.9.0/crates/taskito-python/src/prefork/dispatch.rs +32 -0
- taskito-0.9.0/crates/taskito-python/src/prefork/mod.rs +198 -0
- taskito-0.9.0/crates/taskito-python/src/prefork/protocol.rs +123 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/py_queue/inspection.rs +4 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/py_queue/worker.rs +45 -16
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/__init__.py +1 -1
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/_taskito.pyi +4 -1
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/app.py +54 -2
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/async_support/mixins.py +127 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/context.py +19 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/fastapi.py +36 -4
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/events.py +3 -0
- taskito-0.9.0/py_src/taskito/prefork/__init__.py +20 -0
- taskito-0.9.0/py_src/taskito/prefork/__main__.py +6 -0
- taskito-0.9.0/py_src/taskito/prefork/child.py +196 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/result.py +95 -0
- {taskito-0.8.0 → taskito-0.9.0}/pyproject.toml +1 -1
- {taskito-0.8.0 → taskito-0.9.0}/Cargo.toml +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/LICENSE +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/README.md +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/error.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/job.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/lib.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/periodic.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/resilience/circuit_breaker.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/resilience/dlq.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/resilience/mod.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/resilience/rate_limiter.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/resilience/retry.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/scheduler/maintenance.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/scheduler/mod.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/scheduler/poller.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/scheduler/result_handler.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/diesel_common/jobs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/diesel_common/locks.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/diesel_common/logs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/diesel_common/metrics.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/diesel_common/mod.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/archival.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/circuit_breakers.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/dead_letter.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/jobs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/locks.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/logs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/metrics.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/periodic.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/queue_state.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/rate_limits.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/postgres/trait_impl.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/archival.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/circuit_breakers.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/dead_letter.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/jobs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/locks.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/logs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/metrics.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/mod.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/periodic.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/queue_state.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/rate_limits.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/redis_backend/trait_impl.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/archival.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/circuit_breakers.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/dead_letter.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/jobs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/locks.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/logs.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/metrics.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/periodic.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/queue_state.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/rate_limits.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/tests.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/storage/sqlite/trait_impl.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/src/worker.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-core/tests/rust.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/async_worker.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/py_config.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/py_job.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/py_queue/mod.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/crates/taskito-python/src/py_worker.rs +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/async_support/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/async_support/context.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/async_support/executor.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/async_support/helpers.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/async_support/locks.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/async_support/result.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/canvas.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/cli.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/admin.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/apps.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/management/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/management/commands/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/management/commands/taskito_dashboard.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/management/commands/taskito_info.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/management/commands/taskito_worker.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/django/settings.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/flask.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/otel.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/prometheus.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/contrib/sentry.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/dashboard.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/exceptions.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/health.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/inject.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/built_in.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/converters.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/errors.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/interceptor.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/metrics.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/reconstruct.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/registry.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/strategy.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/interception/walker.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/locks.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/middleware.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/mixins.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/built_in.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handler.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handlers/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handlers/boto3_client.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handlers/file.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handlers/gcs_client.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handlers/httpx_client.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handlers/logger.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/handlers/requests_session.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/metrics.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/no_proxy.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/reconstruct.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/registry.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/schema.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/proxies/signing.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/py.typed +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/__init__.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/definition.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/frozen.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/graph.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/health.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/pool.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/runtime.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/thread_local.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/resources/toml_config.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/scaler.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/serializers.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/task.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/dashboard.css +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/dashboard.html +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/js/actions.js +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/js/app.js +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/js/chart.js +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/js/components.js +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/js/utils.js +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/templates/js/views.js +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/testing.py +0 -0
- {taskito-0.8.0 → taskito-0.9.0}/py_src/taskito/webhooks.py +0 -0
|
@@ -43,6 +43,12 @@ version = "1.5.0"
|
|
|
43
43
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
44
44
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
45
45
|
|
|
46
|
+
[[package]]
|
|
47
|
+
name = "base64"
|
|
48
|
+
version = "0.22.1"
|
|
49
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
50
|
+
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|
51
|
+
|
|
46
52
|
[[package]]
|
|
47
53
|
name = "bitflags"
|
|
48
54
|
version = "2.11.0"
|
|
@@ -324,6 +330,16 @@ dependencies = [
|
|
|
324
330
|
"percent-encoding",
|
|
325
331
|
]
|
|
326
332
|
|
|
333
|
+
[[package]]
|
|
334
|
+
name = "gethostname"
|
|
335
|
+
version = "1.1.0"
|
|
336
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
337
|
+
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
|
|
338
|
+
dependencies = [
|
|
339
|
+
"rustix",
|
|
340
|
+
"windows-link",
|
|
341
|
+
]
|
|
342
|
+
|
|
327
343
|
[[package]]
|
|
328
344
|
name = "getrandom"
|
|
329
345
|
version = "0.2.17"
|
|
@@ -1201,7 +1217,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
|
1201
1217
|
|
|
1202
1218
|
[[package]]
|
|
1203
1219
|
name = "taskito-async"
|
|
1204
|
-
version = "0.
|
|
1220
|
+
version = "0.9.0"
|
|
1205
1221
|
dependencies = [
|
|
1206
1222
|
"async-trait",
|
|
1207
1223
|
"crossbeam-channel",
|
|
@@ -1213,7 +1229,7 @@ dependencies = [
|
|
|
1213
1229
|
|
|
1214
1230
|
[[package]]
|
|
1215
1231
|
name = "taskito-core"
|
|
1216
|
-
version = "0.
|
|
1232
|
+
version = "0.9.0"
|
|
1217
1233
|
dependencies = [
|
|
1218
1234
|
"async-trait",
|
|
1219
1235
|
"chrono",
|
|
@@ -1237,12 +1253,15 @@ dependencies = [
|
|
|
1237
1253
|
|
|
1238
1254
|
[[package]]
|
|
1239
1255
|
name = "taskito-python"
|
|
1240
|
-
version = "0.
|
|
1256
|
+
version = "0.9.0"
|
|
1241
1257
|
dependencies = [
|
|
1242
1258
|
"async-trait",
|
|
1259
|
+
"base64",
|
|
1243
1260
|
"crossbeam-channel",
|
|
1261
|
+
"gethostname",
|
|
1244
1262
|
"log",
|
|
1245
1263
|
"pyo3",
|
|
1264
|
+
"serde",
|
|
1246
1265
|
"serde_json",
|
|
1247
1266
|
"taskito-async",
|
|
1248
1267
|
"taskito-core",
|
|
@@ -352,8 +352,11 @@ macro_rules! impl_storage {
|
|
|
352
352
|
resources: Option<&str>,
|
|
353
353
|
resource_health: Option<&str>,
|
|
354
354
|
threads: i32,
|
|
355
|
+
hostname: Option<&str>,
|
|
356
|
+
pid: Option<i32>,
|
|
357
|
+
pool_type: Option<&str>,
|
|
355
358
|
) -> $crate::error::Result<()> {
|
|
356
|
-
self.register_worker(worker_id, queues, tags, resources, resource_health, threads)
|
|
359
|
+
self.register_worker(worker_id, queues, tags, resources, resource_health, threads, hostname, pid, pool_type)
|
|
357
360
|
}
|
|
358
361
|
fn heartbeat(
|
|
359
362
|
&self,
|
|
@@ -362,17 +365,27 @@ macro_rules! impl_storage {
|
|
|
362
365
|
) -> $crate::error::Result<()> {
|
|
363
366
|
self.heartbeat(worker_id, resource_health)
|
|
364
367
|
}
|
|
368
|
+
fn update_worker_status(
|
|
369
|
+
&self,
|
|
370
|
+
worker_id: &str,
|
|
371
|
+
status: &str,
|
|
372
|
+
) -> $crate::error::Result<()> {
|
|
373
|
+
self.update_worker_status(worker_id, status)
|
|
374
|
+
}
|
|
365
375
|
fn list_workers(
|
|
366
376
|
&self,
|
|
367
377
|
) -> $crate::error::Result<Vec<$crate::storage::models::WorkerRow>> {
|
|
368
378
|
self.list_workers()
|
|
369
379
|
}
|
|
370
|
-
fn reap_dead_workers(&self) -> $crate::error::Result<
|
|
380
|
+
fn reap_dead_workers(&self) -> $crate::error::Result<Vec<String>> {
|
|
371
381
|
self.reap_dead_workers()
|
|
372
382
|
}
|
|
373
383
|
fn unregister_worker(&self, worker_id: &str) -> $crate::error::Result<()> {
|
|
374
384
|
self.unregister_worker(worker_id)
|
|
375
385
|
}
|
|
386
|
+
fn list_claims_by_worker(&self, worker_id: &str) -> $crate::error::Result<Vec<String>> {
|
|
387
|
+
self.list_claims_by_worker(worker_id)
|
|
388
|
+
}
|
|
376
389
|
fn pause_queue(&self, queue_name: &str) -> $crate::error::Result<()> {
|
|
377
390
|
self.pause_queue(queue_name)
|
|
378
391
|
}
|
|
@@ -739,6 +752,9 @@ impl Storage for StorageBackend {
|
|
|
739
752
|
resources: Option<&str>,
|
|
740
753
|
resource_health: Option<&str>,
|
|
741
754
|
threads: i32,
|
|
755
|
+
hostname: Option<&str>,
|
|
756
|
+
pid: Option<i32>,
|
|
757
|
+
pool_type: Option<&str>,
|
|
742
758
|
) -> Result<()> {
|
|
743
759
|
delegate!(
|
|
744
760
|
self,
|
|
@@ -748,21 +764,30 @@ impl Storage for StorageBackend {
|
|
|
748
764
|
tags,
|
|
749
765
|
resources,
|
|
750
766
|
resource_health,
|
|
751
|
-
threads
|
|
767
|
+
threads,
|
|
768
|
+
hostname,
|
|
769
|
+
pid,
|
|
770
|
+
pool_type
|
|
752
771
|
)
|
|
753
772
|
}
|
|
754
773
|
fn heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> Result<()> {
|
|
755
774
|
delegate!(self, heartbeat, worker_id, resource_health)
|
|
756
775
|
}
|
|
776
|
+
fn update_worker_status(&self, worker_id: &str, status: &str) -> Result<()> {
|
|
777
|
+
delegate!(self, update_worker_status, worker_id, status)
|
|
778
|
+
}
|
|
757
779
|
fn list_workers(&self) -> Result<Vec<models::WorkerRow>> {
|
|
758
780
|
delegate!(self, list_workers)
|
|
759
781
|
}
|
|
760
|
-
fn reap_dead_workers(&self) -> Result<
|
|
782
|
+
fn reap_dead_workers(&self) -> Result<Vec<String>> {
|
|
761
783
|
delegate!(self, reap_dead_workers)
|
|
762
784
|
}
|
|
763
785
|
fn unregister_worker(&self, worker_id: &str) -> Result<()> {
|
|
764
786
|
delegate!(self, unregister_worker, worker_id)
|
|
765
787
|
}
|
|
788
|
+
fn list_claims_by_worker(&self, worker_id: &str) -> Result<Vec<String>> {
|
|
789
|
+
delegate!(self, list_claims_by_worker, worker_id)
|
|
790
|
+
}
|
|
766
791
|
fn pause_queue(&self, queue_name: &str) -> Result<()> {
|
|
767
792
|
delegate!(self, pause_queue, queue_name)
|
|
768
793
|
}
|
|
@@ -307,6 +307,10 @@ pub struct WorkerRow {
|
|
|
307
307
|
pub resources: Option<String>,
|
|
308
308
|
pub resource_health: Option<String>,
|
|
309
309
|
pub threads: i32,
|
|
310
|
+
pub started_at: Option<i64>,
|
|
311
|
+
pub hostname: Option<String>,
|
|
312
|
+
pub pid: Option<i32>,
|
|
313
|
+
pub pool_type: Option<String>,
|
|
310
314
|
}
|
|
311
315
|
|
|
312
316
|
#[derive(Insertable, AsChangeset, Debug)]
|
|
@@ -320,6 +324,10 @@ pub struct NewWorkerRow<'a> {
|
|
|
320
324
|
pub resources: Option<&'a str>,
|
|
321
325
|
pub resource_health: Option<&'a str>,
|
|
322
326
|
pub threads: i32,
|
|
327
|
+
pub started_at: Option<i64>,
|
|
328
|
+
pub hostname: Option<&'a str>,
|
|
329
|
+
pub pid: Option<i32>,
|
|
330
|
+
pub pool_type: Option<&'a str>,
|
|
323
331
|
}
|
|
324
332
|
|
|
325
333
|
// ── Queue State ─────────────────────────────────────────────────
|
|
@@ -359,6 +359,24 @@ impl PostgresStorage {
|
|
|
359
359
|
"ALTER TABLE workers ADD COLUMN IF NOT EXISTS threads INTEGER NOT NULL DEFAULT 0",
|
|
360
360
|
);
|
|
361
361
|
|
|
362
|
+
// Migration: add worker discovery metadata columns
|
|
363
|
+
migration_alter(
|
|
364
|
+
&mut conn,
|
|
365
|
+
"ALTER TABLE workers ADD COLUMN IF NOT EXISTS started_at BIGINT",
|
|
366
|
+
);
|
|
367
|
+
migration_alter(
|
|
368
|
+
&mut conn,
|
|
369
|
+
"ALTER TABLE workers ADD COLUMN IF NOT EXISTS hostname TEXT",
|
|
370
|
+
);
|
|
371
|
+
migration_alter(
|
|
372
|
+
&mut conn,
|
|
373
|
+
"ALTER TABLE workers ADD COLUMN IF NOT EXISTS pid INTEGER",
|
|
374
|
+
);
|
|
375
|
+
migration_alter(
|
|
376
|
+
&mut conn,
|
|
377
|
+
"ALTER TABLE workers ADD COLUMN IF NOT EXISTS pool_type TEXT",
|
|
378
|
+
);
|
|
379
|
+
|
|
362
380
|
diesel::sql_query(
|
|
363
381
|
"CREATE TABLE IF NOT EXISTS queue_state (
|
|
364
382
|
queue_name TEXT PRIMARY KEY,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use diesel::prelude::*;
|
|
2
2
|
|
|
3
3
|
use super::super::models::*;
|
|
4
|
-
use super::super::schema::workers;
|
|
4
|
+
use super::super::schema::{execution_claims, workers};
|
|
5
5
|
use super::PostgresStorage;
|
|
6
6
|
use crate::error::Result;
|
|
7
7
|
use crate::job::now_millis;
|
|
@@ -11,6 +11,7 @@ const DEAD_WORKER_THRESHOLD_MS: i64 = 30_000;
|
|
|
11
11
|
|
|
12
12
|
impl PostgresStorage {
|
|
13
13
|
/// Register a new worker or update an existing one.
|
|
14
|
+
#[allow(clippy::too_many_arguments)]
|
|
14
15
|
pub fn register_worker(
|
|
15
16
|
&self,
|
|
16
17
|
worker_id: &str,
|
|
@@ -19,6 +20,9 @@ impl PostgresStorage {
|
|
|
19
20
|
resources: Option<&str>,
|
|
20
21
|
resource_health: Option<&str>,
|
|
21
22
|
threads: i32,
|
|
23
|
+
hostname: Option<&str>,
|
|
24
|
+
pid: Option<i32>,
|
|
25
|
+
pool_type: Option<&str>,
|
|
22
26
|
) -> Result<()> {
|
|
23
27
|
let mut conn = self.conn()?;
|
|
24
28
|
let now = now_millis();
|
|
@@ -32,6 +36,10 @@ impl PostgresStorage {
|
|
|
32
36
|
resources,
|
|
33
37
|
resource_health,
|
|
34
38
|
threads,
|
|
39
|
+
started_at: Some(now),
|
|
40
|
+
hostname,
|
|
41
|
+
pid,
|
|
42
|
+
pool_type,
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
diesel::insert_into(workers::table)
|
|
@@ -60,6 +68,18 @@ impl PostgresStorage {
|
|
|
60
68
|
Ok(())
|
|
61
69
|
}
|
|
62
70
|
|
|
71
|
+
/// Update the status of a worker.
|
|
72
|
+
pub fn update_worker_status(&self, worker_id: &str, status: &str) -> Result<()> {
|
|
73
|
+
let mut conn = self.conn()?;
|
|
74
|
+
|
|
75
|
+
diesel::update(workers::table)
|
|
76
|
+
.filter(workers::worker_id.eq(worker_id))
|
|
77
|
+
.set(workers::status.eq(status))
|
|
78
|
+
.execute(&mut conn)?;
|
|
79
|
+
|
|
80
|
+
Ok(())
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
/// List all workers with their heartbeat status.
|
|
64
84
|
pub fn list_workers(&self) -> Result<Vec<WorkerRow>> {
|
|
65
85
|
let mut conn = self.conn()?;
|
|
@@ -72,14 +92,22 @@ impl PostgresStorage {
|
|
|
72
92
|
}
|
|
73
93
|
|
|
74
94
|
/// Remove workers that haven't sent a heartbeat within the threshold.
|
|
75
|
-
|
|
95
|
+
/// Returns the IDs of the reaped workers.
|
|
96
|
+
pub fn reap_dead_workers(&self) -> Result<Vec<String>> {
|
|
76
97
|
let mut conn = self.conn()?;
|
|
77
98
|
let cutoff = now_millis().saturating_sub(DEAD_WORKER_THRESHOLD_MS);
|
|
78
99
|
|
|
79
|
-
let
|
|
80
|
-
.
|
|
100
|
+
let dead_ids: Vec<String> = workers::table
|
|
101
|
+
.filter(workers::last_heartbeat.lt(cutoff))
|
|
102
|
+
.select(workers::worker_id)
|
|
103
|
+
.load(&mut conn)?;
|
|
104
|
+
|
|
105
|
+
if !dead_ids.is_empty() {
|
|
106
|
+
diesel::delete(workers::table.filter(workers::worker_id.eq_any(&dead_ids)))
|
|
107
|
+
.execute(&mut conn)?;
|
|
108
|
+
}
|
|
81
109
|
|
|
82
|
-
Ok(
|
|
110
|
+
Ok(dead_ids)
|
|
83
111
|
}
|
|
84
112
|
|
|
85
113
|
/// Unregister a worker (called on shutdown).
|
|
@@ -91,4 +119,16 @@ impl PostgresStorage {
|
|
|
91
119
|
|
|
92
120
|
Ok(())
|
|
93
121
|
}
|
|
122
|
+
|
|
123
|
+
/// List all job IDs currently claimed by a worker.
|
|
124
|
+
pub fn list_claims_by_worker(&self, worker_id: &str) -> Result<Vec<String>> {
|
|
125
|
+
let mut conn = self.conn()?;
|
|
126
|
+
|
|
127
|
+
let job_ids: Vec<String> = execution_claims::table
|
|
128
|
+
.filter(execution_claims::worker_id.eq(worker_id))
|
|
129
|
+
.select(execution_claims::job_id)
|
|
130
|
+
.load(&mut conn)?;
|
|
131
|
+
|
|
132
|
+
Ok(job_ids)
|
|
133
|
+
}
|
|
94
134
|
}
|
|
@@ -8,6 +8,7 @@ use crate::storage::models::WorkerRow;
|
|
|
8
8
|
const DEAD_WORKER_THRESHOLD_MS: i64 = 30_000;
|
|
9
9
|
|
|
10
10
|
impl RedisStorage {
|
|
11
|
+
#[allow(clippy::too_many_arguments)]
|
|
11
12
|
pub fn register_worker(
|
|
12
13
|
&self,
|
|
13
14
|
worker_id: &str,
|
|
@@ -16,6 +17,9 @@ impl RedisStorage {
|
|
|
16
17
|
resources: Option<&str>,
|
|
17
18
|
resource_health: Option<&str>,
|
|
18
19
|
threads: i32,
|
|
20
|
+
hostname: Option<&str>,
|
|
21
|
+
pid: Option<i32>,
|
|
22
|
+
pool_type: Option<&str>,
|
|
19
23
|
) -> Result<()> {
|
|
20
24
|
let mut conn = self.conn()?;
|
|
21
25
|
let now = now_millis();
|
|
@@ -30,6 +34,10 @@ impl RedisStorage {
|
|
|
30
34
|
pipe.hset(&wkey, "resources", resources.unwrap_or(""));
|
|
31
35
|
pipe.hset(&wkey, "resource_health", resource_health.unwrap_or(""));
|
|
32
36
|
pipe.hset(&wkey, "threads", threads);
|
|
37
|
+
pipe.hset(&wkey, "started_at", now);
|
|
38
|
+
pipe.hset(&wkey, "hostname", hostname.unwrap_or(""));
|
|
39
|
+
pipe.hset(&wkey, "pid", pid.unwrap_or(0));
|
|
40
|
+
pipe.hset(&wkey, "pool_type", pool_type.unwrap_or(""));
|
|
33
41
|
pipe.sadd(&wall, worker_id);
|
|
34
42
|
pipe.query::<()>(&mut conn).map_err(map_err)?;
|
|
35
43
|
|
|
@@ -49,6 +57,16 @@ impl RedisStorage {
|
|
|
49
57
|
Ok(())
|
|
50
58
|
}
|
|
51
59
|
|
|
60
|
+
pub fn update_worker_status(&self, worker_id: &str, status: &str) -> Result<()> {
|
|
61
|
+
let mut conn = self.conn()?;
|
|
62
|
+
let wkey = self.key(&["worker", worker_id]);
|
|
63
|
+
|
|
64
|
+
conn.hset::<_, _, _, ()>(&wkey, "status", status)
|
|
65
|
+
.map_err(map_err)?;
|
|
66
|
+
|
|
67
|
+
Ok(())
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
pub fn list_workers(&self) -> Result<Vec<WorkerRow>> {
|
|
53
71
|
let mut conn = self.conn()?;
|
|
54
72
|
let wall = self.key(&["workers", "all"]);
|
|
@@ -89,43 +107,46 @@ impl RedisStorage {
|
|
|
89
107
|
.get("threads")
|
|
90
108
|
.and_then(|s| s.parse().ok())
|
|
91
109
|
.unwrap_or(0),
|
|
110
|
+
started_at: data.get("started_at").and_then(|s| s.parse().ok()),
|
|
111
|
+
hostname: to_opt("hostname"),
|
|
112
|
+
pid: data
|
|
113
|
+
.get("pid")
|
|
114
|
+
.and_then(|s| s.parse().ok())
|
|
115
|
+
.filter(|&v: &i32| v != 0),
|
|
116
|
+
pool_type: to_opt("pool_type"),
|
|
92
117
|
});
|
|
93
118
|
}
|
|
94
119
|
|
|
95
120
|
Ok(rows)
|
|
96
121
|
}
|
|
97
122
|
|
|
98
|
-
pub fn reap_dead_workers(&self) -> Result<
|
|
123
|
+
pub fn reap_dead_workers(&self) -> Result<Vec<String>> {
|
|
99
124
|
let mut conn = self.conn()?;
|
|
100
125
|
let cutoff = now_millis().saturating_sub(DEAD_WORKER_THRESHOLD_MS);
|
|
101
126
|
let wall = self.key(&["workers", "all"]);
|
|
102
127
|
|
|
103
128
|
let worker_ids: Vec<String> = conn.smembers(&wall).map_err(map_err)?;
|
|
104
129
|
|
|
105
|
-
let mut
|
|
130
|
+
let mut reaped = Vec::new();
|
|
106
131
|
for wid in worker_ids {
|
|
107
132
|
let wkey = self.key(&["worker", &wid]);
|
|
108
133
|
let hb: Option<i64> = conn.hget(&wkey, "last_heartbeat").map_err(map_err)?;
|
|
109
134
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
count += 1;
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
// No heartbeat data — remove
|
|
135
|
+
let is_dead = match hb {
|
|
136
|
+
Some(last_hb) => last_hb < cutoff,
|
|
137
|
+
None => true,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if is_dead {
|
|
120
141
|
let pipe = &mut redis::pipe();
|
|
121
142
|
pipe.del(&wkey);
|
|
122
143
|
pipe.srem(&wall, &wid);
|
|
123
144
|
pipe.query::<()>(&mut conn).map_err(map_err)?;
|
|
124
|
-
|
|
145
|
+
reaped.push(wid);
|
|
125
146
|
}
|
|
126
147
|
}
|
|
127
148
|
|
|
128
|
-
Ok(
|
|
149
|
+
Ok(reaped)
|
|
129
150
|
}
|
|
130
151
|
|
|
131
152
|
pub fn unregister_worker(&self, worker_id: &str) -> Result<()> {
|
|
@@ -140,4 +161,43 @@ impl RedisStorage {
|
|
|
140
161
|
|
|
141
162
|
Ok(())
|
|
142
163
|
}
|
|
164
|
+
|
|
165
|
+
pub fn list_claims_by_worker(&self, worker_id: &str) -> Result<Vec<String>> {
|
|
166
|
+
let mut conn = self.conn()?;
|
|
167
|
+
let pattern = self.key(&["exec_claim", "*"]);
|
|
168
|
+
|
|
169
|
+
let mut job_ids = Vec::new();
|
|
170
|
+
let mut cursor: u64 = 0;
|
|
171
|
+
loop {
|
|
172
|
+
let (next_cursor, keys): (u64, Vec<String>) = redis::cmd("SCAN")
|
|
173
|
+
.arg(cursor)
|
|
174
|
+
.arg("MATCH")
|
|
175
|
+
.arg(&pattern)
|
|
176
|
+
.arg("COUNT")
|
|
177
|
+
.arg(100)
|
|
178
|
+
.query(&mut conn)
|
|
179
|
+
.map_err(map_err)?;
|
|
180
|
+
|
|
181
|
+
for key in keys {
|
|
182
|
+
let value: Option<String> = conn.get(&key).map_err(map_err)?;
|
|
183
|
+
if let Some(val) = value {
|
|
184
|
+
// Value format is "{worker_id}:{timestamp}"
|
|
185
|
+
if val.starts_with(worker_id) && val[worker_id.len()..].starts_with(':') {
|
|
186
|
+
// Extract job_id from key: "{prefix}exec_claim:{job_id}"
|
|
187
|
+
let prefix = self.key(&["exec_claim", ""]);
|
|
188
|
+
if let Some(job_id) = key.strip_prefix(&prefix) {
|
|
189
|
+
job_ids.push(job_id.to_string());
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
cursor = next_cursor;
|
|
196
|
+
if cursor == 0 {
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
Ok(job_ids)
|
|
202
|
+
}
|
|
143
203
|
}
|
|
@@ -153,6 +153,10 @@ diesel::table! {
|
|
|
153
153
|
resources -> Nullable<Text>,
|
|
154
154
|
resource_health -> Nullable<Text>,
|
|
155
155
|
threads -> Integer,
|
|
156
|
+
started_at -> Nullable<BigInt>,
|
|
157
|
+
hostname -> Nullable<Text>,
|
|
158
|
+
pid -> Nullable<Integer>,
|
|
159
|
+
pool_type -> Nullable<Text>,
|
|
156
160
|
}
|
|
157
161
|
}
|
|
158
162
|
|
|
@@ -356,6 +356,15 @@ impl SqliteStorage {
|
|
|
356
356
|
"ALTER TABLE workers ADD COLUMN threads INTEGER NOT NULL DEFAULT 0",
|
|
357
357
|
);
|
|
358
358
|
|
|
359
|
+
// Migration: add worker discovery metadata columns
|
|
360
|
+
migration_alter(
|
|
361
|
+
&mut conn,
|
|
362
|
+
"ALTER TABLE workers ADD COLUMN started_at INTEGER",
|
|
363
|
+
);
|
|
364
|
+
migration_alter(&mut conn, "ALTER TABLE workers ADD COLUMN hostname TEXT");
|
|
365
|
+
migration_alter(&mut conn, "ALTER TABLE workers ADD COLUMN pid INTEGER");
|
|
366
|
+
migration_alter(&mut conn, "ALTER TABLE workers ADD COLUMN pool_type TEXT");
|
|
367
|
+
|
|
359
368
|
// ── Queue State ──────────────────────────────────
|
|
360
369
|
diesel::sql_query(
|
|
361
370
|
"CREATE TABLE IF NOT EXISTS queue_state (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use diesel::prelude::*;
|
|
2
2
|
|
|
3
3
|
use super::super::models::*;
|
|
4
|
-
use super::super::schema::workers;
|
|
4
|
+
use super::super::schema::{execution_claims, workers};
|
|
5
5
|
use super::SqliteStorage;
|
|
6
6
|
use crate::error::Result;
|
|
7
7
|
use crate::job::now_millis;
|
|
@@ -11,6 +11,7 @@ const DEAD_WORKER_THRESHOLD_MS: i64 = 30_000;
|
|
|
11
11
|
|
|
12
12
|
impl SqliteStorage {
|
|
13
13
|
/// Register a new worker or update an existing one.
|
|
14
|
+
#[allow(clippy::too_many_arguments)]
|
|
14
15
|
pub fn register_worker(
|
|
15
16
|
&self,
|
|
16
17
|
worker_id: &str,
|
|
@@ -19,6 +20,9 @@ impl SqliteStorage {
|
|
|
19
20
|
resources: Option<&str>,
|
|
20
21
|
resource_health: Option<&str>,
|
|
21
22
|
threads: i32,
|
|
23
|
+
hostname: Option<&str>,
|
|
24
|
+
pid: Option<i32>,
|
|
25
|
+
pool_type: Option<&str>,
|
|
22
26
|
) -> Result<()> {
|
|
23
27
|
let mut conn = self.conn()?;
|
|
24
28
|
let now = now_millis();
|
|
@@ -32,6 +36,10 @@ impl SqliteStorage {
|
|
|
32
36
|
resources,
|
|
33
37
|
resource_health,
|
|
34
38
|
threads,
|
|
39
|
+
started_at: Some(now),
|
|
40
|
+
hostname,
|
|
41
|
+
pid,
|
|
42
|
+
pool_type,
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
diesel::replace_into(workers::table)
|
|
@@ -57,6 +65,18 @@ impl SqliteStorage {
|
|
|
57
65
|
Ok(())
|
|
58
66
|
}
|
|
59
67
|
|
|
68
|
+
/// Update the status of a worker.
|
|
69
|
+
pub fn update_worker_status(&self, worker_id: &str, status: &str) -> Result<()> {
|
|
70
|
+
let mut conn = self.conn()?;
|
|
71
|
+
|
|
72
|
+
diesel::update(workers::table)
|
|
73
|
+
.filter(workers::worker_id.eq(worker_id))
|
|
74
|
+
.set(workers::status.eq(status))
|
|
75
|
+
.execute(&mut conn)?;
|
|
76
|
+
|
|
77
|
+
Ok(())
|
|
78
|
+
}
|
|
79
|
+
|
|
60
80
|
/// List all workers with their heartbeat status.
|
|
61
81
|
pub fn list_workers(&self) -> Result<Vec<WorkerRow>> {
|
|
62
82
|
let mut conn = self.conn()?;
|
|
@@ -69,14 +89,22 @@ impl SqliteStorage {
|
|
|
69
89
|
}
|
|
70
90
|
|
|
71
91
|
/// Remove workers that haven't sent a heartbeat within the threshold.
|
|
72
|
-
|
|
92
|
+
/// Returns the IDs of the reaped workers.
|
|
93
|
+
pub fn reap_dead_workers(&self) -> Result<Vec<String>> {
|
|
73
94
|
let mut conn = self.conn()?;
|
|
74
95
|
let cutoff = now_millis().saturating_sub(DEAD_WORKER_THRESHOLD_MS);
|
|
75
96
|
|
|
76
|
-
let
|
|
77
|
-
.
|
|
97
|
+
let dead_ids: Vec<String> = workers::table
|
|
98
|
+
.filter(workers::last_heartbeat.lt(cutoff))
|
|
99
|
+
.select(workers::worker_id)
|
|
100
|
+
.load(&mut conn)?;
|
|
101
|
+
|
|
102
|
+
if !dead_ids.is_empty() {
|
|
103
|
+
diesel::delete(workers::table.filter(workers::worker_id.eq_any(&dead_ids)))
|
|
104
|
+
.execute(&mut conn)?;
|
|
105
|
+
}
|
|
78
106
|
|
|
79
|
-
Ok(
|
|
107
|
+
Ok(dead_ids)
|
|
80
108
|
}
|
|
81
109
|
|
|
82
110
|
/// Unregister a worker (called on shutdown).
|
|
@@ -88,4 +116,16 @@ impl SqliteStorage {
|
|
|
88
116
|
|
|
89
117
|
Ok(())
|
|
90
118
|
}
|
|
119
|
+
|
|
120
|
+
/// List all job IDs currently claimed by a worker.
|
|
121
|
+
pub fn list_claims_by_worker(&self, worker_id: &str) -> Result<Vec<String>> {
|
|
122
|
+
let mut conn = self.conn()?;
|
|
123
|
+
|
|
124
|
+
let job_ids: Vec<String> = execution_claims::table
|
|
125
|
+
.filter(execution_claims::worker_id.eq(worker_id))
|
|
126
|
+
.select(execution_claims::job_id)
|
|
127
|
+
.load(&mut conn)?;
|
|
128
|
+
|
|
129
|
+
Ok(job_ids)
|
|
130
|
+
}
|
|
91
131
|
}
|
|
@@ -119,6 +119,7 @@ pub trait Storage: Send + Sync + Clone {
|
|
|
119
119
|
|
|
120
120
|
// ── Worker operations ───────────────────────────────────────────
|
|
121
121
|
|
|
122
|
+
#[allow(clippy::too_many_arguments)]
|
|
122
123
|
fn register_worker(
|
|
123
124
|
&self,
|
|
124
125
|
worker_id: &str,
|
|
@@ -127,11 +128,16 @@ pub trait Storage: Send + Sync + Clone {
|
|
|
127
128
|
resources: Option<&str>,
|
|
128
129
|
resource_health: Option<&str>,
|
|
129
130
|
threads: i32,
|
|
131
|
+
hostname: Option<&str>,
|
|
132
|
+
pid: Option<i32>,
|
|
133
|
+
pool_type: Option<&str>,
|
|
130
134
|
) -> Result<()>;
|
|
131
135
|
fn heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> Result<()>;
|
|
136
|
+
fn update_worker_status(&self, worker_id: &str, status: &str) -> Result<()>;
|
|
132
137
|
fn list_workers(&self) -> Result<Vec<WorkerRow>>;
|
|
133
|
-
fn reap_dead_workers(&self) -> Result<
|
|
138
|
+
fn reap_dead_workers(&self) -> Result<Vec<String>>;
|
|
134
139
|
fn unregister_worker(&self, worker_id: &str) -> Result<()>;
|
|
140
|
+
fn list_claims_by_worker(&self, worker_id: &str) -> Result<Vec<String>>;
|
|
135
141
|
|
|
136
142
|
// ── Queue pause/resume ───────────────────────────────────────
|
|
137
143
|
|
|
@@ -165,8 +165,18 @@ fn test_workers(s: &impl Storage) {
|
|
|
165
165
|
let resources = Some(r#"["db","redis"]"#);
|
|
166
166
|
let health = Some(r#"{"db":"healthy","redis":"healthy"}"#);
|
|
167
167
|
|
|
168
|
-
s.register_worker(
|
|
169
|
-
|
|
168
|
+
s.register_worker(
|
|
169
|
+
"w-test-1",
|
|
170
|
+
"q-workers",
|
|
171
|
+
None,
|
|
172
|
+
resources,
|
|
173
|
+
health,
|
|
174
|
+
4,
|
|
175
|
+
Some("test-host"),
|
|
176
|
+
Some(12345),
|
|
177
|
+
Some("thread"),
|
|
178
|
+
)
|
|
179
|
+
.unwrap();
|
|
170
180
|
s.heartbeat("w-test-1", Some(r#"{"db":"unhealthy","redis":"healthy"}"#))
|
|
171
181
|
.unwrap();
|
|
172
182
|
|
|
@@ -176,6 +186,16 @@ fn test_workers(s: &impl Storage) {
|
|
|
176
186
|
assert_eq!(w.threads, 4);
|
|
177
187
|
assert!(w.resources.as_deref().unwrap().contains("db"));
|
|
178
188
|
assert!(w.resource_health.as_deref().unwrap().contains("unhealthy"));
|
|
189
|
+
assert_eq!(w.hostname.as_deref(), Some("test-host"));
|
|
190
|
+
assert_eq!(w.pid, Some(12345));
|
|
191
|
+
assert_eq!(w.pool_type.as_deref(), Some("thread"));
|
|
192
|
+
assert!(w.started_at.is_some());
|
|
193
|
+
|
|
194
|
+
// Test update_worker_status
|
|
195
|
+
s.update_worker_status("w-test-1", "draining").unwrap();
|
|
196
|
+
let workers = s.list_workers().unwrap();
|
|
197
|
+
let w = workers.iter().find(|w| w.worker_id == "w-test-1").unwrap();
|
|
198
|
+
assert_eq!(w.status, "draining");
|
|
179
199
|
|
|
180
200
|
s.unregister_worker("w-test-1").unwrap();
|
|
181
201
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "taskito-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
|
|
6
6
|
[features]
|
|
@@ -23,4 +23,7 @@ uuid = { workspace = true }
|
|
|
23
23
|
async-trait = { workspace = true }
|
|
24
24
|
taskito-async = { path = "../taskito-async", optional = true }
|
|
25
25
|
serde_json = { workspace = true }
|
|
26
|
+
serde = { workspace = true }
|
|
27
|
+
base64 = "0.22"
|
|
26
28
|
log = { workspace = true }
|
|
29
|
+
gethostname = "1.1.0"
|