Is there any way to discard memory from an update call without experiencing a trap? I would love to have that capability. My use case is within Sudograph. There are many possible errors that could occur within a GraphQL mutation (handled by an update call). I am using Rust, so I would love to propagate Results all the way to the exposed canister function from within my GraphQL/database code. I would still like to return a well-formatted GraphQL response detailing the errors, but I would also like to discard all of the memory. This is very important for transaction-like behavior (within a single canister at least).
I’m not sure if this is possible currently, and I think in Rust I have to experience a panic to experience a trap and thus to discard state changes. Panics and traps don’t let me give the user a nice JSON result. I imagine this would function very similarly to a query call, where state changes occur but are simply discarded.
That would be a gross abuse of the API. The message passed to ic0.trap is meant to be text (not enforced, because it’s trapping already, but an implementation may just drop the data if it isn’t valid text), and definitely not used for programmatic consumption. Not that I can stop anyone, of course …
It seems like the system lacks this mechanism (unless you feel like abusing the API in a “gross” way, which could be fun, but probably not reliable for the future.)
I wonder what Rust as a language provides for doing a kind of speculative mutation to state? (“transactions”?)
In Motoko, I’d advise a developer to use applicative data structures for their updates, and get the rollback semantics of a transaction failure from the applicative (purely functional) nature of the “updates”. The final mutation would only happen in the #ok case. Perhaps something similar could be done in Rust?
What is the programmatic way to revert state on the IC? Check any values before making state updates and throw a Result type?
It seems if we have made state updates and have to revert we have to panic out of it and use the string.
I’d say there is no way to revert the state on the system level while still responding in a useful manner (disregarding hacks involving ic0.trap), so I would say that any reversion of state needs to happen within the application.
I’m not saying that “reply but don’t commit the state changes” is completely out of the question, but it’d be a new feature, and there are some open questions around it. For example, while it may make sense when handling a call directly and without doing subsequent calls, it seems pretty much pointless as soon as you are sending off further calls during this message execution (as you’d be losing the callback continuation), or when handling callbacks (as then you’d likely leak the memory resources associated with that callback).
For what its worth, after a conversation with @lastmjs , we raised this as an explicit feature in the internal IC roadmap. This could be prioritised based on community feedback. It would indeed be a useful feature to implement.
I’d be super intersected in what the use cases are here. I get the feeling that someone is doing something clever that would save time, but I can’t envision it. It seems like @matthewhammer ’s suggestion to “not update until you know you need to update” is good advice.
Hmmmm…is it a kind of what if analysis where you need a bunch of stuff to happen and then a value is checked for update validity? It makes sense not to have to do all the work twice.
Its just a complex transaction in our game, so like buying X items from a shop in a single API call. I wanted a quick and dirty method to make either all of the updates happen, or none of them.
I guess I could create a structure that has a list of “what keys in the TrieMap need to be changed” and do that at the end. It’s a bit of work but may end up cleaner than trapping.