All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.flink.runtime.io.network.partition.SpilledSubpartitionView Maven / Gradle / Ivy

/*
 * 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.flink.runtime.io.network.partition;

import org.apache.flink.annotation.VisibleForTesting;
import org.apache.flink.core.memory.MemoryType;
import org.apache.flink.runtime.io.disk.iomanager.BufferFileReader;
import org.apache.flink.runtime.io.disk.iomanager.BufferFileWriter;
import org.apache.flink.runtime.io.disk.iomanager.SynchronousBufferFileReader;
import org.apache.flink.runtime.io.network.buffer.Buffer;
import org.apache.flink.runtime.io.network.partition.ResultSubpartition.BufferAndBacklog;
import org.apache.flink.runtime.util.event.NotificationListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.apache.flink.util.Preconditions.checkArgument;
import static org.apache.flink.util.Preconditions.checkNotNull;

/**
 * Reader for a spilled sub partition.
 *
 * 

The partition availability listener is notified about available buffers * only when the spilling is done. Spilling is done async and if it is still * in progress, we wait with the notification until the spilling is done. * *

Reads of the spilled file are done in synchronously. */ public class SpilledSubpartitionView implements ResultSubpartitionView, NotificationListener { private static final Logger LOG = LoggerFactory.getLogger(SpilledSubpartitionView.class); /** The subpartition this view belongs to. */ private final SpillableSubpartition parent; /** Writer for spills. */ private final BufferFileWriter spillWriter; /** The synchronous file reader to do the actual I/O. */ @GuardedBy("this") private final BufferFileReader fileReader; /** The buffer pool to read data into. */ private final FixedLengthBufferPool bufferPool; /** Buffer availability listener. */ private final BufferAvailabilityListener availabilityListener; /** The total number of spilled buffers. */ private final long numberOfSpilledBuffers; /** Flag indicating whether all resources have been released. */ private AtomicBoolean isReleased = new AtomicBoolean(false); /** The next buffer to hand out. */ @GuardedBy("this") private Buffer nextBuffer; /** Flag indicating whether a spill is still in progress. */ private volatile boolean isSpillInProgress = true; /** The total number of buffers left to read. */ private int remainingBuffers = -1; SpilledSubpartitionView( SpillableSubpartition parent, int memorySegmentSize, BufferFileWriter spillWriter, long numberOfSpilledBuffers, BufferAvailabilityListener availabilityListener) throws IOException { this.parent = checkNotNull(parent); this.bufferPool = new FixedLengthBufferPool(2, memorySegmentSize, MemoryType.HEAP); this.spillWriter = checkNotNull(spillWriter); this.fileReader = new SynchronousBufferFileReader(spillWriter.getChannelID(), false); checkArgument(numberOfSpilledBuffers >= 0); this.numberOfSpilledBuffers = numberOfSpilledBuffers; this.availabilityListener = checkNotNull(availabilityListener); // Check whether async spilling is still in progress. If not, this returns // false and we can notify our availability listener about all available buffers. // Otherwise, we notify only when the spill writer callback happens. if (!spillWriter.registerAllRequestsProcessedListener(this)) { isSpillInProgress = false; availabilityListener.notifyDataAvailable(); LOG.debug("No spilling in progress. Notified about {} available buffers.", numberOfSpilledBuffers); } else { LOG.debug("Spilling in progress. Waiting with notification about {} available buffers.", numberOfSpilledBuffers); } } /** * This is the call back method for the spill writer. If a spill is still * in progress when this view is created we wait until this method is called * before we notify the availability listener. */ @Override public void onNotification() { isSpillInProgress = false; availabilityListener.notifyDataAvailable(); LOG.debug("Finished spilling. Notified about {} available buffers.", numberOfSpilledBuffers); } @Nullable @Override public BufferAndBacklog getNextBuffer() throws IOException, InterruptedException { if (isSpillInProgress) { return null; } if (remainingBuffers < 0) { remainingBuffers = parent.getBuffersInBacklog(); } try { Buffer current; boolean hasMoreData; boolean nextBufferIsEvent; synchronized (this) { if (nextBuffer == null) { current = requestAndFillBuffer(); } else { current = nextBuffer; } nextBuffer = requestAndFillBuffer(); hasMoreData = nextBuffer != null || !fileReader.hasReachedEndOfFile(); nextBufferIsEvent = nextBuffer != null && !nextBuffer.isBuffer(); } if (current == null) { return null; } if (current.isBuffer()) { remainingBuffers -= 1; } return new BufferAndBacklog(current, remainingBuffers > 0 || hasMoreData, remainingBuffers, nextBufferIsEvent); } catch (Throwable t) { // Mark all data retrieval errors as DataConsumptionException. throw new DataConsumptionException(parent.parent.getPartitionId(), t); } } @Nullable private Buffer requestAndFillBuffer() throws IOException, InterruptedException { assert Thread.holdsLock(this); if (fileReader.hasReachedEndOfFile()) { return null; } // TODO This is fragile as we implicitly expect that multiple calls to // this method don't happen before recycling buffers returned earlier. Buffer buffer = bufferPool.requestBufferUnblocking(); if (buffer != null) { fileReader.readInto(buffer); } return buffer; } @Override public void notifyDataAvailable() { // We do the availability listener notification either directly on // construction of this view (when everything has been spilled) or // as soon as spilling is done and we are notified about it in the // #onNotification callback. } @Override public void notifySubpartitionConsumed() throws IOException { parent.onConsumedSubpartition(); } @Override public void releaseAllResources() throws IOException { if (isReleased.compareAndSet(false, true)) { synchronized (this) { fileReader.close(); if (nextBuffer != null) { nextBuffer.recycleBuffer(); nextBuffer = null; } } bufferPool.lazyDestroy(); } } @Override public boolean isReleased() { return parent.isReleased() || isReleased.get(); } @Override public boolean nextBufferIsEvent() { synchronized (this) { if (nextBuffer == null) { try { nextBuffer = requestAndFillBuffer(); } catch (Exception e) { // we can ignore this here (we will get it again once getNextBuffer() is called) return false; } } return nextBuffer != null && !nextBuffer.isBuffer(); } } @Override public synchronized boolean isAvailable() { if (nextBuffer != null) { return true; } return !fileReader.hasReachedEndOfFile(); } @Override public void notifyCreditAdded(int creditDeltas) { // No operations. } @Override public Throwable getFailureCause() { return parent.getFailureCause(); } @Override public String toString() { return String.format("SpilledSubpartitionView(index: %d, buffers: %d) of InternalResultPartition %s", parent.index, numberOfSpilledBuffers, parent.parent.getPartitionId()); } @VisibleForTesting public int getRemainingBuffers() { return remainingBuffers; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy