|
|
@@ -42,6 +42,7 @@ import java.io.IOException;
|
|
|
import java.util.Map;
|
|
|
import java.util.Set;
|
|
|
import java.util.concurrent.Executor;
|
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
|
|
/**
|
|
|
@@ -292,6 +293,7 @@ public final class ExchangeService extends AbstractLifecycleComponent {
|
|
|
final Executor responseExecutor;
|
|
|
|
|
|
final AtomicLong estimatedPageSizeInBytes = new AtomicLong(0L);
|
|
|
+ final AtomicBoolean finished = new AtomicBoolean(false);
|
|
|
|
|
|
TransportRemoteSink(
|
|
|
TransportService transportService,
|
|
|
@@ -311,6 +313,32 @@ public final class ExchangeService extends AbstractLifecycleComponent {
|
|
|
|
|
|
@Override
|
|
|
public void fetchPageAsync(boolean allSourcesFinished, ActionListener<ExchangeResponse> listener) {
|
|
|
+ if (allSourcesFinished) {
|
|
|
+ if (finished.compareAndSet(false, true)) {
|
|
|
+ doFetchPageAsync(true, listener);
|
|
|
+ } else {
|
|
|
+ // already finished or promised
|
|
|
+ listener.onResponse(new ExchangeResponse(blockFactory, null, true));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // already finished
|
|
|
+ if (finished.get()) {
|
|
|
+ listener.onResponse(new ExchangeResponse(blockFactory, null, true));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ doFetchPageAsync(false, ActionListener.wrap(r -> {
|
|
|
+ if (r.finished()) {
|
|
|
+ finished.set(true);
|
|
|
+ }
|
|
|
+ listener.onResponse(r);
|
|
|
+ }, e -> {
|
|
|
+ finished.set(true);
|
|
|
+ listener.onFailure(e);
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void doFetchPageAsync(boolean allSourcesFinished, ActionListener<ExchangeResponse> listener) {
|
|
|
final long reservedBytes = allSourcesFinished ? 0 : estimatedPageSizeInBytes.get();
|
|
|
if (reservedBytes > 0) {
|
|
|
// This doesn't fully protect ESQL from OOM, but reduces the likelihood.
|