Key collisions and idempotency (recap)

Hyperledger Fabric validates read and modified keys across transactions performed within the single block. If there are some conflicts, i.e. the same keys are read and modified in different smart contracts in the same block, then the conflicting smart contracts fail because of key collisions. Because of this behavior two things occur:

Hyperledger Fabric guarantees strong consistency. There are some limitations about the performance and scalability that need to be considered in the implementation.

If there is a risk of violation of the state consistency, smart contracts with key collisions fail. It means that the client application in many cases is forced to invoke smart contracts sequentially (low performance) or group the invocations in a way that no key collisions will occur (the client need to know possible collisions and internal logic of the smart contract).

In the previous post a tradeoff was made. We kept all the increment operations independent — they all had a unique key — but on the other hand, the read operation of the current incrementer value was expensive and there was no guarantee that the value retrieved was most recent.

Besides, the previous post presented a simple way of achieving the idempotency of operations. Each increment operation was saved under the key that was built on idempotency key, and each idempotency key was unique for the operation. If the same increment operation was accidentaly invoked multiple times, the duplicates were ignored.

The new example and the architecture

In this article we will consider the example of handling assets. Let’s start with the declaration of basic types and the contract.

As you can see in the example an asset consists of the symbol, the owner and the quantity. You can issue the asset, i.e. to increase the quantity of the asset with given symbol for given owner. You can also transfer the given quantity of the asset from one owner to another. Each operation returns the OperationResult object which contains idempotency key (it can be called the operation id as well) and the operation status which is either OK or ERROR .

In the previous article in the series the smart contracts were simple, so all the code was implemented in the contracts (class that implements Contract from fabric-contract-api ). The example in the current article is more complex than the incrementer and it requires more complex architecture.

In general it is good to have this kind responsibility separation:

Contract, as an equivalent of controller, which does not handle the business logic.

Service to handle business logic.

Repository to save and read values in the state.

This is just a general scheme. You will probably end up with more complex architecture, you may experiment with DDD or other sophisticated approaches, but this is a good way to start.

Our approach requires two entities to be saved: Asset and OperationResult . There is no surprise that we have AssetRepository and OperationResultRepository , and their role is quite straightforward.