TLDR; Would a post-init hook that allows cross-canister calls be beneficial?
I have a need to make a cross-canister call one time, at canister initialization (see this post). However, neither the init method nor the post-upgrade method allow for cross-canister calls. As a result I have to find another way to kick this off.
The heartbeat method would be too expensive. Having to expose a new method and manually call it is less than ideal. Which leaves the timer API as the current most promising path forward, and how intend to proceed for now.
That being said, I can imagine other scenarios where data from other canisters is needed to initialize a canister. Having a post-init hook where you can make cross-canister calls would be the most intuitive way to support this (that I can think of). Either that or just allowing cross-canister calls in the init method, but I’m sure there’s technical reasons we aren’t doing that.
So, my question is two fold:
Is there a case for adding a post-init hook for this type of work?
Is there currently a better solution than the timer API for doing this?
What about using a “factory” canister to spin up this canister, and then calling a specific API on it that is gated by the principal of the factory that created it? This way you can await the completion of the canister being created and right after call the postInit() API on your canister.
You can also include an instance variable to ensure this postInit() API called only once.
However, adding a proper post-init hook is technically tricky because it must complete before the first “real” message arrives. If we don’t ensure this, the usefulness of the post-init hook is minimal: we must contaminate all update calls with checks in case the first message arrives before the initialization completes.
One promising solution is introducing another canister state, “initializing”. The replica could automatically reject (or buffer) all incoming messages until the initialization completes. This feature will be helpful, but it would be quite a chunk of work for the execution team.
This feature will be helpful, but it would be quite a chunk of work for the execution team.
For DTS we implemented a per-canister system task queue abstraction that guarantees execution of the tasks in the queue before execution of regular messages. I think that will make the implementation of the post-init hook easier.
That depends on the implementation. The spec doesn’t define any ordering between a timer with 0 seconds and other message of the canister. In our current implementation some messages may be executed before the timer with 0 second in the same round.
This would be one solution for an end developer. At Demergent Labs we are building at the CDK level so we can’t rely on having our users make this call. We need to make it for them. But it would be a valid approach for other devs.
@roman-kashitsyn, adding an initialization guard sounds like the right way to go for now as it will guarantee that the initialization logic happens before anything else. I’ll likely proceed this way while waiting on a post-init hook implementation.
What you’ve proposed for the hook though sounds exactly like what I would really like. Having the guarantee that it completes before “real” messages arrive would be amazing. At this point I’m not sure if that means Dfinity is going to start work on this though, or you’re just agreeing it would be useful. The transition from forum post to actual roadmap item is a little vague to me. Could you help get this into the roadmap and provide some way for the community to check the progress of it (or get this in front of the right person)?