|
6 | 6 | package org.elasticsearch.xpack.ml.utils.persistence;
|
7 | 7 |
|
8 | 8 | import org.elasticsearch.ElasticsearchException;
|
| 9 | +import org.elasticsearch.ElasticsearchStatusException; |
9 | 10 | import org.elasticsearch.action.ActionListener;
|
10 | 11 | import org.elasticsearch.action.DocWriteRequest;
|
11 | 12 | import org.elasticsearch.action.bulk.BulkAction;
|
|
28 | 29 | import org.elasticsearch.common.CheckedConsumer;
|
29 | 30 | import org.elasticsearch.common.settings.ClusterSettings;
|
30 | 31 | import org.elasticsearch.common.settings.Settings;
|
31 |
| -import org.elasticsearch.index.IndexNotFoundException; |
| 32 | +import org.elasticsearch.index.Index; |
32 | 33 | import org.elasticsearch.index.shard.ShardId;
|
| 34 | +import org.elasticsearch.indices.IndexPrimaryShardNotAllocatedException; |
| 35 | +import org.elasticsearch.rest.RestStatus; |
33 | 36 | import org.elasticsearch.test.ESTestCase;
|
34 | 37 | import org.elasticsearch.threadpool.ThreadPool;
|
35 | 38 | import org.elasticsearch.xpack.core.ClientHelper;
|
@@ -133,7 +136,8 @@ public void testSearchWithRetries_SuccessAfterRetry() {
|
133 | 136 | }
|
134 | 137 |
|
135 | 138 | public void testSearchWithRetries_SuccessAfterRetryDueToException() {
|
136 |
| - doThrow(new IndexNotFoundException("my-index")).doAnswer(withResponse(SEARCH_RESPONSE_SUCCESS)) |
| 139 | + doThrow(new IndexPrimaryShardNotAllocatedException(new Index("my-index", "UUID"))) |
| 140 | + .doAnswer(withResponse(SEARCH_RESPONSE_SUCCESS)) |
137 | 141 | .when(client).execute(eq(SearchAction.INSTANCE), eq(SEARCH_REQUEST), any());
|
138 | 142 |
|
139 | 143 | List<String> messages = new ArrayList<>();
|
@@ -208,6 +212,21 @@ public void testSearchWithRetries_Failure_ShouldNotRetryAfterRandomNumberOfRetri
|
208 | 212 | verify(client, times(maxRetries + 1)).execute(eq(SearchAction.INSTANCE), eq(SEARCH_REQUEST), any());
|
209 | 213 | }
|
210 | 214 |
|
| 215 | + public void testSearchWithRetries_FailureOnIrrecoverableError() { |
| 216 | + resultsPersisterService.setMaxFailureRetries(5); |
| 217 | + |
| 218 | + doAnswer(withFailure(new ElasticsearchStatusException("bad search request", RestStatus.BAD_REQUEST))) |
| 219 | + .when(client).execute(eq(SearchAction.INSTANCE), eq(SEARCH_REQUEST), any()); |
| 220 | + |
| 221 | + ElasticsearchException e = |
| 222 | + expectThrows( |
| 223 | + ElasticsearchException.class, |
| 224 | + () -> resultsPersisterService.searchWithRetry(SEARCH_REQUEST, JOB_ID, () -> true, (s) -> {})); |
| 225 | + assertThat(e.getMessage(), containsString("experienced failure that cannot be automatically retried")); |
| 226 | + |
| 227 | + verify(client).execute(eq(SearchAction.INSTANCE), eq(SEARCH_REQUEST), any()); |
| 228 | + } |
| 229 | + |
211 | 230 | private static Supplier<Boolean> shouldRetryUntil(int maxRetries) {
|
212 | 231 | return new Supplier<Boolean>() {
|
213 | 232 | int retries = 0;
|
@@ -242,6 +261,29 @@ public void testBulkRequestChangeOnFailures() {
|
242 | 261 | assertThat(lastMessage.get(), containsString("failed to index after [1] attempts. Will attempt again in"));
|
243 | 262 | }
|
244 | 263 |
|
| 264 | + public void testBulkRequestChangeOnIrrecoverableFailures() { |
| 265 | + int maxFailureRetries = 10; |
| 266 | + resultsPersisterService.setMaxFailureRetries(maxFailureRetries); |
| 267 | + BulkItemResponse irrecoverable = new BulkItemResponse( |
| 268 | + 2, |
| 269 | + DocWriteRequest.OpType.INDEX, |
| 270 | + new BulkItemResponse.Failure("my-index", "fail", new ElasticsearchStatusException("boom", RestStatus.BAD_REQUEST))); |
| 271 | + doAnswerWithResponses( |
| 272 | + new BulkResponse(new BulkItemResponse[]{irrecoverable, BULK_ITEM_RESPONSE_SUCCESS}, 0L), |
| 273 | + new BulkResponse(new BulkItemResponse[0], 0L)) |
| 274 | + .when(client).execute(eq(BulkAction.INSTANCE), any(), any()); |
| 275 | + |
| 276 | + BulkRequest bulkRequest = new BulkRequest(); |
| 277 | + bulkRequest.add(INDEX_REQUEST_FAILURE); |
| 278 | + bulkRequest.add(INDEX_REQUEST_SUCCESS); |
| 279 | + |
| 280 | + ElasticsearchException ex = expectThrows(ElasticsearchException.class, |
| 281 | + () -> resultsPersisterService.bulkIndexWithRetry(bulkRequest, JOB_ID, () -> true, (s)->{})); |
| 282 | + |
| 283 | + verify(client).execute(eq(BulkAction.INSTANCE), any(), any()); |
| 284 | + assertThat(ex.getMessage(), containsString("experienced failure that cannot be automatically retried.")); |
| 285 | + } |
| 286 | + |
245 | 287 | public void testBulkRequestDoesNotRetryWhenSupplierIsFalse() {
|
246 | 288 | doAnswerWithResponses(
|
247 | 289 | new BulkResponse(new BulkItemResponse[]{BULK_ITEM_RESPONSE_FAILURE, BULK_ITEM_RESPONSE_SUCCESS}, 0L),
|
@@ -317,6 +359,15 @@ private static <Response> Answer<Response> withResponse(Response response) {
|
317 | 359 | };
|
318 | 360 | }
|
319 | 361 |
|
| 362 | + @SuppressWarnings("unchecked") |
| 363 | + private static <Response> Answer<Response> withFailure(Exception failure) { |
| 364 | + return invocationOnMock -> { |
| 365 | + ActionListener<Response> listener = (ActionListener<Response>) invocationOnMock.getArguments()[2]; |
| 366 | + listener.onFailure(failure); |
| 367 | + return null; |
| 368 | + }; |
| 369 | + } |
| 370 | + |
320 | 371 | private static ResultsPersisterService buildResultsPersisterService(OriginSettingClient client) {
|
321 | 372 | CheckedConsumer<Integer, InterruptedException> sleeper = millis -> {};
|
322 | 373 | ThreadPool tp = mock(ThreadPool.class);
|
|
0 commit comments