|
@@ -27,155 +27,86 @@ import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
|
|
|
import org.elasticsearch.threadpool.ThreadPool;
|
|
|
|
|
|
+import java.util.concurrent.CountDownLatch;
|
|
|
import java.util.concurrent.Semaphore;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.function.BiConsumer;
|
|
|
|
|
|
/**
|
|
|
- * Abstracts the low-level details of bulk request handling
|
|
|
+ * Implements the low-level details of bulk request handling
|
|
|
*/
|
|
|
-abstract class BulkRequestHandler {
|
|
|
- protected final Logger logger;
|
|
|
- protected final BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer;
|
|
|
- protected final ThreadPool threadPool;
|
|
|
-
|
|
|
- protected BulkRequestHandler(BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer, ThreadPool threadPool) {
|
|
|
+public final class BulkRequestHandler {
|
|
|
+ private final Logger logger;
|
|
|
+ private final BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer;
|
|
|
+ private final BulkProcessor.Listener listener;
|
|
|
+ private final Semaphore semaphore;
|
|
|
+ private final Retry retry;
|
|
|
+ private final int concurrentRequests;
|
|
|
+
|
|
|
+ BulkRequestHandler(BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer, BackoffPolicy backoffPolicy,
|
|
|
+ BulkProcessor.Listener listener, ThreadPool threadPool,
|
|
|
+ int concurrentRequests) {
|
|
|
+ assert concurrentRequests >= 0;
|
|
|
this.logger = Loggers.getLogger(getClass());
|
|
|
this.consumer = consumer;
|
|
|
- this.threadPool = threadPool;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public abstract void execute(BulkRequest bulkRequest, long executionId);
|
|
|
-
|
|
|
- public abstract boolean awaitClose(long timeout, TimeUnit unit) throws InterruptedException;
|
|
|
-
|
|
|
-
|
|
|
- public static BulkRequestHandler syncHandler(BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer,
|
|
|
- BackoffPolicy backoffPolicy, BulkProcessor.Listener listener,
|
|
|
- ThreadPool threadPool) {
|
|
|
- return new SyncBulkRequestHandler(consumer, backoffPolicy, listener, threadPool);
|
|
|
+ this.listener = listener;
|
|
|
+ this.concurrentRequests = concurrentRequests;
|
|
|
+ this.retry = new Retry(EsRejectedExecutionException.class, backoffPolicy, threadPool);
|
|
|
+ this.semaphore = new Semaphore(concurrentRequests > 0 ? concurrentRequests : 1);
|
|
|
}
|
|
|
|
|
|
- public static BulkRequestHandler asyncHandler(BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer,
|
|
|
- BackoffPolicy backoffPolicy, BulkProcessor.Listener listener,
|
|
|
- ThreadPool threadPool, int concurrentRequests) {
|
|
|
- return new AsyncBulkRequestHandler(consumer, backoffPolicy, listener, threadPool, concurrentRequests);
|
|
|
- }
|
|
|
-
|
|
|
- private static class SyncBulkRequestHandler extends BulkRequestHandler {
|
|
|
- private final BulkProcessor.Listener listener;
|
|
|
- private final BackoffPolicy backoffPolicy;
|
|
|
-
|
|
|
- SyncBulkRequestHandler(BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer, BackoffPolicy backoffPolicy,
|
|
|
- BulkProcessor.Listener listener, ThreadPool threadPool) {
|
|
|
- super(consumer, threadPool);
|
|
|
- this.backoffPolicy = backoffPolicy;
|
|
|
- this.listener = listener;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void execute(BulkRequest bulkRequest, long executionId) {
|
|
|
- boolean afterCalled = false;
|
|
|
- try {
|
|
|
- listener.beforeBulk(executionId, bulkRequest);
|
|
|
- BulkResponse bulkResponse = Retry
|
|
|
- .on(EsRejectedExecutionException.class)
|
|
|
- .policy(backoffPolicy)
|
|
|
- .using(threadPool)
|
|
|
- .withSyncBackoff(consumer, bulkRequest, Settings.EMPTY);
|
|
|
- afterCalled = true;
|
|
|
- listener.afterBulk(executionId, bulkRequest, bulkResponse);
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- logger.info((Supplier<?>) () -> new ParameterizedMessage("Bulk request {} has been cancelled.", executionId), e);
|
|
|
- if (!afterCalled) {
|
|
|
- listener.afterBulk(executionId, bulkRequest, e);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- logger.warn((Supplier<?>) () -> new ParameterizedMessage("Failed to execute bulk request {}.", executionId), e);
|
|
|
- if (!afterCalled) {
|
|
|
- listener.afterBulk(executionId, bulkRequest, e);
|
|
|
+ public void execute(BulkRequest bulkRequest, long executionId) {
|
|
|
+ Runnable toRelease = () -> {};
|
|
|
+ boolean bulkRequestSetupSuccessful = false;
|
|
|
+ try {
|
|
|
+ listener.beforeBulk(executionId, bulkRequest);
|
|
|
+ semaphore.acquire();
|
|
|
+ toRelease = semaphore::release;
|
|
|
+ CountDownLatch latch = new CountDownLatch(1);
|
|
|
+ retry.withBackoff(consumer, bulkRequest, new ActionListener<BulkResponse>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(BulkResponse response) {
|
|
|
+ try {
|
|
|
+ listener.afterBulk(executionId, bulkRequest, response);
|
|
|
+ } finally {
|
|
|
+ semaphore.release();
|
|
|
+ latch.countDown();
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- @Override
|
|
|
- public boolean awaitClose(long timeout, TimeUnit unit) throws InterruptedException {
|
|
|
- // we are "closed" immediately as there is no request in flight
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static class AsyncBulkRequestHandler extends BulkRequestHandler {
|
|
|
- private final BackoffPolicy backoffPolicy;
|
|
|
- private final BulkProcessor.Listener listener;
|
|
|
- private final Semaphore semaphore;
|
|
|
- private final int concurrentRequests;
|
|
|
-
|
|
|
- private AsyncBulkRequestHandler(BiConsumer<BulkRequest, ActionListener<BulkResponse>> consumer, BackoffPolicy backoffPolicy,
|
|
|
- BulkProcessor.Listener listener, ThreadPool threadPool,
|
|
|
- int concurrentRequests) {
|
|
|
- super(consumer, threadPool);
|
|
|
- this.backoffPolicy = backoffPolicy;
|
|
|
- assert concurrentRequests > 0;
|
|
|
- this.listener = listener;
|
|
|
- this.concurrentRequests = concurrentRequests;
|
|
|
- this.semaphore = new Semaphore(concurrentRequests);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void execute(BulkRequest bulkRequest, long executionId) {
|
|
|
- boolean bulkRequestSetupSuccessful = false;
|
|
|
- boolean acquired = false;
|
|
|
- try {
|
|
|
- listener.beforeBulk(executionId, bulkRequest);
|
|
|
- semaphore.acquire();
|
|
|
- acquired = true;
|
|
|
- Retry.on(EsRejectedExecutionException.class)
|
|
|
- .policy(backoffPolicy)
|
|
|
- .using(threadPool)
|
|
|
- .withAsyncBackoff(consumer, bulkRequest, new ActionListener<BulkResponse>() {
|
|
|
- @Override
|
|
|
- public void onResponse(BulkResponse response) {
|
|
|
- try {
|
|
|
- listener.afterBulk(executionId, bulkRequest, response);
|
|
|
- } finally {
|
|
|
- semaphore.release();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onFailure(Exception e) {
|
|
|
- try {
|
|
|
- listener.afterBulk(executionId, bulkRequest, e);
|
|
|
- } finally {
|
|
|
- semaphore.release();
|
|
|
- }
|
|
|
- }
|
|
|
- }, Settings.EMPTY);
|
|
|
- bulkRequestSetupSuccessful = true;
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- logger.info((Supplier<?>) () -> new ParameterizedMessage("Bulk request {} has been cancelled.", executionId), e);
|
|
|
- listener.afterBulk(executionId, bulkRequest, e);
|
|
|
- } catch (Exception e) {
|
|
|
- logger.warn((Supplier<?>) () -> new ParameterizedMessage("Failed to execute bulk request {}.", executionId), e);
|
|
|
- listener.afterBulk(executionId, bulkRequest, e);
|
|
|
- } finally {
|
|
|
- if (!bulkRequestSetupSuccessful && acquired) { // if we fail on client.bulk() release the semaphore
|
|
|
- semaphore.release();
|
|
|
+ @Override
|
|
|
+ public void onFailure(Exception e) {
|
|
|
+ try {
|
|
|
+ listener.afterBulk(executionId, bulkRequest, e);
|
|
|
+ } finally {
|
|
|
+ semaphore.release();
|
|
|
+ latch.countDown();
|
|
|
+ }
|
|
|
}
|
|
|
+ }, Settings.EMPTY);
|
|
|
+ bulkRequestSetupSuccessful = true;
|
|
|
+ if (concurrentRequests == 0) {
|
|
|
+ latch.await();
|
|
|
+ }
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ logger.info((Supplier<?>) () -> new ParameterizedMessage("Bulk request {} has been cancelled.", executionId), e);
|
|
|
+ listener.afterBulk(executionId, bulkRequest, e);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.warn((Supplier<?>) () -> new ParameterizedMessage("Failed to execute bulk request {}.", executionId), e);
|
|
|
+ listener.afterBulk(executionId, bulkRequest, e);
|
|
|
+ } finally {
|
|
|
+ if (bulkRequestSetupSuccessful == false) { // if we fail on client.bulk() release the semaphore
|
|
|
+ toRelease.run();
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public boolean awaitClose(long timeout, TimeUnit unit) throws InterruptedException {
|
|
|
- if (semaphore.tryAcquire(this.concurrentRequests, timeout, unit)) {
|
|
|
- semaphore.release(this.concurrentRequests);
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
+ boolean awaitClose(long timeout, TimeUnit unit) throws InterruptedException {
|
|
|
+ if (semaphore.tryAcquire(this.concurrentRequests, timeout, unit)) {
|
|
|
+ semaphore.release(this.concurrentRequests);
|
|
|
+ return true;
|
|
|
}
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|