A lifecycle hook or method is a common way of allowing developers a passive way to tie into and handle the occurrence of specific and important events. In the context of React and Angular, lifecycle hooks allow developers to tie into a component mounting or state changes, and in the context of AWS EC2 these hooks are triggered when auto-scaling an instance.
On the IC, developers are currently able to actively interact with canisters but are limited in their ability to passively tie into specific important events in the lifecycle of a canister. This proposal aims to introduce an initial set of these lifecycle hooks for canisters on the IC.
canister_on_low_cycles(cyclesThreshold: Nat): async ()
→ triggers when the canister has
cycles <= cyclesThreshold
canister_on_low_heap_memory(heapMemoryThreshold: Nat): async ()
→ triggers when the canister has
heapMemory >= heapMemoryThreshold
Currently in order to monitor canisters, developers need to to proactively reach out to the canister or call a system level API. Providing canister metric lifecycle hooks allows developers to define their own thresholds for canisters and react when these thresholds are breached. Each of these thresholds will use 8 bytes and be stored in the canister settings within the replica.
If a canister metric threshold has been provided, once the replica detects that a canister has crossed that threshold it will send a message to the specific endpoint (i.e.
canister_on_low_cycles()) of the canister, triggering that specific lifecycle hook once the canister is able to process the message.
canister_on_error(methodName: Text, args: Blob, error: Error): async()
→ triggers on uncaught canister runtime error (trap), allows the developer to capture, analyze, and log different types of errors
Currently, there is no way for canister developers to catch synchronous errors occuring within a single canister, including heartbeat errors. By introducing the
canister_on_error() hook, after executing a message/heartbeat/timer, if the execution fails the replica will schedule a message to be sent to the canister_on_error() endpoint of the canister passing the error message.
→ triggers immediately after the
canister_init()(i.e. constructor) function of the canister
Currently, there is no way to execute inter-canister/asynchronous calls during the canister
canister_init() method. In order to ensure that the asynchronous call happens before any other message is executed, the current workaround is to add a guard in place that either awaits on the completion of the asynchronous task or rejects any messages until that task is completed.
canister_post_init() lifecycle hook would create a message in the per-canister task queue abstraction mentioned here Cross-canister compatible Post-Init hook - #5 by ulan to ensure that the
canister_post_init() hook is executed before any other “regular” message.
canister_output_queue_size(): Nat→ synchronous call that exposes the output queue size of a canister, helping a developer to throttle/pace outgoing calls from a canister.
canister_on_output_queue_full(): ()→ synchronous call that triggers when a canister’s output queue is full
Special thanks to @ulan for his technical background and expertise, and encouraging me to take on this proposal.