Heartbeat getting called before init is finished

But it does not exist today. Maybe it is because people don’t want it enough?

Speaking for myself, I want cheap cron badly but I’m not sure I’m ready to trust a “public” canister that I send cycles to and that invokes my canister on a periodic basis. That canister would need to be autonomous, which very few people have done so far (and for good reason, since the source code needs to be open, airtight, and preferably audited).

I can see it happening in the future, but I need cheap heartbeat right now. I could build such a public canister, but there’s no guarantee that enough people will choose to use it for the time spent to be worth it. I might as well spend an hour setting up a cronjob on AWS that calls my canister.

(I know this sounds like I’m making excuses, but I want to be brutally honest on the thought process that’s going through my head.)

What I’m curious about is how much work it would take for the replica to support configurable heartbeat intervals or even an inspect_message for heartbeats. Is it a lot of work? Even something simple like a canister exposing some simple integer heartbeat_interval configuration variable that tells the replica how often to call its heartbeat (with the unit being # of blocks) would go a very long way, I think.

EDIT: Here is a reason why inter-canister calls may not be the safest choice right now. Another reason why we may not be ready for a public cron canister just yet.

1 Like

I don’t expect that to be much cheaper (in terms of actual work done by the system) than to run the heartbeat and trap quickly if there is nothing to do. And no easier to use either.

Maybe the problems go away if canister developers (or CDK) make sure they really don’t do much in the heartbeat if nothing is there to do, and the system doesn’t overcharge that pattern.

Could someone run an experiment with a canister that has a heartbeat that always immediately traps? I.e. the cheapest possible implementation of a heartbeat? Let’s see how expensive that actually is.

Lets say I spin up this crowdsourced heartbeat canister.

Is there a way for this heartbeat canister to make one-way calls to the 3rd party canisters that sign up such that my heartbeat canister doesn’t have to wait for the response? (i.e. if a 3rd party canister update method hung it wouldn’t stall the heartbeat canister)

I have prototyped a rust canister, with the following interfaces:

service : {
  start_cron : (nat64, opt nat64) -> (bool);
  stop_cron : (opt nat64) -> (bool);
}

Start cron takes an interval_secods and an opt task_id. The canister will be called with that task_id every “interval”.

Under the hood it uses a binaryheap to hold the tasks, and every heartbeat it pops the tasks where timestamp <= now() and calls a predefined fn on that canister.

I still have to write a bunch of tests, but my intention is to publish it in the first days of the hackathon, with the idea of this being used as a “per-project” or “per-org” heartbeat canister. If you need heartbeat in more than one canister, you’d be better off spinning-up one central canister, and have it call all your other canisters with whatever intervals you decide.

I’m now looking into @nomeata’s hints on how to send -1 -1 to make a call that doesn’t need to be awaited. If I figure this out, it should be feasible to run this as a public service, after some more testing. Anyone up to sponsor this, if it works with non-async calls?

2 Likes

Yes, I’d say so: On the system level, pass -1 (a.k.a. MAXINT) as the table index for the callback handler. In Motoko, you invoke the method with a return type of () (not async ()). And in the Rust CDK probably someone has to add support for that.

If you do this, you can safely upgrade your canister, as discussed in https://www.joachim-breitner.de/blog/789-Zero-downtime_upgrades_of_Internet_Computer_canisters.

Or is your question how to invoke a all without await in the Rust CDK? Probably someone else knows that better :slight_smile:

1 Like