|  | @@ -10,6 +10,7 @@ package org.elasticsearch.action.bulk;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import org.apache.logging.log4j.LogManager;
 | 
	
		
			
				|  |  |  import org.apache.logging.log4j.Logger;
 | 
	
		
			
				|  |  | +import org.elasticsearch.ElasticsearchException;
 | 
	
		
			
				|  |  |  import org.elasticsearch.ExceptionsHelper;
 | 
	
		
			
				|  |  |  import org.elasticsearch.action.ActionListener;
 | 
	
		
			
				|  |  |  import org.elasticsearch.action.index.IndexRequest;
 | 
	
	
		
			
				|  | @@ -17,12 +18,14 @@ import org.elasticsearch.common.bytes.BytesReference;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.unit.ByteSizeUnit;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.unit.ByteSizeValue;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.unit.TimeValue;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.util.concurrent.ThreadContext;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.xcontent.XContentType;
 | 
	
		
			
				|  |  |  import org.elasticsearch.test.ESTestCase;
 | 
	
		
			
				|  |  |  import org.elasticsearch.threadpool.Scheduler;
 | 
	
		
			
				|  |  |  import org.elasticsearch.threadpool.TestThreadPool;
 | 
	
		
			
				|  |  |  import org.elasticsearch.threadpool.ThreadPool;
 | 
	
		
			
				|  |  | +import org.elasticsearch.transport.RemoteTransportException;
 | 
	
		
			
				|  |  |  import org.junit.After;
 | 
	
		
			
				|  |  |  import org.junit.Before;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -41,6 +44,11 @@ import java.util.concurrent.atomic.AtomicInteger;
 | 
	
		
			
				|  |  |  import java.util.concurrent.atomic.AtomicReference;
 | 
	
		
			
				|  |  |  import java.util.function.BiConsumer;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +import static org.hamcrest.Matchers.containsString;
 | 
	
		
			
				|  |  | +import static org.hamcrest.Matchers.equalTo;
 | 
	
		
			
				|  |  | +import static org.hamcrest.Matchers.instanceOf;
 | 
	
		
			
				|  |  | +import static org.hamcrest.Matchers.lessThanOrEqualTo;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  public class BulkProcessorTests extends ESTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private ThreadPool threadPool;
 | 
	
	
		
			
				|  | @@ -96,6 +104,56 @@ public class BulkProcessorTests extends ESTestCase {
 | 
	
		
			
				|  |  |          bulkProcessor.close();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    public void testRetry() throws Exception {
 | 
	
		
			
				|  |  | +        final int maxAttempts = between(1, 3);
 | 
	
		
			
				|  |  | +        final AtomicInteger attemptRef = new AtomicInteger();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer = (request, listener) -> {
 | 
	
		
			
				|  |  | +            final int attempt = attemptRef.incrementAndGet();
 | 
	
		
			
				|  |  | +            assertThat(attempt, lessThanOrEqualTo(maxAttempts));
 | 
	
		
			
				|  |  | +            if (attempt != 1) {
 | 
	
		
			
				|  |  | +                assertThat(Thread.currentThread().getName(), containsString("[BulkProcessorTests-retry-scheduler]"));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (attempt == maxAttempts) {
 | 
	
		
			
				|  |  | +                listener.onFailure(new ElasticsearchException("final failure"));
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                listener.onFailure(new RemoteTransportException("remote", new EsRejectedExecutionException("retryable failure")));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final CountDownLatch countDownLatch = new CountDownLatch(1);
 | 
	
		
			
				|  |  | +        final BulkProcessor.Listener listener = new BulkProcessor.Listener() {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            @Override
 | 
	
		
			
				|  |  | +            public void beforeBulk(long executionId, BulkRequest request) {
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            @Override
 | 
	
		
			
				|  |  | +            public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
 | 
	
		
			
				|  |  | +                fail("afterBulk should not return success");
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            @Override
 | 
	
		
			
				|  |  | +            public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
 | 
	
		
			
				|  |  | +                assertThat(failure, instanceOf(ElasticsearchException.class));
 | 
	
		
			
				|  |  | +                assertThat(failure.getMessage(), equalTo("final failure"));
 | 
	
		
			
				|  |  | +                countDownLatch.countDown();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        try (BulkProcessor bulkProcessor = BulkProcessor
 | 
	
		
			
				|  |  | +                .builder(consumer, listener, "BulkProcessorTests")
 | 
	
		
			
				|  |  | +                .setBackoffPolicy(BackoffPolicy.constantBackoff(TimeValue.ZERO, Integer.MAX_VALUE))
 | 
	
		
			
				|  |  | +                .build()) {
 | 
	
		
			
				|  |  | +            bulkProcessor.add(new IndexRequest());
 | 
	
		
			
				|  |  | +            bulkProcessor.flush();
 | 
	
		
			
				|  |  | +            assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        assertThat(attemptRef.get(), equalTo(maxAttempts));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      public void testConcurrentExecutions() throws Exception {
 | 
	
		
			
				|  |  |          final AtomicBoolean called = new AtomicBoolean(false);
 | 
	
		
			
				|  |  |          final AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
 |