fix(managed): bound timeout work per poll #55

Merged
Lochnair merged 10 commits from fix/managed-timeout-budget into main 2026-08-10 04:14:52 +00:00
Lochnair commented 2026-08-09 18:38:47 +00:00 (Migrated from github.com)

Index pending-probe deadlines and process managed timeout transitions through a global bounded work budget, preventing large timeout backlogs from monopolizing a ManagedClientTask poll.
Timeout processing now round-robins across due targets while preserving timeout-before-receive ordering, including partial batches during active and draining states, so overdue replies remain EchoLoss followed by LateReply rather than being misclassified as normal replies. Public timeout polling remains exhaustive, and the deadline index stays exactly bounded by the existing pending-probe limit.
Adds deterministic coverage for per-poll bounds, multi-target fairness, partial-batch receive ordering, drain behavior, cursor pruning, deadline-index consistency, and preserved exhaustive client semantics.

Index pending-probe deadlines and process managed timeout transitions through a global bounded work budget, preventing large timeout backlogs from monopolizing a ManagedClientTask poll. Timeout processing now round-robins across due targets while preserving timeout-before-receive ordering, including partial batches during active and draining states, so overdue replies remain EchoLoss followed by LateReply rather than being misclassified as normal replies. Public timeout polling remains exhaustive, and the deadline index stays exactly bounded by the existing pending-probe limit. Adds deterministic coverage for per-poll bounds, multi-target fairness, partial-batch receive ordering, drain behavior, cursor pruning, deadline-index consistency, and preserved exhaustive client semantics.
chatgpt-codex-connector[bot] (Migrated from github.com) reviewed 2026-08-09 18:43:40 +00:00
chatgpt-codex-connector[bot] (Migrated from github.com) left a comment

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4632e310d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

### 💡 Codex Review Here are some automated review suggestions for this pull request. **Reviewed commit:** `4632e310d7` <details> <summary>ℹ️ About Codex in GitHub</summary> <br/> [Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you - Open a pull request for review - Mark a draft as ready - Comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". </details>
chatgpt-codex-connector[bot] (Migrated from github.com) commented 2026-08-09 18:43:40 +00:00

P2 Badge Bound timeout target discovery work

When a managed task contains more than TIMEOUT_WORK_BUDGET targets, every task poll still scans all target_len entries and, if many are overdue, stores all their indexes before processing only 128 expirations. Because max_live_target_generations is configurable without an upper bound, a large timeout backlog causes repeated O(number-of-targets) scans and allocations across many self-woken polls, defeating the cooperative work budget and potentially monopolizing a current-thread Tokio runtime. Persist the discovery cursor/backlog and inspect only a bounded number of targets per poll, as the existing target and send passes do.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Bound timeout target discovery work** When a managed task contains more than `TIMEOUT_WORK_BUDGET` targets, every task poll still scans all `target_len` entries and, if many are overdue, stores all their indexes before processing only 128 expirations. Because `max_live_target_generations` is configurable without an upper bound, a large timeout backlog causes repeated O(number-of-targets) scans and allocations across many self-woken polls, defeating the cooperative work budget and potentially monopolizing a current-thread Tokio runtime. Persist the discovery cursor/backlog and inspect only a bounded number of targets per poll, as the existing target and send passes do. Useful? React with 👍 / 👎.
chatgpt-codex-connector[bot] (Migrated from github.com) commented 2026-08-09 18:43:40 +00:00

P2 Badge Skip sends for targets with unprocessed timeouts

When more than 128 targets have simultaneous expirations, this pass leaves some overdue targets active, but its immediate result does not prevent the send pass later in the same poll. If one of those targets is selected by send_cursor while its pending map is full—for example, max_pending_probes = 1 with its sole probe already expired—poll_send_probe reports PendingLimitExceeded and the managed target fails even though processing its already-due timeout on the next poll would free the slot. Track which targets still have due timeout work or defer sending while any selected target's timeout deadline is overdue.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Skip sends for targets with unprocessed timeouts** When more than 128 targets have simultaneous expirations, this pass leaves some overdue targets active, but its `immediate` result does not prevent the send pass later in the same poll. If one of those targets is selected by `send_cursor` while its pending map is full—for example, `max_pending_probes = 1` with its sole probe already expired—`poll_send_probe` reports `PendingLimitExceeded` and the managed target fails even though processing its already-due timeout on the next poll would free the slot. Track which targets still have due timeout work or defer sending while any selected target's timeout deadline is overdue. Useful? React with 👍 / 👎.
@@ -46,0 +64,4 @@
if let Some(previous) = previous {
let previous = self
.map
.get_mut(&previous)
chatgpt-codex-connector[bot] (Migrated from github.com) commented 2026-08-09 18:43:40 +00:00

P2 Badge Keep probe commits allocation-free

When the deadline index needs a new B-tree node, deadlines.insert allocates during commit_insert, but preflight_insert only fallibly reserves the hash map. This commit runs after the UDP send has already been accepted, so allocation failure now aborts or panics instead of returning ClientError::AllocationFailed before transmission, breaking the transactional send path that reserves storage during preflight. Use an index with fallible capacity reservation and reserve it alongside the map before allowing the send.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Keep probe commits allocation-free** When the deadline index needs a new B-tree node, `deadlines.insert` allocates during `commit_insert`, but `preflight_insert` only fallibly reserves the hash map. This commit runs after the UDP send has already been accepted, so allocation failure now aborts or panics instead of returning `ClientError::AllocationFailed` before transmission, breaking the transactional send path that reserves storage during preflight. Use an index with fallible capacity reservation and reserve it alongside the map before allowing the send. Useful? React with 👍 / 👎.
chatgpt-codex-connector[bot] (Migrated from github.com) reviewed 2026-08-10 03:12:53 +00:00
chatgpt-codex-connector[bot] (Migrated from github.com) left a comment

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab9912b3d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

### 💡 Codex Review Here are some automated review suggestions for this pull request. **Reviewed commit:** `ab9912b3d5` <details> <summary>ℹ️ About Codex in GitHub</summary> <br/> [Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you - Open a pull request for review - Mark a draft as ready - Comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". </details>
@@ -1731,0 +1875,4 @@
}
}
Err(error) => {
self.targets[index].counters.packets_sent = client.packets_sent();
chatgpt-codex-connector[bot] (Migrated from github.com) commented 2026-08-10 03:12:53 +00:00

P2 Badge Requeue targets that still have due timeouts

When targets.len() > TIMEOUT_WORK_BUDGET and one sparsely located target has multiple overdue probes, advancing timeout_cursor unconditionally after processing only one probe postpones that target's next expiration until a complete target sweep. more_due merely triggers another poll; each intervening poll also calls next_deadline, which scans every target, so a sparse backlog can require O(N / TIMEOUT_WORK_BUDGET) polls and repeated O(N) scans per loss, severely delaying timeout events and drain completion. Preserve or requeue indexes whose TimeoutStep::more_due is true while still round-robining among backlogged targets. Fresh evidence beyond the earlier timeout-discovery comment is that the current fix still advances the cursor despite the returned backlog signal.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Requeue targets that still have due timeouts** When `targets.len() > TIMEOUT_WORK_BUDGET` and one sparsely located target has multiple overdue probes, advancing `timeout_cursor` unconditionally after processing only one probe postpones that target's next expiration until a complete target sweep. `more_due` merely triggers another poll; each intervening poll also calls `next_deadline`, which scans every target, so a sparse backlog can require `O(N / TIMEOUT_WORK_BUDGET)` polls and repeated `O(N)` scans per loss, severely delaying timeout events and drain completion. Preserve or requeue indexes whose `TimeoutStep::more_due` is true while still round-robining among backlogged targets. Fresh evidence beyond the earlier timeout-discovery comment is that the current fix still advances the cursor despite the returned backlog signal. Useful? React with 👍 / 👎.
Sign in to join this conversation.