Canister burning through cycles

Thank you for sharing. Being a new motoko dev I agree that it would be helpful to understand when to use an Array. I feel like I should avoid them entirely and always use a Buffer, but that’s probably incorrect.

Edit: Looking at the Buffer class it seems like it is always mutable. So maybe the preference is to use a Buffer until you need to do some operation on a subset of its entries that won’t change. In which case I would use Buffer.toArray() to create a fixed size array. Does that sound right?

First, on behalf of Motoko the language and ecosystem, I apologize that these design choices are not documented more clearly.

There are roughly two ways that a value of some type could arise in your code (I say “entry point” for any public method of your actor):

  • The value is produced entirely during the activation of some entry point. It serves some role to answer the query or update request there.

  • The value is stored in an actor var (stable or otherwise) and mutated there. Its data persists between entry points’ activation, and generally gets larger and larger over time, with successive activations.

To avoid performance issues related to Array.append

  • For arrays in the first category, Array.append is fine to use, assuming one avoids loops. This category corresponds to the situation mentioned here, by @roman-kashitsyn

  • For every value in the second category, use a Buffer to store large sequences, not an Array. Yes, it is very tempting to use a stable var of type array because buffers are not stable, and cannot be stored that way. However, please resist this temptation unless there happens to be a natural bound for the array and it will never grow, just mutate in place. Since that basically never happens in practice, please use a buffer (stored as a non-stable actor var) to store this data. (To persist, use the pre and post upgrade hooks to write its data and read its data from some arrays used only in that step.). Alternatively, I wrote a tree-based (think “ropes”) representation for sequences that can be stored directly in stable vars. It should be worst-case O(log n) time for all insertions, making even better than Buffer asymptotically. However, we have not tested this structure much; it remains experimental, and not yet part of base.

If any use case you have is hard to classify into those two situations, let me know. I’d be happy to help give more details and figure it out.

2 Likes

Thank you @matthewhammer, this is really helpful!

I will follow this guidance while updating the canister and let you know if I run into any difficult decisions.

1 Like