// This test verifies that we can successfully record metrics from a single
// scheduler thread. We feed the `thread` with a single canister which has 3
// ingress messages. The first one runs out of instructions while the other two
// are executed successfully.
This test verifies that we can successfully record metrics from a single
scheduler thread. We feed the `thread` with a single canister which has 3
ingress messages. The first one runs out of instructions while the other two
are executed successfully.
This test verifies that we can successfully record metrics from a single scheduler thread . We feed the ` thread ` with a single canister which has 3 ingress messages . The first one runs out of instructions while the other two are executed successfully .
fn can_record_metrics_single_scheduler_thread() {
    ic_test_utilities::with_test_replica_logger(|log| {
        let max_instructions_per_message = NumInstructions::from(5);
        let scheduler_config = SchedulerConfig {
            scheduler_cores: 1,
            max_instructions_per_round: NumInstructions::from(18),
            max_instructions_per_message,
            instruction_overhead_per_message: NumInstructions::from(0),
            ..SchedulerConfig::application_subnet()
        };
        let mut exec_env = MockExecutionEnvironment::new();
        let canister_id = canister_test_id(0);
        exec_env
            .expect_execute_canister_message()
            .times(1)
            .returning(move |canister, _, _, _, _, _, _| ExecuteMessageResult {
                canister,
                num_instructions_left: NumInstructions::from(0),
                ingress_status: Some((
                    message_test_id(0),
                    IngressStatus::Failed {
                        receiver: canister_id.get(),
                        user_id: user_test_id(0),
                        error: UserError::new(ErrorCode::CanisterOutOfCycles, "".to_string()),
                        time: mock_time(),
                    },
                )),
                heap_delta: NumBytes::from(0),
            });
        for message_id in 1..3 {
            exec_env
                .expect_execute_canister_message()
                .times(1)
                .returning(move |canister, _, _, _, _, _, _| ExecuteMessageResult {
                    canister,
                    num_instructions_left: NumInstructions::from(1),
                    ingress_status: Some((
                        message_test_id(message_id),
                        IngressStatus::Completed {
                            receiver: canister_id.get(),
                            user_id: user_test_id(0),
                            result: WasmResult::Reply(vec![]),
                            time: mock_time(),
                        },
                    )),
                    heap_delta: NumBytes::from(0),
                });
        }
        let mut canister_state = new_canister_state(
            canister_id,
            user_test_id(24).get(),
            *INITIAL_CYCLES,
            NumSeconds::from(100_000),
        );
        let mut exports = BTreeSet::new();
        exports.insert(WasmMethod::Update("write".to_string()));
        exports.insert(WasmMethod::Query("read".to_string()));
        let mut execution_state = initial_execution_state(None);
        execution_state.exports = ExportedFunctions::new(exports);
        canister_state.execution_state = Some(execution_state);
        for nonce in 0..3 {
            canister_state.push_ingress(
                SignedIngressBuilder::new()
                    .canister_id(canister_id)
                    .method_name("write".to_string())
                    .nonce(nonce)
                    .build()
                    .into(),
            );
        }
        let metrics_registry = MetricsRegistry::new();
        // This is needed for constructing an instance of `SchedulerImpl`, but its
        // methods are not called. That's why the expected number of calls is 0.
        let ingress_history_writer = default_ingress_history_writer_mock(0);
        let ingress_history_writer = Arc::new(ingress_history_writer);
        let cycles_account_manager = Arc::new(
            CyclesAccountManagerBuilder::new()
                .with_max_num_instructions(MAX_INSTRUCTIONS_PER_MESSAGE)
                .build(),
        );
        let subnet_records: Arc<BTreeMap<SubnetId, SubnetType>> = Arc::new(
            [(subnet_test_id(1), SubnetType::Application)]
                .iter()
                .cloned()
                .collect(),
        );
        let scheduler = SchedulerImpl::new(
            scheduler_config.clone(),
            subnet_test_id(1),
            ingress_history_writer,
            Arc::new(exec_env),
            cycles_account_manager,
            &metrics_registry,
            log,
        );
        let measurement_scope = MeasurementScope::root(&scheduler.metrics.round_inner_iteration);
        scheduler.execute_canisters_in_inner_round(
            vec![vec![canister_state]],
            scheduler_config,
            ExecutionRound::from(0),
            mock_time(),
            SUBNET_AVAILABLE_MEMORY,
            Arc::new(RoutingTable::default()),
            subnet_records,
            HeartbeatHandling::Execute {
                only_track_system_errors: true,
            },
            &measurement_scope,
        );
        let cycles_consumed_per_message_stats = fetch_histogram_stats(
            &metrics_registry,
            "scheduler_instructions_consumed_per_message",
        )
        .unwrap();
        let cycles_consumed_per_round_stats = fetch_histogram_stats(
            &metrics_registry,
            "scheduler_instructions_consumed_per_round",
        )
        .unwrap();
        let msg_execution_duration_stats = fetch_histogram_stats(
            &metrics_registry,
            "scheduler_message_execution_duration_seconds",
        )
        .unwrap();
        let canister_messages_where_cycles_were_charged = fetch_int_counter(
            &metrics_registry,
            "scheduler_canister_messages_where_cycles_were_charged",
        )
        .unwrap();
        assert_eq!(msg_execution_duration_stats.count, 3);
        assert_eq!(cycles_consumed_per_message_stats.count, 3);
        assert_eq!(cycles_consumed_per_round_stats.sum as i64, 13);
        assert_eq!(canister_messages_where_cycles_were_charged, 3);
    });
}
fn can_record_metrics_single_scheduler_thread ( ) { ic_test_utilities :: with_test_replica_logger ( | log | { let max_instructions_per_message = NumInstructions :: from ( 5 ) ; let scheduler_config = SchedulerConfig { scheduler_cores : 1 , max_instructions_per_round : NumInstructions :: from ( 18 ) , max_instructions_per_message , instruction_overhead_per_message : NumInstructions :: from ( 0 ) , .. SchedulerConfig :: application_subnet ( ) } ; let mut exec_env = MockExecutionEnvironment :: new ( ) ; let canister_id = canister_test_id ( 0 ) ; exec_env . expect_execute_canister_message ( ) . times ( 1 ) . returning ( move | canister , _ , _ , _ , _ , _ , _ | ExecuteMessageResult { canister , num_instructions_left : NumInstructions :: from ( 0 ) , ingress_status : Some ( ( message_test_id ( 0 ) , IngressStatus :: Failed { receiver : canister_id . get ( ) , user_id : user_test_id ( 0 ) , error : UserError :: new ( ErrorCode :: CanisterOutOfCycles , " " . to_string ( ) ) , time : mock_time ( ) , } , ) ) , heap_delta : NumBytes :: from ( 0 ) , } ) ; for message_id in 1 .. 3 { exec_env . expect_execute_canister_message ( ) . times ( 1 ) . returning ( move | canister , _ , _ , _ , _ , _ , _ | ExecuteMessageResult { canister , num_instructions_left : NumInstructions :: from ( 1 ) , ingress_status : Some ( ( message_test_id ( message_id ) , IngressStatus :: Completed { receiver : canister_id . get ( ) , user_id : user_test_id ( 0 ) , result : WasmResult :: Reply ( vec ! [ ] ) , time : mock_time ( ) , } , ) ) , heap_delta : NumBytes :: from ( 0 ) , } ) ; } let mut canister_state = new_canister_state ( canister_id , user_test_id ( 24 ) . get ( ) , * INITIAL_CYCLES , NumSeconds :: from ( 100_000 ) , ) ; let mut exports = BTreeSet :: new ( ) ; exports . insert ( WasmMethod :: Update ( " " . to_string ( ) ) ) ; exports . insert ( WasmMethod :: Query ( " " . to_string ( ) ) ) ; let mut execution_state = initial_execution_state ( None ) ; execution_state . exports = ExportedFunctions :: new ( exports ) ; canister_state . execution_state = Some ( execution_state ) ; for nonce in 0 .. 3 { canister_state . push_ingress ( SignedIngressBuilder :: new ( ) . canister_id ( canister_id ) . method_name ( " " . to_string ( ) ) . nonce ( nonce ) . build ( ) . into ( ) , ) ; } let metrics_registry = MetricsRegistry :: new ( ) ; // This is needed for constructing an instance of `SchedulerImpl`, but its // methods are not called. That's why the expected number of calls is 0. let ingress_history_writer = default_ingress_history_writer_mock ( 0 ) ; let ingress_history_writer = Arc :: new ( ingress_history_writer ) ; let cycles_account_manager = Arc :: new ( CyclesAccountManagerBuilder :: new ( ) . with_max_num_instructions ( MAX_INSTRUCTIONS_PER_MESSAGE ) . build ( ) , ) ; let subnet_records : Arc < BTreeMap < SubnetId , SubnetType > > = Arc :: new ( [ ( subnet_test_id ( 1 ) , SubnetType :: Application ) ] . iter ( ) . cloned ( ) . collect ( ) , ) ; let scheduler = SchedulerImpl :: new ( scheduler_config . clone ( ) , subnet_test_id ( 1 ) , ingress_history_writer , Arc :: new ( exec_env ) , cycles_account_manager , & metrics_registry , log , ) ; let measurement_scope = MeasurementScope :: root ( & scheduler . metrics . round_inner_iteration ) ; scheduler . execute_canisters_in_inner_round ( vec ! [ vec [ canister_state ] ] , scheduler_config , ExecutionRound :: from ( 0 ) , mock_time ( ) , SUBNET_AVAILABLE_MEMORY , Arc :: new ( RoutingTable :: default ( ) ) , subnet_records , HeartbeatHandling :: Execute { only_track_system_errors : true , } , & measurement_scope , ) ; let cycles_consumed_per_message_stats = fetch_histogram_stats ( & metrics_registry , " " , ) . unwrap ( ) ; let cycles_consumed_per_round_stats = fetch_histogram_stats ( & metrics_registry , " " , ) . unwrap ( ) ; let msg_execution_duration_stats = fetch_histogram_stats ( & metrics_registry , " " , ) . unwrap ( ) ; let canister_messages_where_cycles_were_charged = fetch_int_counter ( & metrics_registry , " " , ) . unwrap ( ) ; assert_eq ! ( msg_execution_duration_stats count 3 ) ; assert_eq ! ( cycles_consumed_per_message_stats count 3 ) ; assert_eq ! ( cycles_consumed_per_round_stats sum as i64 13 ) ; assert_eq ! ( canister_messages_where_cycles_were_charged 3 ) ; } ) ; }
This test verifies that we can successfully record metrics from a single
scheduler thread.
This test verifies that we can successfully record metrics from a single scheduler thread .