Search in sources :

Example 1 with RowCacheSentinel

use of org.apache.cassandra.cache.RowCacheSentinel 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.
     */
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())) {
            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 {
            int rowsToCache = metadata().params.caching.rowsPerPartitionToCache();
            // we close on exception or upon closing the result of this method
            @SuppressWarnings("resource") UnfilteredRowIterator iter = SinglePartitionReadCommand.fullPartitionRead(metadata(), nowInSec(), partitionKey()).queryMemtableAndDisk(cfs, executionController);
            try {
                // We want to cache only rowsToCache rows
                CachedPartition toCache = CachedBTreePartition.create(DataLimits.cqlLimits(rowsToCache).filter(iter, nowInSec()), 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

IRowCacheEntry (org.apache.cassandra.cache.IRowCacheEntry)1 RowCacheKey (org.apache.cassandra.cache.RowCacheKey)1 RowCacheSentinel (org.apache.cassandra.cache.RowCacheSentinel)1