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

org.apache.flink.runtime.throughput.BufferDebloater 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.throughput;

import org.apache.flink.annotation.VisibleForTesting;

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

import java.time.Duration;
import java.util.OptionalInt;

/**
 * Class for automatic calculation of the buffer size based on the current throughput and
 * configuration.
 */
public class BufferDebloater {
    private static final Logger LOG = LoggerFactory.getLogger(BufferDebloater.class);
    private static final long MILLIS_IN_SECOND = 1000;

    private final int gateIndex;
    private final long targetTotalBufferSize;
    private final int maxBufferSize;
    private final int minBufferSize;
    private final double bufferDebloatThresholdFactor;
    private final BufferSizeEMA bufferSizeEMA;

    private Duration lastEstimatedTimeToConsumeBuffers = Duration.ZERO;
    private int lastBufferSize;

    public BufferDebloater(
            int gateIndex,
            long targetTotalBufferSize,
            int maxBufferSize,
            int minBufferSize,
            int bufferDebloatThresholdPercentages,
            long numberOfSamples) {
        this.gateIndex = gateIndex;
        this.targetTotalBufferSize = targetTotalBufferSize;
        this.maxBufferSize = maxBufferSize;
        this.minBufferSize = minBufferSize;
        this.bufferDebloatThresholdFactor = bufferDebloatThresholdPercentages / 100.0;

        this.lastBufferSize = maxBufferSize;
        bufferSizeEMA = new BufferSizeEMA(maxBufferSize, minBufferSize, numberOfSamples);

        LOG.debug(
                "Buffer debloater init settings: gateIndex={}, targetTotalBufferSize={}, maxBufferSize={}, minBufferSize={}, bufferDebloatThresholdPercentages={}, numberOfSamples={}",
                gateIndex,
                targetTotalBufferSize,
                maxBufferSize,
                minBufferSize,
                bufferDebloatThresholdPercentages,
                numberOfSamples);
    }

    public OptionalInt recalculateBufferSize(long currentThroughput, int buffersInUse) {
        int actualBuffersInUse = Math.max(1, buffersInUse);
        long desiredTotalBufferSizeInBytes =
                (currentThroughput * targetTotalBufferSize) / MILLIS_IN_SECOND;

        int newSize =
                bufferSizeEMA.calculateBufferSize(
                        desiredTotalBufferSizeInBytes, actualBuffersInUse);

        lastEstimatedTimeToConsumeBuffers =
                Duration.ofMillis(
                        newSize
                                * actualBuffersInUse
                                * MILLIS_IN_SECOND
                                / Math.max(1, currentThroughput));

        boolean skipUpdate = skipUpdate(newSize);

        LOG.debug(
                "Buffer size recalculation: gateIndex={}, currentSize={}, newSize={}, instantThroughput={}, desiredBufferSize={}, buffersInUse={}, estimatedTimeToConsumeBuffers={}, announceNewSize={}",
                gateIndex,
                lastBufferSize,
                newSize,
                currentThroughput,
                desiredTotalBufferSizeInBytes,
                buffersInUse,
                lastEstimatedTimeToConsumeBuffers,
                !skipUpdate);

        // Skip update if the new value pretty close to the old one.
        if (skipUpdate) {
            return OptionalInt.empty();
        }

        lastBufferSize = newSize;
        return OptionalInt.of(newSize);
    }

    @VisibleForTesting
    boolean skipUpdate(int newSize) {
        if (newSize == lastBufferSize) {
            return true;
        }

        // According to logic of this class newSize can not be less than min or greater than max
        // buffer size but if considering this method independently the behaviour for the small or
        // big value should be the same as for min and max buffer size correspondingly.
        if (newSize <= minBufferSize || newSize >= maxBufferSize) {
            return false;
        }

        int delta = (int) (lastBufferSize * bufferDebloatThresholdFactor);
        return Math.abs(newSize - lastBufferSize) < delta;
    }

    public int getLastBufferSize() {
        return lastBufferSize;
    }

    public Duration getLastEstimatedTimeToConsumeBuffers() {
        return lastEstimatedTimeToConsumeBuffers;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy