feat: implement multi-module API#44
Merged
Merged
Conversation
Introduces `Module` — a builder-style struct that collects typed
executors, module-wide defaults (priority, retry policy, group, TTL,
tags), a concurrency cap, and scoped app state. Task types are stored
with their unprefixed names and will be namespaced as `"{module}::{type}"`
when registered with the scheduler in step 3.
…37 step 2) Introduces SubmitBuilder (src/task/submit_builder.rs) returned by ModuleHandle::submit and ModuleHandle::submit_typed. Implements IntoFuture so bare .await works; chain .priority(), .group(), .key(), .run_after(), .ttl(), .depends_on(), .tag(), .parent() to override individual fields. resolve() applies a three-pass layering: prefix task_type with the module name, fill unset fields from ModuleSubmitDefaults, then apply per-call overrides unconditionally.
Remove executor registration methods from SchedulerBuilder. Executors
must now be registered via Module, which prefixes all task types with
"{module_name}::" at build time.
- SchedulerBuilder: remove executor/typed_executor/executor_with_*
methods; add modules field and .module() method; build() validates
at least one module, no duplicate names, no prefixed-type collisions
- Module: add executor_with_options/ttl/retry_policy builder methods
- ModuleRegistry/ModuleEntry: new structs in src/module.rs storing
per-module metadata (name, prefix, defaults, concurrency cap)
- SchedulerInner: add module_registry field; expose via
Scheduler::module_registry() accessor
- All 45 existing integration tests migrated to module API; 5 new
tests covering routing, zero-module error, duplicate name error,
type collision, and registry storage
…ubscribe (#37 step 4) - `ModuleHandle` wraps a `Scheduler` clone with module name, prefix, and defaults; obtained via `Scheduler::module()` (panics) or `try_module()` - Submission methods (`submit`, `submit_typed`) return `SubmitBuilder`, auto-prefix task_type and inject `_module` tag via module defaults - Single-task ops (`cancel`, `task`, `retry_dead_letter`) validate ownership by checking `task_type.starts_with(prefix)` - Bulk ops (`cancel_all`, `cancel_where`) query by `task_type LIKE prefix%` - `pause`/`resume` set a per-module `AtomicBool` in `SchedulerInner`, pause/resume pending tasks in DB, and cancel running task tokens; `resume` is a no-op if the global scheduler is paused - Scoped queries: `active_tasks`, `dead_letter_tasks`, `tasks_by_tags`, `count_by_tag`, `tag_values`, `estimated_progress`, `byte_progress`, `snapshot` — all filter at the SQL level via `task_type LIKE prefix%` - `ModuleReceiver<E>` filters the global broadcast in `recv()` with no background forwarder; `Paused`/`Resumed` global events always pass through - `subscribe` / `subscribe_progress` wrap the global channels in `ModuleReceiver` - Recurring control (`pause_recurring`, `resume_recurring`, `cancel_recurring`) validate module ownership before delegating - `Scheduler::task(id)` added for cross-module lookup by ID - New store helpers: `tasks_by_type_prefix`, prefix-scoped tag queries, `pause_pending_by_type_prefix`, `resume_paused_by_type_prefix`, `dead_letter_tasks_by_prefix`; `ActiveTaskMap` gains `pause_module`, `records_with_prefix`, and prefix-scoped progress snapshot methods - 9 new integration tests; all 238 tests pass
…step 5)
Add TypedTaskDefaults to SubmitBuilder so that submit_typed() correctly
positions TypedTask values below module defaults in the resolution chain:
SubmitBuilder override (layer 1)
> module defaults (layer 3)
> TypedTask trait values (layer 4)
> scheduler global defaults (layer 5)
The submit() path is unchanged: module defaults still fill in only where
the TaskSubmission is at its zero/None value (explicit submission fields
beat module defaults).
ModuleHandle::submit_typed() now builds a stripped TaskSubmission (without
priority/group/ttl/tags) and captures those fields as TypedTaskDefaults,
enabling resolve() to apply module defaults on top of TypedTask values.
5 tests added: module default overrides TypedTask priority, SubmitBuilder
override beats module default, explicit submission group beats module
default (submit path), global scheduler TTL applies when no layer sets
TTL, and a full integration test verifying all layers together.
- Add module_caps (RwLock<HashMap>) and module_running (Arc<HashMap<AtomicUsize>>) to SchedulerInner, initialized from Module::max_concurrency at build time - Enforce module cap in DefaultDispatchGate::admit() as an O(1) atomic check independent of group limits - Increment module_running counter on dispatch; decrement on every terminal transition (completed, failed, dead-lettered, preempted, waiting) - Add ModuleHandle::set_max_concurrency() / max_concurrency() for runtime control - 5 integration tests covering cap enforcement, group/module independence, ungrouped tasks, global ceiling, and runtime updates
…fallback (#37 step 7) Add per-module app state to SchedulerInner, populated at build time from each Module's app_state() entries. TaskContext::state::<T>() now checks module-scoped state first, falling back to global state registered on the builder.
Adds `current_module()`, `module()`, and `try_module()` to `TaskContext` so executors can submit tasks to any registered module from within their executor body — both same-module follow-ups and cross-module submissions. Removes `TaskContext::submit()`, `submit_typed()`, and `submit_typed_at()`; all submission now goes through module handles, ensuring task types are always auto-prefixed and module defaults are applied. `spawn_child()` / `spawn_children()` are updated to route through `current_module()` so child task types are auto-prefixed; a legacy fallback preserves behaviour for schedulers built with `Scheduler::new()`. `ModuleRegistry` is now stored as `Arc` in `SchedulerInner` and threaded through `SpawnContext` → `TaskContext` so handles can be constructed at dispatch time. `ChildSpawner::prepare()` is added to apply parent-id / TTL / tag inheritance without submitting, enabling the module-handle path.
- SubmitBuilder::submit() now inherits remaining parent TTL and tags when .parent() is set, matching ChildSpawner behaviour (child-set values always win on conflicts) - Cross-module parent-child lifecycle (Waiting/cascade) works via the existing module-agnostic parent_id queries in dispatch — no changes needed there - Add three integration tests: cross_module_parent_child_lifecycle, cross_module_failure_cascade, parent_method_inherits_ttl_and_tags
Add `Scheduler::modules() -> Vec<ModuleHandle>` that returns handles for all registered modules in registration order, enabling cross-cutting operations (cancel-by-tag, dashboard aggregation) without adding methods to Scheduler. `Scheduler::active_tasks()` already aggregated across all modules; added comment to make that explicit. Tests: scheduler_modules_returns_all_registered_modules, scheduler_active_tasks_returns_tasks_from_all_modules, cross_module_cancel_by_tag_via_modules_iterator.
Add `module` field to `TaskEventHeader`, populated from the `task_type` prefix at all construction sites. Update `ModuleReceiver` to filter on `header.module` directly. Also ports benches to use Module API (step 10 leftover).
…ch async lock Module state stored as `StateMap` (backed by a `tokio::sync::RwLock`) was being snapshotted inside `spawn_task` on every dispatch — one `RwLock::read().await` + `HashMap::clone()` per task. Under high concurrency (e.g. 100 simultaneous dispatches) this added 100 async yield points to the hot dispatch path, slowing task startup and inflating benchmark variance. Module app state is write-once: it is fully populated at `SchedulerBuilder::build` time and never mutated afterward. The lock was therefore providing no safety benefit at dispatch time. Change the stored type from `HashMap<String, StateMap>` to `HashMap<String, StateSnapshot>` and call `.snapshot().await` once per module during `build()`. At dispatch time, `spawn_task` now does a plain `HashMap::get` + `clone()` with no async yield — entirely lock-free. Measured improvement on `byte_progress_snapshot_100_tasks` (100 concurrent tasks, 100 snapshot calls): -17.8% vs the pre-fix run (88.7ms -> 72.9ms), eliminating the regression introduced in the module-state dispatch wiring.
Update all docs, guides, and crate-level doc comments to reflect the module system introduced in plan #37. Key changes across all files: - Executor registration moves from SchedulerBuilder to Module - Submission/cancel/query goes through ModuleHandle, not Scheduler - Task types are namespaced (e.g. "media::thumbnail") - Module-scoped app state documented alongside global state - Migration guide updated with DB incompatibility warning - Glossary gains Module, ModuleHandle, SubmitBuilder entries
The 4050-line file is split into 7 focused modules under tests/integration/: - common.rs: shared executor structs and helpers - scheduler_core.rs: sections A–L (priority, retry, preemption, backpressure, concurrency, run loop, child tasks, crash recovery, batch, IO, scheduled tasks) - dependencies.rs: section M (task dependency graph) - retry_policy.rs: Phase 6 (adaptive retry, backoff, per-type policies) - modules.rs: section N (module registration, ModuleHandle) - module_features.rs: sections P–Q + step 7 (default layering, module concurrency, namespaced StateMap) - cross_module.rs: steps 8–11 (TaskContext module access, cross-module child spawning, Scheduler::modules(), event module identity) tests/integration.rs is now a thin entry point with #[path] declarations.
Merged
deepjoy
pushed a commit
that referenced
this pull request
Mar 18, 2026
## 🤖 New release
* `taskmill`: 0.3.1 -> 0.4.0 (⚠ API breaking changes)
### ⚠ `taskmill` breaking changes
```text
--- failure constructible_struct_adds_field: externally-constructible struct adds field ---
Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/constructible_struct_adds_field.ron
Failed in:
field TaskHistoryRecord.expected_io in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:290
field TaskHistoryRecord.actual_io in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:292
field TaskHistoryRecord.ttl_seconds in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:306
field TaskHistoryRecord.ttl_from in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:308
field TaskHistoryRecord.expires_at in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:310
field TaskHistoryRecord.run_after in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:312
field TaskHistoryRecord.tags in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:314
field TaskHistoryRecord.max_retries in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:316
field TaskHistoryRecord.expected_io in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:290
field TaskHistoryRecord.actual_io in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:292
field TaskHistoryRecord.ttl_seconds in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:306
field TaskHistoryRecord.ttl_from in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:308
field TaskHistoryRecord.expires_at in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:310
field TaskHistoryRecord.run_after in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:312
field TaskHistoryRecord.tags in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:314
field TaskHistoryRecord.max_retries in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:316
field TaskError.cancelled in /tmp/.tmp9SeJRW/taskmill/src/task/error.rs:28
field TaskError.retry_after_ms in /tmp/.tmp9SeJRW/taskmill/src/task/error.rs:33
field TaskError.cancelled in /tmp/.tmp9SeJRW/taskmill/src/task/error.rs:28
field TaskError.retry_after_ms in /tmp/.tmp9SeJRW/taskmill/src/task/error.rs:33
field EstimatedProgress.header in /tmp/.tmp9SeJRW/taskmill/src/scheduler/progress.rs:299
field EstimatedProgress.header in /tmp/.tmp9SeJRW/taskmill/src/scheduler/progress.rs:299
field EstimatedProgress.header in /tmp/.tmp9SeJRW/taskmill/src/scheduler/progress.rs:299
field SchedulerSnapshot.byte_progress in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:45
field SchedulerSnapshot.recurring_schedules in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:49
field SchedulerSnapshot.blocked_count in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:51
field SchedulerSnapshot.byte_progress in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:45
field SchedulerSnapshot.recurring_schedules in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:49
field SchedulerSnapshot.blocked_count in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:51
field TaskSubmission.expected_io in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:382
field TaskSubmission.on_duplicate in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:399
field TaskSubmission.ttl in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:403
field TaskSubmission.ttl_from in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:406
field TaskSubmission.run_after in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:413
field TaskSubmission.recurring in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:416
field TaskSubmission.dependencies in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:419
field TaskSubmission.on_dependency_failure in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:422
field TaskSubmission.tags in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:427
field TaskSubmission.max_retries in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:432
field TaskSubmission.expected_io in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:382
field TaskSubmission.on_duplicate in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:399
field TaskSubmission.ttl in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:403
field TaskSubmission.ttl_from in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:406
field TaskSubmission.run_after in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:413
field TaskSubmission.recurring in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:416
field TaskSubmission.dependencies in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:419
field TaskSubmission.on_dependency_failure in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:422
field TaskSubmission.tags in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:427
field TaskSubmission.max_retries in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:432
field SchedulerConfig.progress_interval in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:252
field SchedulerConfig.cancel_hook_timeout in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:256
field SchedulerConfig.default_ttl in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:259
field SchedulerConfig.expiry_sweep_interval in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:262
field SchedulerConfig.progress_interval in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:252
field SchedulerConfig.cancel_hook_timeout in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:256
field SchedulerConfig.default_ttl in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:259
field SchedulerConfig.expiry_sweep_interval in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:262
field TaskRecord.expected_io in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:202
field TaskRecord.ttl_seconds in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:219
field TaskRecord.ttl_from in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:221
field TaskRecord.expires_at in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:223
field TaskRecord.run_after in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:226
field TaskRecord.recurring_interval_secs in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:228
field TaskRecord.recurring_max_executions in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:230
field TaskRecord.recurring_execution_count in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:232
field TaskRecord.recurring_paused in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:234
field TaskRecord.dependencies in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:237
field TaskRecord.on_dependency_failure in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:239
field TaskRecord.tags in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:241
field TaskRecord.max_retries in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:245
field TaskRecord.expected_io in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:202
field TaskRecord.ttl_seconds in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:219
field TaskRecord.ttl_from in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:221
field TaskRecord.expires_at in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:223
field TaskRecord.run_after in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:226
field TaskRecord.recurring_interval_secs in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:228
field TaskRecord.recurring_max_executions in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:230
field TaskRecord.recurring_execution_count in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:232
field TaskRecord.recurring_paused in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:234
field TaskRecord.dependencies in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:237
field TaskRecord.on_dependency_failure in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:239
field TaskRecord.tags in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:241
field TaskRecord.max_retries in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:245
--- failure constructible_struct_adds_private_field: struct no longer constructible due to new private field ---
Description:
A struct constructible with a struct literal has a new non-public field. It can no longer be constructed using a struct literal outside of its crate.
ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/constructible_struct_adds_private_field.ron
Failed in:
field TaskSubmission.payload_error in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:410
field TaskSubmission.payload_error in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:410
--- failure enum_struct_variant_changed_kind: An enum struct variant changed kind ---
Description:
A pub enum's struct variant with at least one pub field has changed to a different kind of enum variant, breaking access to its pub fields.
ref: https://doc.rust-lang.org/reference/items/enumerations.html
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_struct_variant_changed_kind.ron
Failed in:
variant SchedulerEvent::Dispatched in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:85
variant SchedulerEvent::Completed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:87
variant SchedulerEvent::Preempted in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:98
variant SchedulerEvent::Cancelled in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:100
variant SchedulerEvent::Dispatched in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:85
variant SchedulerEvent::Completed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:87
variant SchedulerEvent::Preempted in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:98
variant SchedulerEvent::Cancelled in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:100
--- failure enum_struct_variant_field_added: pub enum struct variant field added ---
Description:
An enum's exhaustive struct variant has a new field, which has to be included when constructing or matching on this variant.
ref: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_struct_variant_field_added.ron
Failed in:
field header of variant SchedulerEvent::Failed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:90
field retry_after of variant SchedulerEvent::Failed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:95
field header of variant SchedulerEvent::Progress in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:110
field header of variant SchedulerEvent::Failed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:90
field retry_after of variant SchedulerEvent::Failed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:95
field header of variant SchedulerEvent::Progress in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:110
--- failure enum_struct_variant_field_missing: pub enum struct variant's field removed or renamed ---
Description:
A publicly-visible enum has a struct variant whose field is no longer available under its prior name. It may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_struct_variant_field_missing.ron
Failed in:
field task_id of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:63
field task_type of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:64
field key of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:65
field label of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:66
field task_id of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:86
field task_type of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:87
field key of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:88
field label of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:89
field task_id of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:63
field task_type of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:64
field key of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:65
field label of variant SchedulerEvent::Failed, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:66
field task_id of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:86
field task_type of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:87
field key of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:88
field label of variant SchedulerEvent::Progress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/event.rs:89
--- failure enum_variant_added: enum variant added on exhaustive enum ---
Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_variant_added.ron
Failed in:
variant StoreError:InvalidDependency in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:54
variant StoreError:DependencyFailed in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:56
variant StoreError:CyclicDependency in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:58
variant StoreError:InvalidTag in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:60
variant StoreError:NotFound in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:62
variant StoreError:InvalidState in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:64
variant StoreError:InvalidDependency in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:54
variant StoreError:DependencyFailed in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:56
variant StoreError:CyclicDependency in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:58
variant StoreError:InvalidTag in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:60
variant StoreError:NotFound in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:62
variant StoreError:InvalidState in /tmp/.tmp9SeJRW/taskmill/src/store/mod.rs:64
variant SubmitOutcome:Superseded in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:113
variant SubmitOutcome:Rejected in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:120
variant SubmitOutcome:Superseded in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:113
variant SubmitOutcome:Rejected in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:120
variant SchedulerEvent:Superseded in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:102
variant SchedulerEvent:BatchSubmitted in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:120
variant SchedulerEvent:TaskExpired in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:127
variant SchedulerEvent:RecurringSkipped in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:134
variant SchedulerEvent:RecurringCompleted in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:140
variant SchedulerEvent:TaskUnblocked in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:147
variant SchedulerEvent:DeadLettered in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:153
variant SchedulerEvent:DependencyFailed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:159
variant SchedulerEvent:Superseded in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:102
variant SchedulerEvent:BatchSubmitted in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:120
variant SchedulerEvent:TaskExpired in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:127
variant SchedulerEvent:RecurringSkipped in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:134
variant SchedulerEvent:RecurringCompleted in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:140
variant SchedulerEvent:TaskUnblocked in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:147
variant SchedulerEvent:DeadLettered in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:153
variant SchedulerEvent:DependencyFailed in /tmp/.tmp9SeJRW/taskmill/src/scheduler/event.rs:159
variant TaskStatus:Blocked in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:106
variant TaskStatus:Blocked in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:106
variant HistoryStatus:Cancelled in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:142
variant HistoryStatus:Superseded in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:143
variant HistoryStatus:Expired in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:144
variant HistoryStatus:DependencyFailed in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:147
variant HistoryStatus:DeadLetter in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:154
variant HistoryStatus:Cancelled in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:142
variant HistoryStatus:Superseded in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:143
variant HistoryStatus:Expired in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:144
variant HistoryStatus:DependencyFailed in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:147
variant HistoryStatus:DeadLetter in /tmp/.tmp9SeJRW/taskmill/src/task/mod.rs:154
--- failure inherent_method_missing: pub method removed or renamed ---
Description:
A publicly-visible method or associated fn is no longer available under its prior name. It may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/inherent_method_missing.ron
Failed in:
TaskSubmission::expected_net_io, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:186
TaskSubmission::with_payload, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:231
TaskSubmission::expected_net_io, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:186
TaskSubmission::with_payload, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:231
TaskContext::submit, previously in file /tmp/.tmptg1HQQ/taskmill/src/registry/context.rs:140
TaskContext::submit_typed, previously in file /tmp/.tmptg1HQQ/taskmill/src/registry/context.rs:151
TaskContext::submit_typed_at, previously in file /tmp/.tmptg1HQQ/taskmill/src/registry/context.rs:160
TaskContext::submit, previously in file /tmp/.tmptg1HQQ/taskmill/src/registry/context.rs:140
TaskContext::submit_typed, previously in file /tmp/.tmptg1HQQ/taskmill/src/registry/context.rs:151
TaskContext::submit_typed_at, previously in file /tmp/.tmptg1HQQ/taskmill/src/registry/context.rs:160
SchedulerBuilder::executor, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/builder.rs:97
SchedulerBuilder::typed_executor, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/builder.rs:108
SchedulerBuilder::executor, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/builder.rs:97
SchedulerBuilder::typed_executor, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/builder.rs:108
--- failure method_parameter_count_changed: pub method parameter count changed ---
Description:
A publicly-visible method now takes a different number of parameters, not counting the receiver (self) parameter.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#fn-change-arity
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/method_parameter_count_changed.ron
Failed in:
taskmill::task::TaskSubmission::expected_io now takes 1 parameters instead of 2, in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:525
taskmill::TaskSubmission::expected_io now takes 1 parameters instead of 2, in /tmp/.tmp9SeJRW/taskmill/src/task/submission.rs:525
taskmill::store::TaskStore::fail now takes 6 parameters instead of 5, in /tmp/.tmp9SeJRW/taskmill/src/store/lifecycle/fail.rs:29
taskmill::store::TaskStore::fail_with_record now takes 6 parameters instead of 5, in /tmp/.tmp9SeJRW/taskmill/src/store/lifecycle/fail.rs:72
taskmill::TaskStore::fail now takes 6 parameters instead of 5, in /tmp/.tmp9SeJRW/taskmill/src/store/lifecycle/fail.rs:29
taskmill::TaskStore::fail_with_record now takes 6 parameters instead of 5, in /tmp/.tmp9SeJRW/taskmill/src/store/lifecycle/fail.rs:72
--- failure struct_missing: pub struct removed or renamed ---
Description:
A publicly-visible struct cannot be imported by its prior path. A `pub use` may have been removed, or the struct itself may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/struct_missing.ron
Failed in:
struct taskmill::task::TaskMetrics, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:192
struct taskmill::TaskMetrics, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:192
--- failure struct_pub_field_missing: pub struct's pub field removed or renamed ---
Description:
A publicly-visible struct has at least one public field that is no longer available under its prior name. It may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/struct_pub_field_missing.ron
Failed in:
field expected_read_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:67
field expected_write_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:68
field expected_net_rx_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:70
field expected_net_tx_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:72
field expected_read_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:67
field expected_write_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:68
field expected_net_rx_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:70
field expected_net_tx_bytes of struct TaskSubmission, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/submission.rs:72
field expected_read_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:110
field expected_write_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:111
field expected_net_rx_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:113
field expected_net_tx_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:115
field expected_read_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:110
field expected_write_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:111
field expected_net_rx_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:113
field expected_net_tx_bytes of struct TaskRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:115
field expected_read_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:158
field expected_write_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:159
field expected_net_rx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:161
field expected_net_tx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:163
field actual_read_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:164
field actual_write_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:165
field actual_net_rx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:167
field actual_net_tx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:169
field expected_read_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:158
field expected_write_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:159
field expected_net_rx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:161
field expected_net_tx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:163
field actual_read_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:164
field actual_write_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:165
field actual_net_rx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:167
field actual_net_tx_bytes of struct TaskHistoryRecord, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/mod.rs:169
field task_id of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:104
field task_type of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:105
field key of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:106
field label of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:107
field task_id of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:104
field task_type of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:105
field key of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:106
field label of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:107
field task_id of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:104
field task_type of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:105
field key of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:106
field label of struct EstimatedProgress, previously in file /tmp/.tmptg1HQQ/taskmill/src/scheduler/progress.rs:107
--- failure trait_method_missing: pub trait method removed or renamed ---
Description:
A trait method is no longer callable, and may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#major-any-change-to-trait-item-signatures
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/trait_method_missing.ron
Failed in:
method expected_read_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:38
method expected_write_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:43
method expected_net_rx_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:48
method expected_net_tx_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:53
method expected_read_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:38
method expected_write_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:43
method expected_net_rx_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:48
method expected_net_tx_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:53
method expected_read_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:38
method expected_write_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:43
method expected_net_rx_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:48
method expected_net_tx_bytes of trait TypedTask, previously in file /tmp/.tmptg1HQQ/taskmill/src/task/typed.rs:53
```
<details><summary><i><b>Changelog</b></i></summary><p>
<blockquote>
## [0.4.0](v0.3.1...v0.4.0)
- 2026-03-18
### Added
- implement multi-module API
([#44](#44))
- adaptive retry with configurable backoff strategies
([#42](#42))
- add task metadata tags
([#40](#40))
- task dependencies with blocked status, cycle detection, and failure
cascading ([#39](#39))
- delayed and recurring task scheduling
([#38](#38))
- task TTL with automatic expiry, sweep, and child inheritance
([#33](#33))
- task superseding with atomic cancel-and-replace
([#32](#32))
- task cancellation with abort hooks
([#31](#31))
- bulk task submission with BatchOutcome, BatchSubmission builder,
intra-batch dedup, and chunking
([#30](#30))
- add byte-level progress reporting with EWMA throughput tracking
([#29](#29))
### Other
- split large store modules into focused sub-modules
([#43](#43))
- rewrite documentation to be user-facing
([#28](#28))
- [**breaking**] consolidate IO fields into IoBudget and introduce
TaskEventHeader ([#26](#26))
</blockquote>
</p></details>
---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the full module-based API for taskmill (#37), replacing the
flat single-executor scheduler with a structured multi-module model.
Tasks are now registered and submitted through named
Moduleinstances,each with scoped defaults, concurrency caps, state, and event streams.
Steps shipped
Modulestruct: groups executors under a named prefixSubmitBuilderwithIntoFuturefor fluent, module-scopedsubmission with per-call overrides
SchedulerBuilderrebuilt around modules; validates noduplicate names or executor collisions at build time
ModuleHandlewith scopedsubmit,cancel,pause,resume,active_tasks, andsubscribeexplicit field > module defaults >
TypedTaskdefaults > schedulerglobal defaults
enforced independently, with
set_max_concurrencyfor runtime changesStateMap: module-scoped app state shadowsglobal state of the same type; executors see only their module's scope
TaskContextmodule access (ctx.module(),ctx.current_module(),ctx.try_module())SubmitBuilder::parent()with TTL and tag inheritance
Scheduler::modules()iterator andScheduler::active_tasks()across all modulesheader.modulefield populated from task typeprefix;
ModuleHandle::subscribe()filters by moduleOther changes
per-dispatch async lock on the hot path
0.4 module API; expanded migration guide
integration.rsinto sevensubmodules by feature area