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

org.apache.cassandra.db.MeteredFlusher 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.cassandra.db;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

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

import org.apache.cassandra.config.DatabaseDescriptor;

public class MeteredFlusher implements Runnable
{
    private static final Logger logger = LoggerFactory.getLogger(MeteredFlusher.class);

    public void run()
    {
        long allowedSize = calculateAllowedSize();

        // find how much memory non-active memtables are using
        long flushingSize = calculateFlushingSize();
        if (flushingSize > 0)
            logger.debug("Currently flushing {} bytes of {} max", flushingSize, allowedSize);

        List affectedCFs = affectedColumnFamilies();
        long liveSize = 0;

        // flush CFs using more than 1 / (maximum number of memtables it could have in the pipeline)
        // of the total size allotted. Then, flush other CFs in order of size if necessary.
        for (ColumnFamilyStore cfs : affectedCFs)
        {
            int maxInFlight = (int) Math.ceil((double) (1 // live memtable
                                                        + 1 // potentially a flushed memtable being counted by jamm
                                                        + DatabaseDescriptor.getFlushWriters()
                                                        + DatabaseDescriptor.getFlushQueueSize())
                                              / (1 + cfs.indexManager.getIndexesBackedByCfs().size()));
            long size = cfs.getTotalMemtableLiveSize();
            if (allowedSize > flushingSize && size > (allowedSize - flushingSize) / maxInFlight)
            {
                logger.info("flushing high-traffic column family {} (estimated {} bytes)", cfs, size);
                cfs.forceFlush();
            }
            else
            {
                liveSize += size;
            }
        }

        if (liveSize + flushingSize <= allowedSize)
            return;
        logger.info("estimated {} live and {} flushing bytes used by all memtables", liveSize, flushingSize);

        Collections.sort(affectedCFs, new Comparator()
        {
            public int compare(ColumnFamilyStore lhs, ColumnFamilyStore rhs)
            {
                return Long.compare(lhs.getTotalMemtableLiveSize(), rhs.getTotalMemtableLiveSize());
            }
        });

        // flush largest first until we get below our threshold.
        // although it looks like liveSize + flushingSize will stay a constant, it will not if flushes finish
        // while we loop, which is especially likely to happen if the flush queue fills up (so further forceFlush calls block)
        while (!affectedCFs.isEmpty())
        {
            flushingSize = calculateFlushingSize();
            if (liveSize + flushingSize <= allowedSize)
                break;

            ColumnFamilyStore cfs = affectedCFs.remove(affectedCFs.size() - 1);
            long size = cfs.getTotalMemtableLiveSize();
            if (size > 0)
            {
                logger.info("flushing {} to free up {} bytes", cfs, size);
                liveSize -= size;
                cfs.forceFlush();
            }
        }

        logger.trace("memtable memory usage is {} bytes with {} live", liveSize + flushingSize, liveSize);
    }

    private static List affectedColumnFamilies()
    {
        List affected = new ArrayList<>();
        // filter out column families that aren't affected by MeteredFlusher
        for (ColumnFamilyStore cfs : ColumnFamilyStore.all())
            if (cfs.getCompactionStrategy().isAffectedByMeteredFlusher())
                affected.add(cfs);
        return affected;
    }

    private static long calculateAllowedSize()
    {
        long allowed = DatabaseDescriptor.getTotalMemtableSpaceInMB() * 1048576L;
        // deduct the combined memory limit of the tables unaffected by the metered flusher (we don't flush them, we
        // should not count their limits to the total limit either).
        for (ColumnFamilyStore cfs : ColumnFamilyStore.all())
            if (!cfs.getCompactionStrategy().isAffectedByMeteredFlusher())
                allowed -= cfs.getCompactionStrategy().getMemtableReservedSize();
        return allowed;
    }

    private static long calculateFlushingSize()
    {
        ColumnFamilyStore measuredCFS = Memtable.activelyMeasuring;
        long flushing = measuredCFS != null && measuredCFS.getCompactionStrategy().isAffectedByMeteredFlusher()
                      ? measuredCFS.getMemtableThreadSafe().getLiveSize()
                      : 0;
        for (ColumnFamilyStore cfs : ColumnFamilyStore.all())
            if (cfs.getCompactionStrategy().isAffectedByMeteredFlusher())
                for (Memtable memtable : cfs.getMemtablesPendingFlush())
                    flushing += memtable.getLiveSize();
        return flushing;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy