taskito 0.7.0__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. {taskito-0.7.0 → taskito-0.8.0}/Cargo.lock +3 -3
  2. {taskito-0.7.0 → taskito-0.8.0}/PKG-INFO +1 -1
  3. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/Cargo.toml +1 -1
  4. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/scheduler/maintenance.rs +1 -1
  5. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/scheduler/mod.rs +15 -6
  6. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/scheduler/poller.rs +4 -1
  7. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/diesel_common/jobs.rs +27 -5
  8. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/mod.rs +24 -9
  9. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/models.rs +3 -0
  10. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/archival.rs +1 -1
  11. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/dead_letter.rs +2 -1
  12. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/mod.rs +10 -0
  13. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/dead_letter.rs +5 -1
  14. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/jobs.rs +39 -3
  15. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/schema.rs +2 -0
  16. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/archival.rs +1 -1
  17. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/dead_letter.rs +2 -1
  18. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/mod.rs +10 -0
  19. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/tests.rs +29 -17
  20. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/traits.rs +9 -2
  21. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/tests/rust/storage_tests.rs +6 -6
  22. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/Cargo.toml +1 -1
  23. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/py_job.rs +3 -0
  24. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/py_queue/inspection.rs +6 -3
  25. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/py_queue/mod.rs +6 -3
  26. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/py_queue/worker.rs +6 -1
  27. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/__init__.py +1 -1
  28. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/_taskito.pyi +4 -0
  29. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/app.py +3 -0
  30. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/mixins.py +19 -2
  31. {taskito-0.7.0 → taskito-0.8.0}/pyproject.toml +1 -1
  32. {taskito-0.7.0 → taskito-0.8.0}/Cargo.toml +0 -0
  33. {taskito-0.7.0 → taskito-0.8.0}/LICENSE +0 -0
  34. {taskito-0.7.0 → taskito-0.8.0}/README.md +0 -0
  35. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/error.rs +0 -0
  36. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/job.rs +0 -0
  37. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/lib.rs +0 -0
  38. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/periodic.rs +0 -0
  39. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/resilience/circuit_breaker.rs +0 -0
  40. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/resilience/dlq.rs +0 -0
  41. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/resilience/mod.rs +0 -0
  42. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/resilience/rate_limiter.rs +0 -0
  43. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/resilience/retry.rs +0 -0
  44. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/scheduler/result_handler.rs +0 -0
  45. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/diesel_common/locks.rs +0 -0
  46. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/diesel_common/logs.rs +0 -0
  47. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/diesel_common/metrics.rs +0 -0
  48. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/diesel_common/mod.rs +0 -0
  49. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/circuit_breakers.rs +0 -0
  50. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/jobs.rs +0 -0
  51. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/locks.rs +0 -0
  52. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/logs.rs +0 -0
  53. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/metrics.rs +0 -0
  54. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/periodic.rs +0 -0
  55. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/queue_state.rs +0 -0
  56. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/rate_limits.rs +0 -0
  57. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/trait_impl.rs +0 -0
  58. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/postgres/workers.rs +0 -0
  59. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/archival.rs +0 -0
  60. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/circuit_breakers.rs +0 -0
  61. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/locks.rs +0 -0
  62. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/logs.rs +0 -0
  63. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/metrics.rs +0 -0
  64. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/mod.rs +0 -0
  65. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/periodic.rs +0 -0
  66. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/queue_state.rs +0 -0
  67. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/rate_limits.rs +0 -0
  68. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/trait_impl.rs +0 -0
  69. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/redis_backend/workers.rs +0 -0
  70. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/circuit_breakers.rs +0 -0
  71. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/jobs.rs +0 -0
  72. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/locks.rs +0 -0
  73. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/logs.rs +0 -0
  74. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/metrics.rs +0 -0
  75. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/periodic.rs +0 -0
  76. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/queue_state.rs +0 -0
  77. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/rate_limits.rs +0 -0
  78. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/trait_impl.rs +0 -0
  79. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/storage/sqlite/workers.rs +0 -0
  80. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/src/worker.rs +0 -0
  81. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-core/tests/rust.rs +0 -0
  82. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/async_worker.rs +0 -0
  83. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/lib.rs +0 -0
  84. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/py_config.rs +0 -0
  85. {taskito-0.7.0 → taskito-0.8.0}/crates/taskito-python/src/py_worker.rs +0 -0
  86. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/async_support/__init__.py +0 -0
  87. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/async_support/context.py +0 -0
  88. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/async_support/executor.py +0 -0
  89. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/async_support/helpers.py +0 -0
  90. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/async_support/locks.py +0 -0
  91. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/async_support/mixins.py +0 -0
  92. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/async_support/result.py +0 -0
  93. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/canvas.py +0 -0
  94. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/cli.py +0 -0
  95. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/context.py +0 -0
  96. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/__init__.py +0 -0
  97. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/__init__.py +0 -0
  98. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/admin.py +0 -0
  99. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/apps.py +0 -0
  100. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/management/__init__.py +0 -0
  101. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/management/commands/__init__.py +0 -0
  102. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/management/commands/taskito_dashboard.py +0 -0
  103. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/management/commands/taskito_info.py +0 -0
  104. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/management/commands/taskito_worker.py +0 -0
  105. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/django/settings.py +0 -0
  106. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/fastapi.py +0 -0
  107. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/flask.py +0 -0
  108. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/otel.py +0 -0
  109. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/prometheus.py +0 -0
  110. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/contrib/sentry.py +0 -0
  111. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/dashboard.py +0 -0
  112. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/events.py +0 -0
  113. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/exceptions.py +0 -0
  114. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/health.py +0 -0
  115. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/inject.py +0 -0
  116. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/__init__.py +0 -0
  117. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/built_in.py +0 -0
  118. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/converters.py +0 -0
  119. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/errors.py +0 -0
  120. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/interceptor.py +0 -0
  121. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/metrics.py +0 -0
  122. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/reconstruct.py +0 -0
  123. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/registry.py +0 -0
  124. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/strategy.py +0 -0
  125. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/interception/walker.py +0 -0
  126. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/locks.py +0 -0
  127. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/middleware.py +0 -0
  128. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/__init__.py +0 -0
  129. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/built_in.py +0 -0
  130. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handler.py +0 -0
  131. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handlers/__init__.py +0 -0
  132. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handlers/boto3_client.py +0 -0
  133. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handlers/file.py +0 -0
  134. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handlers/gcs_client.py +0 -0
  135. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handlers/httpx_client.py +0 -0
  136. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handlers/logger.py +0 -0
  137. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/handlers/requests_session.py +0 -0
  138. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/metrics.py +0 -0
  139. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/no_proxy.py +0 -0
  140. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/reconstruct.py +0 -0
  141. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/registry.py +0 -0
  142. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/schema.py +0 -0
  143. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/proxies/signing.py +0 -0
  144. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/py.typed +0 -0
  145. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/__init__.py +0 -0
  146. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/definition.py +0 -0
  147. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/frozen.py +0 -0
  148. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/graph.py +0 -0
  149. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/health.py +0 -0
  150. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/pool.py +0 -0
  151. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/runtime.py +0 -0
  152. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/thread_local.py +0 -0
  153. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/resources/toml_config.py +0 -0
  154. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/result.py +0 -0
  155. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/scaler.py +0 -0
  156. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/serializers.py +0 -0
  157. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/task.py +0 -0
  158. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/dashboard.css +0 -0
  159. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/dashboard.html +0 -0
  160. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/js/actions.js +0 -0
  161. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/js/app.js +0 -0
  162. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/js/chart.js +0 -0
  163. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/js/components.js +0 -0
  164. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/js/utils.js +0 -0
  165. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/templates/js/views.js +0 -0
  166. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/testing.py +0 -0
  167. {taskito-0.7.0 → taskito-0.8.0}/py_src/taskito/webhooks.py +0 -0
@@ -1201,7 +1201,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
1201
1201
 
1202
1202
  [[package]]
1203
1203
  name = "taskito-async"
1204
- version = "0.7.0"
1204
+ version = "0.8.0"
1205
1205
  dependencies = [
1206
1206
  "async-trait",
1207
1207
  "crossbeam-channel",
@@ -1213,7 +1213,7 @@ dependencies = [
1213
1213
 
1214
1214
  [[package]]
1215
1215
  name = "taskito-core"
1216
- version = "0.7.0"
1216
+ version = "0.8.0"
1217
1217
  dependencies = [
1218
1218
  "async-trait",
1219
1219
  "chrono",
@@ -1237,7 +1237,7 @@ dependencies = [
1237
1237
 
1238
1238
  [[package]]
1239
1239
  name = "taskito-python"
1240
- version = "0.7.0"
1240
+ version = "0.8.0"
1241
1241
  dependencies = [
1242
1242
  "async-trait",
1243
1243
  "crossbeam-channel",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskito
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "taskito-core"
3
- version = "0.7.0"
3
+ version = "0.8.0"
4
4
  edition = "2021"
5
5
 
6
6
  [features]
@@ -99,7 +99,7 @@ impl Scheduler {
99
99
  depends_on: vec![],
100
100
  expires_at: None,
101
101
  result_ttl_ms: None,
102
- namespace: None,
102
+ namespace: self.namespace.clone(),
103
103
  };
104
104
 
105
105
  if let Err(e) = self.storage.enqueue_unique(new_job) {
@@ -128,6 +128,7 @@ pub struct Scheduler {
128
128
  config: SchedulerConfig,
129
129
  shutdown: Arc<Notify>,
130
130
  paused_cache: Mutex<(HashSet<String>, Instant)>,
131
+ namespace: Option<String>,
131
132
  }
132
133
 
133
134
  /// Counters for tick-based scheduling of periodic maintenance tasks.
@@ -139,7 +140,12 @@ struct TickCounters {
139
140
  }
140
141
 
141
142
  impl Scheduler {
142
- pub fn new(storage: StorageBackend, queues: Vec<String>, config: SchedulerConfig) -> Self {
143
+ pub fn new(
144
+ storage: StorageBackend,
145
+ queues: Vec<String>,
146
+ config: SchedulerConfig,
147
+ namespace: Option<String>,
148
+ ) -> Self {
143
149
  let rate_limiter = RateLimiter::new(storage.clone());
144
150
  let dlq = DeadLetterQueue::new(storage.clone());
145
151
  let circuit_breaker = CircuitBreaker::new(storage.clone());
@@ -155,6 +161,7 @@ impl Scheduler {
155
161
  config,
156
162
  shutdown: Arc::new(Notify::new()),
157
163
  paused_cache: Mutex::new((HashSet::new(), Instant::now())),
164
+ namespace,
158
165
  }
159
166
  }
160
167
 
@@ -249,6 +256,7 @@ mod tests {
249
256
  storage,
250
257
  vec!["default".to_string()],
251
258
  SchedulerConfig::default(),
259
+ None,
252
260
  )
253
261
  }
254
262
 
@@ -445,7 +453,7 @@ mod tests {
445
453
  // The second job should be back in pending with a future scheduled_at
446
454
  let jobs = scheduler
447
455
  .storage
448
- .list_jobs(Some(JobStatus::Pending as i32), None, None, 10, 0)
456
+ .list_jobs(Some(JobStatus::Pending as i32), None, None, 10, 0, None)
449
457
  .unwrap();
450
458
  assert_eq!(jobs.len(), 1);
451
459
  assert!(jobs[0].scheduled_at > now_millis());
@@ -486,7 +494,7 @@ mod tests {
486
494
  // Job should be rescheduled
487
495
  let jobs = scheduler
488
496
  .storage
489
- .list_jobs(Some(JobStatus::Pending as i32), None, None, 10, 0)
497
+ .list_jobs(Some(JobStatus::Pending as i32), None, None, 10, 0, None)
490
498
  .unwrap();
491
499
  assert_eq!(jobs.len(), 1);
492
500
  assert!(jobs[0].scheduled_at > now_millis());
@@ -562,6 +570,7 @@ mod tests {
562
570
  Some("periodic_task"),
563
571
  10,
564
572
  0,
573
+ None,
565
574
  )
566
575
  .unwrap();
567
576
  assert_eq!(jobs.len(), 1);
@@ -575,13 +584,13 @@ mod tests {
575
584
  result_ttl_ms: Some(1), // 1ms TTL
576
585
  ..SchedulerConfig::default()
577
586
  };
578
- let scheduler = Scheduler::new(storage, vec!["default".to_string()], config);
587
+ let scheduler = Scheduler::new(storage, vec!["default".to_string()], config, None);
579
588
 
580
589
  // Enqueue, dequeue, and complete a job
581
590
  let job = scheduler.storage.enqueue(make_job("cleanup_task")).unwrap();
582
591
  scheduler
583
592
  .storage
584
- .dequeue("default", now_millis() + 1000)
593
+ .dequeue("default", now_millis() + 1000, None)
585
594
  .unwrap();
586
595
  scheduler.storage.complete(&job.id, Some(vec![1])).unwrap();
587
596
 
@@ -605,7 +614,7 @@ mod tests {
605
614
  cleanup_interval: 1,
606
615
  ..SchedulerConfig::default()
607
616
  };
608
- let scheduler = Scheduler::new(storage, vec!["default".to_string()], config);
617
+ let scheduler = Scheduler::new(storage, vec!["default".to_string()], config, None);
609
618
 
610
619
  scheduler.storage.enqueue(make_job("tick_task")).unwrap();
611
620
 
@@ -51,7 +51,10 @@ impl Scheduler {
51
51
  return Ok(false);
52
52
  }
53
53
 
54
- let job = match self.storage.dequeue_from(&active_queues, now)? {
54
+ let job = match self
55
+ .storage
56
+ .dequeue_from(&active_queues, now, self.namespace.as_deref())?
57
+ {
55
58
  Some(j) => j,
56
59
  None => return Ok(false),
57
60
  };
@@ -270,17 +270,27 @@ macro_rules! impl_diesel_job_ops {
270
270
  }
271
271
 
272
272
  /// Atomically dequeue the highest-priority ready job from the given queue.
273
- /// Skips expired jobs.
274
- pub fn dequeue(&self, queue_name: &str, now: i64) -> Result<Option<Job>> {
273
+ /// Skips expired jobs. When `namespace` is `Some`, only jobs in that
274
+ /// namespace are considered; when `None`, only jobs with no namespace.
275
+ pub fn dequeue(&self, queue_name: &str, now: i64, namespace: Option<&str>) -> Result<Option<Job>> {
275
276
  let mut conn = self.conn()?;
276
277
 
277
278
  conn.transaction(|conn| {
278
- let candidates: Vec<JobRow> = jobs::table
279
+ let mut query = jobs::table
279
280
  .filter(jobs::queue.eq(queue_name))
280
281
  .filter(jobs::status.eq(JobStatus::Pending as i32))
281
282
  .filter(jobs::scheduled_at.le(now))
282
283
  .order((jobs::priority.desc(), jobs::scheduled_at.asc()))
283
284
  .limit(100)
285
+ .into_boxed();
286
+
287
+ if let Some(ns) = namespace {
288
+ query = query.filter(jobs::namespace.eq(ns));
289
+ } else {
290
+ query = query.filter(jobs::namespace.is_null());
291
+ }
292
+
293
+ let candidates: Vec<JobRow> = query
284
294
  .select(JobRow::as_select())
285
295
  .load(conn)?;
286
296
 
@@ -327,9 +337,9 @@ macro_rules! impl_diesel_job_ops {
327
337
  }
328
338
 
329
339
  /// Dequeue from multiple queues, checking each in order.
330
- pub fn dequeue_from(&self, queues: &[String], now: i64) -> Result<Option<Job>> {
340
+ pub fn dequeue_from(&self, queues: &[String], now: i64, namespace: Option<&str>) -> Result<Option<Job>> {
331
341
  for queue_name in queues {
332
- if let Some(job) = self.dequeue(queue_name, now)? {
342
+ if let Some(job) = self.dequeue(queue_name, now, namespace)? {
333
343
  return Ok(Some(job));
334
344
  }
335
345
  }
@@ -554,6 +564,8 @@ macro_rules! impl_diesel_job_ops {
554
564
  }
555
565
 
556
566
  /// List jobs with optional filters and pagination.
567
+ /// When `namespace` is `Some`, only jobs in that namespace are returned.
568
+ /// When `None`, all jobs are returned regardless of namespace.
557
569
  pub fn list_jobs(
558
570
  &self,
559
571
  status: Option<i32>,
@@ -561,6 +573,7 @@ macro_rules! impl_diesel_job_ops {
561
573
  task_name: Option<&str>,
562
574
  limit: i64,
563
575
  offset: i64,
576
+ namespace: Option<&str>,
564
577
  ) -> Result<Vec<Job>> {
565
578
  let mut conn = self.conn()?;
566
579
 
@@ -575,6 +588,9 @@ macro_rules! impl_diesel_job_ops {
575
588
  if let Some(t) = task_name {
576
589
  query = query.filter(jobs::task_name.eq(t));
577
590
  }
591
+ if let Some(ns) = namespace {
592
+ query = query.filter(jobs::namespace.eq(ns));
593
+ }
578
594
 
579
595
  let rows: Vec<JobRow> = query
580
596
  .limit(limit)
@@ -678,6 +694,8 @@ macro_rules! impl_diesel_job_ops {
678
694
  }
679
695
 
680
696
  /// List jobs with extended filters.
697
+ /// When `namespace` is `Some`, only jobs in that namespace are returned.
698
+ /// When `None`, all jobs are returned regardless of namespace.
681
699
  #[allow(clippy::too_many_arguments)]
682
700
  pub fn list_jobs_filtered(
683
701
  &self,
@@ -690,6 +708,7 @@ macro_rules! impl_diesel_job_ops {
690
708
  created_before: Option<i64>,
691
709
  limit: i64,
692
710
  offset: i64,
711
+ namespace: Option<&str>,
693
712
  ) -> Result<Vec<Job>> {
694
713
  let mut conn = self.conn()?;
695
714
 
@@ -716,6 +735,9 @@ macro_rules! impl_diesel_job_ops {
716
735
  if let Some(before) = created_before {
717
736
  query = query.filter(jobs::created_at.le(before));
718
737
  }
738
+ if let Some(ns) = namespace {
739
+ query = query.filter(jobs::namespace.eq(ns));
740
+ }
719
741
 
720
742
  let rows: Vec<JobRow> = query
721
743
  .limit(limit)
@@ -40,6 +40,7 @@ pub struct DeadJob {
40
40
  pub max_retries: i32,
41
41
  pub timeout_ms: i64,
42
42
  pub result_ttl_ms: Option<i64>,
43
+ pub namespace: Option<String>,
43
44
  }
44
45
 
45
46
  impl From<models::DeadLetterRow> for DeadJob {
@@ -58,6 +59,7 @@ impl From<models::DeadLetterRow> for DeadJob {
58
59
  max_retries: row.max_retries,
59
60
  timeout_ms: row.timeout_ms,
60
61
  result_ttl_ms: row.result_ttl_ms,
62
+ namespace: row.namespace,
61
63
  }
62
64
  }
63
65
  }
@@ -93,15 +95,17 @@ macro_rules! impl_storage {
93
95
  &self,
94
96
  queue_name: &str,
95
97
  now: i64,
98
+ namespace: Option<&str>,
96
99
  ) -> $crate::error::Result<Option<$crate::job::Job>> {
97
- self.dequeue(queue_name, now)
100
+ self.dequeue(queue_name, now, namespace)
98
101
  }
99
102
  fn dequeue_from(
100
103
  &self,
101
104
  queues: &[String],
102
105
  now: i64,
106
+ namespace: Option<&str>,
103
107
  ) -> $crate::error::Result<Option<$crate::job::Job>> {
104
- self.dequeue_from(queues, now)
108
+ self.dequeue_from(queues, now, namespace)
105
109
  }
106
110
  fn complete(
107
111
  &self,
@@ -151,8 +155,9 @@ macro_rules! impl_storage {
151
155
  task_name: Option<&str>,
152
156
  limit: i64,
153
157
  offset: i64,
158
+ namespace: Option<&str>,
154
159
  ) -> $crate::error::Result<Vec<$crate::job::Job>> {
155
- self.list_jobs(status, queue_name, task_name, limit, offset)
160
+ self.list_jobs(status, queue_name, task_name, limit, offset, namespace)
156
161
  }
157
162
  fn get_job(&self, id: &str) -> $crate::error::Result<Option<$crate::job::Job>> {
158
163
  self.get_job(id)
@@ -473,6 +478,7 @@ macro_rules! impl_storage {
473
478
  created_before: Option<i64>,
474
479
  limit: i64,
475
480
  offset: i64,
481
+ namespace: Option<&str>,
476
482
  ) -> $crate::error::Result<Vec<$crate::job::Job>> {
477
483
  self.list_jobs_filtered(
478
484
  status,
@@ -484,6 +490,7 @@ macro_rules! impl_storage {
484
490
  created_before,
485
491
  limit,
486
492
  offset,
493
+ namespace,
487
494
  )
488
495
  }
489
496
  }
@@ -526,11 +533,16 @@ impl Storage for StorageBackend {
526
533
  fn enqueue_unique(&self, new_job: NewJob) -> Result<Job> {
527
534
  delegate!(self, enqueue_unique, new_job)
528
535
  }
529
- fn dequeue(&self, queue_name: &str, now: i64) -> Result<Option<Job>> {
530
- delegate!(self, dequeue, queue_name, now)
536
+ fn dequeue(&self, queue_name: &str, now: i64, namespace: Option<&str>) -> Result<Option<Job>> {
537
+ delegate!(self, dequeue, queue_name, now, namespace)
531
538
  }
532
- fn dequeue_from(&self, queues: &[String], now: i64) -> Result<Option<Job>> {
533
- delegate!(self, dequeue_from, queues, now)
539
+ fn dequeue_from(
540
+ &self,
541
+ queues: &[String],
542
+ now: i64,
543
+ namespace: Option<&str>,
544
+ ) -> Result<Option<Job>> {
545
+ delegate!(self, dequeue_from, queues, now, namespace)
534
546
  }
535
547
  fn complete(&self, id: &str, result_bytes: Option<Vec<u8>>) -> Result<()> {
536
548
  delegate!(self, complete, id, result_bytes)
@@ -572,8 +584,9 @@ impl Storage for StorageBackend {
572
584
  task_name: Option<&str>,
573
585
  limit: i64,
574
586
  offset: i64,
587
+ namespace: Option<&str>,
575
588
  ) -> Result<Vec<Job>> {
576
- delegate!(self, list_jobs, status, queue_name, task_name, limit, offset)
589
+ delegate!(self, list_jobs, status, queue_name, task_name, limit, offset, namespace)
577
590
  }
578
591
  fn get_job(&self, id: &str) -> Result<Option<Job>> {
579
592
  delegate!(self, get_job, id)
@@ -818,6 +831,7 @@ impl Storage for StorageBackend {
818
831
  created_before: Option<i64>,
819
832
  limit: i64,
820
833
  offset: i64,
834
+ namespace: Option<&str>,
821
835
  ) -> Result<Vec<Job>> {
822
836
  delegate!(
823
837
  self,
@@ -830,7 +844,8 @@ impl Storage for StorageBackend {
830
844
  created_after,
831
845
  created_before,
832
846
  limit,
833
- offset
847
+ offset,
848
+ namespace
834
849
  )
835
850
  }
836
851
  }
@@ -75,6 +75,7 @@ pub struct DeadLetterRow {
75
75
  pub max_retries: i32,
76
76
  pub timeout_ms: i64,
77
77
  pub result_ttl_ms: Option<i64>,
78
+ pub namespace: Option<String>,
78
79
  }
79
80
 
80
81
  /// Insertable struct for dead letter entries.
@@ -94,6 +95,7 @@ pub struct NewDeadLetterRow<'a> {
94
95
  pub max_retries: i32,
95
96
  pub timeout_ms: i64,
96
97
  pub result_ttl_ms: Option<i64>,
98
+ pub namespace: Option<&'a str>,
97
99
  }
98
100
 
99
101
  /// A row in the `rate_limits` table.
@@ -394,4 +396,5 @@ pub struct ArchivedJobRow {
394
396
  pub cancel_requested: i32,
395
397
  pub expires_at: Option<i64>,
396
398
  pub result_ttl_ms: Option<i64>,
399
+ pub namespace: Option<String>,
397
400
  }
@@ -70,7 +70,7 @@ impl PostgresStorage {
70
70
  cancel_requested: row.cancel_requested != 0,
71
71
  expires_at: row.expires_at,
72
72
  result_ttl_ms: row.result_ttl_ms,
73
- namespace: None,
73
+ namespace: row.namespace,
74
74
  })
75
75
  .collect())
76
76
  }
@@ -30,6 +30,7 @@ impl PostgresStorage {
30
30
  max_retries: job.max_retries,
31
31
  timeout_ms: job.timeout_ms,
32
32
  result_ttl_ms: job.result_ttl_ms,
33
+ namespace: job.namespace.as_deref(),
33
34
  };
34
35
 
35
36
  diesel::insert_into(dead_letter::table)
@@ -94,7 +95,7 @@ impl PostgresStorage {
94
95
  depends_on: vec![],
95
96
  expires_at: None,
96
97
  result_ttl_ms: dead_row.result_ttl_ms,
97
- namespace: None,
98
+ namespace: dead_row.namespace,
98
99
  };
99
100
 
100
101
  let job = new_job.into_job();
@@ -434,6 +434,16 @@ impl PostgresStorage {
434
434
  "ALTER TABLE circuit_breakers ADD COLUMN IF NOT EXISTS half_open_failure_count INTEGER NOT NULL DEFAULT 0",
435
435
  );
436
436
 
437
+ // Migration: add namespace column to dead_letter and archived_jobs
438
+ migration_alter(
439
+ &mut conn,
440
+ "ALTER TABLE dead_letter ADD COLUMN IF NOT EXISTS namespace TEXT",
441
+ );
442
+ migration_alter(
443
+ &mut conn,
444
+ "ALTER TABLE archived_jobs ADD COLUMN IF NOT EXISTS namespace TEXT",
445
+ );
446
+
437
447
  // ── Distributed Locks ─────────────────────────────
438
448
  diesel::sql_query(
439
449
  "CREATE TABLE IF NOT EXISTS distributed_locks (
@@ -21,6 +21,8 @@ struct DeadJobEntry {
21
21
  pub max_retries: i32,
22
22
  pub timeout_ms: i64,
23
23
  pub result_ttl_ms: Option<i64>,
24
+ #[serde(default)]
25
+ pub namespace: Option<String>,
24
26
  }
25
27
 
26
28
  impl From<DeadJobEntry> for DeadJob {
@@ -39,6 +41,7 @@ impl From<DeadJobEntry> for DeadJob {
39
41
  max_retries: e.max_retries,
40
42
  timeout_ms: e.timeout_ms,
41
43
  result_ttl_ms: e.result_ttl_ms,
44
+ namespace: e.namespace,
42
45
  }
43
46
  }
44
47
  }
@@ -63,6 +66,7 @@ impl RedisStorage {
63
66
  max_retries: job.max_retries,
64
67
  timeout_ms: job.timeout_ms,
65
68
  result_ttl_ms: job.result_ttl_ms,
69
+ namespace: job.namespace.clone(),
66
70
  };
67
71
 
68
72
  let json = serde_json::to_string(&entry).map_err(|e| QueueError::Other(e.to_string()))?;
@@ -145,7 +149,7 @@ impl RedisStorage {
145
149
  depends_on: vec![],
146
150
  expires_at: None,
147
151
  result_ttl_ms: entry.result_ttl_ms,
148
- namespace: None,
152
+ namespace: entry.namespace,
149
153
  };
150
154
 
151
155
  let job = self.enqueue(new_job)?;
@@ -326,7 +326,12 @@ impl RedisStorage {
326
326
  }
327
327
  }
328
328
 
329
- pub fn dequeue(&self, queue_name: &str, now: i64) -> Result<Option<Job>> {
329
+ pub fn dequeue(
330
+ &self,
331
+ queue_name: &str,
332
+ now: i64,
333
+ namespace: Option<&str>,
334
+ ) -> Result<Option<Job>> {
330
335
  let mut conn = self.conn()?;
331
336
  let queue_key = self.key(&["queue", queue_name, "pending"]);
332
337
 
@@ -356,6 +361,15 @@ impl RedisStorage {
356
361
  continue;
357
362
  }
358
363
 
364
+ // Filter by namespace: Some(ns) matches that namespace, None matches only jobs without a namespace
365
+ if let Some(ns) = namespace {
366
+ if job.namespace.as_deref() != Some(ns) {
367
+ continue;
368
+ }
369
+ } else if job.namespace.is_some() {
370
+ continue;
371
+ }
372
+
359
373
  // Skip expired jobs
360
374
  if let Some(expires_at) = job.expires_at {
361
375
  if now > expires_at {
@@ -403,9 +417,14 @@ impl RedisStorage {
403
417
  Ok(None)
404
418
  }
405
419
 
406
- pub fn dequeue_from(&self, queues: &[String], now: i64) -> Result<Option<Job>> {
420
+ pub fn dequeue_from(
421
+ &self,
422
+ queues: &[String],
423
+ now: i64,
424
+ namespace: Option<&str>,
425
+ ) -> Result<Option<Job>> {
407
426
  for queue_name in queues {
408
- if let Some(job) = self.dequeue(queue_name, now)? {
427
+ if let Some(job) = self.dequeue(queue_name, now, namespace)? {
409
428
  return Ok(Some(job));
410
429
  }
411
430
  }
@@ -632,6 +651,7 @@ impl RedisStorage {
632
651
  task_name: Option<&str>,
633
652
  limit: i64,
634
653
  offset: i64,
654
+ namespace: Option<&str>,
635
655
  ) -> Result<Vec<Job>> {
636
656
  let mut conn = self.conn()?;
637
657
 
@@ -661,6 +681,11 @@ impl RedisStorage {
661
681
  let mut jobs = Vec::new();
662
682
  for id in &ids {
663
683
  if let Some(job) = self.load_job(&mut conn, id)? {
684
+ if let Some(ns) = namespace {
685
+ if job.namespace.as_deref() != Some(ns) {
686
+ continue;
687
+ }
688
+ }
664
689
  jobs.push(job);
665
690
  }
666
691
  }
@@ -687,6 +712,11 @@ impl RedisStorage {
687
712
  continue;
688
713
  }
689
714
  }
715
+ if let Some(ns) = namespace {
716
+ if job.namespace.as_deref() != Some(ns) {
717
+ continue;
718
+ }
719
+ }
690
720
  jobs.push(job);
691
721
  }
692
722
  }
@@ -802,6 +832,7 @@ impl RedisStorage {
802
832
  created_before: Option<i64>,
803
833
  limit: i64,
804
834
  offset: i64,
835
+ namespace: Option<&str>,
805
836
  ) -> Result<Vec<Job>> {
806
837
  let mut conn = self.conn()?;
807
838
 
@@ -860,6 +891,11 @@ impl RedisStorage {
860
891
  continue;
861
892
  }
862
893
  }
894
+ if let Some(ns) = namespace {
895
+ if job.namespace.as_deref() != Some(ns) {
896
+ continue;
897
+ }
898
+ }
863
899
  jobs.push(job);
864
900
  }
865
901
  }
@@ -40,6 +40,7 @@ diesel::table! {
40
40
  max_retries -> Integer,
41
41
  timeout_ms -> BigInt,
42
42
  result_ttl_ms -> Nullable<BigInt>,
43
+ namespace -> Nullable<Text>,
43
44
  }
44
45
  }
45
46
 
@@ -186,6 +187,7 @@ diesel::table! {
186
187
  cancel_requested -> Integer,
187
188
  expires_at -> Nullable<BigInt>,
188
189
  result_ttl_ms -> Nullable<BigInt>,
190
+ namespace -> Nullable<Text>,
189
191
  }
190
192
  }
191
193
 
@@ -73,7 +73,7 @@ impl SqliteStorage {
73
73
  cancel_requested: row.cancel_requested != 0,
74
74
  expires_at: row.expires_at,
75
75
  result_ttl_ms: row.result_ttl_ms,
76
- namespace: None,
76
+ namespace: row.namespace,
77
77
  })
78
78
  .collect())
79
79
  }
@@ -30,6 +30,7 @@ impl SqliteStorage {
30
30
  max_retries: job.max_retries,
31
31
  timeout_ms: job.timeout_ms,
32
32
  result_ttl_ms: job.result_ttl_ms,
33
+ namespace: job.namespace.as_deref(),
33
34
  };
34
35
 
35
36
  diesel::insert_into(dead_letter::table)
@@ -100,7 +101,7 @@ impl SqliteStorage {
100
101
  depends_on: vec![],
101
102
  expires_at: None,
102
103
  result_ttl_ms: dead_row.result_ttl_ms,
103
- namespace: None,
104
+ namespace: dead_row.namespace,
104
105
  };
105
106
 
106
107
  let job = new_job.into_job();
@@ -430,6 +430,16 @@ impl SqliteStorage {
430
430
  "ALTER TABLE circuit_breakers ADD COLUMN half_open_failure_count INTEGER NOT NULL DEFAULT 0",
431
431
  );
432
432
 
433
+ // Migration: add namespace column to dead_letter and archived_jobs
434
+ migration_alter(
435
+ &mut conn,
436
+ "ALTER TABLE dead_letter ADD COLUMN namespace TEXT",
437
+ );
438
+ migration_alter(
439
+ &mut conn,
440
+ "ALTER TABLE archived_jobs ADD COLUMN namespace TEXT",
441
+ );
442
+
433
443
  // ── Distributed Locks ─────────────────────────────
434
444
  diesel::sql_query(
435
445
  "CREATE TABLE IF NOT EXISTS distributed_locks (