Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions docs/building-apps/canister-management/settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,9 @@ The default value of compute allocation is 0%.

By default, canisters get new memory in a "best-effort" manner. For example, when a canister grows its Wasm or stable memory, the system allocates that memory on demand. That operation can fail if the [subnet](https://learn.internetcomputer.org/hc/en-us/articles/34209955782420-Subnet-Creation) running the canister is at capacity.

Canisters can pre-allocate memory by setting the `memory_allocation` field in their canister settings. This field is expressed in bytes and denotes the total amount of memory that the canister pre-allocated. When a canister with a memory allocation configured grows its Wasm or stable memory, the new memory comes from the pre-allocated memory.
Canisters can pre-allocate memory by setting the `memory_allocation` field in their canister settings. This field is expressed in bytes and denotes the total amount of memory that the canister pre-allocated. When a canister with a memory allocation configured grows its Wasm or stable memory within its memory allocation, the new memory comes from the pre-allocated memory. When a canister with a memory allocation configured grows its Wasm or stable memory beyond its memory allocation, the system allocates that memory on demand and the operation can fail if the subnet running the canister is at capacity.

Currently, `memory_allocation` also denotes the maximum amount of memory that the canister is allowed to use in total.
For example, if a canister has a memory allocation of 8GiB, then it pre-allocates 8GiBs and cannot exceed that amount.
This constraint may be lifted in the future.

Canisters pay a rental fee for memory allocation, meaning that the payment depends on the time duration and the amount of memory allocation, regardless of whether the canister actually uses that memory or not.
Canisters pay a rental fee for memory allocation, meaning that the payment depends on the time duration and the amount of memory allocation, regardless of whether the canister actually uses that memory or not. If the actual memory usage of a canister exceeds its memory allocation, then the canister pays a fee based on its actual memory usage.
[Learn more about payment for memory](/docs/building-apps/essentials/gas-cost).

The rental fee increases the amount of cycles a canister consumes while idle or frozen.
Expand Down Expand Up @@ -211,4 +207,4 @@ The default value of the field is `controllers`.

Common errors related to canister settings include:

- [Invalid settings](/docs/references/execution-errors#invalid-settings).
- [Invalid settings](/docs/references/execution-errors#invalid-settings).
1 change: 0 additions & 1 deletion docs/building-apps/canister-management/storage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,4 @@ Common errors related to canister memory include:
- [Out of memory](/docs/references/execution-errors#out-of-memory).
- [Memory access limit exceeded](/docs/references/execution-errors#memory-access-limit-exceeded).
- [Wasm memory limit exceeded](/docs/references/execution-errors#wasm-memory-limit-exceeded).
- [Not enough memory allocation given](/docs/references/execution-errors#not-enough-memory-allocation-given).
- [Wasm chunk store error](/docs/references/execution-errors#wasm-chunk-store-error).
2 changes: 1 addition & 1 deletion docs/building-apps/developer-tools/dfx-json.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ Other fields applicable to all canister types include:

- **`log_visibility`**: Defines access control for the canister's logs. Can be `"public"`, `"controllers"`, or an object with `"allowed_viewers": ["<principal>", ...]`.

- **`memory_allocation`** (`Byte`): Max memory (in bytes) for the canister. Can be integer or string with units (i.e. `72`, "2KB", or "4 MiB").
- **`memory_allocation`** (`Byte`): Memory (in bytes) guaranteed to be available for the canister. Can be integer or string with units (i.e. `72`, "2KB", or "4 MiB").

- **`reserved_cycles_limit`** (`uint128 | null`): Upper limit of reserved cycles. Reserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 750 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 750 GiB.

Expand Down
5 changes: 2 additions & 3 deletions docs/building-apps/essentials/gas-cost.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,8 @@ The reserved cycles are not transferable, and the amount depends on how full the

A controller of a canister can disable resource reservation by setting the `reserved_cycles_limit=0` in the canister’s settings. Such opted-out canisters are not able to allocate if the subnet usage is above `750GiB`.

To prevent repeated allocations resulting in repeated increases of the canister’s reserved cycles balance, a memory allocation can be specified in the canister settings. This way, the canister will be charged as if the entire amount of allocated storage is being used, but no additional allocations will be performed.
Hence, the canister’s reserved cycles balance might only increase when setting the memory allocation, but does not increase afterwards.
The memory allocation must be set to cover the entire memory usage of the canister (Wasm memory, stable memory, snapshots, Wasm chunks, installed Wasm binary, canister history). The canister’s memory usage cannot exceed its memory allocation while a memory allocation is set.
To prevent repeated allocations resulting in repeated increases of the canister’s reserved cycles balance, a memory allocation can be specified in the canister settings. This way, the canister will be charged as if the entire amount of allocated storage is being used, but no additional allocations will be performed as long as the actual memory usage of the canister stays within the memory allocation.
Hence, the canister’s reserved cycles balance might only increase when setting the memory allocation, but does not increase afterwards as long as the actual memory usage of the canister stays within the memory allocation.

### Special features

Expand Down
2 changes: 1 addition & 1 deletion docs/building-apps/security/dos.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ In a shared resource environment like the Internet Computer, multiple canisters
### Recommendation

To mitigate the "noisy neighbor" issue, manage your canister's resource allocation effectively:
* **Memory allocation**: Memory can be reserved per canister by setting `memory_allocation`, ensuring that your canister can always allocate memory up to the requested `memory_allocation` and preventing other canisters from using up the subnet's available memory. Note that this also reduces the upper bound of memory the canister can allocate to the same value. Monitoring actual memory usage against this value is important to avoid availability issues.
* **Memory allocation**: Memory can be reserved per canister by setting `memory_allocation`, ensuring that your canister can always allocate memory up to the requested `memory_allocation` and preventing other canisters from using up the subnet's available memory. Note that memory availability is not guaranteed beyond the memory allocation and thus monitoring actual memory usage against this value is important to avoid availability issues.
* **Compute reservation**: Similar to memory, computing power can also be reserved by setting `compute_allocation` to a value between 0 and 100, which denotes the percentage of one CPU core to be reserved for this canister. A value of 50 means that every 2 rounds, the canister will be scheduled to execute a message. This guarantees the minimal progress your canister can make, which protects against noisy neighbors. Both allocations are reserving resources for your canister on the subnet, which prevents the other canisters from using them. Hence, they come at a cost. Memory allocation is charged as if all that memory would be allocated. Compute allocation is currently charged at 10M cycles per percentage point.
Learn more about managing memory and compute resources in the [storage](/docs/building-apps/essentials/gas-cost#storage) and [compute](/docs/building-apps/essentials/gas-cost#execution-and-compute-allocation) guides.
* **Subnet and canister distribution**: Implement a smart canister deployment strategy by monitoring the load on subnets. You can choose to deploy new canisters on less busy subnets or adopt a multi-canister architecture that balances the load across subnets. Be mindful to minimize inter-subnet communication for canisters that frequently interact with each other. Additionally, avoid deploying to known high-traffic subnets where possible, though keep in mind that resource usage can change unexpectedly with new dapps.
Expand Down
21 changes: 2 additions & 19 deletions docs/references/execution-errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ To fix this error, consider installing code using [`dfx deploy`](/docs/building-


### Out of memory
The canister was tried to request more memory than available during execution, causing the execution to fail.
The canister tried to request more memory than the system can provide during execution, causing the execution to fail.

An example of this error is:
```
Expand All @@ -91,14 +91,10 @@ An example of this error is:

There are [system-wide limits][limits] on the main and stable memory of each canister, as well as limits on the total memory of a subnet. This error could be triggered by any one of those limits being reached.

In addition, canisters may reserve memory using the [`memory-allocation` setting][memory-allocation-setting]. In this case the canister is guaranteed to be able to use up to the allocated memory but will receive an "Out of memory" error when trying to use more than the allocated amount.

To diagnose this error, use the [`dfx canister status`][dfx-canister-status] command or the [`canister_status` API][canister-status-api] to check the canister's current memory usage and memory allocation. The subnet memory usage can also be seen on the [ICP dashboard][dashboard-subnets].
To diagnose this error, use the [`dfx canister status`][dfx-canister-status] command or the [`canister_status` API][canister-status-api] to check the canister's current memory usage. The subnet memory usage can also be seen on the [ICP dashboard][dashboard-subnets].

To fix this error, consider:

- If the canister has reached its current memory allocation, try allocating more memory.

- If the canister has reached the system-wide limits for memory usage and it seems reasonable for the canister to have used 100s of GiBs, try sharding the data across multiple canisters.

- If the canister has unexpectedly reached the system-wide limits, try debugging it to see if there could be a memory leak. [canbench][canbench] can help with profiling memory usage.
Expand Down Expand Up @@ -500,19 +496,6 @@ An example of this error is:
A canister cannot be deleted while it has pending input or output messages because those messages would be lost. To fix this error, wait until the pending messages have been pulled from the queues. To ensure no new messages appear during that time, consider [stopping the canister][dfx-canister-stop].


### Not enough memory allocation given
The canister does not have enough memory allocation to complete a given request, or there was an attempt to lower the canister's memory allocation below the amount it already uses.

An example of this error is:
```
  Canister was given 20000000000 memory allocation but at least 30000000000 of memory is needed.
```

A canister can set a [memory allocation][memory-allocation-setting] that guarantees it will be able to use a given amount of memory but also limits the canister memory to the allocated amount.

To fix this error, consider increasing the canister's memory allocation, reducing its memory usage, or switching to best-effort memory allocation.


### Create canister not enough cycles
The request to create a canister did not include enough cycles to pay the creation fee.

Expand Down
81 changes: 7 additions & 74 deletions docs/references/ic-interface-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -2365,10 +2365,9 @@ The optional `settings` parameter can be used to set the following settings:

- `memory_allocation` (`nat`)

Must be a number between 0 and 2<sup>48</sup> (i.e 256TB), inclusively.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check bound

It indicates the maximum amount of memory that the canister is allowed to use in total (i.e., any attempt to grow memory usage beyond the memory allocation will fail) and also guarantees availability of this amount of memory.
Must be a number between 0 and 2<sup>64</sup>-1, inclusively.
It indicates an amount of memory that the canister is guaranteed to be allowed to use in total.
If the IC cannot guarantee the requested memory allocation, for example because it is oversubscribed, then the call will be rejected.
If set to 0, then memory growth of the canister will have no explicit limit but will only be best-effort and subject to the available memory on the IC.

Default value: 0

Expand Down Expand Up @@ -4619,18 +4618,7 @@ This transition is executed immediately after [Message execution](#rule-message-
Conditions

```html
Total_memory_usage = memory_usage_wasm_state(S.canisters[C].wasm_state) +
memory_usage_raw_module(S.canisters[C].raw_module) +
memory_usage_canister_history(S.canister_history[C]) +
memory_usage_chunk_store(S.chunk_store[C]) +
memory_usage_snapshots(S.snapshots[C])

if S.memory_allocation[C] = 0:
Wasm_memory_capacity = S.wasm_memory_limit[C]
else:
Wasm_memory_capacity = min(S.memory_allocation[C] - (Total_memory_usage - |S.canisters[C].wasm_state.wasm_memory|), S.wasm_memory_limit[C])

if Wasm_memory_capacity < |S.canisters[C].wasm_state.wasm_memory| + S.wasm_memory_threshold[C]:
if S.wasm_memory_limit[C] < |S.canisters[C].wasm_state.wasm_memory| + S.wasm_memory_threshold[C]:
if S.on_low_wasm_memory_hook_status[C] = ConditionNotSatisfied:
On_low_wasm_memory_hook_status = Ready
else:
Expand Down Expand Up @@ -4772,12 +4760,6 @@ if
New_reserved_balance,
Min_balance
) ≥ 0
Total_memory_usage = memory_usage_wasm_state(res.new_state) +
memory_usage_raw_module(S.canisters[M.receiver].raw_module) +
memory_usage_canister_history(S.canister_history[M.receiver]) +
memory_usage_chunk_store(S.chunk_store[M.receiver]) +
memory_usage_snapshots(S.snapshots[M.receiver])
(S.memory_allocation[M.receiver] = 0) or (Total_memory_usage ≤ S.memory_allocation[M.receiver])
(Wasm_memory_limit = 0) or |res.new_state.wasm_memory| <= Wasm_memory_limit
(res.response = NoResponse) or Ctxt.needs_to_respond
then
Expand Down Expand Up @@ -5074,9 +5056,6 @@ if A.settings.controllers is not null:
else:
New_controllers = [M.caller]

if New_memory_allocation > 0:
memory_usage_canister_history(New_canister_history) ≤ New_memory_allocation

if A.settings.compute_allocation is not null:
New_compute_allocation = A.settings.compute_allocation
else:
Expand Down Expand Up @@ -5215,15 +5194,6 @@ M.caller ∈ S.controllers[A.canister_id]
is_valid_utf8(name) and
is_valid_utf8(value)))

Total_memory_usage = memory_usage_wasm_state(S.canisters[A.canister_id].wasm_state) +
memory_usage_raw_module(S.canisters[A.canister_id].raw_module) +
memory_usage_canister_history(New_canister_history) +
memory_usage_chunk_store(S.chunk_store[A.canister_id]) +
memory_usage_snapshots(S.snapshots[A.canister_id])

if New_memory_allocation > 0:
Total_memory_usage ≤ New_memory_allocation

if New_wasm_memory_limit > 0:
|S.canisters[A.canister_id].wasm_state.wasm_memory| ≤ New_wasm_memory_limit

Expand Down Expand Up @@ -5580,15 +5550,6 @@ liquid_balance(S, A.canister_id) ≥ MAX_CYCLES_PER_MESSAGE

liquid_balance(S', A.canister_id) ≥ 0

Total_memory_usage = memory_usage_wasm_state(New_state) +
memory_usage_raw_module(A.wasm_module) +
memory_usage_canister_history(New_canister_history) +
memory_usage_chunk_store(S.chunk_store[A.canister_id]) +
memory_usage_snapshots(S.snapshots[A.canister_id])

if S.memory_allocation[A.canister_id] > 0:
Total_memory_usage ≤ S.memory_allocation[A.canister_id]

(S.wasm_memory_limit[A.canister_id] = 0) or |New_state.wasm_memory| <= S.wasm_memory_limit[A.canister_id]

S.canister_history[A.canister_id] = {
Expand Down Expand Up @@ -5753,15 +5714,6 @@ liquid_balance(S, A.canister_id) ≥ MAX_CYCLES_PER_MESSAGE

liquid_balance(S', A.canister_id) ≥ 0

Total_memory_usage = memory_usage_wasm_state(New_state) +
memory_usage_raw_module(A.wasm_module) +
memory_usage_canister_history(New_canister_history) +
memory_usage_chunk_store(S.chunk_store[A.canister_id]) +
memory_usage_snapshots(S.snapshots[A.canister_id])

if S.memory_allocation[A.canister_id] > 0:
Total_memory_usage ≤ S.memory_allocation[A.canister_id]

(S.wasm_memory_limit[A.canister_id] = 0) or |New_state.wasm_memory| <= S.wasm_memory_limit[A.canister_id]

S.canister_history[A.canister_id] = {
Expand Down Expand Up @@ -6353,9 +6305,6 @@ if A.settings.controllers is not null:
else:
New_controllers = [M.caller]

if New_memory_allocation > 0:
memory_usage_canister_history(New_canister_history) ≤ New_memory_allocation

if A.settings.compute_allocation is not null:
New_compute_allocation = A.settings.compute_allocation
else:
Expand Down Expand Up @@ -6565,17 +6514,6 @@ M.method_name = 'load_canister_snapshot'
M.arg = candid(A)
M.caller ∈ S.controllers[A.canister_id]

Total_memory_usage = memory_usage_wasm_state(Snapshot.wasm_state) +
memory_usage_raw_module(Snapshot.raw_module) +
memory_usage_canister_history(New_canister_history) +
memory_usage_chunk_store(S.chunk_store[A.canister_id]) +
memory_usage_snapshots(S.snapshots[A.canister_id])

if S.memory_allocation[A.canister_id] = 0:
Wasm_memory_capacity = S.wasm_memory_limit[A.canister_id]
else:
Wasm_memory_capacity = min(S.memory_allocation[A.canister_id] - (Total_memory_usage - |Snapshot.wasm_state.wasm_memory|), S.wasm_memory_limit[A.canister_id])

A.snapshot_id ∈ dom(S.snapshots[A.canister_id])
Snapshot = S.snapshots[A.canister_id][A.snapshot_id]

Expand All @@ -6599,7 +6537,7 @@ if Snapshot.source = MetadataUpload:
HookConditionInSnapshotField = false
else:
HookConditionInSnapshotField = true
if Wasm_memory_capacity < |Snapshot.wasm_state.wasm_memory| + S.wasm_memory_threshold[A.canister_id]:
if S.wasm_memory_limit[A.canister_id] < |Snapshot.wasm_state.wasm_memory| + S.wasm_memory_threshold[A.canister_id]:
HookConditionInSnapshotState = true
else:
HookConditionInSnapshotState = false
Expand Down Expand Up @@ -6651,13 +6589,6 @@ New_canister_history = {

liquid_balance(S', A.canister_id) ≥ 0

if S.memory_allocation[A.canister_id] > 0:
memory_usage_wasm_state(New_state.wasm_state) +
memory_usage_raw_module(New_state.raw_module) +
memory_usage_canister_history(New_canister_history) +
memory_usage_chunk_store(S.chunk_store[A.canister_id]) +
memory_usage_snapshots(S.snapshots[A.canister_id]) ≤ S.memory_allocation[A.canister_id]

```

State after
Expand Down Expand Up @@ -7142,7 +7073,7 @@ S with

#### Canister out of cycles

Once a canister runs out of cycles, its code is uninstalled (cf. [IC Management Canister: Code uninstallation](#rule-uninstall)), the canister changes in the canister history are dropped (their total number is preserved), and the allocations are set to zero (NB: allocations are currently not modeled in the formal model):
Once a canister runs out of cycles, its code is uninstalled (cf. [IC Management Canister: Code uninstallation](#rule-uninstall)), the canister changes in the canister history are dropped (their total number is preserved), and the allocations are set to zero:

Conditions

Expand Down Expand Up @@ -7172,6 +7103,8 @@ S with
canister_logs[CanisterId] = []
canister_version[CanisterId] = S.canister_version[CanisterId] + 1
global_timer[CanisterId] = 0
compute_allocation[Canister_id] = 0
memory_allocation[Canister_id] = 0

messages = S.messages ·
[ ResponseMessage {
Expand Down