Parcourir la source

Add doc's sequence number + primary term to GetResult and use it for updates (#36680)

This commit adds the last sequence number and primary term of the last operation that have
modified a document to `GetResult` and uses it to power the Update API.

Relates #36148 
Relates #10708
Boaz Leskes il y a 6 ans
Parent
commit
e356b8cb95
29 fichiers modifiés avec 262 ajouts et 119 suppressions
  1. 12 4
      docs/plugins/ingest-attachment.asciidoc
  2. 9 3
      docs/plugins/ingest-geoip.asciidoc
  3. 3 1
      docs/plugins/ingest-user-agent.asciidoc
  4. 9 3
      docs/reference/docs/get.asciidoc
  5. 3 1
      docs/reference/docs/reindex.asciidoc
  6. 4 2
      docs/reference/getting-started.asciidoc
  7. 15 9
      docs/reference/ingest/ingest-node.asciidoc
  8. 4 2
      modules/percolator/src/test/java/org/elasticsearch/percolator/PercolateQueryBuilderTests.java
  9. 3 1
      server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java
  10. 14 0
      server/src/main/java/org/elasticsearch/action/get/GetResponse.java
  11. 11 8
      server/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java
  12. 7 7
      server/src/main/java/org/elasticsearch/action/update/UpdateHelper.java
  13. 3 2
      server/src/main/java/org/elasticsearch/action/update/UpdateResponse.java
  14. 54 4
      server/src/main/java/org/elasticsearch/index/get/GetResult.java
  15. 5 3
      server/src/main/java/org/elasticsearch/index/get/ShardGetService.java
  16. 4 2
      server/src/test/java/org/elasticsearch/action/explain/ExplainResponseTests.java
  17. 13 9
      server/src/test/java/org/elasticsearch/action/get/GetResponseTests.java
  18. 1 1
      server/src/test/java/org/elasticsearch/action/get/MultiGetResponseTests.java
  19. 12 11
      server/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java
  20. 3 2
      server/src/test/java/org/elasticsearch/action/update/UpdateResponseTests.java
  21. 41 24
      server/src/test/java/org/elasticsearch/index/get/GetResultTests.java
  22. 2 1
      server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java
  23. 2 1
      server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java
  24. 7 3
      server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java
  25. 6 6
      server/src/test/java/org/elasticsearch/versioning/SimpleVersioningIT.java
  26. 4 3
      x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStoreTests.java
  27. 3 2
      x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java
  28. 6 3
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java
  29. 2 1
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transport/actions/ack/TransportAckWatchActionTests.java

+ 12 - 4
docs/plugins/ingest-attachment.asciidoc

@@ -63,6 +63,8 @@ Returns this:
   "_type": "_doc",
   "_id": "my_id",
   "_version": 1,
+  "_seq_no": 22,
+  "_primary_term": 1,
   "_source": {
     "data": "e1xydGYxXGFuc2kNCkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0DQpccGFyIH0=",
     "attachment": {
@@ -74,7 +76,7 @@ Returns this:
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 To specify only some fields to be extracted:
@@ -146,6 +148,8 @@ Returns this:
   "_type": "_doc",
   "_id": "my_id",
   "_version": 1,
+  "_seq_no": 35,
+  "_primary_term": 1,
   "_source": {
     "data": "e1xydGYxXGFuc2kNCkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0DQpccGFyIH0=",
     "attachment": {
@@ -157,7 +161,7 @@ Returns this:
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 [source,js]
@@ -194,6 +198,8 @@ Returns this:
   "_type": "_doc",
   "_id": "my_id_2",
   "_version": 1,
+  "_seq_no": 40,
+  "_primary_term": 1,
   "_source": {
     "data": "e1xydGYxXGFuc2kNCkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0DQpccGFyIH0=",
     "max_size": 5,
@@ -206,7 +212,7 @@ Returns this:
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 [[ingest-attachment-with-arrays]]
@@ -285,6 +291,8 @@ Returns this:
   "_type" : "_doc",
   "_id" : "my_id",
   "_version" : 1,
+  "_seq_no" : 50,
+  "_primary_term" : 1,
   "found" : true,
   "_source" : {
     "attachments" : [
@@ -312,7 +320,7 @@ Returns this:
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 Note that the `target_field` needs to be set, otherwise the

+ 9 - 3
docs/plugins/ingest-geoip.asciidoc

@@ -75,6 +75,8 @@ Which returns:
   "_type": "_doc",
   "_id": "my_id",
   "_version": 1,
+  "_seq_no": 55,
+  "_primary_term": 1,
   "_source": {
     "ip": "8.8.8.8",
     "geoip": {
@@ -85,7 +87,7 @@ Which returns:
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term":1/"_primary_term" : $body._primary_term/]
 
 Here is an example that uses the default country database and adds the
 geographical information to the `geo` field based on the `ip` field`. Note that
@@ -124,6 +126,8 @@ returns this:
   "_type": "_doc",
   "_id": "my_id",
   "_version": 1,
+  "_seq_no": 65,
+  "_primary_term": 1,
   "_source": {
     "ip": "8.8.8.8",
     "geo": {
@@ -133,7 +137,7 @@ returns this:
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 Not all IP addresses find geo information from the database, When this
@@ -174,13 +178,15 @@ Which returns:
   "_type" : "_doc",
   "_id" : "my_id",
   "_version" : 1,
+  "_seq_no" : 71,
+  "_primary_term": 1,
   "found" : true,
   "_source" : {
     "ip" : "80.231.5.0"
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 [[ingest-geoip-mappings-note]]
 ===== Recognizing Location as a Geopoint

+ 3 - 1
docs/plugins/ingest-user-agent.asciidoc

@@ -57,6 +57,8 @@ Which returns
   "_type": "_doc",
   "_id": "my_id",
   "_version": 1,
+  "_seq_no": 22,
+  "_primary_term": 1,
   "_source": {
     "agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
     "user_agent": {
@@ -73,7 +75,7 @@ Which returns
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term": 1/"_primary_term" : $body._primary_term/]
 
 ===== Using a custom regex file
 To use a custom regex file for parsing the user agents, that file has to be put into the `config/ingest-user-agent` directory and

+ 9 - 3
docs/reference/docs/get.asciidoc

@@ -21,6 +21,8 @@ The result of the above get operation is:
     "_type" : "_doc",
     "_id" : "0",
     "_version" : 1,
+    "_seq_no" : 10,
+    "_primary_term" : 1,
     "found": true,
     "_source" : {
         "user" : "kimchy",
@@ -30,7 +32,7 @@ The result of the above get operation is:
     }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 The above result includes the `_index`, `_type`, `_id` and `_version`
 of the document we wish to retrieve, including the actual `_source`
@@ -156,6 +158,8 @@ The result of the above get operation is:
    "_type": "_doc",
    "_id": "1",
    "_version": 1,
+   "_seq_no" : 22,
+   "_primary_term" : 1,
    "found": true,
    "fields": {
       "tags": [
@@ -164,7 +168,7 @@ The result of the above get operation is:
    }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 Field values fetched from the document itself are always returned as an array.
@@ -199,6 +203,8 @@ The result of the above get operation is:
    "_type": "_doc",
    "_id": "2",
    "_version": 1,
+   "_seq_no" : 13,
+   "_primary_term" : 1,
    "_routing": "user1",
    "found": true,
    "fields": {
@@ -208,7 +214,7 @@ The result of the above get operation is:
    }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 Also only leaf fields can be returned via the `stored_field` option. So object fields can't be returned and such requests
 will fail.

+ 3 - 1
docs/reference/docs/reindex.asciidoc

@@ -870,13 +870,15 @@ which will return:
   "_index": "test2",
   "_type": "_doc",
   "_version": 1,
+  "_seq_no": 44,
+  "_primary_term": 1,
   "_source": {
     "text": "words words",
     "tag": "foo"
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term": 1/"_primary_term" : $body._primary_term/]
 
 [float]
 [[docs-reindex-slice]]

+ 4 - 2
docs/reference/getting-started.asciidoc

@@ -421,7 +421,7 @@ And the response:
   "_primary_term" : 1
 }
 --------------------------------------------------
-// TESTRESPONSE[s/"_seq_no" : 0/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 From the above, we can see that a new customer document was successfully created inside the customer index. The document also has an internal id of 1 which we specified at index time.
 
@@ -445,11 +445,13 @@ And the response:
   "_type" : "_doc",
   "_id" : "1",
   "_version" : 1,
+  "_seq_no" : 25,
+  "_primary_term" : 1,
   "found" : true,
   "_source" : { "name": "John Doe" }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 Nothing out of the ordinary here other than a field, `found`, stating that we found a document with the requested ID 1 and another field, `_source`, which returns the full JSON document that we indexed from the previous step.
 

+ 15 - 9
docs/reference/ingest/ingest-node.asciidoc

@@ -730,13 +730,15 @@ GET test/_doc/2
   "_type": "_doc",
   "_id": "2",
   "_version": 1,
+  "_seq_no": 22,
+  "_primary_term": 1,
   "found": true,
   "_source": {
     "foo": "bar"
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term": 1/"_primary_term" : $body._primary_term/]
 ////
 
 The source document can also use dot delimited fields to represent nested fields.
@@ -967,6 +969,8 @@ GET test/_doc/2
   "_type": "_doc",
   "_id": "2",
   "_version": 1,
+  "_seq_no": 34,
+  "_primary_term": 1,
   "found": true,
   "_source": {
     "tags": [
@@ -976,7 +980,7 @@ GET test/_doc/2
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 ////
 
 
@@ -1088,6 +1092,8 @@ GET test/_doc/1
   "_type": "_doc",
   "_id": "1",
   "_version": 1,
+  "_seq_no": 60,
+  "_primary_term": 1,
   "found": true,
   "_source": {
     "href": {
@@ -1097,7 +1103,7 @@ GET test/_doc/1
   }
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 Regular expressions can be expensive and should be avoided if viable
@@ -1548,11 +1554,11 @@ PUT /myindex/_doc/1?pipeline=monthlyindex
     "successful" : 1,
     "failed" : 0
   },
-  "_seq_no" : 0,
+  "_seq_no" : 55,
   "_primary_term" : 1
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 
 The above request will not index this document into the `myindex` index, but into the `myindex-2016-04-01` index because
@@ -2787,11 +2793,11 @@ Response from the index request:
     "successful": 1,
     "failed": 0
   },
-  "_seq_no": 0,
+  "_seq_no": 66,
   "_primary_term": 1,
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 Indexed document:
 
@@ -2963,11 +2969,11 @@ The response from the above index request:
     "successful": 1,
     "failed": 0
   },
-  "_seq_no": 0,
+  "_seq_no": 89,
   "_primary_term": 1,
 }
 --------------------------------------------------
-// TESTRESPONSE
+// TESTRESPONSE[s/"_seq_no": \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 1/"_primary_term" : $body._primary_term/]
 
 In the above response, you can see that our document was actually indexed into `my_index` instead of
 `any_index`. This type of manipulation is often convenient in pipelines that have various branches of transformation,

+ 4 - 2
modules/percolator/src/test/java/org/elasticsearch/percolator/PercolateQueryBuilderTests.java

@@ -54,6 +54,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.hamcrest.Matchers.equalTo;
 
 public class PercolateQueryBuilderTests extends AbstractQueryTestCase<PercolateQueryBuilder> {
@@ -152,12 +153,13 @@ public class PercolateQueryBuilderTests extends AbstractQueryTestCase<PercolateQ
         assertThat(getRequest.version(), Matchers.equalTo(indexedDocumentVersion));
         if (indexedDocumentExists) {
             return new GetResponse(
-                    new GetResult(indexedDocumentIndex, indexedDocumentType, indexedDocumentId, 0L, true,
+                    new GetResult(indexedDocumentIndex, indexedDocumentType, indexedDocumentId, 0, 1, 0L, true,
                             documentSource.iterator().next(), Collections.emptyMap())
             );
         } else {
             return new GetResponse(
-                    new GetResult(indexedDocumentIndex, indexedDocumentType, indexedDocumentId, -1, false, null, Collections.emptyMap())
+                    new GetResult(indexedDocumentIndex, indexedDocumentType, indexedDocumentId, UNASSIGNED_SEQ_NO, 0, -1,
+                        false, null, Collections.emptyMap())
             );
         }
     }

+ 3 - 1
server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java

@@ -307,6 +307,7 @@ public class TransportShardBulkAction extends TransportWriteAction<BulkShardRequ
                     final Tuple<XContentType, Map<String, Object>> sourceAndContent =
                         XContentHelper.convertToMap(indexSourceAsBytes, true, updateIndexRequest.getContentType());
                     updateResponse.setGetResult(UpdateHelper.extractGetResult(updateRequest, concreteIndex,
+                        indexResponse.getSeqNo(), indexResponse.getPrimaryTerm(),
                         indexResponse.getVersion(), sourceAndContent.v2(), sourceAndContent.v1(), indexSourceAsBytes));
                 }
             } else if (translatedResult == DocWriteResponse.Result.DELETED) {
@@ -315,7 +316,8 @@ public class TransportShardBulkAction extends TransportWriteAction<BulkShardRequ
                     deleteResponse.getType(), deleteResponse.getId(), deleteResponse.getSeqNo(), deleteResponse.getPrimaryTerm(),
                     deleteResponse.getVersion(), deleteResponse.getResult());
 
-                final GetResult getResult = UpdateHelper.extractGetResult(updateRequest, concreteIndex, deleteResponse.getVersion(),
+                final GetResult getResult = UpdateHelper.extractGetResult(updateRequest, concreteIndex,
+                    deleteResponse.getSeqNo(), deleteResponse.getPrimaryTerm(), deleteResponse.getVersion(),
                     translate.updatedSourceAsMap(), translate.updateSourceContentType(), null);
 
                 updateResponse.setGetResult(getResult);

+ 14 - 0
server/src/main/java/org/elasticsearch/action/get/GetResponse.java

@@ -90,6 +90,20 @@ public class GetResponse extends ActionResponse implements Iterable<DocumentFiel
         return getResult.getVersion();
     }
 
+    /**
+     * The sequence number assigned to the last operation to have changed this document, if found.
+     */
+    public long getSeqNo() {
+        return getResult.getSeqNo();
+    }
+
+    /**
+     * The primary term of the last primary that has changed this document, if found.
+     */
+    public long getPrimaryTerm() {
+        return getResult.getPrimaryTerm();
+    }
+
     /**
      * The source of the document if exists.
      */

+ 11 - 8
server/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java

@@ -19,6 +19,7 @@
 
 package org.elasticsearch.action.update;
 
+import org.elasticsearch.ResourceAlreadyExistsException;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.ActionRunnable;
 import org.elasticsearch.action.RoutingMissingException;
@@ -50,7 +51,6 @@ import org.elasticsearch.index.IndexService;
 import org.elasticsearch.index.engine.VersionConflictEngineException;
 import org.elasticsearch.index.shard.IndexShard;
 import org.elasticsearch.index.shard.ShardId;
-import org.elasticsearch.ResourceAlreadyExistsException;
 import org.elasticsearch.indices.IndicesService;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.threadpool.ThreadPool;
@@ -185,8 +185,9 @@ public class TransportUpdateAction extends TransportInstanceSingleOperationActio
                             if (request.fetchSource() != null && request.fetchSource().fetchSource()) {
                                 Tuple<XContentType, Map<String, Object>> sourceAndContent =
                                         XContentHelper.convertToMap(upsertSourceBytes, true, upsertRequest.getContentType());
-                                update.setGetResult(UpdateHelper.extractGetResult(request, request.concreteIndex(), response.getVersion(),
-                                    sourceAndContent.v2(), sourceAndContent.v1(), upsertSourceBytes));
+                                update.setGetResult(UpdateHelper.extractGetResult(request, request.concreteIndex(),
+                                    response.getSeqNo(), response.getPrimaryTerm(), response.getVersion(), sourceAndContent.v2(),
+                                    sourceAndContent.v1(), upsertSourceBytes));
                             } else {
                                 update.setGetResult(null);
                             }
@@ -205,7 +206,8 @@ public class TransportUpdateAction extends TransportInstanceSingleOperationActio
                             UpdateResponse update = new UpdateResponse(response.getShardInfo(), response.getShardId(),
                                 response.getType(), response.getId(), response.getSeqNo(), response.getPrimaryTerm(),
                                 response.getVersion(), response.getResult());
-                            update.setGetResult(UpdateHelper.extractGetResult(request, request.concreteIndex(), response.getVersion(),
+                            update.setGetResult(UpdateHelper.extractGetResult(request, request.concreteIndex(),
+                                response.getSeqNo(), response.getPrimaryTerm(), response.getVersion(),
                                 result.updatedSourceAsMap(), result.updateSourceContentType(), indexSourceBytes));
                             update.setForcedRefresh(response.forcedRefresh());
                             listener.onResponse(update);
@@ -216,10 +218,11 @@ public class TransportUpdateAction extends TransportInstanceSingleOperationActio
                 DeleteRequest deleteRequest = result.action();
                 client.bulk(toSingleItemBulkRequest(deleteRequest), wrapBulkResponse(
                         ActionListener.<DeleteResponse>wrap(response -> {
-                            UpdateResponse update = new UpdateResponse(response.getShardInfo(), response.getShardId(),
-                                response.getType(), response.getId(), response.getSeqNo(), response.getPrimaryTerm(),
-                                response.getVersion(), response.getResult());
-                            update.setGetResult(UpdateHelper.extractGetResult(request, request.concreteIndex(), response.getVersion(),
+                            UpdateResponse update = new UpdateResponse(response.getShardInfo(), response.getShardId(), response.getType(),
+                                response.getId(), response.getSeqNo(), response.getPrimaryTerm(), response.getVersion(),
+                                response.getResult());
+                            update.setGetResult(UpdateHelper.extractGetResult(request, request.concreteIndex(),
+                                response.getSeqNo(), response.getPrimaryTerm(), response.getVersion(),
                                 result.updatedSourceAsMap(), result.updateSourceContentType(), null));
                             update.setForcedRefresh(response.forcedRefresh());
                             listener.onResponse(update);

+ 7 - 7
server/src/main/java/org/elasticsearch/action/update/UpdateHelper.java

@@ -209,8 +209,8 @@ public class UpdateHelper {
         if (detectNoop && noop) {
             UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(),
                     getResult.getVersion(), DocWriteResponse.Result.NOOP);
-            update.setGetResult(extractGetResult(request, request.index(), getResult.getVersion(), updatedSourceAsMap,
-                            updateSourceContentType, getResult.internalSourceRef()));
+            update.setGetResult(extractGetResult(request, request.index(), getResult.getSeqNo(), getResult.getPrimaryTerm(),
+                getResult.getVersion(), updatedSourceAsMap, updateSourceContentType, getResult.internalSourceRef()));
             return new Result(update, DocWriteResponse.Result.NOOP, updatedSourceAsMap, updateSourceContentType);
         } else {
             final IndexRequest finalIndexRequest = Requests.indexRequest(request.index())
@@ -270,10 +270,9 @@ public class UpdateHelper {
                 // If it was neither an INDEX or DELETE operation, treat it as a noop
                 UpdateResponse update = new UpdateResponse(shardId, getResult.getType(), getResult.getId(),
                         getResult.getVersion(), DocWriteResponse.Result.NOOP);
-                update.setGetResult(extractGetResult(request, request.index(), getResult.getVersion(), updatedSourceAsMap,
-                                updateSourceContentType, getResult.internalSourceRef()));
+                update.setGetResult(extractGetResult(request, request.index(), getResult.getSeqNo(), getResult.getPrimaryTerm(),
+                    getResult.getVersion(), updatedSourceAsMap, updateSourceContentType, getResult.internalSourceRef()));
                 return new Result(update, DocWriteResponse.Result.NOOP, updatedSourceAsMap, updateSourceContentType);
-
         }
     }
 
@@ -293,7 +292,7 @@ public class UpdateHelper {
     /**
      * Applies {@link UpdateRequest#fetchSource()} to the _source of the updated document to be returned in a update response.
      */
-    public static GetResult extractGetResult(final UpdateRequest request, String concreteIndex, long version,
+    public static GetResult extractGetResult(final UpdateRequest request, String concreteIndex, long seqNo, long primaryTerm, long version,
                                              final Map<String, Object> source, XContentType sourceContentType,
                                              @Nullable final BytesReference sourceAsBytes) {
         if (request.fetchSource() == null || request.fetchSource().fetchSource() == false) {
@@ -318,7 +317,8 @@ public class UpdateHelper {
         }
 
         // TODO when using delete/none, we can still return the source as bytes by generating it (using the sourceContentType)
-        return new GetResult(concreteIndex, request.type(), request.id(), version, true, sourceFilteredAsBytes, Collections.emptyMap());
+        return new GetResult(concreteIndex, request.type(), request.id(), seqNo, primaryTerm, version, true, sourceFilteredAsBytes,
+            Collections.emptyMap());
     }
 
     public static class Result {

+ 3 - 2
server/src/main/java/org/elasticsearch/action/update/UpdateResponse.java

@@ -162,8 +162,9 @@ public class UpdateResponse extends DocWriteResponse {
                 update = new UpdateResponse(shardId, type, id, version, result);
             }
             if (getResult != null) {
-                update.setGetResult(new GetResult(update.getIndex(), update.getType(), update.getId(), update.getVersion(),
-                        getResult.isExists(),getResult.internalSourceRef(), getResult.getFields()));
+                update.setGetResult(new GetResult(update.getIndex(), update.getType(), update.getId(),
+                    getResult.getSeqNo(), getResult.getPrimaryTerm(), update.getVersion(),
+                    getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
             }
             update.setForcedRefresh(forcedRefresh);
             return update;

+ 54 - 4
server/src/main/java/org/elasticsearch/index/get/GetResult.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.index.get;
 
 import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.compress.CompressorFactory;
@@ -33,6 +34,7 @@ import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.mapper.IgnoredFieldMapper;
 import org.elasticsearch.index.mapper.SourceFieldMapper;
+import org.elasticsearch.index.seqno.SequenceNumbers;
 import org.elasticsearch.search.lookup.SourceLookup;
 
 import java.io.IOException;
@@ -53,6 +55,8 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
     public static final String _TYPE = "_type";
     public static final String _ID = "_id";
     private static final String _VERSION = "_version";
+    private static final String _SEQ_NO = "_seq_no";
+    private static final String _PRIMARY_TERM = "_primary_term";
     private static final String FOUND = "found";
     private static final String FIELDS = "fields";
 
@@ -60,6 +64,8 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
     private String type;
     private String id;
     private long version;
+    private long seqNo;
+    private long primaryTerm;
     private boolean exists;
     private Map<String, DocumentField> fields;
     private Map<String, Object> sourceAsMap;
@@ -69,11 +75,17 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
     GetResult() {
     }
 
-    public GetResult(String index, String type, String id, long version, boolean exists, BytesReference source,
-                     Map<String, DocumentField> fields) {
+    public GetResult(String index, String type, String id, long seqNo, long primaryTerm, long version, boolean exists,
+                     BytesReference source, Map<String, DocumentField> fields) {
         this.index = index;
         this.type = type;
         this.id = id;
+        this.seqNo = seqNo;
+        this.primaryTerm = primaryTerm;
+        assert (seqNo == SequenceNumbers.UNASSIGNED_SEQ_NO && primaryTerm == 0) || (seqNo >= 0 && primaryTerm >= 1) :
+            "seqNo: " + seqNo + " primaryTerm: " + primaryTerm;
+        assert exists || (seqNo == SequenceNumbers.UNASSIGNED_SEQ_NO && primaryTerm == 0) :
+            "doc not found but seqNo/primaryTerm are set";
         this.version = version;
         this.exists = exists;
         this.source = source;
@@ -118,6 +130,20 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
         return version;
     }
 
+    /**
+     * The sequence number assigned to the last operation to have changed this document, if found.
+     */
+    public long getSeqNo() {
+        return seqNo;
+    }
+
+    /**
+     * The primary term of the last primary that has changed this document, if found.
+     */
+    public long getPrimaryTerm() {
+        return primaryTerm;
+    }
+
     /**
      * The source of the document if exists.
      */
@@ -213,6 +239,11 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
     }
 
     public XContentBuilder toXContentEmbedded(XContentBuilder builder, Params params) throws IOException {
+        if (seqNo != SequenceNumbers.UNASSIGNED_SEQ_NO) { // seqNo may not be assigned if read from an old node
+            builder.field(_SEQ_NO, seqNo);
+            builder.field(_PRIMARY_TERM, primaryTerm);
+        }
+
         List<DocumentField> metaFields = new ArrayList<>();
         List<DocumentField> otherFields = new ArrayList<>();
         if (fields != null && !fields.isEmpty()) {
@@ -282,6 +313,8 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
 
         String currentFieldName = parser.currentName();
         long version = -1;
+        long seqNo = SequenceNumbers.UNASSIGNED_SEQ_NO;
+        long primaryTerm = 0;
         Boolean found = null;
         BytesReference source = null;
         Map<String, DocumentField> fields = new HashMap<>();
@@ -297,6 +330,10 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
                     id = parser.text();
                 }  else if (_VERSION.equals(currentFieldName)) {
                     version = parser.longValue();
+                }  else if (_SEQ_NO.equals(currentFieldName)) {
+                    seqNo = parser.longValue();
+                }  else if (_PRIMARY_TERM.equals(currentFieldName)) {
+                    primaryTerm = parser.longValue();
                 } else if (FOUND.equals(currentFieldName)) {
                     found = parser.booleanValue();
                 } else {
@@ -326,7 +363,7 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
                 }
             }
         }
-        return new GetResult(index, type, id, version, found, source, fields);
+        return new GetResult(index, type, id, seqNo, primaryTerm, version, found, source, fields);
     }
 
     public static GetResult fromXContent(XContentParser parser) throws IOException {
@@ -347,6 +384,13 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
         index = in.readString();
         type = in.readOptionalString();
         id = in.readString();
+        if (in.getVersion().onOrAfter(Version.V_7_0_0)) {
+            seqNo = in.readZLong();
+            primaryTerm = in.readVLong();
+        } else {
+            seqNo = SequenceNumbers.UNASSIGNED_SEQ_NO;
+            primaryTerm = 0L;
+        }
         version = in.readLong();
         exists = in.readBoolean();
         if (exists) {
@@ -372,6 +416,10 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
         out.writeString(index);
         out.writeOptionalString(type);
         out.writeString(id);
+        if (out.getVersion().onOrAfter(Version.V_7_0_0)) {
+            out.writeZLong(seqNo);
+            out.writeVLong(primaryTerm);
+        }
         out.writeLong(version);
         out.writeBoolean(exists);
         if (exists) {
@@ -397,6 +445,8 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
         }
         GetResult getResult = (GetResult) o;
         return version == getResult.version &&
+                seqNo == getResult.seqNo &&
+                primaryTerm == getResult.primaryTerm &&
                 exists == getResult.exists &&
                 Objects.equals(index, getResult.index) &&
                 Objects.equals(type, getResult.type) &&
@@ -407,7 +457,7 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
 
     @Override
     public int hashCode() {
-        return Objects.hash(version, exists, index, type, id, fields, sourceAsMap());
+        return Objects.hash(version, seqNo, primaryTerm, exists, index, type, id, fields, sourceAsMap());
     }
 
     @Override

+ 5 - 3
server/src/main/java/org/elasticsearch/index/get/ShardGetService.java

@@ -45,6 +45,7 @@ import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.index.mapper.RoutingFieldMapper;
 import org.elasticsearch.index.mapper.SourceFieldMapper;
 import org.elasticsearch.index.mapper.Uid;
+import org.elasticsearch.index.seqno.SequenceNumbers;
 import org.elasticsearch.index.shard.AbstractIndexShardComponent;
 import org.elasticsearch.index.shard.IndexShard;
 import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
@@ -112,7 +113,7 @@ public final class ShardGetService extends AbstractIndexShardComponent {
     public GetResult get(Engine.GetResult engineGetResult, String id, String type,
                             String[] fields, FetchSourceContext fetchSourceContext) {
         if (!engineGetResult.exists()) {
-            return new GetResult(shardId.getIndexName(), type, id, -1, false, null, null);
+            return new GetResult(shardId.getIndexName(), type, id, SequenceNumbers.UNASSIGNED_SEQ_NO, 0, -1, false, null, null);
         }
 
         currentMetric.inc();
@@ -168,7 +169,7 @@ public final class ShardGetService extends AbstractIndexShardComponent {
         }
 
         if (get == null || get.exists() == false) {
-            return new GetResult(shardId.getIndexName(), type, id, -1, false, null, null);
+            return new GetResult(shardId.getIndexName(), type, id, SequenceNumbers.UNASSIGNED_SEQ_NO, 0, -1, false, null, null);
         }
 
         try {
@@ -233,7 +234,8 @@ public final class ShardGetService extends AbstractIndexShardComponent {
             }
         }
 
-        return new GetResult(shardId.getIndexName(), type, id, get.version(), get.exists(), source, fields);
+        return new GetResult(shardId.getIndexName(), type, id, get.docIdAndVersion().seqNo, get.docIdAndVersion().primaryTerm,
+            get.version(), get.exists(), source, fields);
     }
 
     private static FieldsVisitor buildFieldsVisitors(String[] fields, FetchSourceContext fetchSourceContext) {

+ 4 - 2
server/src/test/java/org/elasticsearch/action/explain/ExplainResponseTests.java

@@ -65,7 +65,7 @@ public class ExplainResponseTests extends AbstractStreamableXContentTestCase<Exp
         GetResult getResult = new GetResult(randomAlphaOfLengthBetween(3, 10),
             randomAlphaOfLengthBetween(3, 10),
             randomAlphaOfLengthBetween(3, 10),
-            randomNonNegativeLong(),
+            0, 1, randomNonNegativeLong(),
             true,
             RandomObjects.randomSource(random()),
             singletonMap(fieldName, new DocumentField(fieldName, values)));
@@ -83,7 +83,7 @@ public class ExplainResponseTests extends AbstractStreamableXContentTestCase<Exp
         String id = "1";
         boolean exist = true;
         Explanation explanation = Explanation.match(1.0f, "description", Collections.emptySet());
-        GetResult getResult = new GetResult(null, null, null, -1, true, new BytesArray("{ \"field1\" : " +
+        GetResult getResult = new GetResult(null, null, null, 0, 1, -1, true, new BytesArray("{ \"field1\" : " +
             "\"value1\", \"field2\":\"value2\"}"), singletonMap("field1", new DocumentField("field1",
             singletonList("value1"))));
         ExplainResponse response = new ExplainResponse(index, type, id, exist, explanation, getResult);
@@ -105,6 +105,8 @@ public class ExplainResponseTests extends AbstractStreamableXContentTestCase<Exp
             "        \"details\":[]\n" +
             "    },\n" +
             "    \"get\":{\n" +
+            "        \"_seq_no\":0," +
+            "        \"_primary_term\":1," +
             "        \"found\":true,\n" +
             "        \"_source\":{\n" +
             "            \"field1\":\"value1\",\n" +

+ 13 - 9
server/src/test/java/org/elasticsearch/action/get/GetResponseTests.java

@@ -39,6 +39,7 @@ import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
 import static org.elasticsearch.index.get.GetResultTests.copyGetResult;
 import static org.elasticsearch.index.get.GetResultTests.mutateGetResult;
 import static org.elasticsearch.index.get.GetResultTests.randomGetResult;
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
 import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
@@ -91,26 +92,28 @@ public class GetResponseTests extends ESTestCase {
 
     public void testToXContent() {
         {
-            GetResponse getResponse = new GetResponse(new GetResult("index", "type", "id", 1, true, new BytesArray("{ \"field1\" : " +
+            GetResponse getResponse = new GetResponse(new GetResult("index", "type", "id", 0, 1, 1, true, new BytesArray("{ \"field1\" : " +
                     "\"value1\", \"field2\":\"value2\"}"), Collections.singletonMap("field1", new DocumentField("field1",
                     Collections.singletonList("value1")))));
             String output = Strings.toString(getResponse);
-            assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":1,\"found\":true,\"_source\":{ \"field1\" " +
-                    ": \"value1\", \"field2\":\"value2\"},\"fields\":{\"field1\":[\"value1\"]}}", output);
+            assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":1,\"_seq_no\":0,\"_primary_term\":1," +
+                "\"found\":true,\"_source\":{ \"field1\" : \"value1\", \"field2\":\"value2\"},\"fields\":{\"field1\":[\"value1\"]}}",
+                output);
         }
         {
-            GetResponse getResponse = new GetResponse(new GetResult("index", "type", "id", 1, false, null, null));
+            GetResponse getResponse = new GetResponse(new GetResult("index", "type", "id", UNASSIGNED_SEQ_NO, 0, 1, false, null, null));
             String output = Strings.toString(getResponse);
             assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"found\":false}", output);
         }
     }
 
     public void testToString() {
-        GetResponse getResponse = new GetResponse(
-                new GetResult("index", "type", "id", 1, true, new BytesArray("{ \"field1\" : " + "\"value1\", \"field2\":\"value2\"}"),
+        GetResponse getResponse = new GetResponse(new GetResult("index", "type", "id", 0, 1, 1, true,
+                    new BytesArray("{ \"field1\" : " + "\"value1\", \"field2\":\"value2\"}"),
                         Collections.singletonMap("field1", new DocumentField("field1", Collections.singletonList("value1")))));
-        assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":1,\"found\":true,\"_source\":{ \"field1\" "
-                + ": \"value1\", \"field2\":\"value2\"},\"fields\":{\"field1\":[\"value1\"]}}", getResponse.toString());
+        assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":1,\"_seq_no\":0,\"_primary_term\":1," +
+            "\"found\":true,\"_source\":{ \"field1\" : \"value1\", \"field2\":\"value2\"},\"fields\":{\"field1\":[\"value1\"]}}",
+            getResponse.toString());
     }
 
     public void testEqualsAndHashcode() {
@@ -119,7 +122,8 @@ public class GetResponseTests extends ESTestCase {
     }
 
     public void testFromXContentThrowsParsingException() throws IOException {
-        GetResponse getResponse = new GetResponse(new GetResult(null, null, null, randomIntBetween(1, 5), randomBoolean(), null, null));
+        GetResponse getResponse =
+            new GetResponse(new GetResult(null, null, null, UNASSIGNED_SEQ_NO, 0, randomIntBetween(1, 5), randomBoolean(), null, null));
 
         XContentType xContentType = randomFrom(XContentType.values());
         BytesReference originalBytes = toShuffledXContent(getResponse, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());

+ 1 - 1
server/src/test/java/org/elasticsearch/action/get/MultiGetResponseTests.java

@@ -70,7 +70,7 @@ public class MultiGetResponseTests extends ESTestCase {
         for (int i = 0; i < items.length; i++) {
             if (randomBoolean()) {
                 items[i] = new MultiGetItemResponse(new GetResponse(new GetResult(
-                        randomAlphaOfLength(4), randomAlphaOfLength(4), randomAlphaOfLength(4), randomNonNegativeLong(),
+                        randomAlphaOfLength(4), randomAlphaOfLength(4), randomAlphaOfLength(4), 0, 1, randomNonNegativeLong(),
                         true, null, null
                 )), null);
             } else {

+ 12 - 11
server/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java

@@ -60,6 +60,7 @@ import java.util.function.Function;
 import static java.util.Collections.emptyMap;
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.elasticsearch.script.MockScriptEngine.mockInlineScript;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
 import static org.hamcrest.CoreMatchers.hasItems;
@@ -359,7 +360,7 @@ public class UpdateRequestTests extends ESTestCase {
                 .scriptedUpsert(true);
             long nowInMillis = randomNonNegativeLong();
             // We simulate that the document is not existing yet
-            GetResult getResult = new GetResult("test", "type1", "2", 0, false, null, null);
+            GetResult getResult = new GetResult("test", "type1", "2", UNASSIGNED_SEQ_NO, 0, 0, false, null, null);
             UpdateHelper.Result result = updateHelper.prepare(new ShardId("test", "_na_", 0), updateRequest, getResult, () -> nowInMillis);
             Streamable action = result.action();
             assertThat(action, instanceOf(IndexRequest.class));
@@ -372,7 +373,7 @@ public class UpdateRequestTests extends ESTestCase {
                 .script(mockInlineScript("ctx._timestamp = ctx._now"))
                 .scriptedUpsert(true);
             // We simulate that the document is not existing yet
-            GetResult getResult = new GetResult("test", "type1", "2", 0, true, new BytesArray("{}"), null);
+            GetResult getResult = new GetResult("test", "type1", "2", 0, 1, 0, true, new BytesArray("{}"), null);
             UpdateHelper.Result result = updateHelper.prepare(new ShardId("test", "_na_", 0), updateRequest, getResult, () -> 42L);
             Streamable action = result.action();
             assertThat(action, instanceOf(IndexRequest.class));
@@ -381,7 +382,7 @@ public class UpdateRequestTests extends ESTestCase {
 
     public void testIndexTimeout() {
         final GetResult getResult =
-                new GetResult("test", "type", "1", 0, true, new BytesArray("{\"f\":\"v\"}"), null);
+                new GetResult("test", "type", "1", 0, 1, 0, true, new BytesArray("{\"f\":\"v\"}"), null);
         final UpdateRequest updateRequest =
                 new UpdateRequest("test", "type", "1")
                         .script(mockInlineScript("return"))
@@ -391,7 +392,7 @@ public class UpdateRequestTests extends ESTestCase {
 
     public void testDeleteTimeout() {
         final GetResult getResult =
-                new GetResult("test", "type", "1", 0, true, new BytesArray("{\"f\":\"v\"}"), null);
+                new GetResult("test", "type", "1", 0, 1, 0, true, new BytesArray("{\"f\":\"v\"}"), null);
         final UpdateRequest updateRequest =
                 new UpdateRequest("test", "type", "1")
                         .script(mockInlineScript("ctx.op = delete"))
@@ -402,7 +403,7 @@ public class UpdateRequestTests extends ESTestCase {
     public void testUpsertTimeout() throws IOException {
         final boolean exists = randomBoolean();
         final BytesReference source = exists ? new BytesArray("{\"f\":\"v\"}") : null;
-        final GetResult getResult = new GetResult("test", "type", "1", 0, exists, source, null);
+        final GetResult getResult = new GetResult("test", "type", "1", UNASSIGNED_SEQ_NO, 0, 0, exists, source, null);
         final XContentBuilder sourceBuilder = jsonBuilder();
         sourceBuilder.startObject();
         {
@@ -535,7 +536,7 @@ public class UpdateRequestTests extends ESTestCase {
     }
 
     public void testRoutingExtraction() throws Exception {
-        GetResult getResult = new GetResult("test", "type", "1", 0, false, null, null);
+        GetResult getResult = new GetResult("test", "type", "1", UNASSIGNED_SEQ_NO, 0, 0, false, null, null);
         IndexRequest indexRequest = new IndexRequest("test", "type", "1");
 
         // There is no routing and parent because the document doesn't exist
@@ -545,7 +546,7 @@ public class UpdateRequestTests extends ESTestCase {
         assertNull(UpdateHelper.calculateRouting(getResult, indexRequest));
 
         // Doc exists but has no source or fields
-        getResult = new GetResult("test", "type", "1", 0, true, null, null);
+        getResult = new GetResult("test", "type", "1", 0, 1, 0, true, null, null);
 
         // There is no routing and parent on either request
         assertNull(UpdateHelper.calculateRouting(getResult, indexRequest));
@@ -554,7 +555,7 @@ public class UpdateRequestTests extends ESTestCase {
         fields.put("_routing", new DocumentField("_routing", Collections.singletonList("routing1")));
 
         // Doc exists and has the parent and routing fields
-        getResult = new GetResult("test", "type", "1", 0, true, null, fields);
+        getResult = new GetResult("test", "type", "1", 0, 1, 0, true, null, fields);
 
         // Use the get result parent and routing
         assertThat(UpdateHelper.calculateRouting(getResult, indexRequest), equalTo("routing1"));
@@ -563,7 +564,7 @@ public class UpdateRequestTests extends ESTestCase {
     @SuppressWarnings("deprecated") // VersionType.FORCE is deprecated
     public void testCalculateUpdateVersion() throws Exception {
         long randomVersion = randomIntBetween(0, 100);
-        GetResult getResult = new GetResult("test", "type", "1", randomVersion, true, new BytesArray("{}"), null);
+        GetResult getResult = new GetResult("test", "type", "1", 0, 1, randomVersion, true, new BytesArray("{}"), null);
 
         UpdateRequest request = new UpdateRequest("test", "type1", "1");
         long version = UpdateHelper.calculateUpdateVersion(request, getResult);
@@ -580,7 +581,7 @@ public class UpdateRequestTests extends ESTestCase {
 
     public void testNoopDetection() throws Exception {
         ShardId shardId = new ShardId("test", "", 0);
-        GetResult getResult = new GetResult("test", "type", "1", 0, true,
+        GetResult getResult = new GetResult("test", "type", "1", 0, 1, 0, true,
                 new BytesArray("{\"body\": \"foo\"}"),
                 null);
 
@@ -611,7 +612,7 @@ public class UpdateRequestTests extends ESTestCase {
 
     public void testUpdateScript() throws Exception {
         ShardId shardId = new ShardId("test", "", 0);
-        GetResult getResult = new GetResult("test", "type", "1", 0, true,
+        GetResult getResult = new GetResult("test", "type", "1", 0, 1, 0, true,
                 new BytesArray("{\"body\": \"bar\"}"),
                 null);
 

+ 3 - 2
server/src/test/java/org/elasticsearch/action/update/UpdateResponseTests.java

@@ -74,11 +74,12 @@ public class UpdateResponseTests extends ESTestCase {
 
             UpdateResponse updateResponse = new UpdateResponse(new ReplicationResponse.ShardInfo(3, 2),
                     new ShardId("books", "books_uuid", 2), "book", "1", 7, 17, 2, UPDATED);
-            updateResponse.setGetResult(new GetResult("books", "book", "1", 2, true, source, fields));
+            updateResponse.setGetResult(new GetResult("books", "book", "1",0, 1, 2, true, source, fields));
 
             String output = Strings.toString(updateResponse);
             assertEquals("{\"_index\":\"books\",\"_type\":\"book\",\"_id\":\"1\",\"_version\":2,\"result\":\"updated\"," +
-                    "\"_shards\":{\"total\":3,\"successful\":2,\"failed\":0},\"_seq_no\":7,\"_primary_term\":17,\"get\":{\"found\":true," +
+                    "\"_shards\":{\"total\":3,\"successful\":2,\"failed\":0},\"_seq_no\":7,\"_primary_term\":17,\"get\":{" +
+                    "\"_seq_no\":0,\"_primary_term\":1,\"found\":true," +
                     "\"_source\":{\"title\":\"Book title\",\"isbn\":\"ABC-123\"},\"fields\":{\"isbn\":[\"ABC-123\"],\"title\":[\"Book " +
                     "title\"]}}}", output);
         }

+ 41 - 24
server/src/test/java/org/elasticsearch/index/get/GetResultTests.java

@@ -44,6 +44,7 @@ import static java.util.Collections.singletonMap;
 import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
 import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
 import static org.elasticsearch.index.get.DocumentFieldTests.randomDocumentField;
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
 
@@ -72,15 +73,16 @@ public class GetResultTests extends ESTestCase {
 
     public void testToXContent() throws IOException {
         {
-            GetResult getResult = new GetResult("index", "type", "id", 1, true, new BytesArray("{ \"field1\" : " +
+            GetResult getResult = new GetResult("index", "type", "id", 0, 1, 1, true, new BytesArray("{ \"field1\" : " +
                     "\"value1\", \"field2\":\"value2\"}"), singletonMap("field1", new DocumentField("field1",
                     singletonList("value1"))));
             String output = Strings.toString(getResult);
-            assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":1,\"found\":true,\"_source\":{ \"field1\" " +
-                    ": \"value1\", \"field2\":\"value2\"},\"fields\":{\"field1\":[\"value1\"]}}", output);
+            assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":1,\"_seq_no\":0,\"_primary_term\":1," +
+                "\"found\":true,\"_source\":{ \"field1\" : \"value1\", \"field2\":\"value2\"},\"fields\":{\"field1\":[\"value1\"]}}",
+                output);
         }
         {
-            GetResult getResult = new GetResult("index", "type", "id", 1, false, null, null);
+            GetResult getResult = new GetResult("index", "type", "id", UNASSIGNED_SEQ_NO, 0, 1, false, null, null);
             String output = Strings.toString(getResult);
             assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"found\":false}", output);
         }
@@ -92,7 +94,7 @@ public class GetResultTests extends ESTestCase {
         GetResult getResult = tuple.v1();
         // We don't expect to retrieve the index/type/id of the GetResult because they are not rendered
         // by the toXContentEmbedded method.
-        GetResult expectedGetResult = new GetResult(null, null, null, -1,
+        GetResult expectedGetResult = new GetResult(null, null, null, tuple.v2().getSeqNo(), tuple.v2().getPrimaryTerm(), -1,
                 tuple.v2().isExists(), tuple.v2().sourceRef(), tuple.v2().getFields());
 
         boolean humanReadable = randomBoolean();
@@ -118,16 +120,16 @@ public class GetResultTests extends ESTestCase {
         fields.put("foo", new DocumentField("foo", singletonList("bar")));
         fields.put("baz", new DocumentField("baz", Arrays.asList("baz_0", "baz_1")));
 
-        GetResult getResult = new GetResult("index", "type", "id", 2, true,
+        GetResult getResult = new GetResult("index", "type", "id", 0, 1, 2, true,
                 new BytesArray("{\"foo\":\"bar\",\"baz\":[\"baz_0\",\"baz_1\"]}"), fields);
 
         BytesReference originalBytes = toXContentEmbedded(getResult, XContentType.JSON, false);
-        assertEquals("{\"found\":true,\"_source\":{\"foo\":\"bar\",\"baz\":[\"baz_0\",\"baz_1\"]}," +
+        assertEquals("{\"_seq_no\":0,\"_primary_term\":1,\"found\":true,\"_source\":{\"foo\":\"bar\",\"baz\":[\"baz_0\",\"baz_1\"]}," +
                 "\"fields\":{\"foo\":[\"bar\"],\"baz\":[\"baz_0\",\"baz_1\"]}}", originalBytes.utf8ToString());
     }
 
     public void testToXContentEmbeddedNotFound() throws IOException {
-        GetResult getResult = new GetResult("index", "type", "id", 1, false, null, null);
+        GetResult getResult = new GetResult("index", "type", "id", UNASSIGNED_SEQ_NO, 0, 1, false, null, null);
 
         BytesReference originalBytes = toXContentEmbedded(getResult, XContentType.JSON, false);
         assertEquals("{\"found\":false}", originalBytes.utf8ToString());
@@ -149,25 +151,34 @@ public class GetResultTests extends ESTestCase {
     }
 
     public static GetResult copyGetResult(GetResult getResult) {
-        return new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(), getResult.getVersion(),
-                getResult.isExists(), getResult.internalSourceRef(), getResult.getFields());
+        return new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(),
+            getResult.getSeqNo(), getResult.getPrimaryTerm(), getResult.getVersion(),
+            getResult.isExists(), getResult.internalSourceRef(), getResult.getFields());
     }
 
     public static GetResult mutateGetResult(GetResult getResult) {
         List<Supplier<GetResult>> mutations = new ArrayList<>();
-        mutations.add(() -> new GetResult(randomUnicodeOfLength(15), getResult.getType(), getResult.getId(), getResult.getVersion(),
+        mutations.add(() -> new GetResult(randomUnicodeOfLength(15), getResult.getType(), getResult.getId(),
+            getResult.getSeqNo(), getResult.getPrimaryTerm(), getResult.getVersion(),
                 getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
-        mutations.add(() -> new GetResult(getResult.getIndex(), randomUnicodeOfLength(15), getResult.getId(), getResult.getVersion(),
-                getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
-        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), randomUnicodeOfLength(15), getResult.getVersion(),
-                getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
-        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(), randomNonNegativeLong(),
-                getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
-        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(), getResult.getVersion(),
-                getResult.isExists() == false, getResult.internalSourceRef(), getResult.getFields()));
-        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(), getResult.getVersion(),
-                getResult.isExists(), RandomObjects.randomSource(random()), getResult.getFields()));
-        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(), getResult.getVersion(),
+        mutations.add(() -> new GetResult(getResult.getIndex(), randomUnicodeOfLength(15), getResult.getId(),
+            getResult.getSeqNo(), getResult.getPrimaryTerm(), getResult.getVersion(),
+            getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
+        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), randomUnicodeOfLength(15),
+            getResult.getSeqNo(), getResult.getPrimaryTerm(), getResult.getVersion(),
+            getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
+        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(),
+            getResult.getSeqNo(), getResult.getPrimaryTerm(), randomNonNegativeLong(),
+            getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()));
+        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(),
+            getResult.isExists() ? UNASSIGNED_SEQ_NO : getResult.getSeqNo(),
+            getResult.isExists() ? 0 : getResult.getPrimaryTerm(),
+            getResult.getVersion(), getResult.isExists() == false, getResult.internalSourceRef(), getResult.getFields()));
+        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(),
+            getResult.getSeqNo(), getResult.getPrimaryTerm(), getResult.getVersion(), getResult.isExists(),
+            RandomObjects.randomSource(random()), getResult.getFields()));
+        mutations.add(() -> new GetResult(getResult.getIndex(), getResult.getType(), getResult.getId(),
+            getResult.getSeqNo(), getResult.getPrimaryTerm(), getResult.getVersion(),
                 getResult.isExists(), getResult.internalSourceRef(), randomDocumentFields(XContentType.JSON).v1()));
         return randomFrom(mutations).get();
     }
@@ -177,12 +188,16 @@ public class GetResultTests extends ESTestCase {
         final String type = randomAlphaOfLengthBetween(3, 10);
         final String id = randomAlphaOfLengthBetween(3, 10);
         final long version;
+        final long seqNo;
+        final long primaryTerm;
         final boolean exists;
         BytesReference source = null;
         Map<String, DocumentField> fields = null;
         Map<String, DocumentField> expectedFields = null;
         if (frequently()) {
             version = randomNonNegativeLong();
+            seqNo = randomNonNegativeLong();
+            primaryTerm = randomLongBetween(1, 100);
             exists = true;
             if (frequently()) {
                 source = RandomObjects.randomSource(random());
@@ -193,11 +208,13 @@ public class GetResultTests extends ESTestCase {
                 expectedFields = tuple.v2();
             }
         } else {
+            seqNo = UNASSIGNED_SEQ_NO;
+            primaryTerm = 0;
             version = -1;
             exists = false;
         }
-        GetResult getResult = new GetResult(index, type, id, version, exists, source, fields);
-        GetResult expectedGetResult = new GetResult(index, type, id, version, exists, source, expectedFields);
+        GetResult getResult = new GetResult(index, type, id, seqNo, primaryTerm, version, exists, source, fields);
+        GetResult expectedGetResult = new GetResult(index, type, id, seqNo, primaryTerm, version, exists, source, expectedFields);
         return Tuple.tuple(getResult, expectedGetResult);
     }
 

+ 2 - 1
server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java

@@ -131,7 +131,8 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
         } catch (IOException ex) {
             throw new ElasticsearchException("boom", ex);
         }
-        return new GetResponse(new GetResult(indexedShapeIndex, indexedShapeType, indexedShapeId, 0, true, new BytesArray(json), null));
+        return new GetResponse(new GetResult(indexedShapeIndex, indexedShapeType, indexedShapeId, 0, 1, 0, true, new BytesArray(json),
+            null));
     }
 
     @After

+ 2 - 1
server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java

@@ -201,7 +201,8 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
         } catch (IOException ex) {
             throw new ElasticsearchException("boom", ex);
         }
-        return new GetResponse(new GetResult(getRequest.index(), getRequest.type(), getRequest.id(), 0, true, new BytesArray(json), null));
+        return new GetResponse(new GetResult(getRequest.index(), getRequest.type(), getRequest.id(), 0, 1, 0, true,
+            new BytesArray(json), null));
     }
 
     public void testNumeric() throws IOException {

+ 7 - 3
server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java

@@ -32,6 +32,7 @@ import org.elasticsearch.test.rest.FakeRestRequest;
 import org.junit.AfterClass;
 
 import static java.util.Collections.emptyMap;
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.elasticsearch.rest.RestStatus.OK;
 import static org.elasticsearch.rest.action.document.RestGetSourceAction.RestGetSourceResponseListener;
 import static org.hamcrest.Matchers.equalTo;
@@ -51,7 +52,8 @@ public class RestGetSourceActionTests extends ESTestCase {
 
     public void testRestGetSourceAction() throws Exception {
         final BytesReference source = new BytesArray("{\"foo\": \"bar\"}");
-        final GetResponse response = new GetResponse(new GetResult("index1", "_doc", "1", -1, true, source, emptyMap()));
+        final GetResponse response =
+            new GetResponse(new GetResult("index1", "_doc", "1", UNASSIGNED_SEQ_NO, 0, -1, true, source, emptyMap()));
 
         final RestResponse restResponse = listener.buildResponse(response);
 
@@ -61,7 +63,8 @@ public class RestGetSourceActionTests extends ESTestCase {
     }
 
     public void testRestGetSourceActionWithMissingDocument() {
-        final GetResponse response = new GetResponse(new GetResult("index1", "_doc", "1", -1, false, null, emptyMap()));
+        final GetResponse response =
+            new GetResponse(new GetResult("index1", "_doc", "1", UNASSIGNED_SEQ_NO, 0, -1, false, null, emptyMap()));
 
         final ResourceNotFoundException exception = expectThrows(ResourceNotFoundException.class, () -> listener.buildResponse(response));
 
@@ -69,7 +72,8 @@ public class RestGetSourceActionTests extends ESTestCase {
     }
 
     public void testRestGetSourceActionWithMissingDocumentSource() {
-        final GetResponse response = new GetResponse(new GetResult("index1", "_doc", "1", -1, true, null, emptyMap()));
+        final GetResponse response =
+            new GetResponse(new GetResult("index1", "_doc", "1", UNASSIGNED_SEQ_NO, 0, -1, true, null, emptyMap()));
 
         final ResourceNotFoundException exception = expectThrows(ResourceNotFoundException.class, () -> listener.buildResponse(response));
 

+ 6 - 6
server/src/test/java/org/elasticsearch/versioning/SimpleVersioningIT.java

@@ -24,6 +24,7 @@ import org.elasticsearch.action.DocWriteRequest;
 import org.elasticsearch.action.DocWriteResponse;
 import org.elasticsearch.action.bulk.BulkResponse;
 import org.elasticsearch.action.delete.DeleteResponse;
+import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.action.index.IndexResponse;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.cluster.metadata.IndexMetaData;
@@ -311,12 +312,11 @@ public class SimpleVersioningIT extends ESIntegTestCase {
         assertThrows(client().prepareDelete("test", "type", "1").setIfMatch(1, 2).execute(), VersionConflictEngineException.class);
 
         client().admin().indices().prepareRefresh().execute().actionGet();
-        // TODO: Enable once get response returns seqNo
-//        for (int i = 0; i < 10; i++) {
-//            final GetResponse response = client().prepareGet("test", "type", "1").get();
-//            assertThat(response.getSeqNo(), equalTo(1L));
-//            assertThat(response.getPrimaryTerm(), equalTo(1L));
-//        }
+        for (int i = 0; i < 10; i++) {
+            final GetResponse response = client().prepareGet("test", "type", "1").get();
+            assertThat(response.getSeqNo(), equalTo(1L));
+            assertThat(response.getPrimaryTerm(), equalTo(1L));
+        }
 
         // search with versioning
         for (int i = 0; i < 10; i++) {

+ 4 - 3
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStoreTests.java

@@ -45,6 +45,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Consumer;
 
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.notNullValue;
@@ -112,7 +113,7 @@ public class NativeUsersStoreTests extends ESTestCase {
                 SecurityIndexManager.SECURITY_INDEX_NAME,
                 NativeUsersStore.INDEX_TYPE,
                 NativeUsersStore.getIdForUser(NativeUsersStore.RESERVED_USER_TYPE, randomAlphaOfLength(12)),
-                1L,
+            0, 1, 1L,
                 true,
                 BytesReference.bytes(jsonBuilder().map(values)),
                 Collections.emptyMap());
@@ -181,7 +182,7 @@ public class NativeUsersStoreTests extends ESTestCase {
                 SecurityIndexManager.SECURITY_INDEX_NAME,
                 NativeUsersStore.INDEX_TYPE,
                 NativeUsersStore.getIdForUser(NativeUsersStore.USER_DOC_TYPE, username),
-                1L,
+                UNASSIGNED_SEQ_NO, 0, 1L,
                 false,
                 null,
                 Collections.emptyMap());
@@ -223,7 +224,7 @@ public class NativeUsersStoreTests extends ESTestCase {
                 SecurityIndexManager.SECURITY_INDEX_NAME,
                 NativeUsersStore.INDEX_TYPE,
                 NativeUsersStore.getIdForUser(NativeUsersStore.USER_DOC_TYPE, username),
-                1L,
+                0, 1, 1L,
                 true,
                 source,
                 Collections.emptyMap());

+ 3 - 2
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java

@@ -56,6 +56,7 @@ import java.util.function.Consumer;
 
 import static java.util.Collections.emptyMap;
 import static org.elasticsearch.common.util.set.Sets.newHashSet;
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.hamcrest.Matchers.arrayContaining;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
@@ -129,7 +130,7 @@ public class NativePrivilegeStoreTests extends ESTestCase {
 
         final String docSource = Strings.toString(sourcePrivilege);
         listener.get().onResponse(new GetResponse(
-            new GetResult(request.index(), request.type(), request.id(), 1L, true, new BytesArray(docSource), emptyMap())
+            new GetResult(request.index(), request.type(), request.id(), 0, 1, 1L, true, new BytesArray(docSource), emptyMap())
         ));
         final ApplicationPrivilegeDescriptor getPrivilege = future.get(1, TimeUnit.SECONDS);
         assertThat(getPrivilege, equalTo(sourcePrivilege));
@@ -146,7 +147,7 @@ public class NativePrivilegeStoreTests extends ESTestCase {
         assertThat(request.id(), equalTo("application-privilege_myapp:admin"));
 
         listener.get().onResponse(new GetResponse(
-            new GetResult(request.index(), request.type(), request.id(), -1, false, null, emptyMap())
+            new GetResult(request.index(), request.type(), request.id(), UNASSIGNED_SEQ_NO, 0, -1, false, null, emptyMap())
         ));
         final ApplicationPrivilegeDescriptor getPrivilege = future.get(1, TimeUnit.SECONDS);
         assertThat(getPrivilege, Matchers.nullValue());

+ 6 - 3
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java

@@ -24,6 +24,7 @@ import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.common.xcontent.DeprecationHandler;
 import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.ObjectPath;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
@@ -53,7 +54,6 @@ import org.elasticsearch.xpack.core.watcher.execution.Wid;
 import org.elasticsearch.xpack.core.watcher.history.WatchRecord;
 import org.elasticsearch.xpack.core.watcher.input.ExecutableInput;
 import org.elasticsearch.xpack.core.watcher.input.Input;
-import org.elasticsearch.common.xcontent.ObjectPath;
 import org.elasticsearch.xpack.core.watcher.transform.ExecutableTransform;
 import org.elasticsearch.xpack.core.watcher.transform.Transform;
 import org.elasticsearch.xpack.core.watcher.trigger.TriggerEvent;
@@ -88,6 +88,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonMap;
 import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.hasSize;
@@ -1148,7 +1149,8 @@ public class ExecutionServiceTests extends ESTestCase {
             if (request.id().equals(id)) {
                 listener.onResponse(response);
             } else {
-                GetResult notFoundResult = new GetResult(request.index(), request.type(), request.id(), -1, false, null, null);
+                GetResult notFoundResult =
+                    new GetResult(request.index(), request.type(), request.id(), UNASSIGNED_SEQ_NO, 0, -1, false, null, null);
                 listener.onResponse(new GetResponse(notFoundResult));
             }
             return null;
@@ -1162,7 +1164,8 @@ public class ExecutionServiceTests extends ESTestCase {
             if (request.id().equals(id)) {
                 listener.onFailure(e);
             } else {
-                GetResult notFoundResult = new GetResult(request.index(), request.type(), request.id(), -1, false, null, null);
+                GetResult notFoundResult =
+                    new GetResult(request.index(), request.type(), request.id(), UNASSIGNED_SEQ_NO, 0, -1, false, null, null);
                 listener.onResponse(new GetResponse(notFoundResult));
             }
             return null;

+ 2 - 1
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transport/actions/ack/TransportAckWatchActionTests.java

@@ -38,6 +38,7 @@ import java.time.Clock;
 import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 
+import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.hamcrest.Matchers.is;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.eq;
@@ -67,7 +68,7 @@ public class TransportAckWatchActionTests extends ESTestCase {
         String watchId = "my_watch_id";
         doAnswer(invocation -> {
             ActionListener<GetResponse> listener = (ActionListener<GetResponse>) invocation.getArguments()[1];
-            listener.onResponse(new GetResponse(new GetResult(Watch.INDEX, Watch.DOC_TYPE, watchId, -1, false,
+            listener.onResponse(new GetResponse(new GetResult(Watch.INDEX, Watch.DOC_TYPE, watchId, UNASSIGNED_SEQ_NO, 0, -1, false,
                     BytesArray.EMPTY, Collections.emptyMap())));
             return null;
         }).when(client).get(anyObject(), anyObject());