Wednesday 14 October 2015

Elasticsearch: Handling conflicts using version number

As I said, Elasticsearch is a distributed document store, we need to resolve conflicts that come with concurrency in distributed application.

For example
Suppose we are maintaining our products information in elastics search. Every product has price, number of items available.

GET xyz/products/1

{
   "_index": "xyz",
   "_type": "products",
   "_id": "1",
   "_version": 1,
   "found": true,
   "_source": {
      "price": "2560.19",
      "items_available": "132"
   }
}

Lets say there are two persons ‘A’ and ‘B’ want to purchase product 1 The sequence like below.

1. A gets information about product 1
         "_source": {
          "price": "2560.19",
         "items_available": "132"
          }
          
2. B also gets information about product 1
         "_source": {
          "price": "2560.19",
         "items_available": "132"
          }
          
3. A purchase product 1, decrement items_available by 1, and saves the document.
         "_source": {
          "price": "2560.19",
         "items_available": "131"
          }
          
4.  B purchase product 1, decrement items_available by 1, and saves the document.
         "_source": {
          "price": "2560.19",
         "items_available": "131"
          }
          
But "items_available" should be 130 (since two purchases happened), but it is 131.

How to solve this concurrency problem?
We can solve this problem by using version number of the document. Whenever somebody wants to update a document, they have to tell which version of the document they are going to update, if that version is not current, request will fail.

How to specify version number while updating?
By using query parameter version.

PUT /website/blog/1?version=1

following is the order purchase sequence using version number.

1. A gets current document of product 1.
{
   "_index": "xyz",
   "_type": "products",
   "_id": "1",
   "_version": 1,
   "found": true,
   "_source": {
      "price": "2560.19",
      "items_available": "132"
   }
}

2. B also gets current document of product 1.

{
   "_index": "xyz",
   "_type": "products",
   "_id": "1",
   "_version": 1,
   "found": true,
   "_source": {
      "price": "2560.19",
      "items_available": "132"
   }
}
          
3. A purchase product 1, sends the update request by specifying version number.

PUT xyz/products/1?version=1
{
  "price": "2560.19",
  "items_available": "131"
}
          
4.  B purchase product 1, sends the update request by specifying version number. Since B is trying to update older version of the document, B gets VersionConflictEngineException.

PUT xyz/products/1?version=1
{
  "price": "2560.19",
  "items_available": "131"
}

When B submits above request, it gets following response.

{
   "error": "VersionConflictEngineException[[xyz][2] [products][1]: version conflict, current [2], provided [1]]",
   "status": 409
}


At application side you can take decision when VersionConflictEngineException happens. Suppose, for our application, if we got VersionConflictEngineException, we read the latest document again and decrease the items_available by 1. If items_available is 0, we simply send response, “Items Out Of stock”.




Prevoius                                                 Next                                                 Home

No comments:

Post a Comment