143143import org .elasticsearch .test .VersionUtils ;
144144import org .elasticsearch .threadpool .ThreadPool ;
145145import org .hamcrest .MatcherAssert ;
146+ import org .hamcrest .Matchers ;
146147
147148import java .io .Closeable ;
148149import java .io .IOException ;
@@ -1979,7 +1980,7 @@ private int assertOpsOnPrimary(List<Engine.Operation> ops, long currentOpVersion
19791980 currentTerm .set (currentTerm .get () + 1L );
19801981 engine .rollTranslogGeneration ();
19811982 }
1982- final long correctVersion = docDeleted && randomBoolean () ? Versions .MATCH_DELETED : lastOpVersion ;
1983+ final long correctVersion = docDeleted ? Versions .MATCH_DELETED : lastOpVersion ;
19831984 logger .info ("performing [{}]{}{}" ,
19841985 op .operationType ().name ().charAt (0 ),
19851986 versionConflict ? " (conflict " + conflictingVersion + ")" : "" ,
@@ -2002,7 +2003,7 @@ private int assertOpsOnPrimary(List<Engine.Operation> ops, long currentOpVersion
20022003 final Engine .IndexResult result ;
20032004 if (versionedOp ) {
20042005 // TODO: add support for non-existing docs
2005- if (randomBoolean () && lastOpSeqNo != SequenceNumbers .UNASSIGNED_SEQ_NO ) {
2006+ if (randomBoolean () && lastOpSeqNo != SequenceNumbers .UNASSIGNED_SEQ_NO && docDeleted == false ) {
20062007 result = engine .index (indexWithSeq .apply (lastOpSeqNo , lastOpTerm , index ));
20072008 } else {
20082009 result = engine .index (indexWithVersion .apply (correctVersion , index ));
@@ -2037,8 +2038,9 @@ private int assertOpsOnPrimary(List<Engine.Operation> ops, long currentOpVersion
20372038 assertThat (result .getFailure (), instanceOf (VersionConflictEngineException .class ));
20382039 } else {
20392040 final Engine .DeleteResult result ;
2041+ long correctSeqNo = docDeleted ? UNASSIGNED_SEQ_NO : lastOpSeqNo ;
20402042 if (versionedOp && lastOpSeqNo != UNASSIGNED_SEQ_NO && randomBoolean ()) {
2041- result = engine .delete (delWithSeq .apply (lastOpSeqNo , lastOpTerm , delete ));
2043+ result = engine .delete (delWithSeq .apply (correctSeqNo , lastOpTerm , delete ));
20422044 } else if (versionedOp ) {
20432045 result = engine .delete (delWithVersion .apply (correctVersion , delete ));
20442046 } else {
@@ -4312,6 +4314,36 @@ public void testOutOfOrderSequenceNumbersWithVersionConflict() throws IOExceptio
43124314 }
43134315 }
43144316
4317+ /**
4318+ * Test that we do not leak out information on a deleted doc due to it existing in version map. There are at least 2 cases:
4319+ * <ul>
4320+ * <li>Guessing the deleted seqNo makes the operation succeed</li>
4321+ * <li>Providing any other seqNo leaks info that the doc was deleted (and its SeqNo)</li>
4322+ * </ul>
4323+ */
4324+ public void testVersionConflictIgnoreDeletedDoc () throws IOException {
4325+ ParsedDocument doc = testParsedDocument ("1" , null , testDocument (),
4326+ new BytesArray ("{}" .getBytes (Charset .defaultCharset ())), null );
4327+ engine .delete (new Engine .Delete ("test" , "1" , newUid ("1" ), 1 ));
4328+ for (long seqNo : new long []{0 , 1 , randomNonNegativeLong ()}) {
4329+ assertDeletedVersionConflict (engine .index (new Engine .Index (newUid ("1" ), doc , UNASSIGNED_SEQ_NO , 1 ,
4330+ Versions .MATCH_ANY , VersionType .INTERNAL ,
4331+ PRIMARY , randomNonNegativeLong (), IndexRequest .UNSET_AUTO_GENERATED_TIMESTAMP , false , seqNo , 1 )),
4332+ "update: " + seqNo );
4333+
4334+ assertDeletedVersionConflict (engine .delete (new Engine .Delete ("test" , "1" , newUid ("1" ), UNASSIGNED_SEQ_NO , 1 ,
4335+ Versions .MATCH_ANY , VersionType .INTERNAL , PRIMARY , randomNonNegativeLong (), seqNo , 1 )),
4336+ "delete: " + seqNo );
4337+ }
4338+ }
4339+
4340+ private void assertDeletedVersionConflict (Engine .Result result , String operation ) {
4341+ assertNotNull ("Must have failure for " + operation , result .getFailure ());
4342+ assertThat (operation , result .getFailure (), Matchers .instanceOf (VersionConflictEngineException .class ));
4343+ VersionConflictEngineException exception = (VersionConflictEngineException ) result .getFailure ();
4344+ assertThat (operation , exception .getMessage (), containsString ("but no document was found" ));
4345+ }
4346+
43154347 /*
43164348 * This test tests that a no-op does not generate a new sequence number, that no-ops can advance the local checkpoint, and that no-ops
43174349 * are correctly added to the translog.
0 commit comments