Let’s say I have a Motoko canister that has state like this:
type UserProfile = {
mut count : Nat;
};
actor {
var users : Trie.Trie<Types.UserId, Types.UserProfile> = Trie.empty();
public func foo(userId : Types.UserId) : async () {
let ?user = Trie.find(users, Utils.key(userId), Text.equal);
// make inter-canister call
await AnotherCanister.bar();
user.count += 1;
};
};
If I immediately call foo twice with the same userId, will user.count be equal to 2 when both calls have fully run to completion? (Assume I call foo twice very quickly, before either AnotherCanister.bar() call has a chance to return a response.)
My guess is yes, but I wanted to confirm. Tagging @nomeata for visibility.
I would say yes provided there are no other concurrent clients and assuming the user account already exists with initial value 0 (your code doesn’t ensure that)
If you are worried about interference during the +=, those are guaranteed to be atomic as they are await-free.
I’ve played around with a canister receiving multiple requests that are processed at the exact same time (with the exact same timestamp), and can attest that each record does get processed correctly and updates/mutates the global state in the order one would expect.
Funny enough, I did find that sometimes the responses coming back from the multiple update calls are received by client in a different order than they were sent out and processed. (Might be a client/server latency thing).