Skip to content

feat: add network IO pressure and per-group concurrency limiting#7

Merged
deepjoy merged 2 commits into
mainfrom
network-pressure
Mar 14, 2026
Merged

feat: add network IO pressure and per-group concurrency limiting#7
deepjoy merged 2 commits into
mainfrom
network-pressure

Conversation

@deepjoy
Copy link
Copy Markdown
Owner

@deepjoy deepjoy commented Mar 14, 2026

  • Network IO tracking — tasks can declare expected network RX/TX bytes via TaskSubmission::expected_net_io() (and TypedTask::expected_net_rx_bytes / expected_net_tx_bytes). Executors report actuals via TaskContext::record_net_rx_bytes() / record_net_tx_bytes(). Network metrics are persisted in both tasks and task_history tables.
  • Network bandwidth pressure — new built-in NetworkPressure source maps observed RX+TX throughput (via sysinfo) against a configurable bandwidth cap to produce backpressure. Enable with SchedulerBuilder::bandwidth_limit(bytes_per_sec). The SysinfoSampler now tracks aggregate network interface throughput alongside CPU and disk.
  • Per-group concurrency limiting — tasks can be assigned to a named group via TaskSubmission::group(key) (or TypedTask::group_key()). The scheduler enforces per-group concurrency limits independent of global max_concurrency. Configure at build time with .group_concurrency(group, limit) / .default_group_concurrency(n), or adjust at runtime via set_group_limit() / remove_group_limit() / set_default_group_concurrency().

Changes

  • ResourceSnapshot gains net_rx_bytes_per_sec / net_tx_bytes_per_sec fields; EWMA smoothing applied in sampler loop
  • SysinfoSampler tracks Networks alongside Disks for cross-platform network throughput
  • New resource::network_pressure module with NetworkPressure implementing PressureSource
  • TaskRecord, TaskHistoryRecord, TaskSubmission, TaskMetrics gain network IO and group_key fields
  • TaskContext gains record_net_rx_bytes() / record_net_tx_bytes() (via IoTracker)
  • DefaultDispatchGate adds network IO budget check (has_net_io_headroom) and group concurrency check
  • New GroupLimits struct (atomic default + RwLock<HashMap> overrides) exposed on Scheduler
  • SchedulerBuilder gains bandwidth_limit(), default_group_concurrency(), group_concurrency() methods
  • TaskStore gains running_net_io_totals() and running_count_for_group() queries
  • New migration 003_net_io_and_groups.sql adds net IO columns, group_key, and a partial index
  • TypedTask trait gains expected_net_rx_bytes(), expected_net_tx_bytes(), and group_key() defaults
  • Docs updated across README, features, IO/backpressure guide, configuration, progress reporting, and quick-start

deepjoy added 2 commits March 13, 2026 21:45
Add network bandwidth monitoring alongside existing disk IO tracking,
with a new NetworkPressure backpressure source and budget-based
admission gating. Introduce group-based concurrency limits so tasks
targeting the same resource (e.g. an S3 endpoint) can be throttled
independently from the global concurrency cap.
…currency

Update README, feature docs, quick-start, IO/backpressure guide,
progress reporting, and inline doc comments to cover network-aware
scheduling, NetworkPressure, and per-group concurrency limits.
@deepjoy deepjoy merged commit 76f2296 into main Mar 14, 2026
1 check passed
@github-actions github-actions Bot mentioned this pull request Mar 14, 2026
deepjoy pushed a commit that referenced this pull request Mar 14, 2026
## 🤖 New release

* `taskmill`: 0.2.0 -> 0.3.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.label in /tmp/.tmpgCrMRt/taskmill/src/task.rs:147
  field TaskHistoryRecord.expected_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:154
  field TaskHistoryRecord.expected_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:156
  field TaskHistoryRecord.actual_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:160
  field TaskHistoryRecord.actual_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:162
  field TaskHistoryRecord.group_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:174
  field TaskHistoryRecord.label in /tmp/.tmpgCrMRt/taskmill/src/task.rs:147
  field TaskHistoryRecord.expected_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:154
  field TaskHistoryRecord.expected_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:156
  field TaskHistoryRecord.actual_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:160
  field TaskHistoryRecord.actual_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:162
  field TaskHistoryRecord.group_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:174
  field EstimatedProgress.label in /tmp/.tmpgCrMRt/taskmill/src/scheduler/progress.rs:95
  field EstimatedProgress.label in /tmp/.tmpgCrMRt/taskmill/src/scheduler/progress.rs:95
  field EstimatedProgress.label in /tmp/.tmpgCrMRt/taskmill/src/scheduler/progress.rs:95
  field TaskRecord.label in /tmp/.tmpgCrMRt/taskmill/src/task.rs:99
  field TaskRecord.expected_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:106
  field TaskRecord.expected_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:108
  field TaskRecord.group_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:123
  field TaskRecord.label in /tmp/.tmpgCrMRt/taskmill/src/task.rs:99
  field TaskRecord.expected_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:106
  field TaskRecord.expected_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:108
  field TaskRecord.group_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:123
  field ResourceSnapshot.net_rx_bytes_per_sec in /tmp/.tmpgCrMRt/taskmill/src/resource/mod.rs:34
  field ResourceSnapshot.net_tx_bytes_per_sec in /tmp/.tmpgCrMRt/taskmill/src/resource/mod.rs:36
  field ResourceSnapshot.net_rx_bytes_per_sec in /tmp/.tmpgCrMRt/taskmill/src/resource/mod.rs:34
  field ResourceSnapshot.net_tx_bytes_per_sec in /tmp/.tmpgCrMRt/taskmill/src/resource/mod.rs:36
  field TaskSubmission.dedup_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:332
  field TaskSubmission.label in /tmp/.tmpgCrMRt/taskmill/src/task.rs:335
  field TaskSubmission.expected_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:341
  field TaskSubmission.expected_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:343
  field TaskSubmission.group_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:357
  field TaskSubmission.dedup_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:332
  field TaskSubmission.label in /tmp/.tmpgCrMRt/taskmill/src/task.rs:335
  field TaskSubmission.expected_net_rx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:341
  field TaskSubmission.expected_net_tx_bytes in /tmp/.tmpgCrMRt/taskmill/src/task.rs:343
  field TaskSubmission.group_key in /tmp/.tmpgCrMRt/taskmill/src/task.rs:357

--- 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 label of variant SchedulerEvent::Dispatched in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:82
  field label of variant SchedulerEvent::Completed in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:89
  field label of variant SchedulerEvent::Failed in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:96
  field label of variant SchedulerEvent::Preempted in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:105
  field label of variant SchedulerEvent::Cancelled in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:112
  field label of variant SchedulerEvent::Progress in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:119
  field label of variant SchedulerEvent::Dispatched in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:82
  field label of variant SchedulerEvent::Completed in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:89
  field label of variant SchedulerEvent::Failed in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:96
  field label of variant SchedulerEvent::Preempted in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:105
  field label of variant SchedulerEvent::Cancelled in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:112
  field label of variant SchedulerEvent::Progress in /tmp/.tmpgCrMRt/taskmill/src/scheduler/mod.rs:119

--- 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:
  TaskContext::deserialize_typed, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:157
  TaskContext::deserialize_typed, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:157

--- 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::store::TaskStore::fail now takes 5 parameters instead of 6, in /tmp/.tmpgCrMRt/taskmill/src/store.rs:591
  taskmill::TaskStore::fail now takes 5 parameters instead of 6, in /tmp/.tmpgCrMRt/taskmill/src/store.rs:591

--- 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::TaskResult, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:151
  struct taskmill::TaskResult, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:151

--- 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 actual_read_bytes of struct TaskError, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:161
  field actual_write_bytes of struct TaskError, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:162
  field actual_read_bytes of struct TaskError, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:161
  field actual_write_bytes of struct TaskError, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:162
  field key of struct TaskSubmission, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:222
  field key of struct TaskSubmission, previously in file /tmp/.tmpuPxM8D/taskmill/src/task.rs:222
  field record of struct TaskContext, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:140
  field token of struct TaskContext, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:143
  field progress of struct TaskContext, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:145
  field record of struct TaskContext, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:140
  field token of struct TaskContext, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:143
  field progress of struct TaskContext, previously in file /tmp/.tmpuPxM8D/taskmill/src/registry.rs:145

--- failure struct_pub_field_now_doc_hidden: pub struct field is now #[doc(hidden)] ---

Description:
A pub field of a pub struct is now marked #[doc(hidden)] and is no longer part of the public API.
        ref: https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/struct_pub_field_now_doc_hidden.ron

Failed in:
  field TaskContext.record in file /tmp/.tmpgCrMRt/taskmill/src/registry.rs:182
  field TaskContext.token in file /tmp/.tmpgCrMRt/taskmill/src/registry.rs:182
  field TaskContext.progress in file /tmp/.tmpgCrMRt/taskmill/src/registry.rs:182
  field TaskContext.record in file /tmp/.tmpgCrMRt/taskmill/src/registry.rs:182
  field TaskContext.token in file /tmp/.tmpgCrMRt/taskmill/src/registry.rs:182
  field TaskContext.progress in file /tmp/.tmpgCrMRt/taskmill/src/registry.rs:182
```

<details><summary><i><b>Changelog</b></i></summary><p>

<blockquote>

## [0.3.0](v0.2.0...v0.3.0)
- 2026-03-14

### Added

- add network IO pressure and per-group concurrency limiting
([#7](#7))

### Other

- [**breaking**] builder pattern for TaskSubmission and accessor methods
for TaskContext ([#6](#6))
- [**breaking**] simplify executor API with incremental IO tracking and
expanded docs ([#4](#4))
</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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant