I’m running into some strange issues locally while testing out some heartbeat functionality, and I just want to check my reasoning. Is it possible that the heartbeat gets called before the init update call is completed and its state is committed?
Hi lastmjs,
init
an integral part of install_code
, so if it fails, the whole install_code
fails. So it’s definitely happening before any heartbeat or update call being processed, but might run in parallel with queries.
Could you please provide more detail on what exactly you’re observing?
oh, btw if the “init update call” is your own handler, not the canister_init
method the IC calls on install/reinstall code, then it’s exactly the opposite: the heartbeat will be called before any ingress or inter-canister messages will be processed.
sorry for the confusion…
@berestovskyy Excuse me for the simple questions:
- is the hearbeat call also an update call? has it the same cost in cycles than an update call?
- what is an ingress message? is this different than query and update calls?
Thanks for the replies…actually the issues were probably not stemming from what I thought. I had an obvious bug in my code. But these thoughts are useful as I analyze behavior, thanks!
hey @ildefons
No problem at all!
Regarding the the heartbeat:
- The heartbeat method gets called once per round at most, at the beginning of the round.
- It can mutate the Canister state (i.e. update the state).
- The Canister gets charged for executing the heartbeats as normal.
- The instructions limit is shared across the heartbeat and the update messages.
Regarding the ingress messages:
- There are messages which can mutate (update) the Canister state and which have no effect on the state (queries).
- The state could be mutated with a message from another Canister (inter-canister call) or from a user (ingress message).
- The query methods can technically mutate the state (i.e. they can change variables as normal), but all those changes are discarded at the end of the query execution.
Hope that helps
So this is resolved?
For now yes, it is resolved to my satisfaction
Then, if convenient, you can check the “resolved” checkbox next to one of the responses, and the post will appear as resolved in the main overview. Makes it easier to spot posts that still need attention.
It would be nice if there was a way for a canister to subscribe to every X heartbeats instead of every heartbeat. Right now, it’s pretty cycle intensive. (You can implement some approximation of that in the heartbeat function, but it still gets called every round.)
I would really like to see this functionality as well. There is at least one very major use case that @bob11 and I have discussed that realistically be done without a much cheaper heartbeat.
We would like register a heartbeat to be called only once per day or per week or per month for example. Also, it would be nice to have the ability to register multiple heartbeats. I think this is essential to our use case as well.
The idea was that people write fully featured cron canisters that you then subscribe to, and that the heartbeat is the low-level feature used by such cron canisters. Would that not work for you? Why don’t you write such a canister?
That canister will be very expensive to run. And then someone has to maintain that service for other canisters to use. The best DX is to allow individual canisters to register directly with the system with whatever cron parameters it needs.
But I agree it could work in the mean time. I still think the system should provide a more granular heartbeat.
I’m just finishing up the starter app for the scalability bounty, and I’m beginning to think that the way forward will be with a per-project dedicated cron canister, where you’ll call your canisters from a central heartbeat-enabled canister. It would bring the best from both worlds, you’d have granular control, you’d be able to know what services you call into, and you’d get to enjoy the benefits of a cron system.
If I have time before the hackathon I’ll give it a go, see what I can come up with in a weekend.
Sure, it’s alway seems easier for the system provide anything. But I doubt that sentiment will scale – even something as simple sounding as a cron service has so many different possible features, just starting with configuring of how to specify the intervals.
And why stop at a cron canister? How about a cycle balance watching service? A shareded database? A log viewer? A Single Sign On service? …
I think it’ll be better in the long run if the Internet Computer system focuses on bare primitives, embrace the vision of composable service, and allow anyone to build such services, instead of just impatiently waiting for the core devs at DFINITY to build them.
Obligatory reference to unix: While cron
is part of every (well, most) distributions, it is not part of the kernel. I often find that the kernel/userspace separation in Linux and unix gives good guidance for the system/platform separation in the Internet Computer.
And conversely, if it is impractical to use a canister to provide cron services to many canisters, then that smells like a failure of the Internet Computer to achieve the vision behind it. So if “cron canisters” don’t work now, we should maybe use this to improve the system to make them work, rather than giving up and stuffing more features into the core components.
I agree that we need to be pragmatic about what features to leave in or out of the system. In this case I lean towards the system providing this functionality leading to the simplest and most efficient implementation compared to an implementation in application-space.
And conversely, if it is impractical to use a canister to provide cron services to many canisters, then that smells like a failure of the Internet Computer to achieve the vision behind it. So if “cron canisters” don’t work now, we should maybe use this to improve the system to make them work, rather than giving up and stuffing more features into the core components.
That’s exactly what I am suggesting we do, improve the system to make cron canisters work. Any cron canister is going to rely on the heartbeat functionality if it is to run entirely on the IC. How I suggest we do this is change the system to allow canisters to register when the heartbeat function should be called, instead of calling it nearly every second on every canister that has a registered heartbeat function.
It doesn’t seem to me that a lot of information would need to be stored by the system per canister to achieve this, and hopefully it could just be stored in the canister’s memory space somehow.
In my experience with heartbeat it is simply too course and expensive to use nicely. Any canisters spun up to provide cron services to other canisters will become incredibly expensive to run and will reach the scalability limits of a single canister performing many cross-canister calls per heartbeat function.
The design of the heartbeat is too course right now, and I think a relatively simple improvement at the system level could make the feature much more usable.
There is definitely something wrong with heartbeat, various people are smelling it:
- Cycle burn rate heartbeat
- Why does a canister keep consuming cycles? - #7 by PaulLiu
- @bob11’s experience at Toniq
This is an interesting idea: Cycle burn rate heartbeat - #27 by jzxchiang, an inspect_message
for heartbeat.
It is very clear that no one-size-fits-all solution exists for cron.
But on the other hand, I can write a public cron service with the following interface:
type Seconds = Nat64;
type Tag = Nat64;
type Job = Nat64;
service {
schedule: ({ interval: Seconds; tag: Tag; callback: (Tag) -> () }) -> (Job);
cancel: (Job) -> ();
}
Another canister will have to send some cycles along when scheduling a job. These cycles will be deducted by a fixed fee each time when callback is invoked. A job will no longer run if cancelled or it runs out of cycles.
I don’t see why it is so hard to offer such a service for people to use. It might even be profitable with appropriate fee setting and enough users.
But it does not exist today. Maybe it is because people don’t want it enough?
That seems to be the crux. I’d expect for a (popular) cron server, decently implemeted to keep the heartbeat short when nothing to do, the extra cost due to the “stupid” heartbeat interface is acceptable. But it’s just an expectation without doing the reseach and math.
Maybe the system charges too much for the heartbeat? Maybe some per-message constant cost is charged that isn’t really needed for such a “non-message” that doesn’t take space in the blocks.