@claudio and I have been discussing recursive composite query calls. We need your input to decide what is the most natural (least surprising) semantics for developers.
Please take a look at this example, where we have two canisters: A and B. Each canister has a global counter set to 0 and a composite query method inc_counter()
that increments the counter and returns its new value. Additionally, canister B has two composite query methods: call_a()
and call_b()
.
What would you expect the queries to return when invoked by a user (e.g. using dfx
).
Canister A:
counter: int32 = 0; // <= Global variable
#[composite_query]
fn inc_counter() -> int32 {
counter += 1;
return counter;
}
Canister B:
counter: int32 = 0; // <= Global variable
#[composite_query]
fn inc_counter() -> int32 {
counter += 1;
return counter;
}
#[composite_query]
fn call_a() -> int32 {
counter += 1;
// Canister B calls canister A twice.
let a1 = call("Canister A", "inc_counter").await;
let a2 = call("Canister A", "inc_counter").await;
return counter + a1 + a2;
}
#[composite_query]
fn call_b() -> int32 {
counter += 1;
// Canister B calls canister B (self) twice.
let b1 = call("Canister B", "inc_counter").await;
let b2 = call("Canister B", "inc_counter").await;
return counter + b1 + b2;
}
Option 1
Composite queries are fully isolated from each other (do not see each other’s changes):
-
A.inc_counter()
returns1
. -
B.inc_counter()
returns1
. -
B.call_a()
returns3 (=1+1+1)
. -
B.call_b()
returns3 (=1+1+1)
.
Option 2
Composite queries see each other’s changes if they are in the same call graph:
-
A.inc_counter()
returns1
. -
B.inc_counter()
returns1
. -
B.call_a()
returns4 (=1+1+2)
. That is:counter=1
,b1=1
,b2=2
. -
B.call_b()
returns8 (=3+2+3)
. That is:counter=3
,a1=2
,a2=3
.
Option 3
Composite queries are isolated except when they are recursive:
-
A.inc_counter()
returns1
. -
B.inc_counter()
returns1
. -
B.call_a()
returns3 (=1+1+1)
. -
B.call_b()
returns8 (=3+2+3)
.
Option 4
Composite queries are isolated and recursion is not allowed:
-
A.inc_counter()
returns1
. -
B.inc_counter()
returns1
. -
B.call_a()
returns3 (=1+1+1)
. -
B.call_b()
returnsError: recursive calls are not allowed
.
Tagging everyone how commented in this thread: @skilesare, @Manu, @paulyoung, @jzxchiang, @lastmjs, @Seb, @nomeata.