Hyperledger Fabric: Concurrency… really?

When working with Hyperledger Fabric on different projects, we get to understand the in’s and out’s of this technology. That’s interesting because you run into issues that aren’t really documented anywhere. To be honest, even though the documentation is increasingly getting better, the technology is still so new, it’s hard to find much information about anything.

So, what about concurrency?

In a recent project we allowed a user to execute multiple transactions one after another. This was a no-brainer and seemed best for the UX especially coming from centralised technology. After doing some testing without waiting until previous transaction was completed, we ran into issues.

Let’s explain

Take for example this object from the marbles chaincode (altered to better explain this issue).

type marble struct { 
ObjectType string `json:"docType"`
Id int `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
  // Added fields
CheckedByOwner bool `json:"checkedByOwner"`
CheckedBySpecialist bool `json:"checkedBySpecialist"`
}

Let’s say you have a marble. You want to make sure it’s all in excellent condition, so you and the marbles specialist want to check if it’s in mint condition and update this on the blockchain. No problem there.

Example transaction problem

This example just relies on luck, but what if both parties validate this marble at the same time? Well, then the latest transaction will not be executed.

Fabric’s underlying databases use MVCC (Multiversion concurrency control) to make sure no double spending or inconsistency in data will occur. In understandable terms, this means it’s using versioned documents or records (Since it’s a key value store, we call each row a document or record). When updating the same state, a new version of an existing document will be made to overwrite the old one. Any transaction executed between this time will not be cleared and a MVCC error will be triggered. The advantage of using versioning over locking like other databases is that even under high load, the DB can run at full speed asynchronously. This is why in modern databases MVCC is more widely used.

Locking vs MVCC

Doesn’t Fabric handle this?

No, right now, Fabric will let the transaction happen and throw an error when trying to execute. Not that they didn’t think about this, but they just haven’t implemented anything yet themselves. Following item can be found on their old roadmap on github (which has been last edited on 25 Jul 2016 and since then has been archived). So It’ll probably be added eventually.

The “solutions”

Bad UX, just own it

This is not a fix, it’s a just a way of handling this issue. If you want, you can just tell the user he should try again. It’s poor User Experience, but the user knows something went wrong.

Or you can disable the transaction trigger (e.g. Button) until the first transaction is finished. This get’s a bit tricky when there’s multiple users using different browsers. It’s a bit nicer way to handle it UX-wise but still not a great solution.

Divide and conquer

There’s another workaround/partial fix which requires more analysis and an object with a bit more complexity. Splitting your large object (which can have a lot of blocking transactions) into separate smaller maintainable objects, will not fix, but make the blocking transactions less frequent. This of course all depends on your use case.

For example:

Let’s say you really like marbles, and you want to keep an access log of all people who touched your precious marbles. You may initially come up with an object like this.

type marble struct { 
ObjectType string `json:"docType"`
Id int `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
CheckedByOwner bool `json:"checkedByOwner"`
CheckedBySpecialist bool `json:"checkedBySpecialist"`
  // New property
  TouchedBy           []string `json:"touchedBy"`
}

Same issue here when you want to add someone to your touchedBy logs, at the same time as a specialist validates your marble. The latest transaction won’t go through. The MVCC error will only occur when trying to alter the same state, so let’s split it!

type marble struct { 
ObjectType string `json:"docType"`
Id int `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
CheckedByOwner bool `json:"checkedByOwner"`
CheckedBySpecialist bool `json:"checkedBySpecialist"`
}
// We extract the log and place it in a new seperate state
type marbleLog struct {
ObjectType string `json:"docType"`
Id int `json:"id"`
TouchedBy []string `json:"touchedBy"`
}

Since your touchedBy log isn’t in the same state as the marble anymore, there won’t be any interference when validating the marbles. But that’s the only thing it fixes, the issue when validating a marble by the owner and specialist at the same time is still an issue.

Queueing transactions

This is a more permanent fix, and probably the best one considering invoke operations take time. By placing a First-In-First-Out (FIFO) queue between your Business Logic and your fabric SDK, you allow for automatic re-execution of transactions even after they failed. When one fails, they are re-added to the queue and tried again later. This in combination with splitting your objects into smaller ones will make concurrent operations be less of a nightmare in your Hyperledger Fabric app.

We will be utilising this method in our next project, if we run into certain things utilising this method, we may add a part 2 to this article.

Note: This may work for you but depending on the way you implement this, this solution will need to be centralised. Meaning this will be a bottleneck or single point of failure using fabric.

Other suggestions?

These are our findings, but there may be better solutions. Let us know if you have any suggestions.

Note: At the the time of writing this, we’re using Hyperledger Fabric v1.0

Sources

CouchDB consistency: http://guide.couchdb.org/draft/consistency.html#locking

Hyperledger RocketChat: https://chat.hyperledger.org/