@@ -1401,6 +1401,91 @@ fn try_schedule_retry_respects_weight_limits() {
14011401 } ) ;
14021402}
14031403
1404+ fn schedule_retry_fails_when_retry_target_block_is_full ( named : bool ) {
1405+ let max: u32 = <Test as Config >:: MaxScheduledPerBlock :: get ( ) ;
1406+ let retry_period = 3 ;
1407+ let task_name = [ 42u8 ; 32 ] ;
1408+
1409+ new_test_ext ( ) . execute_with ( || {
1410+ // Task will fail until block 100 (effectively always fails in this test).
1411+ Threshold :: < Test > :: put ( ( 100 , 200 ) ) ;
1412+
1413+ // Fill block 7 (4 + retry_period) to capacity with dummy tasks.
1414+ let filler_call =
1415+ RuntimeCall :: Logger ( LoggerCall :: log { i : 99 , weight : Weight :: from_parts ( 10 , 0 ) } ) ;
1416+ let filler_bound = Preimage :: bound ( filler_call) . unwrap ( ) ;
1417+ for _ in 0 ..max {
1418+ assert_ok ! ( Scheduler :: do_schedule(
1419+ DispatchTime :: At ( 4 + retry_period) ,
1420+ None ,
1421+ 127 ,
1422+ root( ) ,
1423+ filler_bound. clone( ) ,
1424+ ) ) ;
1425+ }
1426+ assert_eq ! ( Agenda :: <Test >:: get( 4 + retry_period) . len( ) as u32 , max) ;
1427+
1428+ // Schedule a task at block 4 that will fail.
1429+ let failing_call =
1430+ RuntimeCall :: Logger ( LoggerCall :: timed_log { i : 42 , weight : Weight :: from_parts ( 10 , 0 ) } ) ;
1431+ if named {
1432+ assert_ok ! ( Scheduler :: do_schedule_named(
1433+ task_name,
1434+ DispatchTime :: At ( 4 ) ,
1435+ None ,
1436+ 127 ,
1437+ root( ) ,
1438+ Preimage :: bound( failing_call) . unwrap( ) ,
1439+ ) ) ;
1440+ // Set retry config for named task.
1441+ assert_ok ! ( Scheduler :: set_retry_named( root( ) . into( ) , task_name, 10 , retry_period) ) ;
1442+ } else {
1443+ assert_ok ! ( Scheduler :: do_schedule(
1444+ DispatchTime :: At ( 4 ) ,
1445+ None ,
1446+ 127 ,
1447+ root( ) ,
1448+ Preimage :: bound( failing_call) . unwrap( ) ,
1449+ ) ) ;
1450+ // Set retry config for anonymous task.
1451+ assert_ok ! ( Scheduler :: set_retry( root( ) . into( ) , ( 4 , 0 ) , 10 , retry_period) ) ;
1452+ }
1453+ assert_eq ! ( Retries :: <Test >:: iter( ) . count( ) , 1 ) ;
1454+
1455+ // Run to block 4: task fails and tries to schedule retry at block 7, but it's full.
1456+ System :: run_to_block :: < AllPalletsWithSystem > ( 4 ) ;
1457+
1458+ // The retry config should be removed since scheduling failed.
1459+ assert_eq ! ( Retries :: <Test >:: iter( ) . count( ) , 0 ) ;
1460+ // Task at block 4 should be gone.
1461+ assert ! ( Agenda :: <Test >:: get( 4 ) . is_empty( ) ) ;
1462+ // Block 7 should still have exactly `max` tasks (the fillers, no retry added).
1463+ assert_eq ! ( Agenda :: <Test >:: get( 4 + retry_period) . len( ) as u32 , max) ;
1464+ // Task 42 never executed.
1465+ assert ! ( !logger:: log( ) . iter( ) . any( |( _, i) | * i == 42 ) ) ;
1466+
1467+ // Verify `RetryFailed` event was emitted.
1468+ // Note: `id` is always `None` because `as_retry()` clears the task name.
1469+ let events = frame_system:: Pallet :: < Test > :: events ( ) ;
1470+ let retry_failed_event: <Test as frame_system:: Config >:: RuntimeEvent =
1471+ Event :: RetryFailed { task : ( 4 , 0 ) , id : None } . into ( ) ;
1472+ assert ! (
1473+ events. iter( ) . any( |record| record. event == retry_failed_event) ,
1474+ "Expected RetryFailed event not found"
1475+ ) ;
1476+ } ) ;
1477+ }
1478+
1479+ #[ test]
1480+ fn schedule_retry_fails_when_retry_target_block_is_full_anon ( ) {
1481+ schedule_retry_fails_when_retry_target_block_is_full ( false ) ;
1482+ }
1483+
1484+ #[ test]
1485+ fn schedule_retry_fails_when_retry_target_block_is_full_named ( ) {
1486+ schedule_retry_fails_when_retry_target_block_is_full ( true ) ;
1487+ }
1488+
14041489/// Permanently overweight calls are not deleted but also not executed.
14051490#[ test]
14061491fn scheduler_does_not_delete_permanently_overweight_call ( ) {
@@ -2721,6 +2806,8 @@ fn scheduler_v3_named_cancel_named_works() {
27212806 . unwrap ( ) ;
27222807 // Cancel the call by name.
27232808 assert_ok ! ( <Scheduler as Named <_, _, _>>:: cancel_named( name) ) ;
2809+ // Lookup storage should be empty.
2810+ assert ! ( Lookup :: <Test >:: get( name) . is_none( ) ) ;
27242811 // It did not get executed.
27252812 System :: run_to_block :: < AllPalletsWithSystem > ( 100 ) ;
27262813 assert ! ( logger:: log( ) . is_empty( ) ) ;
@@ -2751,6 +2838,8 @@ fn scheduler_v3_named_cancel_without_name_works() {
27512838 . unwrap ( ) ;
27522839 // Cancel the call by address.
27532840 assert_ok ! ( <Scheduler as Anon <_, _, _>>:: cancel( address) ) ;
2841+ // Address-cancellation of named task should still clear the lookup storage.
2842+ assert ! ( Lookup :: <Test >:: get( name) . is_none( ) ) ;
27542843 // It did not get executed.
27552844 System :: run_to_block :: < AllPalletsWithSystem > ( 100 ) ;
27562845 assert ! ( logger:: log( ) . is_empty( ) ) ;
0 commit comments