firefly
firefly copied to clipboard
Idempotent transaction submission
The firefly connector framework supports idempotent transaction submission because the REST Client provides the uuid of the new transaction REST resource. Usually, that REST Client is firefly core which generates a new UUID each time - https://github.com/hyperledger/firefly/blob/main/internal/txcommon/txcommon.go#L108
This issue proposes to expose this idempotent behaviour from firefly core. i.e. to allow the REST Client to firefly core ( typically the users' business application) to provide a unique ID when invoking any API that results in transaction submission. If the client is ever in doubt as to whether the request was received (e.g. maybe network timeout or maybe dropped connection before getting the response) then it can safely retry the API, with the same ID and be assured that will not result in 2 separate transactions.
A few bits of background that I think are worth considering with this one.
Currently in FireFly there are two related objects which represent an attempt to perform a blockchain transaction:
- The FireFly
Transaction
object - one of these is created for each high-level type of action being performed, and there could be zero, one or (in the future) more than one blockchain transaction associated with this. - The Blockchain
Operation
object - the ID of this is used when calling EthConnect/EVMConnect. This is a re-submittable item, and a new ID is generated if a user chooses to submit it again.
So on the external API, there's an important choice about how idempotency is represented/stored, and I suggest it's actually the Transaction
object that holds the idempotency key that's passed in. That way it can be implemented in one place in the code in FireFly, and exposed consistently on the APIs.
There is a tx
sub-object on most resources (sometimes input/output, sometimes just output on the API), which currently has an id
(generated) and at type
. Bit of detailed surveying needed, but potentially this could be extended with an idempotency key, and then the Transactions table could have a unique namespace+idempotency_key index on it to assure uniqueness.
One complexity is rollback(/crass) during the DB transaction submitting the request - because I believe on all paths the blockchain transaction is currently submitted sequentially with the API call. It must not be the case that a database rollback could occur after the blockchain transaction is submitted on any path, and allow re-submission (hence duplication of the TX).
I think this means that the submission of operations need to become a post-commit callback, which is quite a big change to the model. e.g. the set of operations with their IDs would need to be generated during the DB operation, then the actual submission of the actions would happen post-commit. This could be quite a pervasive change.
It might be that only blockchain operations are considered in the first pass. A starting point would be looking at where this is called: https://github.com/hyperledger/firefly/blob/b81acd54eba90988cbc4c27f86c93a960276b383/internal/txcommon/txcommon.go#L105-L123
Then also, referring back to @awrichar's great work on summarizing all the different types of transaction that exist. I'll ask him to add some context to that.
This was the diagram of transactions and the types of operations they can contain: https://miro.com/app/board/uXjVOWHk_6s=/?moveToWidget=3458764518195707270&cot=14. I don't think it's 100% up-to-date as of this moment. There was also this FIR containing some of the history of how FireFly transactions evolved: https://github.com/hyperledger/firefly-fir/pull/8.
Was this solved by https://github.com/hyperledger/firefly/pull/1096, or is it something different?
Yes my understanding is that https://github.com/hyperledger/firefly/pull/1096 delivered this requirement