Skip to content

Commit ad67c00

Browse files
authored
Add VersionConflictException.
Original Pull Request #2643 Closes #2467
1 parent 4e9bbe5 commit ad67c00

File tree

4 files changed

+78
-17
lines changed

4 files changed

+78
-17
lines changed

Diff for: src/main/asciidoc/reference/elasticsearch-new.adoc

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
* The `JsonpMapper` for Elasticsearch is now configurable and provided as bean.
99
* Improved AOT runtime hints for Elasticsearch client library classes.
1010
* Add Kotlin extensions and repository coroutine support.
11+
* Introducing `VersionConflictException` class thrown in case thatElasticsearch reports an 409 error with a version conflict.
1112

1213
[[new-features.5-1-0]]
1314
== New in Spring Data Elasticsearch 5.1
1415

1516
* Upgrade to Elasticsearch 8.7.1
16-
* Allow specification of the TLS certificate when connecting to an Elasticsearch 8 cluster
17+
* Allow specification of the TLS certificate when connecting to an Elasticsearch 8 cluster
1718

1819
[[new-features.5-0-0]]
1920
== New in Spring Data Elasticsearch 5.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch;
17+
18+
import org.springframework.dao.DataIntegrityViolationException;
19+
20+
/**
21+
* Exception that is thrown when a version conflict from the server is detected.
22+
*
23+
* @author Peter-Josef Meisch
24+
* @since 5.2
25+
*/
26+
public class VersionConflictException extends DataIntegrityViolationException {
27+
public VersionConflictException(String msg) {
28+
super(msg);
29+
}
30+
31+
public VersionConflictException(String msg, Throwable cause) {
32+
super(msg, cause);
33+
}
34+
}

Diff for: src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchExceptionTranslator.java

+15-9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.data.elasticsearch.NoSuchIndexException;
3333
import org.springframework.data.elasticsearch.ResourceNotFoundException;
3434
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
35+
import org.springframework.data.elasticsearch.VersionConflictException;
3536

3637
/**
3738
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
@@ -68,9 +69,7 @@ public RuntimeException translateException(Throwable throwable) {
6869
@Override
6970
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
7071

71-
if (isSeqNoConflict(ex)) {
72-
return new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict", ex);
73-
}
72+
checkForConflictException(ex);
7473

7574
if (ex instanceof ElasticsearchException elasticsearchException) {
7675

@@ -93,6 +92,10 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
9392

9493
return new ResourceNotFoundException(errorReason);
9594
}
95+
96+
if (response.status() == 409) {
97+
98+
}
9699
String body = JsonUtils.toJson(response, jsonpMapper);
97100

98101
if (errorType != null && errorType.contains("validation_exception")) {
@@ -110,22 +113,25 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
110113
return null;
111114
}
112115

113-
private boolean isSeqNoConflict(Throwable exception) {
116+
private void checkForConflictException(Throwable exception) {
114117
Integer status = null;
115118
String message = null;
116119

117120
if (exception instanceof ResponseException responseException) {
118121
status = responseException.getResponse().getStatusLine().getStatusCode();
119122
message = responseException.getMessage();
120123
} else if (exception.getCause() != null) {
121-
return isSeqNoConflict(exception.getCause());
124+
checkForConflictException(exception.getCause());
122125
}
123126

124127
if (status != null && message != null) {
125-
return status == 409 && message.contains("type\":\"version_conflict_engine_exception")
126-
&& message.contains("version conflict, required seqNo");
128+
if (status == 409 && message.contains("type\":\"version_conflict_engine_exception"))
129+
if (message.contains("version conflict, required seqNo")) {
130+
throw new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict",
131+
exception);
132+
} else if (message.contains("version conflict, current version [")) {
133+
throw new VersionConflictException("Version conflict", exception);
134+
}
127135
}
128-
129-
return false;
130136
}
131137
}

Diff for: src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchIntegrationTests.java

+27-7
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
*/
1616
package org.springframework.data.elasticsearch.core;
1717

18-
import static java.util.Collections.singletonList;
18+
import static java.util.Collections.*;
1919
import static org.assertj.core.api.Assertions.*;
20-
import static org.springframework.data.elasticsearch.annotations.Document.VersionType.EXTERNAL_GTE;
20+
import static org.springframework.data.elasticsearch.annotations.Document.VersionType.*;
2121
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
2222
import static org.springframework.data.elasticsearch.annotations.FieldType.Integer;
23-
import static org.springframework.data.elasticsearch.core.document.Document.create;
24-
import static org.springframework.data.elasticsearch.core.query.StringQuery.MATCH_ALL;
25-
import static org.springframework.data.elasticsearch.utils.IdGenerator.nextIdAsString;
26-
import static org.springframework.data.elasticsearch.utils.IndexBuilder.buildIndex;
23+
import static org.springframework.data.elasticsearch.core.document.Document.*;
24+
import static org.springframework.data.elasticsearch.core.query.StringQuery.*;
25+
import static org.springframework.data.elasticsearch.utils.IdGenerator.*;
26+
import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
2727

2828
import java.lang.Double;
2929
import java.lang.Integer;
@@ -50,6 +50,7 @@
5050
import org.springframework.data.domain.Pageable;
5151
import org.springframework.data.domain.Sort;
5252
import org.springframework.data.elasticsearch.BulkFailureException;
53+
import org.springframework.data.elasticsearch.VersionConflictException;
5354
import org.springframework.data.elasticsearch.annotations.*;
5455
import org.springframework.data.elasticsearch.annotations.Field;
5556
import org.springframework.data.elasticsearch.annotations.ScriptedField;
@@ -2008,7 +2009,7 @@ public void shouldIndexGteEntityWithVersionType() {
20082009

20092010
// reindex with version one below
20102011
assertThatThrownBy(() -> operations.index(indexQueryBuilder.withVersion(entity.getVersion() - 1).build(), index))
2011-
.hasMessageContaining("version").hasMessageContaining("conflict");
2012+
.isInstanceOf(VersionConflictException.class);
20122013
}
20132014

20142015
@Test
@@ -3639,6 +3640,18 @@ void shouldFailWithConflictOnAttemptToSaveWithSameVersion() {
36393640
.allMatch(failureStatus -> failureStatus.status().equals(409));
36403641
}
36413642

3643+
@Test // #2467
3644+
@DisplayName("should throw VersionConflictException when saving invalid version")
3645+
void shouldThrowVersionConflictExceptionWhenSavingInvalidVersion() {
3646+
3647+
var entity = new VersionedEntity("42", 1L);
3648+
operations.save(entity);
3649+
3650+
assertThatThrownBy(() -> {
3651+
operations.save(entity);
3652+
}).isInstanceOf(VersionConflictException.class);
3653+
}
3654+
36423655
// region entities
36433656
@Document(indexName = "#{@indexNameProvider.indexName()}")
36443657
@Setting(shards = 1, replicas = 0, refreshInterval = "-1")
@@ -4431,6 +4444,13 @@ static class VersionedEntity {
44314444
@Nullable
44324445
@Version private Long version;
44334446

4447+
public VersionedEntity() {}
4448+
4449+
public VersionedEntity(@Nullable String id, @Nullable java.lang.Long version) {
4450+
this.id = id;
4451+
this.version = version;
4452+
}
4453+
44344454
@Nullable
44354455
public String getId() {
44364456
return id;

0 commit comments

Comments
 (0)