Conversation
Summary by CodeRabbit
WalkthroughAdds an ExtendHold command (tag 6) with codec and snapshot support, state-machine handling that validates and updates hold deadlines (including queue rescheduling), and recovery/test additions ensuring extended deadlines persist through WAL replay. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant WAL as WAL
participant SM as StateMachine
participant DB as ReservationDb
participant Queue as HoldRetireQueue
participant REC as Recovery
Client->>WAL: append(ClientCommand::ExtendHold)
WAL-->>SM: deliver(ClientCommand::ExtendHold)
SM->>DB: load hold by hold_id
alt hold missing or invalid state
DB-->>SM: not found / invalid state
SM-->>Client: reject (HoldNotFound / InvalidState / HoldExpired)
else valid held and deadline increase
SM->>DB: update hold.deadline_slot
SM->>Queue: rebuild_live_hold_retire_queue()
DB-->>SM: persist OK
SM-->>Client: OK
end
Note over REC, WAL: Recovery path (WAL replay)
REC->>WAL: read frames
WAL-->>REC: ClientCommand::ExtendHold frames
REC->>DB: apply commands (including ExtendHold)
REC->>Queue: rebuild_live_hold_retire_queue()
REC-->>DB: recovered state
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/reservation-core/src/snapshot.rs (1)
546-558:⚠️ Potential issue | 🟡 MinorMake the fixture reflect the post-extension state.
Line 550 still leaves the hold at
Slot(5), while Lines 556-558 record a successfulExtendHoldtoSlot(7). That fixture cannot come from the live state machine, so the restore test no longer validates the extended-deadline state this PR is adding.As per coding guidelines, "Write extensive tests for every meaningful behavior change. Favor invariant tests, negative-path tests, recovery tests, and regression tests over shallow happy-path coverage."🧪 Minimal fix
holds: vec![HoldRecord { hold_id: HoldId(21), pool_id: PoolId(11), quantity: 2, - deadline_slot: Slot(5), + deadline_slot: Slot(7), state: HoldState::Held, }],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/reservation-core/src/snapshot.rs` around lines 546 - 558, The fixture's held record does not reflect the successful ExtendHold operation: update the HoldRecord used in the snapshot (the holds vec containing HoldRecord with HoldId(21), PoolId(11), etc.) so its deadline_slot is Slot(7) instead of Slot(5) to match the OperationRecord that records Command::ExtendHold { hold_id: HoldId(21), deadline_slot: Slot(7) }; ensure HoldState::Held remains unchanged and no other fields are altered.
🧹 Nitpick comments (1)
crates/reservation-core/src/command_codec.rs (1)
198-205: Keep the old variant coverage when adding the new round-trip case.Swapping the single test input to
ExtendHolddrops direct regression coverage forCreatePool. A small table-driven test over allCommandvariants would keep both existing tags and the new tag pinned.As per coding guidelines, "Write extensive tests for every meaningful behavior change. Favor invariant tests, negative-path tests, recovery tests, and regression tests over shallow happy-path coverage."🧪 Possible expansion
#[test] -fn internal_command_round_trips() { - let command = Command::ExtendHold { - hold_id: HoldId(7), - deadline_slot: Slot(9), - }; - - let decoded = decode_internal_command(&encode_internal_command(command)).unwrap(); - assert_eq!(decoded, command); +fn internal_commands_round_trip() { + let commands = [ + Command::CreatePool { + pool_id: PoolId(5), + total_capacity: 9, + }, + Command::PlaceHold { + pool_id: PoolId(5), + hold_id: HoldId(6), + quantity: 2, + deadline_slot: Slot(7), + }, + Command::ConfirmHold { hold_id: HoldId(6) }, + Command::ReleaseHold { hold_id: HoldId(6) }, + Command::ExtendHold { + hold_id: HoldId(6), + deadline_slot: Slot(8), + }, + Command::ExpireHold { hold_id: HoldId(6) }, + ]; + + for command in commands { + let decoded = decode_internal_command(&encode_internal_command(command)).unwrap(); + assert_eq!(decoded, command); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/reservation-core/src/command_codec.rs` around lines 198 - 205, Replace the single-case test in internal_command_round_trips with a table-driven loop that iterates over all Command variants (including CreatePool and the new ExtendHold), calling encode_internal_command and decode_internal_command for each and asserting equality to preserve regression coverage; locate the test in internal_command_round_trips and build a Vec or array of Command instances, then for each element call decode_internal_command(&encode_internal_command(cmd)).unwrap() and assert_eq!(decoded, cmd).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/reservation-core/src/state_machine.rs`:
- Around line 477-479: The code currently always calls
self.push_hold_expiry(hold_id, deadline_slot) after updating hold.deadline_slot
and self.replace_hold(hold), which enqueues a duplicate entry into
hold_retire_queue and can hit RetireQueueError::Full (e.g., max_holds = 1) when
PlaceHold → ExtendHold occurs; change push_hold_expiry to first check for an
existing queued entry for hold_id and either update its deadline in-place or
skip adding a new entry (i.e., deduplicate keyed by hold_id), or provide an API
on the retire queue to reschedule an existing entry instead of pushing; update
the implementation around self.push_hold_expiry/self.replace_hold to reschedule
rather than append, and add a regression test (max_holds = 1) that PlaceHold
followed by ExtendHold does not panic and results in only a single retire entry
for the hold_id.
---
Outside diff comments:
In `@crates/reservation-core/src/snapshot.rs`:
- Around line 546-558: The fixture's held record does not reflect the successful
ExtendHold operation: update the HoldRecord used in the snapshot (the holds vec
containing HoldRecord with HoldId(21), PoolId(11), etc.) so its deadline_slot is
Slot(7) instead of Slot(5) to match the OperationRecord that records
Command::ExtendHold { hold_id: HoldId(21), deadline_slot: Slot(7) }; ensure
HoldState::Held remains unchanged and no other fields are altered.
---
Nitpick comments:
In `@crates/reservation-core/src/command_codec.rs`:
- Around line 198-205: Replace the single-case test in
internal_command_round_trips with a table-driven loop that iterates over all
Command variants (including CreatePool and the new ExtendHold), calling
encode_internal_command and decode_internal_command for each and asserting
equality to preserve regression coverage; locate the test in
internal_command_round_trips and build a Vec or array of Command instances, then
for each element call
decode_internal_command(&encode_internal_command(cmd)).unwrap() and
assert_eq!(decoded, cmd).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 912c9dea-3d58-45b9-be0d-ea96c35137e2
📒 Files selected for processing (5)
crates/reservation-core/src/command.rscrates/reservation-core/src/command_codec.rscrates/reservation-core/src/recovery.rscrates/reservation-core/src/snapshot.rscrates/reservation-core/src/state_machine.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: semgrep-cloud-platform/scan
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Write extensive tests for every meaningful behavior change. Favor invariant tests, negative-path tests, recovery tests, and regression tests over shallow happy-path coverage.
Add extensive logging where it materially improves debuggability or operational clarity. Use the right log level:errorfor invariant breaks, corruption, and failed operations that require intervention;warnfor degraded but expected conditions such as overload, lag, or rejected requests;infofor meaningful lifecycle and state-transition events;debugfor detailed execution traces useful in development;traceonly for very high-volume diagnostic detail.
Logging must be structured and purposeful. Do not add noisy logs that obscure signal or hide bugs.
Files:
crates/reservation-core/src/command_codec.rscrates/reservation-core/src/command.rscrates/reservation-core/src/snapshot.rscrates/reservation-core/src/recovery.rscrates/reservation-core/src/state_machine.rs
🔇 Additional comments (5)
crates/reservation-core/src/command.rs (1)
8-8: LGTM.The new tag and explicit
Command::ExtendHoldvariant keep the command surface and wire tags aligned.Also applies to: 47-50
crates/reservation-core/src/snapshot.rs (1)
369-376: LGTM.
ExtendHoldis encoded and decoded with the same field order, so snapshot round-tripping stays symmetric.Also applies to: 402-405
crates/reservation-core/src/command_codec.rs (1)
82-89: LGTM.The new wire tag is serialized and parsed consistently, so
ExtendHoldround-trips cleanly through the codec.Also applies to: 115-118
crates/reservation-core/src/state_machine.rs (1)
732-734: Good stale-expiry guard.This check prevents an old queued deadline from expiring a hold before its current
deadline_slot.crates/reservation-core/src/recovery.rs (1)
306-339: Nice recovery regression.The helpers keep the scenario deterministic, and the new test proves an extended hold survives replay plus later logical-slot advancement.
Also applies to: 521-620
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
crates/reservation-core/src/state_machine.rs (1)
452-459: Factor the expire-and-release transition into a helper.This block is now another copy of the same
held_capacitydecrement +HoldState::Expiredmutation already present inapply_confirm_holdandretire_state. A small helper would keep those paths aligned the next time expiry side effects change.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/reservation-core/src/state_machine.rs` around lines 452 - 459, Extract the repeated logic that decrements pool.held_capacity, sets hold.state = HoldState::Expired and calls self.replace_hold(hold) into a single helper method (e.g., expire_and_release_hold(&mut self, hold: &mut Hold) or similar) and call it from this expiration branch as well as from apply_confirm_hold and retire_state; ensure the helper locates the pool via self.pools.get_mut(hold.pool_id) (preserving the expect(...) behavior or returning a Result) and performs the held_capacity -= hold.quantity, updates hold.state to HoldState::Expired, and invokes self.replace_hold(hold) so all paths share the same side-effect sequence.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/reservation-core/src/state_machine.rs`:
- Around line 1207-1421: Add tests that cover the two missing ExtendHold failure
branches: (1) the exact-deadline late-arrival path in ExtendHold (the branch
around lines 452-465) by creating a Hold with deadline_slot == current slot and
asserting apply_client(context(...), Command::ExtendHold{...}) returns
ResultCode::InvalidState and does not change the hold, and (2) the non-Held
guard in ExtendHold (the branch around lines 442-449) by attempting ExtendHold
on a hold whose state is Released or Consumed and asserting
ResultCode::InvalidState and no state/deadline change; use
ReservationDb::apply_client, Command::ExtendHold, HoldId, Slot, and verify via
db.snapshot().holds and db.hold_retire_queue as in existing tests.
---
Nitpick comments:
In `@crates/reservation-core/src/state_machine.rs`:
- Around line 452-459: Extract the repeated logic that decrements
pool.held_capacity, sets hold.state = HoldState::Expired and calls
self.replace_hold(hold) into a single helper method (e.g.,
expire_and_release_hold(&mut self, hold: &mut Hold) or similar) and call it from
this expiration branch as well as from apply_confirm_hold and retire_state;
ensure the helper locates the pool via self.pools.get_mut(hold.pool_id)
(preserving the expect(...) behavior or returning a Result) and performs the
held_capacity -= hold.quantity, updates hold.state to HoldState::Expired, and
invokes self.replace_hold(hold) so all paths share the same side-effect
sequence.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 20c5fc49-c7cd-4394-abd2-3103e76ebec9
📒 Files selected for processing (1)
crates/reservation-core/src/state_machine.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: semgrep-cloud-platform/scan
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Write extensive tests for every meaningful behavior change. Favor invariant tests, negative-path tests, recovery tests, and regression tests over shallow happy-path coverage.
Add extensive logging where it materially improves debuggability or operational clarity. Use the right log level:errorfor invariant breaks, corruption, and failed operations that require intervention;warnfor degraded but expected conditions such as overload, lag, or rejected requests;infofor meaningful lifecycle and state-transition events;debugfor detailed execution traces useful in development;traceonly for very high-volume diagnostic detail.
Logging must be structured and purposeful. Do not add noisy logs that obscure signal or hide bugs.
Files:
crates/reservation-core/src/state_machine.rs
🔇 Additional comments (1)
crates/reservation-core/src/state_machine.rs (1)
551-574: Nice fix for the reschedule edge case.Rebuilding the live hold-expiry queue from current
Heldrecords, plus the stale-entry guard at Lines 757-759, closes the duplicate-entry/full-queue failure mode cleanly. Themax_holds = 1regression also does a good job pinning it down.Also applies to: 757-759, 1372-1421
| #[test] | ||
| fn extend_hold_moves_deadline_forward_without_changing_capacity() { | ||
| let mut db = ReservationDb::new(config()).unwrap(); | ||
| db.apply_client( | ||
| context(1, 1), | ||
| request( | ||
| 1, | ||
| Command::CreatePool { | ||
| pool_id: PoolId(11), | ||
| total_capacity: 10, | ||
| }, | ||
| ), | ||
| ); | ||
| db.apply_client( | ||
| context(2, 2), | ||
| request( | ||
| 2, | ||
| Command::PlaceHold { | ||
| pool_id: PoolId(11), | ||
| hold_id: HoldId(21), | ||
| quantity: 3, | ||
| deadline_slot: Slot(5), | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| let outcome = db.apply_client( | ||
| context(3, 3), | ||
| request( | ||
| 3, | ||
| Command::ExtendHold { | ||
| hold_id: HoldId(21), | ||
| deadline_slot: Slot(9), | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| assert_eq!(outcome.result_code, ResultCode::Ok); | ||
| assert_eq!(db.snapshot().pools[0].held_capacity, 3); | ||
| assert_eq!(db.snapshot().pools[0].consumed_capacity, 0); | ||
| assert_eq!(db.snapshot().holds[0].deadline_slot, Slot(9)); | ||
| assert_eq!(db.snapshot().holds[0].state, HoldState::Held); | ||
| } | ||
|
|
||
| #[test] | ||
| fn extend_hold_rejects_elapsed_or_non_increasing_deadline() { | ||
| let mut db = ReservationDb::new(config()).unwrap(); | ||
| db.apply_client( | ||
| context(1, 1), | ||
| request( | ||
| 1, | ||
| Command::CreatePool { | ||
| pool_id: PoolId(11), | ||
| total_capacity: 10, | ||
| }, | ||
| ), | ||
| ); | ||
| db.apply_client( | ||
| context(2, 2), | ||
| request( | ||
| 2, | ||
| Command::PlaceHold { | ||
| pool_id: PoolId(11), | ||
| hold_id: HoldId(21), | ||
| quantity: 3, | ||
| deadline_slot: Slot(8), | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| let stale = db.apply_client( | ||
| context(3, 3), | ||
| request( | ||
| 3, | ||
| Command::ExtendHold { | ||
| hold_id: HoldId(21), | ||
| deadline_slot: Slot(3), | ||
| }, | ||
| ), | ||
| ); | ||
| let non_increasing = db.apply_client( | ||
| context(4, 4), | ||
| request( | ||
| 4, | ||
| Command::ExtendHold { | ||
| hold_id: HoldId(21), | ||
| deadline_slot: Slot(8), | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| assert_eq!(stale.result_code, ResultCode::InvalidState); | ||
| assert_eq!(non_increasing.result_code, ResultCode::InvalidState); | ||
| assert_eq!(db.snapshot().holds[0].deadline_slot, Slot(8)); | ||
| assert_eq!(db.snapshot().holds[0].state, HoldState::Held); | ||
| } | ||
|
|
||
| #[test] | ||
| fn extended_hold_does_not_auto_expire_at_old_deadline() { | ||
| let mut db = ReservationDb::new(config()).unwrap(); | ||
| db.apply_client( | ||
| context(1, 1), | ||
| request( | ||
| 1, | ||
| Command::CreatePool { | ||
| pool_id: PoolId(11), | ||
| total_capacity: 10, | ||
| }, | ||
| ), | ||
| ); | ||
| db.apply_client( | ||
| context(2, 2), | ||
| request( | ||
| 2, | ||
| Command::PlaceHold { | ||
| pool_id: PoolId(11), | ||
| hold_id: HoldId(21), | ||
| quantity: 3, | ||
| deadline_slot: Slot(5), | ||
| }, | ||
| ), | ||
| ); | ||
| db.apply_client( | ||
| context(3, 3), | ||
| request( | ||
| 3, | ||
| Command::ExtendHold { | ||
| hold_id: HoldId(21), | ||
| deadline_slot: Slot(10), | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| let outcome = db.apply_client( | ||
| context(4, 8), | ||
| request( | ||
| 4, | ||
| Command::CreatePool { | ||
| pool_id: PoolId(12), | ||
| total_capacity: 1, | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| assert_eq!(outcome.result_code, ResultCode::Ok); | ||
| assert_eq!( | ||
| db.snapshot() | ||
| .holds | ||
| .iter() | ||
| .find(|record| record.hold_id == HoldId(21)) | ||
| .unwrap() | ||
| .state, | ||
| HoldState::Held | ||
| ); | ||
| assert_eq!( | ||
| db.snapshot() | ||
| .holds | ||
| .iter() | ||
| .find(|record| record.hold_id == HoldId(21)) | ||
| .unwrap() | ||
| .deadline_slot, | ||
| Slot(10) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn extend_hold_reschedules_without_overfilling_single_hold_queue() { | ||
| let mut config = config(); | ||
| config.max_holds = 1; | ||
| let mut db = ReservationDb::new(config).unwrap(); | ||
| db.apply_client( | ||
| context(1, 1), | ||
| request( | ||
| 1, | ||
| Command::CreatePool { | ||
| pool_id: PoolId(11), | ||
| total_capacity: 10, | ||
| }, | ||
| ), | ||
| ); | ||
| db.apply_client( | ||
| context(2, 2), | ||
| request( | ||
| 2, | ||
| Command::PlaceHold { | ||
| pool_id: PoolId(11), | ||
| hold_id: HoldId(21), | ||
| quantity: 3, | ||
| deadline_slot: Slot(5), | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| let outcome = db.apply_client( | ||
| context(3, 3), | ||
| request( | ||
| 3, | ||
| Command::ExtendHold { | ||
| hold_id: HoldId(21), | ||
| deadline_slot: Slot(10), | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| assert_eq!(outcome.result_code, ResultCode::Ok); | ||
| assert_eq!( | ||
| db.hold_retire_queue.front(), | ||
| Some(allocdb_retire_queue::RetireEntry { | ||
| key: HoldId(21), | ||
| retire_after_slot: Slot(10), | ||
| }) | ||
| ); | ||
| assert_eq!(db.hold_retire_queue.pop_front().unwrap().key, HoldId(21)); | ||
| assert_eq!(db.hold_retire_queue.pop_front(), None); | ||
| } |
There was a problem hiding this comment.
Add regressions for the remaining ExtendHold failure branches.
The new tests only pin successful extension, invalid proposed deadlines, and queue rescheduling. The exact-deadline late-arrival path at Lines 452-465 and the non-Held guards at Lines 442-449 are still unpinned, and those are the branches most likely to drift because they encode the new business rules.
🧪 Suggested regression to add at minimum
+ #[test]
+ fn extend_hold_at_deadline_expires_hold() {
+ let mut db = ReservationDb::new(config()).unwrap();
+ db.apply_client(
+ context(1, 1),
+ request(
+ 1,
+ Command::CreatePool {
+ pool_id: PoolId(11),
+ total_capacity: 10,
+ },
+ ),
+ );
+ db.apply_client(
+ context(2, 2),
+ request(
+ 2,
+ Command::PlaceHold {
+ pool_id: PoolId(11),
+ hold_id: HoldId(21),
+ quantity: 3,
+ deadline_slot: Slot(5),
+ },
+ ),
+ );
+
+ let outcome = db.apply_client(
+ context(3, 5),
+ request(
+ 3,
+ Command::ExtendHold {
+ hold_id: HoldId(21),
+ deadline_slot: Slot(9),
+ },
+ ),
+ );
+
+ assert_eq!(outcome.result_code, ResultCode::HoldExpired);
+ assert_eq!(db.snapshot().pools[0].held_capacity, 0);
+ assert_eq!(db.snapshot().holds[0].state, HoldState::Expired);
+ }As per coding guidelines, "Write extensive tests for every meaningful behavior change. Favor invariant tests, negative-path tests, recovery tests, and regression tests over shallow happy-path coverage."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/reservation-core/src/state_machine.rs` around lines 1207 - 1421, Add
tests that cover the two missing ExtendHold failure branches: (1) the
exact-deadline late-arrival path in ExtendHold (the branch around lines 452-465)
by creating a Hold with deadline_slot == current slot and asserting
apply_client(context(...), Command::ExtendHold{...}) returns
ResultCode::InvalidState and does not change the hold, and (2) the non-Held
guard in ExtendHold (the branch around lines 442-449) by attempting ExtendHold
on a hold whose state is Released or Consumed and asserting
ResultCode::InvalidState and no state/deadline change; use
ReservationDb::apply_client, Command::ExtendHold, HoldId, Slot, and verify via
db.snapshot().holds and db.hold_retire_queue as in existing tests.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/reservation-core/src/command_codec.rs (1)
199-222: Add wire-layout invariant and negative-path tests forExtendHold.The loop in Line 199-222 proves round-trip symmetry, but it won’t catch accidental wire-format drift (tag/field order/width) if encode+decode change together. Please add an explicit byte-layout assertion and a truncated
TAG_EXTEND_HOLDdecode failure test.♻️ Proposed test additions
#[test] fn internal_command_round_trips() { @@ for command in commands { let decoded = decode_internal_command(&encode_internal_command(command)).unwrap(); assert_eq!(decoded, command); } } + +#[test] +fn extend_hold_internal_command_wire_layout_is_stable() { + let encoded = encode_internal_command(Command::ExtendHold { + hold_id: HoldId(0x0102_0304_0506_0708_090A_0B0C_0D0E_0F10), + deadline_slot: Slot(0x1112_1314_1516_1718), + }); + + let mut expected = vec![crate::command::TAG_EXTEND_HOLD]; + expected.extend_from_slice( + &0x0102_0304_0506_0708_090A_0B0C_0D0E_0F10_u128.to_le_bytes(), + ); + expected.extend_from_slice(&0x1112_1314_1516_1718_u64.to_le_bytes()); + assert_eq!(encoded, expected); +} + +#[test] +fn rejects_truncated_extend_hold_internal_payload() { + let bytes = [crate::command::TAG_EXTEND_HOLD, 0_u8; 10]; + let error = decode_internal_command(&bytes).unwrap_err(); + assert_eq!(error, CommandCodecError::BufferTooShort); +}As per coding guidelines:
**/*.rs: Write extensive tests for every meaningful behavior change. Favor invariant tests, negative-path tests, recovery tests, and regression tests over shallow happy-path coverage.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/reservation-core/src/command_codec.rs` around lines 199 - 222, Add a targeted invariant and negative-path test for Command::ExtendHold: after encoding an ExtendHold value via encode_internal_command, assert the produced bytes exactly equal a hard-coded expected byte array representing the canonical wire layout (use the same fields/values as the existing test) to lock the tag/field order/width; then create a truncated slice starting with the TAG_EXTEND_HOLD bytes (use the TAG_EXTEND_HOLD constant) but missing trailing bytes and assert decode_internal_command on that truncated input returns an error (unwrap_err / matches Err) to ensure truncated/tag decode fails. Name the test to reflect wire-layout and truncated-decode checks and place it alongside the existing round-trip tests referencing encode_internal_command, decode_internal_command, Command::ExtendHold, and TAG_EXTEND_HOLD.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@crates/reservation-core/src/command_codec.rs`:
- Around line 199-222: Add a targeted invariant and negative-path test for
Command::ExtendHold: after encoding an ExtendHold value via
encode_internal_command, assert the produced bytes exactly equal a hard-coded
expected byte array representing the canonical wire layout (use the same
fields/values as the existing test) to lock the tag/field order/width; then
create a truncated slice starting with the TAG_EXTEND_HOLD bytes (use the
TAG_EXTEND_HOLD constant) but missing trailing bytes and assert
decode_internal_command on that truncated input returns an error (unwrap_err /
matches Err) to ensure truncated/tag decode fails. Name the test to reflect
wire-layout and truncated-decode checks and place it alongside the existing
round-trip tests referencing encode_internal_command, decode_internal_command,
Command::ExtendHold, and TAG_EXTEND_HOLD.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 03f9d027-216f-4f44-9223-49d186ff34a7
📒 Files selected for processing (2)
crates/reservation-core/src/command_codec.rscrates/reservation-core/src/snapshot.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/reservation-core/src/snapshot.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Write extensive tests for every meaningful behavior change. Favor invariant tests, negative-path tests, recovery tests, and regression tests over shallow happy-path coverage.
Add extensive logging where it materially improves debuggability or operational clarity. Use the right log level:errorfor invariant breaks, corruption, and failed operations that require intervention;warnfor degraded but expected conditions such as overload, lag, or rejected requests;infofor meaningful lifecycle and state-transition events;debugfor detailed execution traces useful in development;traceonly for very high-volume diagnostic detail.
Logging must be structured and purposeful. Do not add noisy logs that obscure signal or hide bugs.
Files:
crates/reservation-core/src/command_codec.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: skel84/allocdb PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T15:18:53.086Z
Learning: Treat CodeRabbit as part of the required review path when it is enabled on the repository. Wait for its status to complete before merge. If it completes without a visible review comment or review thread, request visible output with `coderabbitai summary`. Address every substantive CodeRabbit comment explicitly before merge by either applying the change or documenting why it is not being applied. Apply correctness, safety, recovery, test, and docs-alignment feedback by default; document why you reject suggestions that would weaken determinism, boundedness, or trusted-core discipline.
🔇 Additional comments (1)
crates/reservation-core/src/command_codec.rs (1)
82-89:ExtendHoldcodec encode/decode mapping looks correct.Line 82-89 and Line 115-118 are consistent on tag, field order, and primitive widths (
u128hold id,u64deadline slot). Nice, deterministic wire mapping.Also applies to: 115-118
Summary
ExtendHoldtoreservation-corecommands, codecs, snapshot encoding, and live state transitionsVerification
cargo test -p reservation-corecargo clippy -p reservation-core --all-targets --all-features -- -D warningscargo fmt --all --checkcargo testscripts/check_repo.shRefs #128
Closes #129