-
Notifications
You must be signed in to change notification settings - Fork 436
Motoko low_wasm_memory example #1139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
98c4abc
Motoko low_wasm_memory_hook that matches rust example
Kamirus 2de1693
ci
Kamirus 51da581
fix CI both rust and motoko
Kamirus 473c969
experiment
Kamirus dc455fc
set -euo pipefail
Kamirus baba288
try shell:bash
Kamirus d2a60e1
Revert "try shell:bash"
Kamirus e6a275e
Revert "experiment"
Kamirus 7bb897f
chunk with 20k elements to grow memory faster
Kamirus 946ea7d
readme draft
Kamirus 9e03588
readme update
Kamirus 2da935d
add links between rust and motoko examples
Kamirus 23447d3
matrix strategy
Kamirus 108acdc
Apply suggestions from code review
Kamirus de822e8
update
Kamirus 9c9d284
review + adjust rust readme to match motokos
Kamirus 14203e9
Merge branch 'master' into LANG-362-example-for-low-wasm-memory-hook
Kamirus 96aa64f
Apply suggestions from code review
Kamirus ec0f571
Apply suggestions from code review
Kamirus 5f17307
notes on starting memory usage + low memory hook from Motoko docs link
Kamirus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name: motoko-low_wasm_memory | ||
on: | ||
push: | ||
branches: | ||
- master | ||
pull_request: | ||
paths: | ||
- motoko/low_wasm_memory/** | ||
- .github/workflows/provision-darwin.sh | ||
- .github/workflows/provision-linux.sh | ||
- .github/workflows/motoko-low_wasm_memory-example.yml | ||
- .github/workflows/motoko-low_wasm_memory-skip.yml | ||
- .ic-commit | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }} | ||
cancel-in-progress: true | ||
jobs: | ||
motoko-low_wasm_memory: | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
include: | ||
- os: macos-15 | ||
provision: .github/workflows/provision-darwin.sh | ||
- os: ubuntu-22.04 | ||
provision: .github/workflows/provision-linux.sh | ||
steps: | ||
- uses: actions/checkout@v1 | ||
- name: Provision | ||
run: bash ${{ matrix.provision }} | ||
- name: Motoko low_wasm_memory | ||
run: | | ||
set -euo pipefail | ||
pushd motoko/low_wasm_memory | ||
dfx start --background | ||
dfx deploy low_wasm_memory_hook | ||
dfx canister update-settings low_wasm_memory_hook --wasm-memory-limit 5000000 --wasm-memory-threshold 2000000 | ||
dfx canister status low_wasm_memory_hook | ||
max_wait=50 | ||
waited=0 | ||
until [[ $(dfx canister call low_wasm_memory_hook --query getExecutedFunctionsOrder) == *onLowWasmMemory* ]]; do | ||
sleep 1 | ||
dfx canister status low_wasm_memory_hook | grep 'Memory Size' | ||
waited=$((waited+1)) | ||
if [ $waited -ge $max_wait ]; then | ||
echo "Timed out waiting for onLowWasmMemory event" | ||
exit 1 | ||
fi | ||
done | ||
popd |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
# Low Wasm memory hook | ||
|
||
The Internet Computer can automatically execute a special type of function called a low Wasm memory hook, which runs when the canister's available Wasm memory falls below the `wasm_memory_threshold`. | ||
|
||
This Motoko example demonstrates using the low Wasm memory hook on ICP. If you're interested in how this example is implemented in Rust, check out the [Rust variation](../../rust/low_wasm_memory). | ||
|
||
The example consists of a canister named `low_wasm_memory_hook` that increases usage of Wasm memory in every heartbeat execution until the low Wasm memory hook is run. | ||
|
||
## Prerequisites | ||
This example requires an installation of: | ||
|
||
- [x] Install the [IC SDK](https://internetcomputer.org/docs/current/developer-docs/getting-started/install). | ||
- [x] Clone the example dapp project: `git clone https://github.com/dfinity/examples` | ||
|
||
## Using the example | ||
|
||
In this example, the canister will periodically increase its memory usage (as defined in the `heartbeat` function) until the low Wasm memory hook is run | ||
when the memory usage exceeds the `wasm_memory_threshold`. | ||
|
||
- #### Step 1: Setup project environment | ||
|
||
Navigate into the folder containing the project's files and start a local developer environment with the commands: | ||
|
||
```sh | ||
cd examples/motoko/low_wasm_memory | ||
dfx start --clean | ||
``` | ||
|
||
This terminal will stay blocked, printing log messages, until the `Ctrl+C` is pressed or the `dfx stop` command is run. | ||
|
||
- #### Step 2: Open another terminal window in the same directory: | ||
|
||
```sh | ||
cd examples/motoko/low_wasm_memory | ||
``` | ||
|
||
- #### Step 3: Create the `low_wasm_memory_hook` canister | ||
|
||
```sh | ||
dfx canister create low_wasm_memory_hook | ||
``` | ||
|
||
Example output: | ||
|
||
```sh | ||
Created a wallet canister on the "local" network for user "default" with ID "uqqxf-5h777-77774-qaaaa-cai" | ||
low_wasm_memory_hook canister created with canister id: uxrrr-q7777-77774-qaaaq-cai | ||
``` | ||
|
||
- #### Step 4: Update canister settings | ||
|
||
Update the canister's settings to modify the Wasm memory limit and threshold: | ||
|
||
```sh | ||
dfx canister update-settings low_wasm_memory_hook --wasm-memory-limit 5000000 --wasm-memory-threshold 2000000 | ||
``` | ||
|
||
- #### Step 5: Compile and deploy the `low_wasm_memory_hook` canister: | ||
|
||
```sh | ||
dfx deploy low_wasm_memory_hook | ||
``` | ||
|
||
Example output: | ||
|
||
```sh | ||
Deploying: low_wasm_memory_hook | ||
All canisters have already been created. | ||
Building canister 'low_wasm_memory_hook'. | ||
Installed code for canister low_wasm_memory_hook, with canister ID uxrrr-q7777-77774-qaaaq-cai | ||
Deployed canisters. | ||
URLs: | ||
Backend canister via Candid interface: | ||
low_wasm_memory_hook: http://127.0.0.1:4943/?canisterId=u6s2n-gx777-77774-qaaba-cai&id=uxrrr-q7777-77774-qaaaq-cai | ||
``` | ||
|
||
After the deployment, the memory usage periodically increases as defined in the `heartbeat` function. | ||
|
||
The `dfx canister update-settings` command sets the 'wasm_memory_limit' to 5MiB and 'wasm_memory_threshold' to 2MiB. | ||
Hence, whenever the Wasm memory used by the canister is above 3MiB (in other words, the remaining Wasm memory is less than 'wasm_memory_threshold'), the low Wasm memory hook will be triggered. | ||
|
||
This Motoko canister starts with ~2.3MiB of memory usage after the deployment, so it will trigger the low Wasm memory hook after allocating ~0.7MiB of memory. | ||
|
||
You can verify that the canister settings got updated and check the current 'Memory Size' with the following command: | ||
|
||
```sh | ||
dfx canister status low_wasm_memory_hook | ||
``` | ||
|
||
Example output: | ||
|
||
```sh | ||
Canister status call result for low_wasm_memory_hook. | ||
Status: Running | ||
Controllers: 3apfx-fn75o-xtwmf-svjzg-hu5xt-bg2dr-ubczq-uhvlq-2a5gf-ya4fn-dqe uqqxf-5h777-77774-qaaaa-cai | ||
Memory allocation: 0 Bytes | ||
Compute allocation: 0 % | ||
Freezing threshold: 2_592_000 Seconds | ||
Idle cycles burned per day: 24_269_759 Cycles | ||
Memory Size: 2_374_914 Bytes | ||
Balance: 2_996_231_412_893 Cycles | ||
Reserved: 0 Cycles | ||
Reserved cycles limit: 5_000_000_000_000 Cycles | ||
Wasm memory limit: 5_000_000 Bytes | ||
Wasm memory threshold: 2_000_000 Bytes | ||
Module hash: 0x0c100162e1be161b9ef6fd95efb07f0541903c77a47e85a49a800705d6a09a11 | ||
Number of queries: 0 | ||
Instructions spent in queries: 0 | ||
Total query request payload size: 0 Bytes | ||
Total query response payload size: 0 Bytes | ||
Log visibility: controllers | ||
``` | ||
|
||
- #### Step 6: After a few seconds, observe the output of the `getExecutedFunctionsOrder` query: | ||
|
||
Query the canister by calling `getExecutedFunctionsOrder` to get the order of executed functions. | ||
|
||
```sh | ||
dfx canister call low_wasm_memory_hook --query getExecutedFunctionsOrder | ||
``` | ||
|
||
Repeat the call until the last executed method is `onLowWasmMemory`. | ||
|
||
Example output: | ||
|
||
```sh | ||
( | ||
vec { variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { heartbeat }; variant { onLowWasmMemory };}, | ||
) | ||
``` | ||
|
||
To repeat the example, you can redeploy the canister and reset its state with the following command: | ||
|
||
```sh | ||
dfx deploy low_wasm_memory_hook --mode reinstall | ||
``` | ||
|
||
## Further learning | ||
1. [Low memory hook](https://internetcomputer.org/docs/motoko/main/canister-maintenance/memory) in Motoko documentation | ||
2. Have a look at the locally running dashboard. The URL is at the end of the `dfx start` command: `Dashboard: http://localhost:...` | ||
3. Check out `low_wasm_memory_hook` canister's Candid user interface. The URLs are at the end of the `dfx deploy` command: `low_wasm_memory_hook: http://127.0.0.1:...` | ||
|
||
### Canister interface | ||
|
||
The `low_wasm_memory_hook` canister provides the following interface: | ||
* `getExecutedFunctionsOrder` ; returns the vector with values of `FnType` (a variant with `#heartbeat` or `#onLowWasmMemory`) representing the order of functions executed. | ||
|
||
Example usage: | ||
```sh | ||
dfx canister call low_wasm_memory_hook --query getExecutedFunctionsOrder | ||
``` | ||
|
||
## Resources | ||
For more information take a look at [low Wasm memory hook specification](https://internetcomputer.org/docs/references/ic-interface-spec#on-low-wasm-memory). | ||
|
||
## Security considerations and best practices | ||
If you base your application on this example, we recommend you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/current/references/security/) for developing on the Internet Computer. This example may not implement all the best practices. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"canisters": { | ||
"low_wasm_memory_hook": { | ||
"main": "src/low_wasm_memory_hook/main.mo", | ||
"type": "motoko" | ||
} | ||
}, | ||
"defaults": { | ||
"build": { | ||
"args": "", | ||
"packtool": "" | ||
} | ||
}, | ||
"output_env_file": ".env", | ||
"version": 1 | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import Array "mo:base/Array"; | ||
import Deque "mo:base/Deque"; | ||
import Buffer "mo:base/Buffer"; | ||
|
||
actor { | ||
// Types | ||
type FnType = { | ||
#heartbeat; | ||
#onLowWasmMemory; | ||
}; | ||
|
||
// State | ||
var fnOrderBuffer = Buffer.Buffer<FnType>(30); | ||
var bytes : Deque.Deque<[Nat]> = Deque.empty(); | ||
var hookExecuted : Bool = false; | ||
|
||
// Query function to get execution order | ||
public query func getExecutedFunctionsOrder() : async [FnType] { | ||
Buffer.toArray(fnOrderBuffer); | ||
}; | ||
|
||
// Heartbeat function that increases memory usage | ||
system func heartbeat() : async () { | ||
if (not hookExecuted) { | ||
fnOrderBuffer.add(#heartbeat); | ||
// Allocate more memory by creating a large array | ||
let chunk = Array.tabulate<Nat>(10_000, func _ = 0); | ||
bytes := Deque.pushBack(bytes, chunk); | ||
}; | ||
}; | ||
|
||
// Low WASM memory hook | ||
system func lowmemory() : async* () { | ||
hookExecuted := true; | ||
fnOrderBuffer.add(#onLowWasmMemory); | ||
}; | ||
}; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.