Personal opinion: I would see the use of a system provided mechanism to execute heartbeats at lower frequency (in blocks or seconds) mostly as a way of spreading out the execution of said heartbeats over time. I.e. instead of (very likely) all heartbeats trying to run all at once at midnight or on the hour
Exactly! It frees up more CPU cycles on the nodes to do other stuff instead of wasting resources running stuff that’s not needed.
Yes and no. It saves CPU cycles for canisters, but there still needs to be logic somewhere to decide for each heartbeat handler whether it should run that round or not. So we would be merely moving said logic out of canisters and not charging them for it.
As said, for me the main benefit would be that (as opposed to developers likely all choosing to execute periodic logic at the same time, causing latency spikes) the system could randomly spread out periodic heartbeats and the load that comes with them. I.e. it would only guarantee that the heartbeat is called once an hour (e.g. at 23 past) rather than once an hour on the hour.
I would like to chime in on this. At Entrepot we run a few update calls every heartbeat for each NFT canister, and so our cycles burn rate is about 0.5T cycles per day per NFT canister. We are maintaining about 130 canisters at this point, so we are burning through around 65T cycles per day. If we had a nice configurable heartbeat cron (and could run every 10 seconds instead of every second), we could immediately cut our costs by 10x.
We are currently thinking maybe we just use an external (centralized) cron service to call functions regularly rather than rely on heartbeat because of the costs right now. Our cycles burn per canister before heartbeat was closer to 0.05T cycles / day.
A few other notes:
- yes, we should just push this cost on NFT creators, but we don’t have good tooling for that yet
- yes, we could optimize our heartbeat update calls, but not by THAT much I don’t think
- generally, we just need a nice cron service on the IC, and I don’t care where it is. Could be system level (my strong preference), but even a community cron service would work. We might just build one for ourselves and then open it up to the community, but if anyone else is working on it that would be awesome
How bout make the heartbeat wake up every 10 blocks instead of every block on the whole network? to start for now.
0.5 TC a day works out to almost $0.70 per day, which is roughly $21 per month or $252 per year.
I tried to reproduce those costs for a simple heartbeat canister.
I created a simple benchmark to measure a heartbeat cost for empty canisters written both in Rust and Motoko, see GitHub - maksymar/heartbeat-cost
Taking into account a median finalization rate of 1.09 blocks/s (or 917 ms per block) I got the following results after ~15 minutes of measurements:
- Rust – ~21 TC/year
- Motoko – ~71 TC/year
Calculation for Rust
execution_cost = update_message_execution_fee + instructions_to_cycles(0) = update_message_execution_fee per single heartbeat call
update_message_execution_fee = 590_000 it converts to
590_000 * (1_000/917) * (60*60*24*365) / 10^12 = 20.29 TC /year, very close to the measured ~21 TC/year
Motoko implementation has some extra code with sending a message which results in higher cost.
Without seeing the initial code it’s difficult to explain 182 TC/year cost.
Maybe it executes a heavy heartbeat payload on every call. In that case I’d suggest not executing heartbeat payload on every call which may reduce the cost x2.5 times for Motoko or x8.5 times for Rust.