Add Cache.getCachedBytes() which returns the length of the cached or not data block length

This method can be used to determine not cached parts of a content.
The 'length' parameter allows quicker responses without going through all adjacent spans.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=149005688
This commit is contained in:
eguven 2017-03-02 08:12:01 -08:00 committed by Oliver Woodman
parent 247da48e9d
commit e40bba2852
4 changed files with 89 additions and 41 deletions

View file

@ -192,6 +192,41 @@ public class SimpleCacheTest extends InstrumentationTestCase {
assertEquals(0, cacheDir.listFiles().length);
}
public void testGetCachedBytes() throws Exception {
SimpleCache simpleCache = getSimpleCache();
CacheSpan cacheSpan = simpleCache.startReadWrite(KEY_1, 0);
// No cached bytes, returns -'length'
assertEquals(-100, simpleCache.getCachedBytes(KEY_1, 0, 100));
// Position value doesn't affect the return value
assertEquals(-100, simpleCache.getCachedBytes(KEY_1, 20, 100));
addCache(simpleCache, KEY_1, 0, 15);
// Returns the length of a single span
assertEquals(15, simpleCache.getCachedBytes(KEY_1, 0, 100));
// Value is capped by the 'length'
assertEquals(10, simpleCache.getCachedBytes(KEY_1, 0, 10));
addCache(simpleCache, KEY_1, 15, 35);
// Returns the length of two adjacent spans
assertEquals(50, simpleCache.getCachedBytes(KEY_1, 0, 100));
addCache(simpleCache, KEY_1, 60, 10);
// Not adjacent span doesn't affect return value
assertEquals(50, simpleCache.getCachedBytes(KEY_1, 0, 100));
// Returns length of hole up to the next cached span
assertEquals(-5, simpleCache.getCachedBytes(KEY_1, 55, 100));
simpleCache.releaseHoleSpan(cacheSpan);
}
private SimpleCache getSimpleCache() {
return new SimpleCache(cacheDir, new NoOpCacheEvictor());
}

View file

@ -198,6 +198,18 @@ public interface Cache {
*/
boolean isCached(String key, long position, long length);
/**
* Returns the length of the cached data block starting from the {@code position} to the block end
* up to {@code length} bytes. If the {@code position} isn't cached then -(the length of the gap
* to the next cached data up to {@code length} bytes) is returned.
*
* @param key The cache key for the data.
* @param position The starting position of the data.
* @param length The maximum length of the data to be returned.
* @return the length of the cached or not cached data block length.
*/
long getCachedBytes(String key, long position, long length);
/**
* Sets the content length for the given key.
*

View file

@ -106,43 +106,49 @@ import java.util.TreeSet;
* which defines the maximum extents of the hole in the cache.
*/
public SimpleCacheSpan getSpan(long position) {
SimpleCacheSpan span = getSpanInternal(position);
if (!span.isCached) {
SimpleCacheSpan ceilEntry = cachedSpans.ceiling(span);
return ceilEntry == null ? SimpleCacheSpan.createOpenHole(key, position)
: SimpleCacheSpan.createClosedHole(key, position, ceilEntry.position - position);
SimpleCacheSpan lookupSpan = SimpleCacheSpan.createLookup(key, position);
SimpleCacheSpan floorSpan = cachedSpans.floor(lookupSpan);
if (floorSpan != null && floorSpan.position + floorSpan.length > position) {
return floorSpan;
}
return span;
SimpleCacheSpan ceilSpan = cachedSpans.ceiling(lookupSpan);
return ceilSpan == null ? SimpleCacheSpan.createOpenHole(key, position)
: SimpleCacheSpan.createClosedHole(key, position, ceilSpan.position - position);
}
/** Queries if a range is entirely available in the cache. */
public boolean isCached(long position, long length) {
SimpleCacheSpan floorSpan = getSpanInternal(position);
if (!floorSpan.isCached) {
/**
* Returns the length of the cached data block starting from the {@code position} to the block end
* up to {@code length} bytes. If the {@code position} isn't cached then -(the length of the gap
* to the next cached data up to {@code length} bytes) is returned.
*
* @param position The starting position of the data.
* @param length The maximum length of the data to be returned.
* @return the length of the cached or not cached data block length.
*/
public long getCachedBytes(long position, long length) {
SimpleCacheSpan span = getSpan(position);
if (span.isHoleSpan()) {
// We don't have a span covering the start of the queried region.
return false;
return -Math.min(span.isOpenEnded() ? Long.MAX_VALUE : span.length, length);
}
long queryEndPosition = position + length;
long currentEndPosition = floorSpan.position + floorSpan.length;
if (currentEndPosition >= queryEndPosition) {
// floorSpan covers the queried region.
return true;
}
for (SimpleCacheSpan next : cachedSpans.tailSet(floorSpan, false)) {
if (next.position > currentEndPosition) {
// There's a hole in the cache within the queried region.
return false;
}
// We expect currentEndPosition to always equal (next.position + next.length), but
// perform a max check anyway to guard against the existence of overlapping spans.
currentEndPosition = Math.max(currentEndPosition, next.position + next.length);
if (currentEndPosition >= queryEndPosition) {
// We've found spans covering the queried region.
return true;
long currentEndPosition = span.position + span.length;
if (currentEndPosition < queryEndPosition) {
for (SimpleCacheSpan next : cachedSpans.tailSet(span, false)) {
if (next.position > currentEndPosition) {
// There's a hole in the cache within the queried region.
break;
}
// We expect currentEndPosition to always equal (next.position + next.length), but
// perform a max check anyway to guard against the existence of overlapping spans.
currentEndPosition = Math.max(currentEndPosition, next.position + next.length);
if (currentEndPosition >= queryEndPosition) {
// We've found spans covering the queried region.
break;
}
}
}
// We ran out of spans before covering the queried region.
return false;
return Math.min(currentEndPosition - position, length);
}
/**
@ -190,15 +196,4 @@ import java.util.TreeSet;
return result;
}
/**
* Returns the span containing the position. If there isn't one, it returns the lookup span it
* used for searching.
*/
private SimpleCacheSpan getSpanInternal(long position) {
SimpleCacheSpan lookupSpan = SimpleCacheSpan.createLookup(key, position);
SimpleCacheSpan floorSpan = cachedSpans.floor(lookupSpan);
return floorSpan == null || floorSpan.position + floorSpan.length <= position ? lookupSpan
: floorSpan;
}
}

View file

@ -354,7 +354,13 @@ public final class SimpleCache implements Cache {
@Override
public synchronized boolean isCached(String key, long position, long length) {
CachedContent cachedContent = index.get(key);
return cachedContent != null && cachedContent.isCached(position, length);
return cachedContent != null && cachedContent.getCachedBytes(position, length) >= length;
}
@Override
public synchronized long getCachedBytes(String key, long position, long length) {
CachedContent cachedContent = index.get(key);
return cachedContent != null ? cachedContent.getCachedBytes(position, length) : -length;
}
@Override