Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

Commit

Permalink
[FINAL] feat: WASM heap memory limit for canisters (#278)
Browse files Browse the repository at this point in the history
* feat: WASM heap memory limit for canisters

* rename wasm heap memory limit to wasm memory limit

* update default

* update 3 GiB to 4 GiB when effectively ignoring the limit

* feedback

* Update spec/_attachments/ic.did

* define semantics of zero wasm_memory_limit

* no default limit and no enforcement for global timers and heartbeats

* changelog

* note on future changes

* conflict

* fix

* note
  • Loading branch information
mraszyk authored Jun 12, 2024
1 parent 78c12c2 commit 21d64ed
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 0 deletions.
2 changes: 2 additions & 0 deletions spec/_attachments/ic.did
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type canister_settings = record {
freezing_threshold : opt nat;
reserved_cycles_limit : opt nat;
log_visibility : opt log_visibility;
wasm_memory_limit : opt nat;
};

type definite_canister_settings = record {
Expand All @@ -22,6 +23,7 @@ type definite_canister_settings = record {
freezing_threshold : nat;
reserved_cycles_limit : nat;
log_visibility : log_visibility;
wasm_memory_limit : nat;
};

type change_origin = variant {
Expand Down
1 change: 1 addition & 0 deletions spec/_attachments/interface-spec-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* New `wasm_memory_persistence` option for canister upgrades.
* Rename `num_blocks_total` to `num_blocks_proposed_total` in node metrics served by the management canister.
* Management canister query call to fetch canister logs.
* WASM heap memory limit in canisters settings.

### 0.24.0 (2024-04-23) {#0_24_0}
* Wrap chunk hash for install chunked code in a record and rename `storage_canister` to `store_canister`.
Expand Down
44 changes: 44 additions & 0 deletions spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,19 @@ The optional `settings` parameter can be used to set the following settings:

Default value: 5_000_000_000_000 (5 trillion cycles).

- `wasm_memory_limit` (`nat`)

Must be a number between 0 and 2<sup>48</sup>-1 (i.e., 256TB), inclusively, and indicates the upper limit on the WASM heap memory consumption of the canister.

An operation (update method, canister init, canister post_upgrade) that causes the WASM heap memory consumption to exceed this limit will trap.
The WASM heap memory limit is ignored for query methods, response callback handlers, global timers, heartbeats, and canister pre_upgrade.

If set to 0, then there's no upper limit on the WASM heap memory consumption of the canister subject to the available memory on the IC.

Default value: 0 (i.e., no explicit limit).

Note: in a future release of this specification, the default value and whether the limit is enforced for global timers and heartbeats might change.

The optional `sender_canister_version` parameter can contain the caller's canister version. If provided, its value must be equal to `ic0.canister_version`.

Until code is installed, the canister is `Empty` and behaves like a canister that has no public methods.
Expand Down Expand Up @@ -2112,6 +2125,8 @@ Indicates various information about the canister. It contains:

- The canister log visibility of the canister.

- The WASM heap memory limit of the canister in bytes (the value of `0` means that there is no explicit limit).

- A SHA256 hash of the module installed on the canister. This is `null` if the canister is empty.

- The actual memory usage of the canister.
Expand Down Expand Up @@ -3101,6 +3116,7 @@ Finally, we can describe the state of the IC as a record having the following fi
balances: CanisterId ↦ Nat;
reserved_balances: CanisterId ↦ Nat;
reserved_balance_limits: CanisterId ↦ Nat;
wasm_memory_limit: CanisterId ↦ Nat;
certified_data: CanisterId ↦ Blob;
canister_history: CanisterId ↦ CanisterHistory;
canister_log_visibility: CanisterId ↦ CanisterLogVisibility;
Expand Down Expand Up @@ -3172,6 +3188,7 @@ The initial state of the IC is
balances = ();
reserved_balances = ();
reserved_balance_limits = ();
wasm_memory_limit = ();
certified_data = ();
canister_history = ();
canister_log_visibility = ();
Expand Down Expand Up @@ -3686,26 +3703,31 @@ Available = S.call_contexts[M.call_contexts].available_cycles
( M.entry_point = PublicMethod Name Caller Arg
F = Mod.update_methods[Name](Arg, Caller, Env, Available)
New_canister_version = S.canister_version[M.receiver] + 1
Wasm_memory_limit = S.wasm_memory_limit[M.receiver]
)
or
( M.entry_point = PublicMethod Name Caller Arg
F = query_as_update(Mod.query_methods[Name], Arg, Caller, Env)
New_canister_version = S.canister_version[M.receiver]
Wasm_memory_limit = 0
)
or
( M.entry_point = Callback Callback Response RefundedCycles
F = Mod.callbacks(Callback, Response, RefundedCycles, Env, Available)
New_canister_version = S.canister_version[M.receiver] + 1
Wasm_memory_limit = 0
)
or
( M.entry_point = Heartbeat
F = system_task_as_update(Mod.heartbeat, Env)
New_canister_version = S.canister_version[M.receiver] + 1
Wasm_memory_limit = 0
)
or
( M.entry_point = GlobalTimer
F = system_task_as_update(Mod.global_timer, Env)
New_canister_version = S.canister_version[M.receiver] + 1
Wasm_memory_limit = 0
)

R = F(S.canisters[M.receiver].wasm_state)
Expand Down Expand Up @@ -3747,6 +3769,7 @@ if
(S.memory_allocation[M.receiver] = 0) or (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]) ≤ S.memory_allocation[M.receiver])
(Wasm_memory_limit = 0) or |res.new_state.store.mem| <= Wasm_memory_limit
(res.response = NoResponse) or S.call_contexts[M.call_context].needs_to_respond
then
S with
Expand Down Expand Up @@ -3978,6 +4001,10 @@ if A.settings.reserved_cycles_limit is not null:
New_reserved_balance_limit = A.settings.reserved_cycles_limit
else:
New_reserved_balance_limit = 5_000_000_000_000
if A.settings.wasm_memory_limit is not null:
New_wasm_memory_limit = A.settings.wasm_memory_limit
else:
New_wasm_memory_limit = 0

Cycles_reserved = cycles_to_reserve(S, Canister_id, New_compute_allocation, New_memory_allocation, EmptyCanister.wasm_state)
New_balance = M.transferred_cycles - Cycles_reserved
Expand Down Expand Up @@ -4032,6 +4059,7 @@ S with
balances[Canister_id] = New_balance
reserved_balances[Canister_id] = New_reserved_balance
reserved_balance_limits[Canister_id] = New_reserved_balance_limit
wasm_memory_limit[Canister_id] = New_wasm_memory_limit
certified_data[Canister_id] = ""
query_stats[Canister_id] = []
canister_history[Canister_id] = New_canister_history
Expand Down Expand Up @@ -4104,6 +4132,10 @@ if A.settings.reserved_cycles_limit is not null:
New_reserved_balance_limit = A.settings.reserved_cycles_limit
else:
New_reserved_balance_limit = S.reserved_balance_limits[A.canister_id]
if A.settings.wasm_memory_limit is not null:
New_wasm_memory_limit = A.settings.wasm_memory_limit
else:
New_wasm_memory_limit = S.wasm_memory_limit[A.canister_id]

Cycles_reserved = cycles_to_reserve(S, A.canister_id, New_compute_allocation, New_memory_allocation, S.canisters[A.canister_id].wasm_state)
New_balance = S.balances[A.canister_id] - Cycles_reserved
Expand Down Expand Up @@ -4159,6 +4191,7 @@ S with
balances[A.canister_id] = New_balance
reserved_balances[A.canister_id] = New_reserved_balance
reserved_balance_limits[A.canister_id] = New_reserved_balance_limit
wasm_memory_limit[A.canister_id] = New_wasm_memory_limit
canister_version[A.canister_id] = S.canister_version[A.canister_id] + 1
if A.settings.log_visibility is not null:
canister_log_visibility[A.canister_id] = A.settings.log_visibility
Expand Down Expand Up @@ -4208,6 +4241,7 @@ S with
memory_allocation = S.memory_allocation[A.canister_id];
freezing_threshold = S.freezing_threshold[A.canister_id];
reserved_cycles_limit = S.reserved_balance_limit[A.canister_id];
wasm_memory_limit = S.wasm_memory_limit[A.canister_id];
}
module_hash =
if S.canisters[A.canister_id] = EmptyCanister
Expand Down Expand Up @@ -4455,6 +4489,8 @@ if S.memory_allocation[A.canister_id] > 0:
memory_usage_raw_module(A.wasm_module) +
memory_usage_canister_history(New_canister_history) ≤ S.memory_allocation[A.canister_id]
(S.wasm_memory_limit[A.canister_id] = 0) or |New_state.store.mem| <= S.wasm_memory_limit[A.canister_id]
S.canister_history[A.canister_id] = {
total_num_changes = N;
recent_changes = H;
Expand Down Expand Up @@ -4633,6 +4669,8 @@ if S.memory_allocation[A.canister_id] > 0:
memory_usage_raw_module(A.wasm_module) +
memory_usage_canister_history(New_canister_history) ≤ S.memory_allocation[A.canister_id]
(S.wasm_memory_limit[A.canister_id] = 0) or |New_state.store.mem| <= S.wasm_memory_limit[A.canister_id]
S.canister_history[A.canister_id] = {
total_num_changes = N;
recent_changes = H;
Expand Down Expand Up @@ -5044,6 +5082,7 @@ S with
balances[A.canister_id] = (deleted)
reserved_balances[A.canister_id] = (deleted)
reserved_balance_limits[A.canister_id] = (deleted)
wasm_memory_limit[A.canister_id] = (deleted)
certified_data[A.canister_id] = (deleted)
canister_history[A.canister_id] = (deleted)
canister_log_visibility[A.canister_id] = (deleted)
Expand Down Expand Up @@ -5201,6 +5240,10 @@ if A.settings.reserved_cycles_limit is not null:
New_reserved_balance_limit = A.settings.reserved_cycles_limit
else:
New_reserved_balance_limit = 5_000_000_000_000
if A.settings.wasm_memory_limit is not null:
New_wasm_memory_limit = A.settings.wasm_memory_limit
else:
New_wasm_memory_limit = 0

Cycles_reserved = cycles_to_reserve(S, Canister_id, New_compute_allocation, New_memory_allocation, EmptyCanister.wasm_state)
if A.amount is not null:
Expand Down Expand Up @@ -5257,6 +5300,7 @@ S with
balances[Canister_id] = New_balance
reserved_balances[Canister_id] = New_reserved_balance
reserved_balance_limits[Canister_id] = New_reserved_balance_limit
wasm_memory_limit[Canister_id] = New_wasm_memory_limit
certified_data[Canister_id] = ""
canister_history[Canister_id] = New_canister_history
canister_log_visibility[Canister_id] = New_canister_log_visibility
Expand Down

0 comments on commit 21d64ed

Please sign in to comment.