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

io.trino.operator.OutputSpoolingController Maven / Gradle / Ivy

/*
 * Licensed 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 io.trino.operator;

import io.trino.spi.Page;

import static com.google.common.base.Verify.verify;
import static java.lang.Math.clamp;

public class OutputSpoolingController
{
    public enum Mode
    {
        INLINE,
        BUFFER,
        SPOOL
    }

    private long currentSpooledSegmentTarget;
    private final long maximumSpooledSegmentTarget;

    private final long maximumInlinedPositions;
    private final long maximumInlinedSize;

    private long spooledPositions;
    private long spooledPages;
    private long spooledRawBytes;
    private long spooledEncodedBytes;

    private long inlinedPositions;
    private long inlinedPages;
    private long inlinedRawBytes;
    private long bufferedRawSize;
    private long bufferedPositions;

    private Mode mode;

    public OutputSpoolingController(boolean inlineFirstRows, long maximumInlinedPositions, long maximumInlinedSize, long initialSpooledSegmentTarget, long maximumSpooledSegmentTarget)
    {
        this.currentSpooledSegmentTarget = initialSpooledSegmentTarget;
        this.maximumSpooledSegmentTarget = maximumSpooledSegmentTarget;
        this.maximumInlinedPositions = maximumInlinedPositions;
        this.maximumInlinedSize = maximumInlinedSize;

        mode = inlineFirstRows ? Mode.INLINE : Mode.SPOOL;
    }

    public Mode getNextMode(Page page)
    {
        return getNextMode(page.getPositionCount(), page.getSizeInBytes());
    }

    public Mode getNextMode(int positionCount, long sizeInBytes)
    {
        return switch (mode) {
            case INLINE -> {
                // If we still didn't inline maximum number of positions
                if (inlinedPositions + positionCount >= maximumInlinedPositions) {
                    mode = Mode.SPOOL; // switch to spooling mode
                    yield getNextMode(positionCount, sizeInBytes); // and now decide whether to buffer or spool this page
                }

                // We don't want to many inlined segments
                if (inlinedPages > 3) { // or better bound
                    mode = Mode.SPOOL; // switch to spooling mode
                    yield getNextMode(positionCount, sizeInBytes); // and now decide whether to buffer or spool this page
                }

                // If we still didn't inline maximum number of bytes
                if (inlinedRawBytes + sizeInBytes >= maximumInlinedSize) {
                    mode = Mode.SPOOL; // switch to spooling mode
                    yield getNextMode(positionCount, sizeInBytes); // and now decide whether to buffer or spool this page
                }

                verify(bufferedRawSize == 0, "There should be no buffered pages when streaming");
                recordInlined(positionCount, sizeInBytes);
                yield Mode.INLINE; // we are ok to stream this page
            }
            case SPOOL -> {
                if (bufferedRawSize + sizeInBytes >= currentSpooledSegmentTarget) {
                    recordSpooled(bufferedPositions + positionCount, bufferedRawSize + sizeInBytes);
                    yield Mode.SPOOL;
                }

                recordBuffered(positionCount, sizeInBytes);
                yield Mode.BUFFER;
            }

            case BUFFER -> throw new IllegalStateException("Current mode can be either STREAM or SPOOL");
        };
    }

    public void recordSpooled(long rows, long size)
    {
        bufferedRawSize = 0;
        bufferedPositions = 0; // Buffer cleared when spooled

        spooledPositions += rows;
        spooledRawBytes += size;
        spooledPages++;

        // Double spool target size until we reach maximum
        currentSpooledSegmentTarget = clamp(currentSpooledSegmentTarget * 2, currentSpooledSegmentTarget, maximumSpooledSegmentTarget);
    }

    public void recordEncoded(long encodedSize)
    {
        spooledEncodedBytes += encodedSize;
    }

    public void recordInlined(int positionCount, long sizeInBytes)
    {
        inlinedPositions += positionCount;
        inlinedPages++;
        inlinedRawBytes += sizeInBytes;
    }

    public void recordBuffered(int positionCount, long sizeInBytes)
    {
        bufferedPositions += positionCount;
        bufferedRawSize += sizeInBytes;
    }

    public long getSpooledPositions()
    {
        return spooledPositions;
    }

    public long getSpooledPages()
    {
        return spooledPages;
    }

    public long getSpooledRawBytes()
    {
        return spooledRawBytes;
    }

    public long getSpooledEncodedBytes()
    {
        return spooledEncodedBytes;
    }

    public long getInlinedPositions()
    {
        return inlinedPositions;
    }

    public long getInlinedPages()
    {
        return inlinedPages;
    }

    public long getInlinedRawBytes()
    {
        return inlinedRawBytes;
    }

    public long getBufferedRawSize()
    {
        return bufferedRawSize;
    }

    public long getBufferedPositions()
    {
        return bufferedPositions;
    }

    public long getCurrentSpooledSegmentTarget()
    {
        return currentSpooledSegmentTarget;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy