Skip to content

feat(cell): add PdoReplayCell for heavy PDO traces - #216

Open
limonuser wants to merge 4 commits into
stack-rs:mainfrom
limonuser:feat/pdo-replay-cell
Open

limonuser wants to merge 4 commits into
stack-rs:mainfrom
limonuser:feat/pdo-replay-cell

Conversation

@limonuser

@limonuser limonuser commented Oct 20, 2026 •

Copy link
Copy Markdown

This is the first part of #98: a cell that replays heavy PDOs (mahimahi style traces), following the design @Lethe10137 described there. It uses the pdo-model feature and PDO trace types from stack-rs/netem-trace#29, released in netem-trace 0.5.0.

What it does:

  • PdoReplayCell takes a HeavyPdoTrace ((interval since last PDO, bytes)) and a packet queue. No bandwidth anywhere
  • The bytes of a PDO go to the packet at the head of the queue, counted by L3 length like mahimahi. A big packet can take several PDOs (it keeps the bytes it already got), and one PDO can send several small packets
  • dequeue() returns one packet at a time. The bytes left in the current PDO stay in the cell, so the next dequeue() sends the next packet right away without sleeping
  • If nothing is queued, the bytes of a PDO are wasted. While the cell is idle it doesn't wake up for every PDO, it just skips the ones from before the next packet arrived
  • When the trace ends nothing more is sent. For mahimahi files load_mahimahi_heavy_pdo_trace repeats forever by default anyway
  • With a zero buffer queue, a packet that arrives while another one is waiting for a PDO is dropped
  • Small helpers on AQM: is_empty() and is_queue_empty()

Not in this PR (next ones):

  • config wiring, so it can be used from a config file with a mahimahi trace
  • the CellReplay light PDO part (bypass packets, base delay, the inactive timer)

Things I wasn't sure about:

  • I count bytes with l3_length() to match mahimahi, not with the queue's get_packet_size() (which can add 14 B for L2). Should it follow the queue's BwType instead?
  • Leftover bytes of a PDO are dropped as soon as the queue is empty, they're never saved for later. I think that's what you meant in Add functionality to replay cellular traces #98 but tell me if not

How I tested (Ubuntu 24.04 in WSL, stable Rust, with netem-trace 0.5.0):

  • 8 new tests in cells::pdo: bytes carrying over, many small packets in one PDO, wasted bytes after being idle, a mahimahi trace with two lines in the same ms, zero buffer, and a random test (3 seeds) that checks every departure time against a small copy of mahimahi's use_a_delivery_opportunity logic
  • I broke the "keep leftover bytes" line on purpose to check that the tests catch it (6 of 8 failed). That's also how I found the test could hang forever, so replay() now fails after 5 s
  • cargo clippy --all-features --all-targets -- -Dwarnings and cargo fmt --all -- --check are clean
  • 3 old timing tests fail on my machine (zero_buffer_bw_replay, test_replay_delay_cell, test_replay_delay_cell_change_state), but they fail the same way on main without this change, so I think it's my WSL setup

Refs #98

🤖 Generated with Claude Code

limonuser and others added 3 commits October 19, 2026 19:08
Replay packet delivery opportunities (PDOs) instead of bandwidth, like the
link queue of mahimahi. The head packet uses the bytes of each PDO (counted
by L3 length), so a packet can need several PDOs and one PDO can send
several packets. Unused bytes are wasted when there is nothing to send.

dequeue() returns one packet and keeps the bytes left in the current PDO,
so the next call goes on with the same PDO without waiting. PDOs before
the next packet's arrival are skipped instead of waking up for each one.

Needs the pdo-model feature of netem-trace (stack-rs/netem-trace#29).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
If a bug keeps some packet in the cell, the test now fails after 5s
instead of waiting forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0.5.0 includes the pdo-model feature from stack-rs/netem-trace#29,
so PdoReplayCell builds without a local patch now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@limonuser
limonuser marked this pull request as ready for review October 23, 2026 14:55
@limonuser

Copy link
Copy Markdown
Author

netem-trace#29 is merged and 0.5.0 is out, thanks @Lethe10137 and @BobAnkh for the reviews!

I pushed 88eb4e7, which bumps netem-trace to 0.5.0 (no local patch anymore), so CI should be able to build this now. On my machine the 8 cells::pdo tests, clippy and fmt all pass with 0.5.0.

I'm moving this out of draft. The two questions from the description are still open: should bytes be counted with l3_length() or follow the queue's BwType, and is it right to drop leftover PDO bytes when the queue is empty? Once those are settled I'll start on part 2, the config wiring.

@Lethe10137

Copy link
Copy Markdown
Member

netem-trace#29 is merged and 0.5.0 is out, thanks @Lethe10137 and @BobAnkh for the reviews!

I pushed 88eb4e7, which bumps netem-trace to 0.5.0 (no local patch anymore), so CI should be able to build this now. On my machine the 8 cells::pdo tests, clippy and fmt all pass with 0.5.0.

I'm moving this out of draft. The two questions from the description are still open: should bytes be counted with l3_length() or follow the queue's BwType, and is it right to drop leftover PDO bytes when the queue is empty? Once those are settled I'll start on part 2, the config wiring.

  • For the BwType: just follow the queue's BwType. And it should be defaulted to be l3_length() as a convention in Rattan
  • Yes, clear the PDO bytes when the queue is empty.

A packet now uses l3_length() plus the extra length from the BwType of
the queue, so it is the L3 length by default (BwType::NetworkLayer), as
in the other cells. Add a test with BwType::LinkLayer.
@limonuser

Copy link
Copy Markdown
Author

Thanks @Lethe10137! I pushed 4a697d2. Now the cell counts each packet as l3_length() plus the extra length from the queue's BwType, so by default it's still just the L3 length (NetworkLayer), and I added a test with LinkLayer. The leftover PDO bytes were already dropped when the queue is empty, so nothing changed there.

I'll start on part 2 (the config wiring) next.

This branch has not been deployed

No deployments
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.

2 participants