This is an automated email from the ASF dual-hosted git repository.
ethanfeng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-celeborn.git
The following commit(s) were added to refs/heads/main by this push:
new bfb39632 [CELEBORN-235] Implement flink plugin. (#1244)
bfb39632 is described below
commit bfb39632d95ad0c6d160f70b0a3ee302d34af8f7
Author: Ethan Feng <[email protected]>
AuthorDate: Fri Feb 17 19:31:12 2023 +0800
[CELEBORN-235] Implement flink plugin. (#1244)
---
.../celeborn/plugin/flink/buffer/Buffer.java | 49 ++
.../plugin/flink/buffer/BufferRecycler.java | 26 +
.../plugin/flink/buffer/CreditListener.java | 47 ++
.../plugin/flink/buffer/TransferBufferPool.java | 218 +++++++
.../plugin/flink/network/ReadClientHandler.java | 8 +
.../flink/readclient/FlinkShuffleClientImpl.java | 121 ++++
.../plugin/flink/readclient/RssBufferStream.java | 159 +++++
.../plugin/flink/RemoteBufferStreamReader.java | 129 ++++
.../plugin/flink/RemoteShuffleDescriptor.java | 6 +-
.../plugin/flink/RemoteShuffleEnvironment.java | 19 +-
.../plugin/flink/RemoteShuffleInputGate.java | 705 +++++++++++++++++++++
.../flink/RemoteShuffleInputGateFactory.java | 123 ++++
.../plugin/flink/RemoteShuffleServiceFactory.java | 8 +-
.../apache/celeborn/client/ShuffleClientImpl.java | 125 ++--
14 files changed, 1680 insertions(+), 63 deletions(-)
diff --git
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/Buffer.java
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/Buffer.java
new file mode 100644
index 00000000..3a4844f3
--- /dev/null
+++
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/Buffer.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.celeborn.plugin.flink.buffer;
+
+import java.nio.ByteBuffer;
+
+import org.apache.flink.shaded.netty4.io.netty.buffer.UnpooledByteBufAllocator;
+import org.apache.flink.shaded.netty4.io.netty.buffer.UnpooledDirectByteBuf;
+
+/**
+ * We use {@link UnpooledDirectByteBuf} directly to reduce one copy from
{@link Buffer} to Netty's
+ * {@link org.apache.flink.shaded.netty4.io.netty.buffer.ByteBuf} when
transmitting data over the
+ * network.
+ */
+public class Buffer extends UnpooledDirectByteBuf {
+
+ private final ByteBuffer buffer;
+
+ private final BufferRecycler recycler;
+
+ public Buffer(ByteBuffer buffer, BufferRecycler recycler, int readableBytes)
{
+ super(UnpooledByteBufAllocator.DEFAULT, buffer, buffer.capacity());
+
+ this.buffer = buffer;
+ this.recycler = recycler;
+ writerIndex(readableBytes);
+ }
+
+ @Override
+ protected void deallocate() {
+ buffer.clear();
+ recycler.recycle(buffer);
+ }
+}
diff --git
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/BufferRecycler.java
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/BufferRecycler.java
new file mode 100644
index 00000000..47a51f97
--- /dev/null
+++
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/BufferRecycler.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.celeborn.plugin.flink.buffer;
+
+import java.nio.ByteBuffer;
+
+/** Recycler which can recycle {@link ByteBuffer}s for reuse. */
+public interface BufferRecycler {
+
+ /** Recycles the target {@link ByteBuffer}. */
+ void recycle(ByteBuffer buffer);
+}
diff --git
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/CreditListener.java
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/CreditListener.java
new file mode 100644
index 00000000..47b9b847
--- /dev/null
+++
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/CreditListener.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.celeborn.plugin.flink.buffer;
+
+/** Listener to be notified when there is any available credit. */
+public abstract class CreditListener {
+
+ private int numCreditsNeeded;
+
+ private boolean isRegistered;
+
+ void increaseNumCreditsNeeded(int numCredits) {
+ numCreditsNeeded += numCredits;
+ }
+
+ void decreaseNumCreditsNeeded(int numCredits) {
+ numCreditsNeeded -= numCredits;
+ }
+
+ int getNumCreditsNeeded() {
+ return numCreditsNeeded;
+ }
+
+ boolean isRegistered() {
+ return isRegistered;
+ }
+
+ void setRegistered(boolean registered) {
+ isRegistered = registered;
+ }
+
+ public abstract void notifyAvailableCredits(int numCredits);
+}
diff --git
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/TransferBufferPool.java
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/TransferBufferPool.java
new file mode 100644
index 00000000..2e07fd16
--- /dev/null
+++
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/buffer/TransferBufferPool.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.celeborn.plugin.flink.buffer;
+
+import static org.apache.celeborn.plugin.flink.utils.Utils.checkArgument;
+import static org.apache.celeborn.plugin.flink.utils.Utils.checkState;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.apache.flink.shaded.netty4.io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** A buffer pool which will dispatch buffers to all {@link CreditListener}s.
*/
+public class TransferBufferPool implements BufferRecycler {
+ private static Logger logger =
LoggerFactory.getLogger(TransferBufferPool.class);
+
+ private static final int MIN_CREDITS_TO_NOTIFY = 2;
+
+ private final Object lock = new Object();
+
+ private final Queue<ByteBuf> buffers = new ArrayDeque<>();
+
+ @GuardedBy("lock")
+ private final Queue<CreditListener> listeners = new ArrayDeque<>();
+
+ @GuardedBy("lock")
+ private int numAvailableBuffers;
+
+ @GuardedBy("lock")
+ private boolean isDestroyed;
+
+ public TransferBufferPool(Collection<ByteBuf> initialBuffers) {
+ synchronized (lock) {
+ buffers.addAll(initialBuffers);
+ numAvailableBuffers += initialBuffers.size();
+ }
+ }
+
+ /** Requests a data transmitting unit. */
+ public ByteBuf requestBuffer() {
+ synchronized (lock) {
+ checkState(!isDestroyed, "Buffer pool has been destroyed.");
+
+ return buffers.poll();
+ }
+ }
+
+ /** Adds an available buffer to this buffer pool. */
+ public void addBuffers(List<? extends ByteBuf> byteBufs) {
+ List<CreditAssignment> creditAssignments;
+ synchronized (lock) {
+ if (isDestroyed) {
+ byteBufs.forEach(ByteBuf::release);
+ return;
+ }
+
+ buffers.addAll(byteBufs);
+ numAvailableBuffers += byteBufs.size();
+ creditAssignments = dispatchReservedCredits();
+ }
+ for (CreditAssignment creditAssignment : creditAssignments) {
+
creditAssignment.getCreditListener().notifyAvailableCredits(creditAssignment.getNumCredits());
+ }
+ }
+
+ /** Tries to reserve buffers for the target {@link CreditListener}. */
+ public void reserveBuffers(CreditListener creditListener, int
numRequiredBuffers) {
+ int numCredits;
+ CreditListener listener = null;
+ synchronized (lock) {
+ if (isDestroyed) {
+ throw new IllegalStateException("Buffer pool has been destroyed.");
+ }
+
+ if (numRequiredBuffers > numAvailableBuffers) {
+ creditListener.increaseNumCreditsNeeded(numRequiredBuffers -
numAvailableBuffers);
+ }
+
+ if (!creditListener.isRegistered() &&
creditListener.getNumCreditsNeeded() > 0) {
+ listeners.add(creditListener);
+ creditListener.setRegistered(true);
+ }
+
+ numCredits = Math.min(numAvailableBuffers, numRequiredBuffers);
+ if (numCredits > 0) {
+ numAvailableBuffers -= numCredits;
+ listener = creditListener;
+ }
+
+ logger.warn("reserveBuffers,numCredits: {}, required: {}", numCredits,
numRequiredBuffers);
+ }
+ if (listener != null) {
+ listener.notifyAvailableCredits(numCredits);
+ }
+ }
+
+ /** Returns the number of available buffers. */
+ public int numBuffers() {
+ synchronized (lock) {
+ return buffers.size();
+ }
+ }
+
+ /** Destroys buffer pool. */
+ public void destroy() {
+ synchronized (lock) {
+ isDestroyed = true;
+ listeners.clear();
+ buffers.forEach(ByteBuf::release);
+ buffers.clear();
+ }
+ }
+
+ /** Returns true if this buffer pool has been destroyed. */
+ public boolean isDestroyed() {
+ synchronized (lock) {
+ return isDestroyed;
+ }
+ }
+
+ @Override
+ public void recycle(ByteBuffer buffer) {
+ List<CreditAssignment> creditAssignments;
+ synchronized (lock) {
+ // unmanaged memory no need to recycle, currently it is used only by
tests
+ if (isDestroyed) {
+ return;
+ }
+
+ buffers.add(new Buffer(buffer, this, 0));
+ ++numAvailableBuffers;
+ creditAssignments = dispatchReservedCredits();
+ }
+ for (CreditAssignment creditAssignment : creditAssignments) {
+
creditAssignment.getCreditListener().notifyAvailableCredits(creditAssignment.getNumCredits());
+ }
+ }
+
+ private int assignCredits(CreditListener creditListener) {
+ assert Thread.holdsLock(lock);
+
+ if (creditListener == null) {
+ return 0;
+ }
+
+ int numCredits = Math.min(creditListener.getNumCreditsNeeded(),
numAvailableBuffers);
+ if (numCredits > 0) {
+ creditListener.decreaseNumCreditsNeeded(numCredits);
+ numAvailableBuffers -= numCredits;
+ }
+
+ if (creditListener.getNumCreditsNeeded() > 0) {
+ listeners.add(creditListener);
+ } else {
+ creditListener.setRegistered(false);
+ }
+ return numCredits;
+ }
+
+ private List<CreditAssignment> dispatchReservedCredits() {
+ assert Thread.holdsLock(lock);
+
+ if (numAvailableBuffers < MIN_CREDITS_TO_NOTIFY || listeners.size() <= 0) {
+ return Collections.emptyList();
+ }
+
+ List<CreditAssignment> creditAssignments = new ArrayList<>();
+ while (numAvailableBuffers > 0 && listeners.size() > 0) {
+ CreditListener creditListener = listeners.poll();
+ int numCredits = assignCredits(creditListener);
+ if (numCredits > 0) {
+ creditAssignments.add(new CreditAssignment(numCredits,
creditListener));
+ }
+ }
+ return creditAssignments;
+ }
+
+ private static class CreditAssignment {
+
+ private final int numCredits;
+ private final CreditListener creditListener;
+
+ CreditAssignment(int numCredits, CreditListener creditListener) {
+ checkArgument(numCredits > 0, "Must be positive.");
+ checkArgument(creditListener != null, "Must be not null.");
+
+ this.numCredits = numCredits;
+ this.creditListener = creditListener;
+ }
+
+ public int getNumCredits() {
+ return numCredits;
+ }
+
+ public CreditListener getCreditListener() {
+ return creditListener;
+ }
+ }
+}
diff --git
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/network/ReadClientHandler.java
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/network/ReadClientHandler.java
index fc0dfacc..0da9f67d 100644
---
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/network/ReadClientHandler.java
+++
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/network/ReadClientHandler.java
@@ -101,4 +101,12 @@ public class ReadClientHandler extends BaseMessageHandler {
public void exceptionCaught(Throwable cause, TransportClient client) {
logger.warn("exception caught {}", client.getSocketAddress(), cause);
}
+
+ public void close() {
+ streamHandlers.clear();
+ for (TransportClient value : streamClients.values()) {
+ value.close();
+ }
+ streamClients.clear();
+ }
}
diff --git
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/readclient/FlinkShuffleClientImpl.java
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/readclient/FlinkShuffleClientImpl.java
new file mode 100644
index 00000000..d11b0dcb
--- /dev/null
+++
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/readclient/FlinkShuffleClientImpl.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.celeborn.plugin.flink.readclient;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.celeborn.client.ShuffleClientImpl;
+import org.apache.celeborn.common.CelebornConf;
+import org.apache.celeborn.common.identity.UserIdentifier;
+import org.apache.celeborn.common.network.TransportContext;
+import org.apache.celeborn.common.network.util.TransportConf;
+import org.apache.celeborn.common.protocol.PartitionLocation;
+import org.apache.celeborn.common.protocol.TransportModuleConstants;
+import org.apache.celeborn.common.util.Utils;
+import org.apache.celeborn.plugin.flink.network.FlinkTransportClientFactory;
+import org.apache.celeborn.plugin.flink.network.ReadClientHandler;
+
+public class FlinkShuffleClientImpl extends ShuffleClientImpl {
+ public static final Logger logger =
LoggerFactory.getLogger(FlinkShuffleClientImpl.class);
+ private static volatile FlinkShuffleClientImpl _instance;
+ private static volatile boolean initialized = false;
+ private FlinkTransportClientFactory flinkTransportClientFactory;
+ private ReadClientHandler readClientHandler = new ReadClientHandler();
+
+ public static FlinkShuffleClientImpl get(
+ String driverHost, int port, CelebornConf conf, UserIdentifier
userIdentifier) {
+ if (null == _instance || !initialized) {
+ synchronized (FlinkShuffleClientImpl.class) {
+ if (null == _instance) {
+ _instance = new FlinkShuffleClientImpl(driverHost, port, conf,
userIdentifier);
+ _instance.setupMetaServiceRef(driverHost, port);
+ initialized = true;
+ } else if (!initialized) {
+ _instance.shutdown();
+ _instance = new FlinkShuffleClientImpl(driverHost, port, conf,
userIdentifier);
+ _instance.setupMetaServiceRef(driverHost, port);
+ initialized = true;
+ }
+ }
+ }
+ return _instance;
+ }
+
+ @Override
+ public void shutdown() {
+ super.shutdown();
+ if (flinkTransportClientFactory != null) {
+ flinkTransportClientFactory.close();
+ }
+ if (readClientHandler != null) {
+ readClientHandler.close();
+ }
+ }
+
+ public FlinkShuffleClientImpl(
+ String driverHost, int port, CelebornConf conf, UserIdentifier
userIdentifier) {
+ super(conf, userIdentifier);
+ String module = TransportModuleConstants.DATA_MODULE;
+ TransportConf dataTransportConf =
+ Utils.fromCelebornConf(conf, module, conf.getInt("celeborn." + module
+ ".io.threads", 8));
+ TransportContext context =
+ new TransportContext(
+ dataTransportConf, readClientHandler,
conf.clientCloseIdleConnections());
+ this.flinkTransportClientFactory = new
FlinkTransportClientFactory(context);
+ this.setupMetaServiceRef(driverHost, port);
+ }
+
+ public RssBufferStream readBufferedPartition(
+ String applicationId,
+ int shuffleId,
+ int partitionId,
+ int subPartitionIndexStart,
+ int subPartitionIndexEnd)
+ throws IOException {
+ String shuffleKey = Utils.makeShuffleKey(applicationId, shuffleId);
+ ReduceFileGroups fileGroups = loadFileGroup(applicationId, shuffleKey,
shuffleId, partitionId);
+ if (fileGroups.partitionGroups.size() == 0
+ || !fileGroups.partitionGroups.containsKey(partitionId)) {
+ logger.warn("Shuffle data is empty for shuffle {} partitionId {}.",
shuffleId, partitionId);
+ return RssBufferStream.empty();
+ } else {
+ return RssBufferStream.create(
+ conf,
+ flinkTransportClientFactory,
+ shuffleKey,
+ fileGroups.partitionGroups.get(partitionId).toArray(new
PartitionLocation[0]),
+ subPartitionIndexStart,
+ subPartitionIndexEnd);
+ }
+ }
+
+ @Override
+ protected ReduceFileGroups updateFileGroup(
+ String applicationId, String shuffleKey, int shuffleId) {
+ ReduceFileGroups newGroup = loadFileGroupInternal(applicationId,
shuffleKey, shuffleId);
+ reduceFileGroupsMap.put(shuffleId, newGroup);
+ return newGroup;
+ }
+
+ public ReadClientHandler getReadClientHandler() {
+ return readClientHandler;
+ }
+}
diff --git
a/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/readclient/RssBufferStream.java
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/readclient/RssBufferStream.java
new file mode 100644
index 00000000..0be757c2
--- /dev/null
+++
b/client-flink/common/src/main/java/org/apache/celeborn/plugin/flink/readclient/RssBufferStream.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.celeborn.plugin.flink.readclient;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import org.apache.flink.shaded.netty4.io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.celeborn.common.CelebornConf;
+import org.apache.celeborn.common.network.client.RpcResponseCallback;
+import org.apache.celeborn.common.network.client.TransportClient;
+import org.apache.celeborn.common.network.protocol.*;
+import org.apache.celeborn.common.protocol.PartitionLocation;
+import org.apache.celeborn.plugin.flink.network.FlinkTransportClientFactory;
+
+public class RssBufferStream {
+
+ private static Logger logger =
LoggerFactory.getLogger(RssBufferStream.class);
+ private CelebornConf conf;
+ private FlinkTransportClientFactory clientFactory;
+ private String shuffleKey;
+ private PartitionLocation[] locations;
+ private int subIndexStart;
+ private int subIndexEnd;
+ private TransportClient client;
+ private int currentLocationIndex = 0;
+ private long streamId = 0;
+
+ public RssBufferStream() {}
+
+ public RssBufferStream(
+ CelebornConf conf,
+ FlinkTransportClientFactory dataClientFactory,
+ String shuffleKey,
+ PartitionLocation[] locations,
+ int subIndexStart,
+ int subIndexEnd) {
+ this.conf = conf;
+ this.clientFactory = dataClientFactory;
+ this.shuffleKey = shuffleKey;
+ this.locations = locations;
+ this.subIndexStart = subIndexStart;
+ this.subIndexEnd = subIndexEnd;
+ }
+
+ public void open(
+ Supplier<ByteBuf> supplier,
+ int initialCredit,
+ FlinkShuffleClientImpl mapShuffleClient,
+ Consumer<RequestMessage> messageConsumer)
+ throws IOException, InterruptedException {
+ this.client =
+ clientFactory.createClient(
+ locations[currentLocationIndex].getHost(),
+ locations[currentLocationIndex].getFetchPort(),
+ -1,
+ supplier);
+ OpenStreamWithCredit openBufferStream =
+ new OpenStreamWithCredit(
+ shuffleKey,
+ locations[currentLocationIndex].getFileName(),
+ subIndexStart,
+ subIndexEnd,
+ initialCredit);
+ long timeoutMs = conf.fetchTimeoutMs();
+ CountDownLatch latch = new CountDownLatch(1);
+ client.sendRpc(
+ openBufferStream.toByteBuffer(),
+ new RpcResponseCallback() {
+
+ @Override
+ public void onSuccess(ByteBuffer response) {
+ StreamHandle streamHandle = (StreamHandle)
Message.decode(response);
+ RssBufferStream.this.streamId = streamHandle.streamId;
+ mapShuffleClient
+ .getReadClientHandler()
+ .registerHandler(streamId, messageConsumer, client);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ throw new RuntimeException("OpenStream failed.", e);
+ }
+ });
+ if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
+ throw new IOException("Send openStream RPC failed to " +
client.getSocketAddress());
+ }
+ logger.debug("rssbufferstream streamid:{}", streamId);
+ }
+
+ public void addCredit(ReadAddCredit addCredit) {
+ this.client.sendRpc(
+ addCredit.toByteBuffer(),
+ new RpcResponseCallback() {
+ @Override
+ public void onSuccess(ByteBuffer response) {
+ // ignore response
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ logger.warn(
+ "Send Add Credit {} failed to {}", addCredit,
client.getSocketAddress().toString());
+ }
+ });
+ }
+
+ public static RssBufferStream empty() {
+ return emptyRssBufferStream;
+ }
+
+ public long getStreamId() {
+ return streamId;
+ }
+
+ public static RssBufferStream create(
+ CelebornConf conf,
+ FlinkTransportClientFactory dataClientFactory,
+ String shuffleKey,
+ PartitionLocation[] locations,
+ int subIndexStart,
+ int subIndexEnd) {
+ if (locations == null || locations.length == 0) {
+ return empty();
+ } else {
+ return new RssBufferStream(
+ conf, dataClientFactory, shuffleKey, locations, subIndexStart,
subIndexEnd);
+ }
+ }
+
+ private static final RssBufferStream emptyRssBufferStream = new
RssBufferStream();
+
+ public TransportClient getClient() {
+ return client;
+ }
+}
diff --git
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteBufferStreamReader.java
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteBufferStreamReader.java
new file mode 100644
index 00000000..80f96942
--- /dev/null
+++
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteBufferStreamReader.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.celeborn.plugin.flink;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.flink.shaded.netty4.io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.celeborn.common.network.protocol.BacklogAnnouncement;
+import org.apache.celeborn.common.network.protocol.ReadAddCredit;
+import org.apache.celeborn.common.network.protocol.RequestMessage;
+import org.apache.celeborn.plugin.flink.buffer.CreditListener;
+import org.apache.celeborn.plugin.flink.buffer.TransferBufferPool;
+import org.apache.celeborn.plugin.flink.protocol.ReadData;
+import org.apache.celeborn.plugin.flink.readclient.FlinkShuffleClientImpl;
+import org.apache.celeborn.plugin.flink.readclient.RssBufferStream;
+
+public class RemoteBufferStreamReader extends CreditListener {
+ private static Logger logger =
LoggerFactory.getLogger(RemoteBufferStreamReader.class);
+ private TransferBufferPool bufferPool;
+ private FlinkShuffleClientImpl client;
+ private String applicationId;
+ private int shuffleId;
+ private int partitionId;
+ private int subPartitionIndexStart;
+ private int subPartitionIndexEnd;
+ private boolean isOpen;
+ private Consumer<ByteBuf> dataListener;
+ private Consumer<Throwable> failureListener;
+ private RssBufferStream bufferStream;
+ private boolean closed = false;
+ private Consumer<RequestMessage> messageConsumer;
+
+ public RemoteBufferStreamReader(
+ FlinkShuffleClientImpl client,
+ ShuffleResourceDescriptor shuffleDescriptor,
+ String applicationId,
+ int startSubIdx,
+ int endSubIdx,
+ TransferBufferPool bufferPool,
+ Consumer<ByteBuf> dataListener,
+ Consumer<Throwable> failureListener) {
+ this.client = client;
+ this.applicationId = applicationId;
+ this.shuffleId = shuffleDescriptor.getShuffleId();
+ this.partitionId = shuffleDescriptor.getPartitionId();
+ this.bufferPool = bufferPool;
+ this.subPartitionIndexStart = startSubIdx;
+ this.subPartitionIndexEnd = endSubIdx;
+ this.dataListener = dataListener;
+ this.failureListener = failureListener;
+ this.messageConsumer =
+ requestMessage -> {
+ if (requestMessage instanceof ReadData) {
+ dataReceived((ReadData) requestMessage);
+ } else if (requestMessage instanceof BacklogAnnouncement) {
+ backlogReceived(((BacklogAnnouncement)
requestMessage).getBacklog());
+ }
+ };
+ }
+
+ public void open(int initialCredit) throws IOException {
+ try {
+ this.bufferStream =
+ client.readBufferedPartition(
+ applicationId, shuffleId, partitionId, subPartitionIndexStart,
subPartitionIndexEnd);
+ bufferStream.open(
+ RemoteBufferStreamReader.this::requestBuffer, initialCredit, client,
messageConsumer);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to openStream.", e);
+ }
+ isOpen = true;
+ }
+
+ public void close() {
+ isOpen = false;
+
client.getReadClientHandler().removeHandler(this.bufferStream.getStreamId());
+ }
+
+ public boolean isOpened() {
+ return isOpen;
+ }
+
+ public void notifyAvailableCredits(int numCredits) {
+ ReadAddCredit addCredit = new
ReadAddCredit(this.bufferStream.getStreamId(), numCredits);
+ bufferStream.addCredit(addCredit);
+ }
+
+ public ByteBuf requestBuffer() {
+ return bufferPool.requestBuffer();
+ }
+
+ public void backlogReceived(int backlog) {
+ bufferPool.reserveBuffers(this, backlog);
+ }
+
+ public void dataReceived(ReadData readData) {
+ logger.debug(
+ "Rss buffer stream reader get streamid {} received readable bytes {}.",
+ readData.getStreamId(),
+ readData.getFlinkBuffer().readableBytes());
+ if (closed) {
+ readData.body().release();
+ return;
+ }
+ int backLogInData = readData.getBacklog();
+ if (backLogInData > 0) {
+ backlogReceived(backLogInData);
+ }
+ dataListener.accept(readData.getFlinkBuffer());
+ }
+}
diff --git
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleDescriptor.java
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleDescriptor.java
index 12db3495..30e050f2 100644
---
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleDescriptor.java
+++
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleDescriptor.java
@@ -30,10 +30,10 @@ public class RemoteShuffleDescriptor implements
ShuffleDescriptor {
private final JobID jobID;
- private final ShuffleResource shuffleResource;
+ private final RemoteShuffleResource shuffleResource;
public RemoteShuffleDescriptor(
- JobID jobID, ResultPartitionID resultPartitionID, ShuffleResource
shuffleResource) {
+ JobID jobID, ResultPartitionID resultPartitionID, RemoteShuffleResource
shuffleResource) {
this.jobID = jobID;
this.resultPartitionID = resultPartitionID;
this.shuffleResource = shuffleResource;
@@ -48,7 +48,7 @@ public class RemoteShuffleDescriptor implements
ShuffleDescriptor {
return jobID;
}
- public ShuffleResource getShuffleResource() {
+ public RemoteShuffleResource getShuffleResource() {
return shuffleResource;
}
diff --git
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleEnvironment.java
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleEnvironment.java
index 0599f433..7ad50963 100644
---
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleEnvironment.java
+++
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleEnvironment.java
@@ -77,6 +77,8 @@ public class RemoteShuffleEnvironment
private final CelebornConf conf;
+ private final RemoteShuffleInputGateFactory inputGateFactory;
+
/**
* @param networkBufferPool Network buffer pool for shuffle read and shuffle
write.
* @param resultPartitionManager A trivial {@link ResultPartitionManager}.
@@ -87,13 +89,13 @@ public class RemoteShuffleEnvironment
NetworkBufferPool networkBufferPool,
ResultPartitionManager resultPartitionManager,
RemoteShuffleResultPartitionFactory resultPartitionFactory,
- // RemoteShuffleInputGateFactory inputGateFactory,
+ RemoteShuffleInputGateFactory inputGateFactory,
CelebornConf conf) {
this.networkBufferPool = networkBufferPool;
this.resultPartitionManager = resultPartitionManager;
this.resultPartitionFactory = resultPartitionFactory;
- // this.inputGateFactory = inputGateFactory;
+ this.inputGateFactory = inputGateFactory;
this.conf = conf;
this.isClosed = false;
}
@@ -123,7 +125,18 @@ public class RemoteShuffleEnvironment
ShuffleIOOwnerContext ownerContext,
PartitionProducerStateProvider producerStateProvider,
List<InputGateDeploymentDescriptor> inputGateDescriptors) {
- return null;
+ synchronized (lock) {
+ checkState(!isClosed, "The RemoteShuffleEnvironment has already been
shut down.");
+
+ IndexedInputGate[] inputGates = new
IndexedInputGate[inputGateDescriptors.size()];
+ for (int gateIndex = 0; gateIndex < inputGates.length; gateIndex++) {
+ InputGateDeploymentDescriptor igdd =
inputGateDescriptors.get(gateIndex);
+ RemoteShuffleInputGate inputGate =
+ inputGateFactory.create(ownerContext.getOwnerName(), gateIndex,
igdd);
+ inputGates[gateIndex] = inputGate;
+ }
+ return Arrays.asList(inputGates);
+ }
}
@Override
diff --git
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleInputGate.java
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleInputGate.java
new file mode 100644
index 00000000..07472f40
--- /dev/null
+++
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleInputGate.java
@@ -0,0 +1,705 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.celeborn.plugin.flink;
+
+import static org.apache.celeborn.plugin.flink.utils.Utils.checkState;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.flink.core.memory.MemorySegment;
+import org.apache.flink.core.memory.MemorySegmentProvider;
+import org.apache.flink.metrics.SimpleCounter;
+import org.apache.flink.runtime.checkpoint.CheckpointOptions;
+import org.apache.flink.runtime.checkpoint.channel.ChannelStateWriter;
+import org.apache.flink.runtime.checkpoint.channel.InputChannelInfo;
+import org.apache.flink.runtime.checkpoint.channel.ResultSubpartitionInfo;
+import org.apache.flink.runtime.deployment.InputGateDeploymentDescriptor;
+import org.apache.flink.runtime.event.AbstractEvent;
+import org.apache.flink.runtime.event.TaskEvent;
+import org.apache.flink.runtime.io.network.ConnectionID;
+import org.apache.flink.runtime.io.network.LocalConnectionManager;
+import org.apache.flink.runtime.io.network.api.CheckpointBarrier;
+import org.apache.flink.runtime.io.network.api.EndOfData;
+import org.apache.flink.runtime.io.network.api.EndOfPartitionEvent;
+import org.apache.flink.runtime.io.network.api.serialization.EventSerializer;
+import org.apache.flink.runtime.io.network.buffer.Buffer;
+import org.apache.flink.runtime.io.network.buffer.BufferDecompressor;
+import org.apache.flink.runtime.io.network.buffer.BufferPool;
+import
org.apache.flink.runtime.io.network.partition.PartitionNotFoundException;
+import org.apache.flink.runtime.io.network.partition.ResultPartitionID;
+import org.apache.flink.runtime.io.network.partition.ResultPartitionType;
+import org.apache.flink.runtime.io.network.partition.consumer.*;
+import org.apache.flink.runtime.jobgraph.IntermediateDataSetID;
+import org.apache.flink.runtime.shuffle.ShuffleDescriptor;
+import org.apache.flink.shaded.netty4.io.netty.buffer.ByteBuf;
+import org.apache.flink.util.CloseableIterator;
+import org.apache.flink.util.ExceptionUtils;
+import org.apache.flink.util.FlinkRuntimeException;
+import org.apache.flink.util.function.SupplierWithException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.celeborn.common.CelebornConf;
+import org.apache.celeborn.common.identity.UserIdentifier;
+import org.apache.celeborn.plugin.flink.buffer.BufferPacker;
+import org.apache.celeborn.plugin.flink.buffer.TransferBufferPool;
+import org.apache.celeborn.plugin.flink.readclient.FlinkShuffleClientImpl;
+import org.apache.celeborn.plugin.flink.utils.BufferUtils;
+
+/** A {@link IndexedInputGate} which ingest data from remote shuffle workers.
*/
+public class RemoteShuffleInputGate extends IndexedInputGate {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(RemoteShuffleInputGate.class);
+
+ /** Lock to protect {@link #receivedBuffers} and {@link #cause} and {@link
#closed}. */
+ private final Object lock = new Object();
+
+ /** Name of the corresponding computing task. */
+ private final String taskName;
+
+ /** Index of the gate of the corresponding computing task. */
+ private final int gateIndex;
+
+ /** Deployment descriptor for a single input gate instance. */
+ private final InputGateDeploymentDescriptor gateDescriptor;
+
+ /** Buffer pool provider. */
+ private final SupplierWithException<BufferPool, IOException>
bufferPoolFactory;
+
+ /** Flink buffer pools to allocate network memory. */
+ private BufferPool bufferPool;
+
+ /** Buffer pool used by the transfer layer. */
+ private final TransferBufferPool transferBufferPool =
+ new TransferBufferPool(Collections.emptySet());
+
+ private final List<RemoteBufferStreamReader> bufferReaders = new
ArrayList<>();
+ private final List<InputChannelInfo> channelsInfo;
+ /** Map from channel index to shuffle client index. */
+ private final int[] clientIndexMap;
+
+ /** Map from shuffle client index to channel index. */
+ private final int[] channelIndexMap;
+
+ /** The number of subpartitions that has not consumed per channel. */
+ private final int[] numSubPartitionsHasNotConsumed;
+
+ /** The overall number of subpartitions that has not been consumed. */
+ private long numUnconsumedSubpartitions;
+
+ /** Received buffers from remote shuffle worker. It's consumed by upper
computing task. */
+ private final Queue<Pair<Buffer, InputChannelInfo>> receivedBuffers = new
LinkedList<>();
+
+ /** {@link Throwable} when reading failure. */
+ private Throwable cause;
+
+ /** Whether this remote input gate has been closed or not. */
+ private boolean closed;
+
+ /** Whether we have opened all initial channels or not. */
+ private boolean initialChannelsOpened;
+
+ /** Number of pending {@link EndOfData} events to be received. */
+ private long pendingEndOfDataEvents;
+ /** Max concurrent reader count */
+ private int numConcurrentReading = Integer.MAX_VALUE;
+ /** Keep compatibility with streaming mode. */
+ private boolean shouldDrainOnEndOfData = true;
+
+ /** Data decompressor. */
+ private final BufferDecompressor bufferDecompressor;
+
+ private FlinkShuffleClientImpl shuffleClient;
+
+ public RemoteShuffleInputGate(
+ CelebornConf celebornConf,
+ String taskName,
+ int gateIndex,
+ InputGateDeploymentDescriptor gateDescriptor,
+ SupplierWithException<BufferPool, IOException> bufferPoolFactory,
+ BufferDecompressor bufferDecompressor) {
+
+ this.taskName = taskName;
+ this.gateIndex = gateIndex;
+ this.gateDescriptor = gateDescriptor;
+ this.bufferPoolFactory = bufferPoolFactory;
+
+ int numChannels = gateDescriptor.getShuffleDescriptors().length;
+ this.clientIndexMap = new int[numChannels];
+ this.channelIndexMap = new int[numChannels];
+ this.numSubPartitionsHasNotConsumed = new int[numChannels];
+ this.bufferDecompressor = bufferDecompressor;
+
+ RemoteShuffleDescriptor remoteShuffleDescriptor =
+ (RemoteShuffleDescriptor) gateDescriptor.getShuffleDescriptors()[0];
+ this.shuffleClient =
+ FlinkShuffleClientImpl.get(
+
remoteShuffleDescriptor.getShuffleResource().getRssMetaServiceHost(),
+
remoteShuffleDescriptor.getShuffleResource().getRssMetaServicePort(),
+ celebornConf,
+ new UserIdentifier("default", "default"));
+
+ this.numUnconsumedSubpartitions = initShuffleReadClients();
+ this.pendingEndOfDataEvents = numUnconsumedSubpartitions;
+ this.channelsInfo = createChannelInfos();
+ }
+
+ private long initShuffleReadClients() {
+ int startSubIdx = gateDescriptor.getConsumedSubpartitionIndex();
+ int endSubIdx = gateDescriptor.getConsumedSubpartitionIndex();
+ int numSubpartitionsPerChannel = endSubIdx - startSubIdx + 1;
+ long numUnconsumedSubpartitions = 0;
+
+ // left element is index
+ List<Pair<Integer, ShuffleDescriptor>> descriptors =
+ IntStream.range(0, gateDescriptor.getShuffleDescriptors().length)
+ .mapToObj(i -> Pair.of(i,
gateDescriptor.getShuffleDescriptors()[i]))
+ .collect(Collectors.toList());
+
+ int clientIndex = 0;
+ for (Pair<Integer, ShuffleDescriptor> descriptor : descriptors) {
+ RemoteShuffleDescriptor remoteDescriptor = (RemoteShuffleDescriptor)
descriptor.getRight();
+ ShuffleResourceDescriptor shuffleDescriptor =
+
remoteDescriptor.getShuffleResource().getMapPartitionShuffleDescriptor();
+
+ LOG.debug("create shuffle reader for descriptor {}", shuffleDescriptor);
+ String applicationId = remoteDescriptor.getJobID().toString();
+
+ RemoteBufferStreamReader reader =
+ new RemoteBufferStreamReader(
+ shuffleClient,
+ shuffleDescriptor,
+ applicationId,
+ startSubIdx,
+ endSubIdx,
+ transferBufferPool,
+ getDataListener(descriptor.getLeft()),
+ getFailureListener(remoteDescriptor.getResultPartitionID()));
+
+ bufferReaders.add(reader);
+ numSubPartitionsHasNotConsumed[descriptor.getLeft()] =
numSubpartitionsPerChannel;
+ numUnconsumedSubpartitions += numSubpartitionsPerChannel;
+ clientIndexMap[descriptor.getLeft()] = clientIndex;
+ channelIndexMap[clientIndex] = descriptor.getLeft();
+ ++clientIndex;
+ }
+ return numUnconsumedSubpartitions;
+ }
+
+ /** Setup gate and build network connections. */
+ @Override
+ public void setup() throws IOException {
+ long startTime = System.nanoTime();
+
+ bufferPool = bufferPoolFactory.get();
+ BufferUtils.reserveNumRequiredBuffers(bufferPool, 16);
+
+ tryRequestBuffers();
+ // Complete availability future though handshake not fired yet, thus to
allow fetcher to
+ // 'pollNext' and fire handshake to remote. This mechanism is to avoid
bookkeeping remote
+ // reading resource before task start processing data from input gate.
+ availabilityHelper.getUnavailableToResetAvailable().complete(null);
+ LOG.info("Set up read gate by {} ms.", (System.nanoTime() - startTime) /
1000_000);
+ }
+
+ /** Index of the gate of the corresponding computing task. */
+ @Override
+ public int getGateIndex() {
+ return gateIndex;
+ }
+
+ /** Get number of input channels. A channel is a data flow from one shuffle
worker. */
+ @Override
+ public int getNumberOfInputChannels() {
+ return bufferReaders.size();
+ }
+
+ /** Whether reading is finished -- all channels are finished and cached
buffers are drained. */
+ @Override
+ public boolean isFinished() {
+ synchronized (lock) {
+ return allReadersEOF() && receivedBuffers.isEmpty();
+ }
+ }
+
+ @Override
+ public Optional<BufferOrEvent> getNext() {
+ throw new UnsupportedOperationException("Not implemented (DataSet API is
not supported).");
+ }
+
+ /** Poll a received {@link BufferOrEvent}. */
+ @Override
+ public Optional<BufferOrEvent> pollNext() throws IOException {
+ if (!initialChannelsOpened) {
+ tryOpenSomeChannels();
+ initialChannelsOpened = true;
+ // DO NOT return, method of 'getReceived' will manipulate
'availabilityHelper'.
+ }
+
+ Pair<Buffer, InputChannelInfo> pair = getReceived();
+ Optional<BufferOrEvent> bufferOrEvent = Optional.empty();
+ LOG.debug("pollNext called with pair null {}", pair == null);
+ while (pair != null) {
+ Buffer buffer = pair.getLeft();
+ InputChannelInfo channelInfo = pair.getRight();
+ LOG.debug("get buffer {} on channel {}", buffer, channelInfo);
+ if (buffer.isBuffer()) {
+ bufferOrEvent = transformBuffer(buffer, channelInfo);
+ } else {
+ bufferOrEvent = transformEvent(buffer, channelInfo);
+ LOG.info("recevied event: " +
bufferOrEvent.get().getEvent().getClass().getName());
+ }
+
+ if (bufferOrEvent.isPresent()) {
+ break;
+ }
+ pair = getReceived();
+ }
+
+ tryRequestBuffers();
+ return bufferOrEvent;
+ }
+
+ private Buffer decompressBufferIfNeeded(Buffer buffer) throws IOException {
+ if (buffer.isCompressed()) {
+ try {
+ checkState(bufferDecompressor != null, "Buffer decompressor not set.");
+ return bufferDecompressor.decompressToIntermediateBuffer(buffer);
+ } catch (Throwable t) {
+ throw new IOException("Decompress failure", t);
+ } finally {
+ buffer.recycleBuffer();
+ }
+ }
+ return buffer;
+ }
+
+ /** Close all reading channels inside this {@link RemoteShuffleInputGate}. */
+ @Override
+ public void close() throws Exception {
+ List<Buffer> buffersToRecycle;
+ Throwable closeException = null;
+ // Do not check closed flag, thus to allow calling this method from both
task thread and
+ // cancel thread.
+ for (RemoteBufferStreamReader shuffleReadClient : bufferReaders) {
+ try {
+ shuffleReadClient.close();
+ } catch (Throwable throwable) {
+ closeException = closeException == null ? throwable : closeException;
+ LOG.error("Failed to close shuffle read client.", throwable);
+ }
+ }
+ synchronized (lock) {
+ buffersToRecycle =
receivedBuffers.stream().map(Pair::getLeft).collect(Collectors.toList());
+ receivedBuffers.clear();
+ closed = true;
+ }
+
+ try {
+ buffersToRecycle.forEach(Buffer::recycleBuffer);
+ } catch (Throwable throwable) {
+ closeException = closeException == null ? throwable : closeException;
+ LOG.error("Failed to recycle buffers.", throwable);
+ }
+
+ try {
+ transferBufferPool.destroy();
+ } catch (Throwable throwable) {
+ closeException = closeException == null ? throwable : closeException;
+ LOG.error("Failed to close transfer buffer pool.", throwable);
+ }
+
+ try {
+ if (bufferPool != null) {
+ bufferPool.lazyDestroy();
+ }
+ } catch (Throwable throwable) {
+ closeException = closeException == null ? throwable : closeException;
+ LOG.error("Failed to close local buffer pool.", throwable);
+ }
+
+ if (closeException != null) {
+ ExceptionUtils.rethrowException(closeException);
+ }
+ }
+
+ /** Get {@link InputChannelInfo}s of this {@link RemoteShuffleInputGate}. */
+ @Override
+ public List<InputChannelInfo> getChannelInfos() {
+ return channelsInfo;
+ }
+
+ /** Each one corresponds to a reading channel. */
+ public List<RemoteBufferStreamReader> getBufferReaders() {
+ return bufferReaders;
+ }
+
+ private List<InputChannelInfo> createChannelInfos() {
+ return IntStream.range(0, gateDescriptor.getShuffleDescriptors().length)
+ .mapToObj(i -> new InputChannelInfo(gateIndex, i))
+ .collect(Collectors.toList());
+ }
+
+ /** Try to open more readers to {@link #numConcurrentReading}. */
+ private void tryOpenSomeChannels() throws IOException {
+ List<RemoteBufferStreamReader> clientsToOpen = new ArrayList<>();
+
+ synchronized (lock) {
+ if (closed) {
+ throw new IOException("Input gate already closed.");
+ }
+
+ LOG.debug("Try open some partition readers.");
+ int numOnGoing = 0;
+ for (int i = 0; i < bufferReaders.size(); i++) {
+ RemoteBufferStreamReader bufferStreamReader = bufferReaders.get(i);
+ LOG.debug(
+ "Trying reader: {}, isOpened={},
numSubPartitionsHasNotConsumed={}.",
+ bufferStreamReader,
+ bufferStreamReader.isOpened(),
+ numSubPartitionsHasNotConsumed[channelIndexMap[i]]);
+ if (numOnGoing >= numConcurrentReading) {
+ break;
+ }
+
+ if (bufferStreamReader.isOpened()
+ && numSubPartitionsHasNotConsumed[channelIndexMap[i]] > 0) {
+ numOnGoing++;
+ continue;
+ }
+
+ if (!bufferStreamReader.isOpened()) {
+ clientsToOpen.add(bufferStreamReader);
+ numOnGoing++;
+ }
+ }
+ }
+
+ for (RemoteBufferStreamReader reader : clientsToOpen) {
+ reader.open(0);
+ }
+ }
+
+ private void tryRequestBuffers() {
+ checkState(bufferPool != null, "Not initialized yet.");
+
+ Buffer buffer;
+ List<ByteBuf> buffers = new ArrayList<>();
+ while ((buffer = bufferPool.requestBuffer()) != null) {
+ buffers.add(buffer.asByteBuf());
+ }
+
+ if (!buffers.isEmpty()) {
+ transferBufferPool.addBuffers(buffers);
+ }
+ }
+
+ private void onBuffer(Buffer buffer, int channelIdx) {
+ synchronized (lock) {
+ if (closed || cause != null) {
+ buffer.recycleBuffer();
+ throw new IllegalStateException("Input gate already closed or
failed.");
+ }
+
+ boolean needRecycle = true;
+ try {
+ boolean wasEmpty = receivedBuffers.isEmpty();
+ InputChannelInfo channelInfo = channelsInfo.get(channelIdx);
+ checkState(channelInfo.getInputChannelIdx() == channelIdx, "Illegal
channel index.");
+ LOG.debug("ReceivedBuffers is adding buffer {} on {}", buffer,
channelInfo);
+ receivedBuffers.add(Pair.of(buffer, channelInfo));
+ needRecycle = false;
+ if (wasEmpty) {
+ availabilityHelper.getUnavailableToResetAvailable().complete(null);
+ }
+ } catch (Throwable throwable) {
+ if (needRecycle) {
+ buffer.recycleBuffer();
+ }
+ throw throwable;
+ }
+ }
+ }
+
+ private Consumer<ByteBuf> getDataListener(int channelIdx) {
+ return byteBuf -> {
+ Queue<Buffer> unpackedBuffers = null;
+ try {
+ unpackedBuffers = BufferPacker.unpack(byteBuf);
+ while (!unpackedBuffers.isEmpty()) {
+ onBuffer(unpackedBuffers.poll(), channelIdx);
+ }
+ } catch (Throwable throwable) {
+ synchronized (lock) {
+ cause = cause == null ? throwable : cause;
+ availabilityHelper.getUnavailableToResetAvailable().complete(null);
+ }
+
+ if (unpackedBuffers != null) {
+ unpackedBuffers.forEach(Buffer::recycleBuffer);
+ }
+ LOG.error("Failed to process the received buffer.", throwable);
+ }
+ };
+ }
+
+ private Consumer<Throwable> getFailureListener(ResultPartitionID rpID) {
+ return throwable -> {
+ synchronized (lock) {
+ if (cause != null) {
+ return;
+ }
+ Class<?> clazz = PartitionNotFoundException.class;
+ if (throwable.getMessage() != null &&
throwable.getMessage().contains(clazz.getName())) {
+ cause = new PartitionNotFoundException(rpID);
+ } else {
+ cause = throwable;
+ }
+ availabilityHelper.getUnavailableToResetAvailable().complete(null);
+ }
+ };
+ }
+
+ private Pair<Buffer, InputChannelInfo> getReceived() throws IOException {
+ synchronized (lock) {
+ healthCheck();
+ if (!receivedBuffers.isEmpty()) {
+ return receivedBuffers.poll();
+ } else {
+ if (!allReadersEOF()) {
+ availabilityHelper.resetUnavailable();
+ }
+ return null;
+ }
+ }
+ }
+
+ private void healthCheck() throws IOException {
+ if (closed) {
+ throw new IOException("Input gate already closed.");
+ }
+ if (cause != null) {
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ } else {
+ throw new IOException(cause);
+ }
+ }
+ }
+
+ private boolean allReadersEOF() {
+ return numUnconsumedSubpartitions <= 0;
+ }
+
+ private Optional<BufferOrEvent> transformBuffer(Buffer buf, InputChannelInfo
info)
+ throws IOException {
+ return Optional.of(
+ new BufferOrEvent(decompressBufferIfNeeded(buf), info, !isFinished(),
false));
+ }
+
+ private Optional<BufferOrEvent> transformEvent(Buffer buffer,
InputChannelInfo channelInfo)
+ throws IOException {
+ final AbstractEvent event;
+ try {
+ event = EventSerializer.fromBuffer(buffer, getClass().getClassLoader());
+ } catch (Throwable t) {
+ throw new IOException("Deserialize failure.", t);
+ } finally {
+ buffer.recycleBuffer();
+ }
+ if (event.getClass() == EndOfPartitionEvent.class) {
+ checkState(
+ numSubPartitionsHasNotConsumed[channelInfo.getInputChannelIdx()] > 0,
+ "BUG -- EndOfPartitionEvent received repeatedly.");
+ numSubPartitionsHasNotConsumed[channelInfo.getInputChannelIdx()]--;
+ numUnconsumedSubpartitions--;
+ // not the real end.
+ if (numSubPartitionsHasNotConsumed[channelInfo.getInputChannelIdx()] !=
0) {
+ return Optional.empty();
+ } else {
+ // the real end.
+
bufferReaders.get(clientIndexMap[channelInfo.getInputChannelIdx()]).close();
+ // tryOpenSomeChannels();
+ if (allReadersEOF()) {
+ availabilityHelper.getUnavailableToResetAvailable().complete(null);
+ }
+ }
+ } else if (event.getClass() == EndOfData.class) {
+ checkState(!hasReceivedEndOfData(), "ERROR ");
+ --pendingEndOfDataEvents;
+ }
+
+ return Optional.of(
+ new BufferOrEvent(
+ event,
+ buffer.getDataType().hasPriority(),
+ channelInfo,
+ !isFinished(),
+ buffer.getSize(),
+ false));
+ }
+
+ @Override
+ public void requestPartitions() {
+ // do-nothing
+ }
+
+ @Override
+ public void checkpointStarted(CheckpointBarrier barrier) {
+ // do-nothing.
+ }
+
+ @Override
+ public void checkpointStopped(long cancelledCheckpointId) {
+ // do-nothing.
+ }
+
+ @Override
+ public int getBuffersInUseCount() {
+ return 0;
+ }
+
+ @Override
+ public void announceBufferSize(int i) {}
+
+ @Override
+ public List<InputChannelInfo> getUnfinishedChannels() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean hasReceivedEndOfData() {
+ return pendingEndOfDataEvents <= 0;
+ }
+
+ @Override
+ public void finishReadRecoveredState() {
+ // do-nothing.
+ }
+
+ @Override
+ public InputChannel getChannel(int channelIndex) {
+ return new FakedRemoteInputChannel(channelIndex);
+ }
+
+ @Override
+ public void sendTaskEvent(TaskEvent event) {
+ throw new FlinkRuntimeException("Method should not be called.");
+ }
+
+ @Override
+ public void resumeConsumption(InputChannelInfo channelInfo) {
+ throw new FlinkRuntimeException("Method should not be called.");
+ }
+
+ @Override
+ public void acknowledgeAllRecordsProcessed(InputChannelInfo
inputChannelInfo) {}
+
+ @Override
+ public CompletableFuture<Void> getStateConsumedFuture() {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "ReadGate [owning task: %s, gate index: %d, descriptor: %s]",
+ taskName, gateIndex, gateDescriptor.toString());
+ }
+
+ /** Accommodation for the incompleteness of Flink pluggable shuffle service.
*/
+ private class FakedRemoteInputChannel extends RemoteInputChannel {
+ FakedRemoteInputChannel(int channelIndex) {
+ super(
+ new SingleInputGate(
+ "",
+ gateIndex,
+ new IntermediateDataSetID(),
+ ResultPartitionType.BLOCKING,
+ 0,
+ 1,
+ (a, b, c) -> {},
+ () -> null,
+ null,
+ new FakedMemorySegmentProvider(),
+ 0),
+ channelIndex,
+ new ResultPartitionID(),
+ new ConnectionID(new InetSocketAddress("", 0), 0),
+ new LocalConnectionManager(),
+ 0,
+ 0,
+ 0,
+ new SimpleCounter(),
+ new SimpleCounter(),
+ new FakedChannelStateWriter());
+ }
+ }
+
+ /** Accommodation for the incompleteness of Flink pluggable shuffle service.
*/
+ private static class FakedMemorySegmentProvider implements
MemorySegmentProvider {
+
+ @Override
+ public Collection<MemorySegment> requestUnpooledMemorySegments(int i)
throws IOException {
+ return null;
+ }
+
+ @Override
+ public void recycleUnpooledMemorySegments(Collection<MemorySegment>
collection)
+ throws IOException {}
+ }
+
+ /** Accommodation for the incompleteness of Flink pluggable shuffle service.
*/
+ private static class FakedChannelStateWriter implements ChannelStateWriter {
+
+ @Override
+ public void start(long cpId, CheckpointOptions checkpointOptions) {}
+
+ @Override
+ public void addInputData(
+ long cpId, InputChannelInfo info, int startSeqNum,
CloseableIterator<Buffer> data) {}
+
+ @Override
+ public void addOutputData(
+ long cpId, ResultSubpartitionInfo info, int startSeqNum, Buffer...
data) {}
+
+ @Override
+ public void finishInput(long checkpointId) {}
+
+ @Override
+ public void finishOutput(long checkpointId) {}
+
+ @Override
+ public void abort(long checkpointId, Throwable cause, boolean cleanup) {}
+
+ @Override
+ public ChannelStateWriteResult getAndRemoveWriteResult(long checkpointId) {
+ return null;
+ }
+
+ @Override
+ public void close() {}
+ }
+}
diff --git
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleInputGateFactory.java
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleInputGateFactory.java
new file mode 100644
index 00000000..76f53549
--- /dev/null
+++
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleInputGateFactory.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.celeborn.plugin.flink;
+
+import java.io.IOException;
+
+import org.apache.flink.runtime.deployment.InputGateDeploymentDescriptor;
+import org.apache.flink.runtime.io.network.buffer.BufferDecompressor;
+import org.apache.flink.runtime.io.network.buffer.BufferPool;
+import org.apache.flink.runtime.io.network.buffer.BufferPoolFactory;
+import org.apache.flink.runtime.io.network.buffer.NetworkBufferPool;
+import org.apache.flink.util.function.SupplierWithException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.celeborn.common.CelebornConf;
+import org.apache.celeborn.plugin.flink.utils.Utils;
+
+/** Factory class to create {@link RemoteShuffleInputGate}. */
+public class RemoteShuffleInputGateFactory {
+
+ public static final int MIN_BUFFERS_PER_GATE = 16;
+
+ private static final Logger LOG =
LoggerFactory.getLogger(RemoteShuffleInputGateFactory.class);
+
+ /** Number of max concurrent reading channels. */
+ private final int numConcurrentReading;
+
+ /** Codec used for compression / decompression. */
+ private static final String compressionCodec = "LZ4";
+
+ /** Network buffer size. */
+ private final int networkBufferSize;
+
+ /**
+ * Network buffer pool used for shuffle read buffers. {@link BufferPool}s
will be created from it
+ * and each of them will be used by a channel exclusively.
+ */
+ private final NetworkBufferPool networkBufferPool;
+
+ /** Sum of buffers. */
+ private final int numBuffersPerGate;
+
+ public static final String MEMORY_PER_INPUT_GATE = "32m";
+ public static final String MIN_MEMORY_PER_GATE = "8m";
+ public static final int NUM_CONCURRENT_READINGS = Integer.MAX_VALUE;
+ private CelebornConf celebornConf;
+
+ public RemoteShuffleInputGateFactory(
+ CelebornConf conf, NetworkBufferPool networkBufferPool, int
networkBufferSize) {
+ long configuredMemorySize =
+
org.apache.celeborn.common.util.Utils.byteStringAsBytes(MEMORY_PER_INPUT_GATE);
+ if (configuredMemorySize < MIN_BUFFERS_PER_GATE) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Insufficient network memory per input gate, please increase %s
to at " + "least %s.",
+ MEMORY_PER_INPUT_GATE, MIN_MEMORY_PER_GATE));
+ }
+
+ this.numBuffersPerGate = Utils.checkedDownCast(configuredMemorySize /
networkBufferSize);
+ if (numBuffersPerGate < MIN_BUFFERS_PER_GATE) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Insufficient network memory per input gate, please increase %s
to at "
+ + "least %d bytes.",
+ MEMORY_PER_INPUT_GATE, networkBufferSize *
MIN_BUFFERS_PER_GATE));
+ }
+
+ this.networkBufferSize = networkBufferSize;
+ this.numConcurrentReading = NUM_CONCURRENT_READINGS;
+ this.networkBufferPool = networkBufferPool;
+ this.celebornConf = conf;
+ }
+
+ /** Create {@link RemoteShuffleInputGate} from {@link
InputGateDeploymentDescriptor}. */
+ public RemoteShuffleInputGate create(
+ String owningTaskName, int gateIndex, InputGateDeploymentDescriptor
igdd) {
+ LOG.info(
+ "Create input gate -- number of buffers per input gate={}, "
+ + "number of concurrent readings={}.",
+ numBuffersPerGate,
+ numConcurrentReading);
+
+ SupplierWithException<BufferPool, IOException> bufferPoolFactory =
+ createBufferPoolFactory(networkBufferPool, numBuffersPerGate);
+ BufferDecompressor bufferDecompressor =
+ new BufferDecompressor(networkBufferSize, compressionCodec);
+
+ return createInputGate(owningTaskName, gateIndex, igdd, bufferPoolFactory,
bufferDecompressor);
+ }
+
+ // For testing.
+ RemoteShuffleInputGate createInputGate(
+ String owningTaskName,
+ int gateIndex,
+ InputGateDeploymentDescriptor igdd,
+ SupplierWithException<BufferPool, IOException> bufferPoolFactory,
+ BufferDecompressor bufferDecompressor) {
+ return new RemoteShuffleInputGate(
+ this.celebornConf, owningTaskName, gateIndex, igdd, bufferPoolFactory,
bufferDecompressor);
+ }
+
+ private SupplierWithException<BufferPool, IOException>
createBufferPoolFactory(
+ BufferPoolFactory bufferPoolFactory, int numBuffers) {
+ return () -> bufferPoolFactory.createBufferPool(numBuffers, numBuffers);
+ }
+}
diff --git
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleServiceFactory.java
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleServiceFactory.java
index 7a93511b..d8ba441c 100644
---
a/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleServiceFactory.java
+++
b/client-flink/flink-1.14/src/main/java/org/apache/celeborn/plugin/flink/RemoteShuffleServiceFactory.java
@@ -74,9 +74,15 @@ public class RemoteShuffleServiceFactory
RemoteShuffleResultPartitionFactory resultPartitionFactory =
new RemoteShuffleResultPartitionFactory(
celebornConf, resultPartitionManager, networkBufferPool,
bufferSize);
+ RemoteShuffleInputGateFactory inputGateFactory =
+ new RemoteShuffleInputGateFactory(celebornConf, networkBufferPool,
bufferSize);
return new RemoteShuffleEnvironment(
- networkBufferPool, resultPartitionManager, resultPartitionFactory,
celebornConf);
+ networkBufferPool,
+ resultPartitionManager,
+ resultPartitionFactory,
+ inputGateFactory,
+ celebornConf);
}
private static int calculateNumberOfNetworkBuffers(MemorySize memorySize,
int bufferSize) {
diff --git
a/client/src/main/java/org/apache/celeborn/client/ShuffleClientImpl.java
b/client/src/main/java/org/apache/celeborn/client/ShuffleClientImpl.java
index 8cdaa486..c44f3492 100644
--- a/client/src/main/java/org/apache/celeborn/client/ShuffleClientImpl.java
+++ b/client/src/main/java/org/apache/celeborn/client/ShuffleClientImpl.java
@@ -75,7 +75,7 @@ public class ShuffleClientImpl extends ShuffleClient {
private static final Random RND = new Random();
- private final CelebornConf conf;
+ protected final CelebornConf conf;
private final UserIdentifier userIdentifier;
@@ -116,9 +116,9 @@ public class ShuffleClientImpl extends ShuffleClient {
}
};
- private static class ReduceFileGroups {
- final Map<Integer, Set<PartitionLocation>> partitionGroups;
- final int[] mapAttempts;
+ protected static class ReduceFileGroups {
+ public final Map<Integer, Set<PartitionLocation>> partitionGroups;
+ public final int[] mapAttempts;
ReduceFileGroups(Map<Integer, Set<PartitionLocation>> partitionGroups,
int[] mapAttempts) {
this.partitionGroups = partitionGroups;
@@ -127,7 +127,7 @@ public class ShuffleClientImpl extends ShuffleClient {
}
// key: shuffleId
- private final Map<Integer, ReduceFileGroups> reduceFileGroupsMap = new
ConcurrentHashMap<>();
+ protected final Map<Integer, ReduceFileGroups> reduceFileGroupsMap = new
ConcurrentHashMap<>();
private ConcurrentHashMap<String, TransportClient> currentClient = new
ConcurrentHashMap<>();
@@ -1244,6 +1244,68 @@ public class ShuffleClientImpl extends ShuffleClient {
applicationId, shuffleId, partitionId, attemptNumber, 0,
Integer.MAX_VALUE);
}
+ protected ReduceFileGroups loadFileGroupInternal(
+ String applicationId, String shuffleKey, int shuffleId) {
+ {
+ long getReducerFileGroupStartTime = System.nanoTime();
+ try {
+ if (driverRssMetaService == null) {
+ logger.warn("Driver endpoint is null!");
+ return null;
+ }
+
+ GetReducerFileGroup getReducerFileGroup = new
GetReducerFileGroup(applicationId, shuffleId);
+
+ GetReducerFileGroupResponse response =
+ driverRssMetaService.askSync(
+ getReducerFileGroup,
+ conf.getReducerFileGroupRpcAskTimeout(),
+ ClassTag$.MODULE$.apply(GetReducerFileGroupResponse.class));
+
+ if (response.status() == StatusCode.SUCCESS) {
+ logger.info(
+ "Shuffle {} request reducer file group success using time:{} ms,
result partition ids: {}",
+ shuffleId,
+ (System.nanoTime() - getReducerFileGroupStartTime) / 1000_000,
+ response.fileGroup().keySet());
+ return new ReduceFileGroups(response.fileGroup(),
response.attempts());
+ } else if (response.status() == StatusCode.STAGE_END_TIME_OUT) {
+ logger.warn(
+ "Request {} return {} for {}",
+ getReducerFileGroup,
+ StatusCode.STAGE_END_TIME_OUT.toString(),
+ shuffleKey);
+ } else if (response.status() == StatusCode.SHUFFLE_DATA_LOST) {
+ logger.warn(
+ "Request {} return {} for {}",
+ getReducerFileGroup,
+ StatusCode.SHUFFLE_DATA_LOST.toString(),
+ shuffleKey);
+ }
+ } catch (Exception e) {
+ logger.error("Exception raised while call GetReducerFileGroup for " +
shuffleKey + ".", e);
+ }
+ return null;
+ }
+ }
+
+ protected ReduceFileGroups updateFileGroup(
+ String applicationId, String shuffleKey, int shuffleId) {
+ return reduceFileGroupsMap.computeIfAbsent(
+ shuffleId, (id) -> loadFileGroupInternal(applicationId, shuffleKey,
shuffleId));
+ }
+
+ protected ReduceFileGroups loadFileGroup(
+ String applicationId, String shuffleKey, int shuffleId, int partitionId)
throws IOException {
+ ReduceFileGroups reduceFileGroups = updateFileGroup(applicationId,
shuffleKey, shuffleId);
+ if (reduceFileGroups == null) {
+ String msg = "Shuffle data lost for shuffle " + shuffleId + " reduce " +
partitionId + "!";
+ logger.error(msg);
+ throw new IOException(msg);
+ }
+ return reduceFileGroups;
+ }
+
@Override
public RssInputStream readPartition(
String applicationId,
@@ -1254,58 +1316,9 @@ public class ShuffleClientImpl extends ShuffleClient {
int endMapIndex)
throws IOException {
String shuffleKey = Utils.makeShuffleKey(applicationId, shuffleId);
- ReduceFileGroups fileGroups =
- reduceFileGroupsMap.computeIfAbsent(
- shuffleId,
- (id) -> {
- long getReducerFileGroupStartTime = System.nanoTime();
- try {
- if (driverRssMetaService == null) {
- logger.warn("Driver endpoint is null!");
- return null;
- }
-
- GetReducerFileGroup getReducerFileGroup =
- new GetReducerFileGroup(applicationId, shuffleId);
-
- GetReducerFileGroupResponse response =
- driverRssMetaService.askSync(
- getReducerFileGroup,
- conf.getReducerFileGroupRpcAskTimeout(),
-
ClassTag$.MODULE$.apply(GetReducerFileGroupResponse.class));
-
- if (response.status() == StatusCode.SUCCESS) {
- logger.info(
- "Shuffle {} request reducer file group success using
time:{} ms, result partition ids: {}",
- shuffleId,
- (System.nanoTime() - getReducerFileGroupStartTime) /
1000_000,
- response.fileGroup().keySet());
- return new ReduceFileGroups(response.fileGroup(),
response.attempts());
- } else if (response.status() == StatusCode.STAGE_END_TIME_OUT)
{
- logger.warn(
- "Request {} return {} for {}",
- getReducerFileGroup,
- StatusCode.STAGE_END_TIME_OUT.toString(),
- shuffleKey);
- } else if (response.status() == StatusCode.SHUFFLE_DATA_LOST) {
- logger.warn(
- "Request {} return {} for {}",
- getReducerFileGroup,
- StatusCode.SHUFFLE_DATA_LOST.toString(),
- shuffleKey);
- }
- } catch (Exception e) {
- logger.error(
- "Exception raised while call GetReducerFileGroup for " +
shuffleKey + ".", e);
- }
- return null;
- });
+ ReduceFileGroups fileGroups = loadFileGroup(applicationId, shuffleKey,
shuffleId, partitionId);
- if (fileGroups == null) {
- String msg = "Shuffle data lost for shuffle " + shuffleId + " reduce " +
partitionId + "!";
- logger.error(msg);
- throw new IOException(msg);
- } else if (fileGroups.partitionGroups.size() == 0
+ if (fileGroups.partitionGroups.size() == 0
|| !fileGroups.partitionGroups.containsKey(partitionId)) {
logger.warn("Shuffle data is empty for shuffle {} partitionId {}.",
shuffleId, partitionId);
return RssInputStream.empty();