Search in sources :

Example 6 with RowCacheKey

use of org.apache.cassandra.cache.RowCacheKey in project cassandra by apache.

the class SinglePartitionReadCommand method getThroughCache.

/**
 * Fetch the rows requested if in cache; if not, read it from disk and cache it.
 * <p>
 * If the partition is cached, and the filter given is within its bounds, we return
 * from cache, otherwise from disk.
 * <p>
 * If the partition is is not cached, we figure out what filter is "biggest", read
 * that from disk, then filter the result and either cache that or return it.
 */
@SuppressWarnings("resource")
private UnfilteredRowIterator getThroughCache(ColumnFamilyStore cfs, ReadExecutionController executionController) {
    // CASSANDRA-5732
    assert !cfs.isIndex();
    assert cfs.isRowCacheEnabled() : String.format("Row cache is not enabled on table [%s]", cfs.name);
    RowCacheKey key = new RowCacheKey(metadata(), partitionKey());
    // Attempt a sentinel-read-cache sequence.  if a write invalidates our sentinel, we'll return our
    // (now potentially obsolete) data, but won't cache it. see CASSANDRA-3862
    // TODO: don't evict entire partitions on writes (#2864)
    IRowCacheEntry cached = CacheService.instance.rowCache.get(key);
    if (cached != null) {
        if (cached instanceof RowCacheSentinel) {
            // Some other read is trying to cache the value, just do a normal non-caching read
            Tracing.trace("Row cache miss (race)");
            cfs.metric.rowCacheMiss.inc();
            return queryMemtableAndDisk(cfs, executionController);
        }
        CachedPartition cachedPartition = (CachedPartition) cached;
        if (cfs.isFilterFullyCoveredBy(clusteringIndexFilter(), limits(), cachedPartition, nowInSec(), metadata().enforceStrictLiveness())) {
            cfs.metric.rowCacheHit.inc();
            Tracing.trace("Row cache hit");
            UnfilteredRowIterator unfilteredRowIterator = clusteringIndexFilter().getUnfilteredRowIterator(columnFilter(), cachedPartition);
            cfs.metric.updateSSTableIterated(0);
            return unfilteredRowIterator;
        }
        cfs.metric.rowCacheHitOutOfRange.inc();
        Tracing.trace("Ignoring row cache as cached value could not satisfy query");
        return queryMemtableAndDisk(cfs, executionController);
    }
    cfs.metric.rowCacheMiss.inc();
    Tracing.trace("Row cache miss");
    // Note that on tables with no clustering keys, any positive value of
    // rowsToCache implies caching the full partition
    boolean cacheFullPartitions = metadata().clusteringColumns().size() > 0 ? metadata().params.caching.cacheAllRows() : metadata().params.caching.cacheRows();
    // full partitions, in which case we just always read it all and cache.
    if (cacheFullPartitions || clusteringIndexFilter().isHeadFilter()) {
        RowCacheSentinel sentinel = new RowCacheSentinel();
        boolean sentinelSuccess = CacheService.instance.rowCache.putIfAbsent(key, sentinel);
        boolean sentinelReplaced = false;
        try {
            final int rowsToCache = metadata().params.caching.rowsPerPartitionToCache();
            final boolean enforceStrictLiveness = metadata().enforceStrictLiveness();
            // we close on exception or upon closing the result of this method
            @SuppressWarnings("resource") UnfilteredRowIterator iter = fullPartitionRead(metadata(), nowInSec(), partitionKey()).queryMemtableAndDisk(cfs, executionController);
            try {
                // Use a custom iterator instead of DataLimits to avoid stopping the original iterator
                UnfilteredRowIterator toCacheIterator = new WrappingUnfilteredRowIterator(iter) {

                    private int rowsCounted = 0;

                    @Override
                    public boolean hasNext() {
                        return rowsCounted < rowsToCache && super.hasNext();
                    }

                    @Override
                    public Unfiltered next() {
                        Unfiltered unfiltered = super.next();
                        if (unfiltered.isRow()) {
                            Row row = (Row) unfiltered;
                            if (row.hasLiveData(nowInSec(), enforceStrictLiveness))
                                rowsCounted++;
                        }
                        return unfiltered;
                    }
                };
                // We want to cache only rowsToCache rows
                CachedPartition toCache = CachedBTreePartition.create(toCacheIterator, nowInSec());
                if (sentinelSuccess && !toCache.isEmpty()) {
                    Tracing.trace("Caching {} rows", toCache.rowCount());
                    CacheService.instance.rowCache.replace(key, sentinel, toCache);
                    // Whether or not the previous replace has worked, our sentinel is not in the cache anymore
                    sentinelReplaced = true;
                }
                // We then re-filter out what this query wants.
                // Note that in the case where we don't cache full partitions, it's possible that the current query is interested in more
                // than what we've cached, so we can't just use toCache.
                UnfilteredRowIterator cacheIterator = clusteringIndexFilter().getUnfilteredRowIterator(columnFilter(), toCache);
                if (cacheFullPartitions) {
                    // Everything is guaranteed to be in 'toCache', we're done with 'iter'
                    assert !iter.hasNext();
                    iter.close();
                    return cacheIterator;
                }
                return UnfilteredRowIterators.concat(cacheIterator, clusteringIndexFilter().filterNotIndexed(columnFilter(), iter));
            } catch (RuntimeException | Error e) {
                iter.close();
                throw e;
            }
        } finally {
            if (sentinelSuccess && !sentinelReplaced)
                cfs.invalidateCachedPartition(key);
        }
    }
    Tracing.trace("Fetching data but not populating cache as query does not query from the start of the partition");
    return queryMemtableAndDisk(cfs, executionController);
}
Also used : IRowCacheEntry(org.apache.cassandra.cache.IRowCacheEntry) RowCacheSentinel(org.apache.cassandra.cache.RowCacheSentinel) RowCacheKey(org.apache.cassandra.cache.RowCacheKey)

Aggregations

RowCacheKey (org.apache.cassandra.cache.RowCacheKey)6 Test (org.junit.Test)3 ByteBuffer (java.nio.ByteBuffer)2 CachedPartition (org.apache.cassandra.db.partitions.CachedPartition)2 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 TreeSet (java.util.TreeSet)1 MBeanServer (javax.management.MBeanServer)1 MalformedObjectNameException (javax.management.MalformedObjectNameException)1 ObjectName (javax.management.ObjectName)1 IRowCacheEntry (org.apache.cassandra.cache.IRowCacheEntry)1 KeyCacheKey (org.apache.cassandra.cache.KeyCacheKey)1 RowCacheSentinel (org.apache.cassandra.cache.RowCacheSentinel)1 JMXEnabledThreadPoolExecutorMBean (org.apache.cassandra.concurrent.JMXEnabledThreadPoolExecutorMBean)1 ColumnFamily (org.apache.cassandra.db.ColumnFamily)1 ColumnFamilyStore (org.apache.cassandra.db.ColumnFamilyStore)1 Bounds (org.apache.cassandra.dht.Bounds)1 BytesToken (org.apache.cassandra.dht.ByteOrderedPartitioner.BytesToken)1 Token (org.apache.cassandra.dht.Token)1 SSTableReader (org.apache.cassandra.io.sstable.format.SSTableReader)1