Inter-Canister Query Calls (Status: done)

Queries and updates use complete different mechanisms. The problem is about holding on to dirty state of canisters while they wait for replies.

First let’s consider update calls. If canister executes an update call at state A and modifies its state to A’, the new state is committed and the state of the canister now becomes A’. All future update calls will run again A’. The system is free to drop A whenever it is convenient for it.

And when you consider the current situation without ICQC, we see that queries will either run against state A or state A’. So when state A’ is available, the system can let new queries run against A’ and when the last queries running against A finish, the system can safely drop it.

Note that currently without ICQC and with upper limits on how long queries can run for, we have a simple upper bound on how long we need to hold onto older states of canisters.

Problem 1 with ICQC

Let’s say the user queries canister 1 at state A. Canister 1 sends ICQC to canister 2 and so on. The system has to hold on to state A of canister 1 till the entire ICQC call graph is finished. If we allow for call graphs with unlimited depth (which is currently the case with update call graphs), there would be no upper limit on how long the system has to hold onto state A for canister 1.

Problem 2 with ICQC

The problem above is actually much worse. Note that when canister 1 executed the query at state A, on top of sending queries to other canisters, it also modified its state to A’. The system has to hold on to not state A but actually A’ because when the replies come back, they are expecting to run against A’.

This might not seem like a big problem immediately as the number of states that the system is maintaining is not actually changing (A’ instead of A). However, if you consider how the states are maintained in the system, this is a huge problem i.e. we maintain different states of the canister by maintaining deltas from previous versions of the states. So A’ is actually maintained as A + delta. So what we are seeing here is that each time a canister makes an ICQC, we have to maintain a separate additional delta for that canister. And the more canisters are involved in a ICQC call graph, the more deltas that system has to maintain. And without bounding the call graph, we cannot bound the number of deltas we have to maintain.

Further, note that all the above deltas that are maintained for queries, need to be maintained only temporarily. Once a ICQC finishes, all the deltas can be dropped. They also need to have high availability as generally we expect queries to run fairly quickly. This means that we need to have access to a large amount of storage that also has low latency access behaviour and can be recycled quickly. All of these are very challenging problems.

I hope the above gives some appreciation of how many different versions of a canister’s state (i.e. deltas) we will have to maintain if we implement ICQC naively i.e. with the same guarantees and semantics as we have for update messages.

This is why @ulan and I are proposing a ICQC design where the replies run against a “clean” state of the canister. With that design, we are not maintaining additional canister states that we have to maintain today. This will make programming with ICQC more difficult as has already been discussed above.

3 Likes