[FRAME] Message Queue use proper overweight limit (#1873)

Changes:
- Use a sensible limit for the overweight-cutoff of a single messages
instead of the full configured `ServiceWeight`.
- Add/Update tests

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Oliver Tale-Yazdi
2023-10-19 12:02:13 +02:00
committed by GitHub
parent 21b32849db
commit 099ef8fe11
5 changed files with 282 additions and 22 deletions
+182
View File
@@ -266,6 +266,44 @@ fn service_queues_suspension_works() {
});
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "Not enough weight to service a single message.")]
fn service_queues_low_weight_defensive() {
use MessageOrigin::*;
build_and_execute::<Test>(|| {
DefaultWeightForCall::set(21.into());
// Check that the integrity test would catch this:
assert!(MessageQueue::do_integrity_test().is_err());
MessageQueue::enqueue_message(msg("weight=0"), Here);
MessageQueue::service_queues(104.into_weight());
});
}
/// Regression test for <https://github.com/paritytech/polkadot-sdk/pull/1873>.
#[test]
fn service_queues_regression_1873() {
use MessageOrigin::*;
build_and_execute::<Test>(|| {
DefaultWeightForCall::set(20.into());
MessageQueue::enqueue_message(msg("weight=100"), Here);
assert_eq!(MessageQueue::service_queues(100.into_weight()), 100.into());
// Before the MQ this would not emit any events:
assert_last_event::<Test>(
Event::OverweightEnqueued {
id: blake2_256(b"weight=100"),
origin: MessageOrigin::Here,
message_index: 0,
page_index: 0,
}
.into(),
);
});
}
#[test]
fn reap_page_permanent_overweight_works() {
use MessageOrigin::*;
@@ -1150,6 +1188,116 @@ fn permanently_overweight_book_unknits_multiple() {
});
}
#[test]
fn permanently_overweight_limit_is_valid_basic() {
use MessageOrigin::*;
for w in 50..300 {
build_and_execute::<Test>(|| {
DefaultWeightForCall::set(Weight::MAX);
set_weight("bump_service_head", 10.into());
set_weight("service_queue_base", 10.into());
set_weight("service_page_base_no_completion", 10.into());
set_weight("service_page_base_completion", 0.into());
set_weight("service_page_item", 10.into());
set_weight("ready_ring_unknit", 10.into());
let m = "weight=200".to_string();
MessageQueue::enqueue_message(msg(&m), Here);
MessageQueue::service_queues(w.into());
let last_event =
frame_system::Pallet::<Test>::events().into_iter().last().expect("No event");
// The weight overhead for a single message is set to 50. The message itself needs 200.
// Every weight in range `[50, 249]` should result in a permanently overweight message:
if w < 250 {
assert_eq!(
last_event.event,
RuntimeEvent::MessageQueue(Event::OverweightEnqueued {
id: blake2_256(m.as_bytes()),
origin: Here,
message_index: 0,
page_index: 0,
})
);
} else {
// Otherwise it is processed as normal:
assert_eq!(
last_event.event,
RuntimeEvent::MessageQueue(Event::Processed {
origin: Here,
weight_used: 200.into(),
id: blake2_256(m.as_bytes()),
success: true,
})
);
}
});
}
}
#[test]
fn permanently_overweight_limit_is_valid_fuzzy() {
use MessageOrigin::*;
let mut rng = rand::rngs::StdRng::seed_from_u64(42);
for _ in 0..10 {
// Brainlet code, but works...
let (s1, s2) = (rng.gen_range(0..=10), rng.gen_range(0..=10));
let (s3, s4) = (rng.gen_range(0..=10), rng.gen_range(0..=10));
let s5 = rng.gen_range(0..=10);
let o = s1 + s2 + s3 + s4 + s5;
for w in o..=o + 300 {
build_and_execute::<Test>(|| {
DefaultWeightForCall::set(Weight::MAX);
set_weight("bump_service_head", s1.into());
set_weight("service_queue_base", s2.into());
// Only the larger one of these two is taken:
set_weight("service_page_base_no_completion", s3.into());
set_weight("service_page_base_completion", 0.into());
set_weight("service_page_item", s4.into());
set_weight("ready_ring_unknit", s5.into());
let m = "weight=200".to_string();
MessageQueue::enqueue_message(msg(&m), Here);
MessageQueue::service_queues(w.into());
let last_event =
frame_system::Pallet::<Test>::events().into_iter().last().expect("No event");
if w < o + 200 {
assert_eq!(
last_event.event,
RuntimeEvent::MessageQueue(Event::OverweightEnqueued {
id: blake2_256(m.as_bytes()),
origin: Here,
message_index: 0,
page_index: 0,
})
);
} else {
assert_eq!(
last_event.event,
RuntimeEvent::MessageQueue(Event::Processed {
origin: Here,
weight_used: 200.into(),
id: blake2_256(m.as_bytes()),
success: true,
})
);
}
});
}
}
}
/// We don't want empty books in the ready ring, but if they somehow make their way in there, it
/// should not panic.
#[test]
@@ -1447,3 +1595,37 @@ fn service_queue_suspension_ready_ring_works() {
);
});
}
#[test]
fn integrity_test_checks_service_weight() {
build_and_execute::<Test>(|| {
assert_eq!(<Test as Config>::ServiceWeight::get(), Some(100.into()), "precond");
assert!(MessageQueue::do_integrity_test().is_ok(), "precond");
// Enough for all:
DefaultWeightForCall::set(20.into());
assert!(MessageQueue::do_integrity_test().is_ok());
// Not enough for anything:
DefaultWeightForCall::set(101.into());
assert_eq!(MessageQueue::single_msg_overhead(), 505.into());
assert!(MessageQueue::do_integrity_test().is_err());
// Not enough for a single function:
for f in [
"bump_service_head",
"service_queue_base",
"service_page_base_completion",
"service_page_base_no_completion",
"service_page_item",
"ready_ring_unknit",
] {
WeightForCall::take();
DefaultWeightForCall::set(Zero::zero());
assert!(MessageQueue::do_integrity_test().is_ok());
set_weight(f, 101.into());
assert!(MessageQueue::do_integrity_test().is_err());
}
});
}