Skip to content
Open
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
2 changes: 1 addition & 1 deletion .sources/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@
# appear in docs before they ship to users.
# -------------------------------------------------------

motoko v1.16.1 01aee06
motoko v2.0.0 1bd3011
internetidentity release-2026-09-25 64ba1637
2 changes: 1 addition & 1 deletion .sources/motoko
Submodule motoko updated 1692 files
1 change: 0 additions & 1 deletion docs/languages/motoko/base-core-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ The following modules have been **removed** in the core package:

- `AssocList` - Use `Map` or `pure/Map` instead
- `Buffer` - Use `List` or `VarArray` instead
- `ExperimentalStableMemory` - Deprecated
- `Hash` - Vulnerable to hash collision attacks
- `HashMap` - Use `Map` or `pure/Map`
- `Heap` - Use `Map` or `Set` instead
Expand Down
4 changes: 2 additions & 2 deletions docs/languages/motoko/fundamentals/actors/actors-async.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ Customers place orders at a pizza restaurant, but the chef can only make one piz
import Array "mo:core/Array";
import Text "mo:core/Text";

persistent actor PizzaParlor {
actor PizzaParlor {
var orders : [Text] = [];

public shared func placeOrder(order : Text) : async Text {
Expand Down Expand Up @@ -289,7 +289,7 @@ Use `async*` and `await*` carefully. In Motoko, a regular `await` is a commit po
### Example

```motoko no-repl
persistent actor class (Logger : actor { log : Text -> async () }) {
actor class (Logger : actor { log : Text -> async () }) {

var logging = true;

Expand Down
43 changes: 19 additions & 24 deletions docs/languages/motoko/fundamentals/actors/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ When upgrading a canister, it is important to verify that the upgrade can procee
- Breaking clients due to a Candid interface change.

`dfx` checks these properties statically before attempting the upgrade.
Moreover, with [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), Motoko rejects incompatible changes of stable declarations.
Moreover, thanks to [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), Motoko rejects incompatible changes of stable declarations.

## Upgrade example

Expand All @@ -21,8 +21,8 @@ The following is a simple example of how to declare a stateful counter:
```

Importantly, in this example, when the counter is upgraded, its state is preserved and the counter will resume from its last value before the upgrade.
This is because actor variables are by default `stable`, meaning their state is persisted across upgrades.
The above actor is equivalent to using an explicit `stable` declaration:
This is because actor variables are persisted across upgrades, by default.
The above actor declaration shows the field without an explicit qualifier, which is the same as writing nothing at all: persistence is the default.

```motoko no-repl file=<motokoExamples>/count-v1stable.mo
```
Expand Down Expand Up @@ -120,7 +120,7 @@ This version is neither compatible to stable type declarations, nor to the Candi
- The change in the return type of `read` is also not safe.
If the change were accepted, then existing clients of the `read` method, that still expect to receive integers, would suddenly start receiving incompatible floats.

With [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), Motoko actively rejects any upgrades that require type-incompatible state changes.
Thanks to [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), Motoko actively rejects any upgrades that require type-incompatible state changes.

This is to guarantee that the stable state is always kept safe.

Expand All @@ -133,9 +133,7 @@ In addition to Motoko's runtime check, `dfx` raises a warning message for these
Motoko tolerates Candid interface changes, since these are more likely to be intentional, breaking changes.

:::danger
Versions of Motoko using [classical orthogonal persistence](./orthogonal-persistence/classical.md) will drop the state and reinitialize the counter with `0.0`, if the `dfx` warning is ignored.

For this reason, users should always heed any compatibility warnings issued by `dfx`.
Users should always heed any compatibility warnings issued by `dfx`.
:::


Expand Down Expand Up @@ -179,16 +177,18 @@ The code for the migration function is self-contained and can be placed in its o

The migration function takes a record of stable fields as input and produces a record of stable fields as output.

The input fields extend or override the types of any stable fields in the actor's
stable signature.
The output fields must be declared in the actor's stable signature, and have types that can be consumed by the corresponding declaration in the stable signature.

* All values for the input fields must
be present and of compatible type in the old actor, otherwise the
upgrade traps and rolls back.
* The fields output by the migration
function determine the values of the corresponding stable variables in the
new actor.
* Each input field names a stable variable of the old actor. Its value must be
present and of compatible type in the old actor, otherwise the upgrade traps and
rolls back. The input type is checked against the old actor only: the new actor
may declare the variable with a different type, or not at all.
* An input field is consumed: its old value is not transferred to the new actor.
* Each output field must be declared in the new actor, with a type that can be
consumed by that declaration. The output value becomes the variable's value in
the new actor.
* A consumed field that the new actor declares but the migration function does
not produce is initialized by running its initialization expression, like a
newly declared field (warning M0206). A consumed field that is neither produced
nor declared is dropped (warning M0207).
* All other stable variables of the actor, i.e. those neither consumed nor
produced by the migration function are initialized in the usual way,
either by transfer from the upgraded actor, if declared in that actor, or, if newly declared,
Expand Down Expand Up @@ -367,11 +367,7 @@ cannot be consumed at new type
var Float
```

With [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), compatibility errors of stable variables are always detected in the runtime system and if failing, the upgrade is safely rolled back.

:::danger
With [classical orthogonal persistence](./orthogonal-persistence/classical.md), however, an upgrade attempt from `v2.wasm` to `v3.wasm` is unpredictable and may lead to partial or complete data loss if the `dfx` warning is ignored.
:::
Thanks to [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), compatibility errors of stable variables are always detected in the runtime system and if failing, the upgrade is safely rolled back.

## Adding record fields

Expand Down Expand Up @@ -402,8 +398,7 @@ cannot be consumed at new type

Do you want to proceed? yes/No
```
It is recommended not to continue, as you will lose the state in older versions of Motoko that use [classical orthogonal persistence](./orthogonal-persistence/classical.md).
Upgrading with [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md) will trap and roll back, keeping the old state.
It is recommended not to continue: the upgrade will trap and roll back, keeping the old state.

Adding a new record field to the type of existing stable variable is not supported. The reason is simple: the upgrade would need to supply values for the new field out of thin air. In this example, the upgrade would need to conjure up some value for the `description` field of every existing `card` in `map`. Moreover, allowing adding optional fields is also a problem, as a record can be shared from various variables with different static types, some of them already declaring the added field or adding a same-named optional field with a potentially different type (and/or different semantics).

Expand Down
53 changes: 21 additions & 32 deletions docs/languages/motoko/fundamentals/actors/data-persistence.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,33 @@ sidebar:

One key feature of Motoko is its ability to automatically persist the program's state without explicit user instruction. This is called **orthogonal persistence**. Data persists across transactions and canister upgrades.

Motoko data persistence is not simple, but it prevents data corruption or loss while being efficient at the same time. No database, stable memory API, or stable data structure is required to retain state across upgrades. Instead, a simple `stable` keyword is sufficient to declare a data structure of arbitrary shape persistent, even if the structure uses sharing, has a deep complexity, or contains cycles transfers.
Motoko data persistence is not simple, but it prevents data corruption or loss while being efficient at the same time. No database, stable memory API, or stable data structure is required to retain state across upgrades. Because actors are persistent by default, every actor field is automatically carried across upgrades: even if the data structure uses sharing, has a deep complexity, or contains cycle transfers, its value survives with no declaration needed.

In comparison to other supported languages for building canisters, such as Rust, data persistence must be achieved through explicit use of stable data structures and stable memory, as other languages are not designed for orthogonal persistence and instead rearranges memory structures in an uncontrolled manner on re-compilation or at runtime.

## Declaring stable variables
## State persistence

Within an actor, you can configure which part of the program is considered to be persistent (retained across upgrades) and which part is ephemeral (reset on upgrades).
Every actor persists its state across canister upgrades. You don't need to declare anything: a `let` or `var` actor field is kept, with its value, for the lifetime of the canister.

More precisely, each `let` and `var` variable declaration in an actor can specify whether the variable is `stable` or `transient`. If you don’t provide a modifier, the variable is assumed to be `transient` by default.
```motoko file=<motokoExamples>/StableCounter.mo
```

* `stable` means that all values directly or indirectly reachable from that stable variable are considered persistent and are automatically retained across upgrades. This is the primary choice for most of the program's state.
When you compile and deploy a canister for the first time, all actor fields are initialized in sequence. When a canister is upgraded, fields that existed in the previous version are pre-initialized with their old values, and any newly-added fields are initialized in sequence.

* `transient` means that the variable is re-initialized on upgrade such that the values referenced by the transient variable are discarded, unless the values are transitively reachable by other variables that are stable. `transient` is only used for temporary state or references to high-order types, such as local function references.
## Opting a field out of persistence

:::note
Most of the time, persistence is exactly what you want: it is why the actor's counter, registry, or wallet balance survives an upgrade. Occasionally you want a field to restart fresh on every upgrade: a cache, a one-time initialization, or a value whose type cannot be persisted (for example, an object with methods). Fields like these are the exception, not the rule, so declaring them is explicit:

You can only use the `stable`, `transient` (or legacy `flexible`) modifier on `let` and `var` declarations that are **actor fields**. You cannot use these modifiers anywhere else in your program.
* `stable` fields are persisted across upgrades. This is the default; you generally don't need to write it.
* `transient` fields are not. They are re-initialized on upgrade, unless they are transitively reachable from a persisted field. Use `transient` only for temporary state or references to higher-order types, such as local function references.

:::

The following is a simple example of how to declare a stable counter that can be upgraded while preserving the counter’s value:

```motoko file=<motokoExamples>/StableCounter.mo
```
Only `transient` fields need an explicit `transient` declaration. The `stable` and `transient` modifiers are only allowed on `let` and `var` declarations that are **actor fields**; you cannot use them anywhere else in your program.

When you compile and deploy a canister for the first time, all transient and stable variables in the actor are initialized in sequence. When a canister is upgraded, all stable variables that existed in the previous version of the actor are pre-initialized with their old values and the remaining transient and any newly-added stable variables are initialized in sequence.
:::note

Starting with Motoko v0.13.5, if you prefix the `actor` keyword with the keyword `persistent`, then all `let` and `var` declarations of the actor or actor class are implicitly declared `stable`. Only `transient` variables will need an explicit `transient` declaration.
The `persistent` keyword is redundant for actors, since actors are `persistent` by default. If you see it in code, it can be removed. Older code written against the pre-v2 default used `persistent` (and `stable`) to opt into persistence: now that persistence is the default, these keywords are optional.

Using a `persistent` actor can help avoid unintended data loss. It is the recommended declaration syntax for actors and actor classes. The non-`persistent` declaration is provided for backwards compatibility.
:::

```motoko file=<motokoExamples>/PersistentCounter.mo
```
Expand All @@ -57,14 +54,10 @@ In general, classes are not stable because they can contain local functions. How

For variables that do not have a stable type, there are two options for making them stable:

1. Use a `stable` module for the type, such as:

- [StableBuffer](https://github.com/canscale/StableBuffer)
- [StableHashMap](https://github.com/canscale/StableHashMap)
- [StableRBTree](https://github.com/canscale/StableRBTree)
1. Use a stable data structure. The data structures in [`core`](https://mops.one/core), such as `Map`, `Set`, `List` and `Queue` and their `pure/` counterparts, all have stable types.

:::note
Unlike stable data structures in the Rust CDK, these modules do not use stable memory but instead rely on orthogonal persistence. The adjective "stable" only denotes a stable type in Motoko.
Unlike stable data structures in the Rust CDK, these data structures do not use stable memory but instead rely on orthogonal persistence. The adjective "stable" only denotes a stable type in Motoko.
:::

2. Extract the state in a stable type and wrap it in the non-stable type.
Expand All @@ -74,7 +67,7 @@ For example, the stable type `TemperatureSeries` covers the persistent data, whi
```motoko no-repl file=<motokoExamples>/WeatherActor.mo
```

__Discouraged and not recommended__: [Pre- and post-upgrade hooks](#preupgrade-and-postupgrade-system-methods) allow copying non-stable types to stable types during upgrades. This approach is error-prone and does not scale for large data. **Per best practices, using these methods should be avoided if possible.** Conceptually, it also does not align well with the idea of orthogonal persistence.
__Deprecated__: [Pre- and post-upgrade hooks](#legacy-features) allow copying non-stable types to stable types during upgrades. This approach is error-prone and does not scale for large data. **Per best practices, using these methods should be avoided if possible.** Conceptually, it also does not align well with the idea of orthogonal persistence.

## Stable type signatures

Expand Down Expand Up @@ -121,18 +114,14 @@ When upgrading a canister, it is important to verify that the upgrade can procee
- Introducing an incompatible change in stable declarations.
- Breaking clients due to a Candid interface change.

With [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), Motoko rejects incompatible changes of stable declarations during an upgrade attempt.
Thanks to [enhanced orthogonal persistence](./orthogonal-persistence/enhanced.md), Motoko rejects incompatible changes of stable declarations during an upgrade attempt.
Moreover, `dfx` checks the two conditions before attempting the upgrade and warns users as necessary.

A Motoko canister upgrade is safe provided:

- The canister’s Candid interface evolves to a Candid subtype. You can check valid Candid subtyping between two services described in `.did` files using the [`didc` tool](https://github.com/dfinity/candid) with argument `check file1.did file2.did`.
- The canister’s Motoko stable signature evolves to a stable-compatible one.

:::danger
With [classical orthogonal persistence](./orthogonal-persistence/classical.md), the upgrade can still fail due to resource constraints. This is problematic as the canister can then not be upgraded. It is therefore strongly advised to test the scalability of upgrades extensively. This does not apply to enhanced orthogonal persistence.
:::


## Upgrading a canister

Expand All @@ -142,7 +131,7 @@ If you have a Motoko canister that has already been deployed, then you make chan
You are making a BREAKING change. Other canisters or frontend clients relying on your canister may stop working.
```

Motoko canisters using enhanced orthogonal persistence implement an extra safeguard in the runtime system to ensure that the stable data is compatible to exclude any data corruption or misinterpretation. Moreover, `dfx` also warns about incompatibility and dropping stable variables.
Motoko canisters implement an extra safeguard in the runtime system to ensure that the stable data is compatible to exclude any data corruption or misinterpretation. Moreover, `dfx` also warns about incompatibility and dropping stable variables.

## Data migration

Expand Down Expand Up @@ -185,11 +174,11 @@ reference material on [migration expressions](../../reference/language-manual.md
## Legacy features

:::danger
Using the pre- and post-upgrade system methods is discouraged. It is error-prone and can render a canister unusable. In particular, if a `preupgrade` method traps and cannot be prevented from trapping by other means, then your canister may be left in a state in which it can no longer be upgraded. Per best practices, using these methods should be avoided if possible.
The pre- and post-upgrade system methods are deprecated (warning M0270) in favor of [migration functions](#explicit-migration). They are error-prone and can render a canister unusable. In particular, if a `preupgrade` method traps and cannot be prevented from trapping by other means, then your canister may be left in a state in which it can no longer be upgraded. Per best practices, using these methods should be avoided if possible.
:::

Motoko supports user-defined upgrade hooks that run immediately before and after an upgrade. These upgrade hooks allow triggering additional logic on upgrade.
They are declared as `system` functions with special names, `preugrade` and `postupgrade`. Both functions must have type `: () → ()`.
They are declared as `system` functions with special names, `preupgrade` and `postupgrade`. Both functions must have type `: () → ()`.

If `preupgrade` raises a trap, hits the instruction limit, or hits another IC computing limit, the upgrade can no longer succeed and the canister is stuck with the existing version.

Expand Down
Loading
Loading