Make Atomic Updates Across Chaincodes and Channels

You can use atomic transactions to complete multiple transactions across channels and chaincodes in an atomic manner.

An atomic transaction is an indivisible series of data operations that either all succeed, or none succeed.

Atomic transactions can be useful in complex situations where multiple chaincodes are deployed to separate channels. You can use atomic transactions to maintain data consistency while running multiple blockchain transactions, even if a network or system failure occurs. Oracle Blockchain Platform supports atomic transactions by using the two-phase commit protocol, where an initial phase where each data operation is prepared is followed by a phase where each data operation is actually committed.

Atomic transactions work at the application level. Typically you do not need to change existing chaincode logic to support atomic transactions. Because one or more additional arguments are added by the atomic transactions framework, make sure that any existing chaincode does not perform strict checks on the number of arguments passed in the chaincode method. Atomic transactions are supported by the following REST API endpoint:
  • restproxy/api/v2/atomicTransactions
The REST API endpoint prepares the transactions as defined by your chaincode, and then uses built-in chaincode functions to either to commit all of the transactions, or to roll back all of the transactions if there are any errors during the prepare phase. For more information about the REST endpoints to use to implement atomic transactions, see Atomic Transactions REST Endpoints.

Each atomic transaction is composed of two or more blockchain transactions. The result (the returnCode value) of the atomic transaction is either Success or Failure. In an atomic transaction, each requested blockchain transaction is split into two distinct operations: a prepare phase and then either a commit or a rollback phase.

  • In the prepare phase, each transaction is endorsed as usual, but instead of being finalized, the changes are staged and the values are locked to prevent other transactions from modifying the staged values.
  • If the prepare phase is successful for each blockchain transaction, then the transactions are endorsed and committed by using built-in chaincode. The previously locked values are unlocked, and the result of the atomic transaction is Success.
  • If the prepare phase fails for any blockchain transaction, then all other transactions where the prepare phase succeeded are rolled back, again by using built-in chaincode. The staged changes are removed and the previously locked values are unlocked. The result of the atomic transaction is Failure.
Because the atomic transactions works by locking keys, you might receive a Two_Phase_Commit_Lock error if a different transactions attempts to modify a key that is locked by a currently active atomic transaction that is prepared. This can occur in one of the following two scenarios:
  • An atomic transaction is still in the prepared phase, and a different transaction attempts to modify a key that was locked by the prepared transaction. In this case, the system is working as designed. If you encounter this error, retry the second transaction. This is analogous to how applications handle phantom read errors or multi-version concurrency control (MVCC) errors.
  • The GlobalStatus value returned by the atomic transaction is HeuristicOutcome. In this case, an atomic transaction operation was canceled because one of the commit operations failed. This is a rare occurrence and might need to be resolved manually. One side effect of a heuristic outcome is that some keys may be left locked by transactions which failed to be committed or rolled back. In this case, use the following REST API endpoint to unlock the atomic transaction:
    • restproxy/api/v2/atomicTransactions/{globalTransactionId}
    For more information about the REST endpoint to use to unlock atomic transactions, see Unlock Atomic Transaction.

Scenario: Explore Atomic Transactions Using Samples

Consider the following example, which uses two of the sample chaincodes that are included with Oracle Blockchain Platform, Balance Transfer and Marbles. The Balance Transfer sample represents two parties with the ability to transfer funds between account balances. The Marbles sample lets you create marbles and exchange them between owners. You could use individual (non-atomic) transactions to buy a marble by exchanging funds in the Balance Transfer chaincode and changing the ownership of the marble in the Marbles chaincode. However, if an error occurs with one of those transactions, the ledger might be left in an inconsistent state: either the funds were transferred but not the marble or the marble is transferred but not paid for.

In this scenario, you can use the existing chaincode with the REST API endpoints that support atomic transactions. The exchange of funds and the transfer of ownership of the marble must both succeed or both fail. If either transaction encounters an error, then neither transaction is committed. To explore this scenario, complete the following steps:

  1. Install the Balance Transfer and Marbles samples on different channels. For more information on installing the samples, see Explore Oracle Blockchain Platform Using Samples.
  2. In the Marbles sample, invoke the Create a new marble action to create a number of marbles for various marble owners.
  3. Use the Invoke Atomic Transaction REST endpoint to complete atomic transactions that invoke both the Marbles and the Balance Transfer samples.

For example, the following transaction transfers a marble named marble1 to Tom, and sends 50 coins from account a to account b.

{
 "transactions": [
   {"chaincode":"obcs-marbles","args":["transferMarble", "marble1", "tom"],"timeout":0, "channel":"goods"},
   {"chaincode":"obcs-example02","args":["invoke", "a", "b", "50"],"timeout":0, "channel":"wallet"}
 ],
 "isolationLevel": "serializable",
 "prepareTimeout": 10000,
 "sync": true
}

In the previous transaction, if both transactions succeed in the prepare phase, then both transactions are committed to the ledger. If there is an error with either transaction, then neither transaction is committed during the second phase. Instead, both transactions are rolled back. For example, if there are less than 50 coins in account a, then no money is taken from the account and no marble is transferred to Tom.

There is a known issue with the Marbles sample and the default value of the Marble Owner field. For more information, see: Known Issues for Oracle Blockchain Platform.