Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 306382b

Browse files
authored
MessageQueue: unknit permanently overweight books (#13528)
* Unknit permanently overweight books A book with only permanently overweight messages should be unkit from the ready ring. This does currently not happen since perm. overweight messages are not counted as "processed" and therefore not increase the "total_processed" counter. This is only a problem when the next and only message that is processed is overweight. Eventually this should resolve itself when another non-overweight message is enqueued and processed. But for correctness it should be unknitted. Signed-off-by: Oliver Tale-Yazdi <[email protected]> * Add tests Signed-off-by: Oliver Tale-Yazdi <[email protected]> * fmt Signed-off-by: Oliver Tale-Yazdi <[email protected]> * One more tests Signed-off-by: Oliver Tale-Yazdi <[email protected]> * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet-message-queue --------- Signed-off-by: Oliver Tale-Yazdi <[email protected]> Co-authored-by: command-bot <>
1 parent 8a4746e commit 306382b

File tree

5 files changed

+170
-66
lines changed

5 files changed

+170
-66
lines changed

frame/message-queue/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -961,11 +961,11 @@ impl<T: Config> Pallet<T> {
961961
book_state.begin.saturating_inc();
962962
}
963963
let next_ready = book_state.ready_neighbours.as_ref().map(|x| x.next.clone());
964-
if book_state.begin >= book_state.end && total_processed > 0 {
964+
if book_state.begin >= book_state.end {
965965
// No longer ready - unknit.
966966
if let Some(neighbours) = book_state.ready_neighbours.take() {
967967
Self::ready_ring_unknit(&origin, neighbours);
968-
} else {
968+
} else if total_processed > 0 {
969969
defensive!("Freshly processed queue must have been ready");
970970
}
971971
}

frame/message-queue/src/mock.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,12 @@ pub fn knit(queue: &MessageOrigin) {
320320
pub fn unknit(queue: &MessageOrigin) {
321321
super::mock_helpers::unknit::<Test>(queue);
322322
}
323+
324+
pub fn num_overweight_enqueued_events() -> u32 {
325+
frame_system::Pallet::<Test>::events()
326+
.into_iter()
327+
.filter(|e| {
328+
matches!(e.event, RuntimeEvent::MessageQueue(crate::Event::OverweightEnqueued { .. }))
329+
})
330+
.count() as u32
331+
}

frame/message-queue/src/mock_helpers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ where
7474
}
7575

7676
/// Create a message from the given data.
77-
pub fn msg<N: Get<u32>>(x: &'static str) -> BoundedSlice<u8, N> {
77+
pub fn msg<N: Get<u32>>(x: &str) -> BoundedSlice<u8, N> {
7878
BoundedSlice::defensive_truncate_from(x.as_bytes())
7979
}
8080

81-
pub fn vmsg(x: &'static str) -> Vec<u8> {
81+
pub fn vmsg(x: &str) -> Vec<u8> {
8282
x.as_bytes().to_vec()
8383
}
8484

frame/message-queue/src/tests.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,121 @@ fn execute_overweight_works() {
10831083
assert_eq!(consumed, Err(ExecuteOverweightError::NotFound));
10841084
assert!(QueueChanges::take().is_empty());
10851085
assert!(!Pages::<Test>::contains_key(origin, 0), "Page is gone");
1086+
// The book should have been unknit from the ready ring.
1087+
assert!(!ServiceHead::<Test>::exists(), "No ready book");
1088+
});
1089+
}
1090+
1091+
#[test]
1092+
fn permanently_overweight_book_unknits() {
1093+
use MessageOrigin::*;
1094+
1095+
new_test_ext::<Test>().execute_with(|| {
1096+
set_weight("bump_service_head", 1.into_weight());
1097+
set_weight("service_queue_base", 1.into_weight());
1098+
set_weight("service_page_base_completion", 1.into_weight());
1099+
1100+
MessageQueue::enqueue_messages([msg("weight=9")].into_iter(), Here);
1101+
1102+
// It is the only ready book.
1103+
assert_ring(&[Here]);
1104+
// Mark the message as overweight.
1105+
assert_eq!(MessageQueue::service_queues(8.into_weight()), 4.into_weight());
1106+
assert_last_event::<Test>(
1107+
Event::OverweightEnqueued {
1108+
hash: <Test as frame_system::Config>::Hashing::hash(b"weight=9"),
1109+
origin: Here,
1110+
message_index: 0,
1111+
page_index: 0,
1112+
}
1113+
.into(),
1114+
);
1115+
// The book is not ready anymore.
1116+
assert_ring(&[]);
1117+
assert_eq!(MessagesProcessed::take().len(), 0);
1118+
assert_eq!(BookStateFor::<Test>::get(Here).message_count, 1);
1119+
// Now if we enqueue another message, it will become ready again.
1120+
MessageQueue::enqueue_messages([msg("weight=1")].into_iter(), Here);
1121+
assert_ring(&[Here]);
1122+
assert_eq!(MessageQueue::service_queues(8.into_weight()), 5.into_weight());
1123+
assert_eq!(MessagesProcessed::take().len(), 1);
1124+
assert_ring(&[]);
1125+
});
1126+
}
1127+
1128+
#[test]
1129+
fn permanently_overweight_book_unknits_multiple() {
1130+
use MessageOrigin::*;
1131+
1132+
new_test_ext::<Test>().execute_with(|| {
1133+
set_weight("bump_service_head", 1.into_weight());
1134+
set_weight("service_queue_base", 1.into_weight());
1135+
set_weight("service_page_base_completion", 1.into_weight());
1136+
1137+
MessageQueue::enqueue_messages(
1138+
[msg("weight=1"), msg("weight=9"), msg("weight=9")].into_iter(),
1139+
Here,
1140+
);
1141+
1142+
assert_ring(&[Here]);
1143+
// Process the first message.
1144+
assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight());
1145+
assert_eq!(num_overweight_enqueued_events(), 0);
1146+
assert_eq!(MessagesProcessed::take().len(), 1);
1147+
1148+
// Book is still ready since it was not marked as overweight yet.
1149+
assert_ring(&[Here]);
1150+
assert_eq!(MessageQueue::service_queues(8.into_weight()), 5.into_weight());
1151+
assert_eq!(num_overweight_enqueued_events(), 2);
1152+
assert_eq!(MessagesProcessed::take().len(), 0);
1153+
// Now it is overweight.
1154+
assert_ring(&[]);
1155+
// Enqueue another message.
1156+
MessageQueue::enqueue_messages([msg("weight=1")].into_iter(), Here);
1157+
assert_ring(&[Here]);
1158+
assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight());
1159+
assert_eq!(MessagesProcessed::take().len(), 1);
1160+
assert_ring(&[]);
1161+
});
1162+
}
1163+
1164+
/// We don't want empty books in the ready ring, but if they somehow make their way in there, it
1165+
/// should not panic.
1166+
#[test]
1167+
#[cfg(not(debug_assertions))] // Would trigger a defensive failure otherwise.
1168+
fn ready_but_empty_does_not_panic() {
1169+
use MessageOrigin::*;
1170+
1171+
new_test_ext::<Test>().execute_with(|| {
1172+
BookStateFor::<Test>::insert(Here, empty_book::<Test>());
1173+
BookStateFor::<Test>::insert(There, empty_book::<Test>());
1174+
1175+
knit(&Here);
1176+
knit(&There);
1177+
assert_ring(&[Here, There]);
1178+
1179+
assert_eq!(MessageQueue::service_queues(Weight::MAX), 0.into_weight());
1180+
assert_ring(&[]);
1181+
});
1182+
}
1183+
1184+
/// We don't want permanently books in the ready ring, but if they somehow make their way in there,
1185+
/// it should not panic.
1186+
#[test]
1187+
#[cfg(not(debug_assertions))] // Would trigger a defensive failure otherwise.
1188+
fn ready_but_perm_overweight_does_not_panic() {
1189+
use MessageOrigin::*;
1190+
1191+
new_test_ext::<Test>().execute_with(|| {
1192+
MessageQueue::enqueue_message(msg("weight=9"), Here);
1193+
assert_eq!(MessageQueue::service_queues(8.into_weight()), 0.into_weight());
1194+
assert_ring(&[]);
1195+
// Force it back into the ready ring.
1196+
knit(&Here);
1197+
assert_ring(&[Here]);
1198+
assert_eq!(MessageQueue::service_queues(Weight::MAX), 0.into_weight());
1199+
// Unready again.
1200+
assert_ring(&[]);
10861201
});
10871202
}
10881203

0 commit comments

Comments
 (0)