From 412d5b38cdb08006050ed53757edb5c33a47eb23 Mon Sep 17 00:00:00 2001 From: Bjoern Tackmann Date: Fri, 31 Jul 2026 11:16:33 +0200 Subject: [PATCH 1/2] spec: add subnet_metrics management canister endpoint Proposal for discussion. Adds a subnet_metrics endpoint returning the subnet block height plus the four subnet-wide metrics that are currently only reachable by external users via the certified state tree path /subnet//metrics. --- .../ic-interface-spec/abstract-behavior.md | 39 +++++++++++++++++++ .../references/ic-interface-spec/changelog.md | 8 ++++ .../ic-interface-spec/management-canister.md | 32 +++++++++++++++ docs/references/management-canister.md | 18 +++++++++ public/references/ic.did | 19 +++++++++ 5 files changed, 116 insertions(+) diff --git a/docs/references/ic-interface-spec/abstract-behavior.md b/docs/references/ic-interface-spec/abstract-behavior.md index a99ca398..43d1c800 100644 --- a/docs/references/ic-interface-spec/abstract-behavior.md +++ b/docs/references/ic-interface-spec/abstract-behavior.md @@ -3056,6 +3056,45 @@ S with ``` +#### IC Management Canister: Subnet Metrics + +:::note + +The subnet metrics management canister API is considered EXPERIMENTAL. Canister developers must be aware that the API may evolve in a non-backward-compatible way. + +::: + +The management canister returns subnet-wide metrics for the subnet hosting the calling canister. The definition of the metrics values +is not captured in this formal semantics. + +Conditions + +```html + +S.messages = Older_messages · CallMessage M · Younger_messages +(M.queue = Unordered) or (∀ CallMessage M' | FuncMessage M' ∈ Older_messages. M'.queue ≠ M.queue) +M.callee = ic_principal +M.method_name = 'subnet_metrics' +M.arg = candid(A) +A.subnet_id = S.canister_subnet[M.caller] +R = + +``` + +State after + +```html + +S with + messages = Older_messages · Younger_messages · + ResponseMessage { + origin = M.origin + response = Reply (candid(R)) + refunded_cycles = M.transferred_cycles + } + +``` + #### IC Management Canister: Subnet information The management canister returns subnet metadata given a subnet ID. diff --git a/docs/references/ic-interface-spec/changelog.md b/docs/references/ic-interface-spec/changelog.md index 86e2fcfb..a30cee77 100644 --- a/docs/references/ic-interface-spec/changelog.md +++ b/docs/references/ic-interface-spec/changelog.md @@ -8,6 +8,14 @@ sidebar: ## Changelog {#changelog} +### 0.65.0 (2026-07-31) {$0_65_0} +* New management canister endpoint `subnet_metrics` returning subnet-wide metrics for the + subnet hosting the calling canister: the block height, the number of canisters, the + total canister state size, the total cycles consumed, and the total number of processed + transactions. All fields except the block height were previously only readable by + external users via the certified state tree path `/subnet//metrics`. + The API is EXPERIMENTAL. + ### 0.64.0 (2026-07-06) {$0_64_0} * New optional `permissions` field in request delegations restricting the kinds of requests the delegation applies for: the value `"queries"` restricts the delegation to query calls diff --git a/docs/references/ic-interface-spec/management-canister.md b/docs/references/ic-interface-spec/management-canister.md index 386f669c..2bd1598f 100644 --- a/docs/references/ic-interface-spec/management-canister.md +++ b/docs/references/ic-interface-spec/management-canister.md @@ -749,6 +749,38 @@ A single metric entry is a record with the following fields: - `num_block_failures_total` (`nat64`): the number of failed block proposals by this node. +### IC method `subnet_metrics` {#ic-subnet_metrics} + +This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. + +:::note + +The subnet metrics management canister API is considered EXPERIMENTAL. Canister developers must be aware that the API may evolve in a non-backward-compatible way. + +::: + +Given a subnet ID as input, this method returns a record of subnet-wide metrics describing that subnet's resource usage and performance. The subnet ID must be the subnet that hosts the calling canister; calls specifying any other subnet ID are rejected with reject code `CANISTER_REJECT`. + + + +All fields except `block_height` report the same quantities that the certified state tree exposes at the path `/subnet//metrics` (see [Subnet information](./index.md#state-tree-subnet)). This method makes them available to canisters, which cannot read the state tree. + +The fields returned are: + +- `block_height` (`nat`): the height of the block whose batch contains this call. The value is deterministic, i.e., it is a property of the block rather than of the node executing the call. It is monotonically non-decreasing for a given subnet, but it may jump if the subnet is recovered and it is not guaranteed to count from the subnet's first block. + +- `num_canisters` (`nat`): the number of canisters currently on this subnet. This is a current value, not a counter, so it decreases when canisters are deleted. + +- `canister_state_bytes` (`nat`): the total size in bytes of the state currently taken by canisters on this subnet. This is a current value, not a counter. + +- `consumed_cycles_total` (`nat`): the total number of cycles removed from circulation on this subnet by all current and deleted canisters. Note that this aggregate is not the same quantity as the `burned_cycles` field of [`canister_metrics`](#ic-canister_metrics), which only reports cycles a canister burned explicitly via `ic0.cycles_burn`. + +- `update_transactions_total` (`nat`): the total number of transactions processed on this subnet. + + + +The counter fields `consumed_cycles_total` and `update_transactions_total` accumulate since the subnet was created, or since the respective metric was introduced for subnets that predate it. + ### IC method `subnet_info` {#ic-subnet_info} This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. diff --git a/docs/references/management-canister.md b/docs/references/management-canister.md index 7fb925df..f0110541 100644 --- a/docs/references/management-canister.md +++ b/docs/references/management-canister.md @@ -559,6 +559,24 @@ Returns a time series of node metrics for a given subnet. Returns up to 60 times - `num_blocks_proposed_total` (`nat64`) - `num_block_failures_total` (`nat64`) +### `subnet_metrics` + +> This API is **experimental** and may change in a non-backward-compatible way. + +Returns subnet-wide metrics for the subnet hosting the calling canister. Every field except `block_height` reports the same quantity that the certified state tree exposes at `/subnet//metrics`; this method makes those quantities available to canisters, which cannot read the state tree. + +- **Caller:** Canisters only +- **Parameters:** + - `subnet_id` (`principal`): must be the subnet hosting the caller +- **Returns:** + - `block_height` (`nat`): height of the block whose batch contains the call + - `num_canisters` (`nat`): canisters currently on the subnet + - `canister_state_bytes` (`nat`): current total size of canister state in bytes + - `consumed_cycles_total` (`nat`): total cycles removed from circulation on the subnet + - `update_transactions_total` (`nat`): total transactions processed on the subnet + +`consumed_cycles_total` and `update_transactions_total` are counters; `num_canisters` and `canister_state_bytes` are current values. + ### `subnet_info` Returns metadata about a subnet. diff --git a/public/references/ic.did b/public/references/ic.did index 8d6fab7a..2e7230e5 100644 --- a/public/references/ic.did +++ b/public/references/ic.did @@ -436,6 +436,24 @@ type node_metrics_history_result = vec record { node_metrics : vec node_metrics; }; +type subnet_metrics_args = record { + subnet_id : principal; +}; + +type subnet_metrics_result = record { + // Height of the block whose batch contains this call. + block_height : nat; + // Current number of canisters on this subnet. + num_canisters : nat; + // Current total size in bytes of the state taken by canisters on this subnet. + canister_state_bytes : nat; + // Total cycles removed from circulation on this subnet by all current and + // deleted canisters. + consumed_cycles_total : nat; + // Total number of transactions processed on this subnet. + update_transactions_total : nat; +}; + type subnet_info_args = record { subnet_id : principal; }; @@ -700,6 +718,7 @@ service ic : { // metrics interface node_metrics_history : (node_metrics_history_args) -> (node_metrics_history_result); + subnet_metrics : (subnet_metrics_args) -> (subnet_metrics_result); // subnet info subnet_info : (subnet_info_args) -> (subnet_info_result); From da04d01c6285b856008b5255a96da7ac778c72c6 Mon Sep 17 00:00:00 2001 From: Bjoern Tackmann Date: Fri, 31 Jul 2026 18:14:24 +0200 Subject: [PATCH 2/2] spec: address review feedback on subnet_metrics - Drop the own-subnet restriction: cross-subnet calls are handled by the existing message routing protocol, so no restriction is needed. - Report the subnet's latest certified height rather than the height of the block containing the call, and rename the field to certified_height. - Keep nat for all fields, since consumed_cycles_total cannot be nat64. --- .../ic-interface-spec/abstract-behavior.md | 3 +-- .../references/ic-interface-spec/changelog.md | 11 ++++----- .../ic-interface-spec/management-canister.md | 23 +++++++++++-------- docs/references/management-canister.md | 6 ++--- public/references/ic.did | 13 ++++++----- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/docs/references/ic-interface-spec/abstract-behavior.md b/docs/references/ic-interface-spec/abstract-behavior.md index 43d1c800..4a44d41b 100644 --- a/docs/references/ic-interface-spec/abstract-behavior.md +++ b/docs/references/ic-interface-spec/abstract-behavior.md @@ -3064,7 +3064,7 @@ The subnet metrics management canister API is considered EXPERIMENTAL. Canister ::: -The management canister returns subnet-wide metrics for the subnet hosting the calling canister. The definition of the metrics values +The management canister returns subnet-wide metrics given a subnet ID. The definition of the metrics values is not captured in this formal semantics. Conditions @@ -3076,7 +3076,6 @@ S.messages = Older_messages · CallMessage M · Younger_messages M.callee = ic_principal M.method_name = 'subnet_metrics' M.arg = candid(A) -A.subnet_id = S.canister_subnet[M.caller] R = ``` diff --git a/docs/references/ic-interface-spec/changelog.md b/docs/references/ic-interface-spec/changelog.md index a30cee77..143d5b85 100644 --- a/docs/references/ic-interface-spec/changelog.md +++ b/docs/references/ic-interface-spec/changelog.md @@ -9,12 +9,11 @@ sidebar: ## Changelog {#changelog} ### 0.65.0 (2026-07-31) {$0_65_0} -* New management canister endpoint `subnet_metrics` returning subnet-wide metrics for the - subnet hosting the calling canister: the block height, the number of canisters, the - total canister state size, the total cycles consumed, and the total number of processed - transactions. All fields except the block height were previously only readable by - external users via the certified state tree path `/subnet//metrics`. - The API is EXPERIMENTAL. +* New management canister endpoint `subnet_metrics` returning subnet-wide metrics for a + given subnet: the latest certified height, the number of canisters, the total canister + state size, the total cycles consumed, and the total number of processed transactions. + All fields except the certified height were previously only readable by external users + via the certified state tree path `/subnet//metrics`. The API is EXPERIMENTAL. ### 0.64.0 (2026-07-06) {$0_64_0} * New optional `permissions` field in request delegations restricting the kinds of requests diff --git a/docs/references/ic-interface-spec/management-canister.md b/docs/references/ic-interface-spec/management-canister.md index 2bd1598f..83e3d030 100644 --- a/docs/references/ic-interface-spec/management-canister.md +++ b/docs/references/ic-interface-spec/management-canister.md @@ -759,23 +759,28 @@ The subnet metrics management canister API is considered EXPERIMENTAL. Canister ::: -Given a subnet ID as input, this method returns a record of subnet-wide metrics describing that subnet's resource usage and performance. The subnet ID must be the subnet that hosts the calling canister; calls specifying any other subnet ID are rejected with reject code `CANISTER_REJECT`. +Given a subnet ID as input, this method returns a record of subnet-wide metrics describing that subnet's resource usage and performance. The subnet ID does not have to be the subnet that hosts the calling canister. - +All fields except `certified_height` report the same quantities that the certified state tree exposes at the path `/subnet//metrics` (see [Subnet information](./index.md#state-tree-subnet)). This method makes them available to canisters, which cannot read the state tree. -All fields except `block_height` report the same quantities that the certified state tree exposes at the path `/subnet//metrics` (see [Subnet information](./index.md#state-tree-subnet)). This method makes them available to canisters, which cannot read the state tree. +In the following, *the subnet* refers to the subnet identified by the `subnet_id` argument. The fields returned are: -The fields returned are: +- `certified_height` (`nat`): the height of the latest state of the subnet for which the subnet has produced a certificate (see [Certification](./certification.md#certification)). Heights are consecutive numbers identifying the successive states of a subnet. This specification does not otherwise model state heights, and heights of different subnets are unrelated, so this value is only meaningful when compared against other values for the same subnet. + + The value is monotonically non-decreasing for a given subnet, but it may jump if the subnet is recovered, and it is not guaranteed to count from the subnet's first state. Note that the latest certified height is lower than the height of the subnet's latest state, because certification lags execution. + + All nodes of the subnet executing the call must return the same value, i.e., the value must not be derived from a single node's local view. + + -- `block_height` (`nat`): the height of the block whose batch contains this call. The value is deterministic, i.e., it is a property of the block rather than of the node executing the call. It is monotonically non-decreasing for a given subnet, but it may jump if the subnet is recovered and it is not guaranteed to count from the subnet's first block. -- `num_canisters` (`nat`): the number of canisters currently on this subnet. This is a current value, not a counter, so it decreases when canisters are deleted. +- `num_canisters` (`nat`): the number of canisters currently on the subnet. This is a current value, not a counter, so it decreases when canisters are deleted. -- `canister_state_bytes` (`nat`): the total size in bytes of the state currently taken by canisters on this subnet. This is a current value, not a counter. +- `canister_state_bytes` (`nat`): the total size in bytes of the state currently taken by canisters on the subnet. This is a current value, not a counter. -- `consumed_cycles_total` (`nat`): the total number of cycles removed from circulation on this subnet by all current and deleted canisters. Note that this aggregate is not the same quantity as the `burned_cycles` field of [`canister_metrics`](#ic-canister_metrics), which only reports cycles a canister burned explicitly via `ic0.cycles_burn`. +- `consumed_cycles_total` (`nat`): the total number of cycles removed from circulation on the subnet by all current and deleted canisters. Note that this aggregate is not the same quantity as the `burned_cycles` field of [`canister_metrics`](#ic-canister_metrics), which only reports cycles a canister burned explicitly via `ic0.cycles_burn`. -- `update_transactions_total` (`nat`): the total number of transactions processed on this subnet. +- `update_transactions_total` (`nat`): the total number of transactions processed on the subnet. diff --git a/docs/references/management-canister.md b/docs/references/management-canister.md index f0110541..2859b6ab 100644 --- a/docs/references/management-canister.md +++ b/docs/references/management-canister.md @@ -563,13 +563,13 @@ Returns a time series of node metrics for a given subnet. Returns up to 60 times > This API is **experimental** and may change in a non-backward-compatible way. -Returns subnet-wide metrics for the subnet hosting the calling canister. Every field except `block_height` reports the same quantity that the certified state tree exposes at `/subnet//metrics`; this method makes those quantities available to canisters, which cannot read the state tree. +Returns subnet-wide metrics for a given subnet, which does not have to be the subnet hosting the caller. Every field except `certified_height` reports the same quantity that the certified state tree exposes at `/subnet//metrics`; this method makes those quantities available to canisters, which cannot read the state tree. - **Caller:** Canisters only - **Parameters:** - - `subnet_id` (`principal`): must be the subnet hosting the caller + - `subnet_id` (`principal`): any subnet - **Returns:** - - `block_height` (`nat`): height of the block whose batch contains the call + - `certified_height` (`nat`): height of the subnet's latest certified state. Lags the subnet's latest state, since certification lags execution. - `num_canisters` (`nat`): canisters currently on the subnet - `canister_state_bytes` (`nat`): current total size of canister state in bytes - `consumed_cycles_total` (`nat`): total cycles removed from circulation on the subnet diff --git a/public/references/ic.did b/public/references/ic.did index 2e7230e5..c6b131dd 100644 --- a/public/references/ic.did +++ b/public/references/ic.did @@ -441,16 +441,17 @@ type subnet_metrics_args = record { }; type subnet_metrics_result = record { - // Height of the block whose batch contains this call. - block_height : nat; - // Current number of canisters on this subnet. + // Height of the latest state of the subnet for which the subnet has + // produced a certificate. + certified_height : nat; + // Current number of canisters on the subnet. num_canisters : nat; - // Current total size in bytes of the state taken by canisters on this subnet. + // Current total size in bytes of the state taken by canisters on the subnet. canister_state_bytes : nat; - // Total cycles removed from circulation on this subnet by all current and + // Total cycles removed from circulation on the subnet by all current and // deleted canisters. consumed_cycles_total : nat; - // Total number of transactions processed on this subnet. + // Total number of transactions processed on the subnet. update_transactions_total : nat; };