Class DfsBlockCache
- java.lang.Object
-
- org.eclipse.jgit.internal.storage.dfs.DfsBlockCache
-
public final class DfsBlockCache extends java.lang.Object
Caches slices of aBlockBasedFile
in memory for faster read access.The DfsBlockCache serves as a Java based "buffer cache", loading segments of a BlockBasedFile into the JVM heap prior to use. As JGit often wants to do reads of only tiny slices of a file, the DfsBlockCache tries to smooth out these tiny reads into larger block-sized IO operations.
Whenever a cache miss occurs, loading is invoked by exactly one thread for the given
(DfsStreamKey,position)
key tuple. This is ensured by an array of locks, with the tuple hashed to a lock instance.Its too expensive during object access to be accurate with a least recently used (LRU) algorithm. Strictly ordering every read is a lot of overhead that typically doesn't yield a corresponding benefit to the application. This cache implements a clock replacement algorithm, giving each block at least one chance to have been accessed during a sweep of the cache to save itself from eviction. The number of swipe chances is configurable per pack extension.
Entities created by the cache are held under hard references, preventing the Java VM from clearing anything. Blocks are discarded by the replacement algorithm when adding a new block would cause the cache to exceed its configured maximum size.
The key tuple is passed through to methods as a pair of parameters rather than as a single Object, thus reducing the transient memory allocations of callers. It is more efficient to avoid the allocation, as we can't be 100% sure that a JIT would be able to stack-allocate a key tuple.
The internal hash table does not expand at runtime, instead it is fixed in size at cache creation time. The internal lock table used to gate load invocations is also fixed in size.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description private static class
DfsBlockCache.EvictKey
private static class
DfsBlockCache.HashEntry
(package private) static interface
DfsBlockCache.ReadableChannelSupplier
Supplier for readable channel(package private) static class
DfsBlockCache.Ref<T>
(package private) static interface
DfsBlockCache.RefLoader<T>
-
Field Summary
Fields Modifier and Type Field Description private int
blockSize
Suggested block size to read from pack files in.private int
blockSizeShift
AsblockSize
is a power of 2, bits to shift for a / blockSize.private static DfsBlockCache
cache
private int[]
cacheHotLimits
Limits of cache hot count per pack file extension.private DfsBlockCache.Ref
clockHand
Current position of the clock.private java.util.concurrent.locks.ReentrantLock
clockLock
Protects the clock and its related data.private DfsBlockCacheConfig.IndexEventConsumer
indexEventConsumer
Consumer of loading and eviction events of indexes.private java.util.Map<DfsBlockCache.EvictKey,java.lang.Long>
indexEvictionMap
Stores timestamps of the last eviction of indexes.private java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]>
liveBytes
Number of bytes currently loaded in the cache, per pack file extension.private java.util.concurrent.locks.ReentrantLock[]
loadLocks
Locks to prevent concurrent loads for same (PackFile,position) block.private long
maxBytes
Maximum number of bytes the cache should hold.private long
maxStreamThroughCache
Pack files smaller than this size can be copied through the cache.private java.util.concurrent.locks.ReentrantLock[][]
refLocks
A separate pool of locks per pack extension to prevent concurrent loads for same index or bitmap from PackFile.private java.util.function.Consumer<java.lang.Long>
refLockWaitTime
A consumer of object reference lock wait time milliseconds.private java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]>
statEvict
Number of blocks evicted due to cache being full, per pack file extension.private java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]>
statHit
Number of times a block was found in the cache, per pack file extension.private java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]>
statMiss
Number of times a block was not found, and had to be loaded, per pack file extension.private java.util.concurrent.atomic.AtomicReferenceArray<DfsBlockCache.HashEntry>
table
Hash bucket directory; entries are chained below.private int
tableSize
Number of entries intable
.
-
Constructor Summary
Constructors Modifier Constructor Description private
DfsBlockCache(DfsBlockCacheConfig cfg)
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description private void
addToClock(DfsBlockCache.Ref ref, long credit)
private static DfsBlockCache.HashEntry
clean(DfsBlockCache.HashEntry top)
(package private) boolean
contains(DfsStreamKey key, long position)
private void
creditSpace(long credit, DfsStreamKey key)
(package private) <T> T
get(DfsStreamKey key, long position)
(package private) int
getBlockSize()
long[]
getCurrentSize()
Get total number of bytes in the cache, per pack file extension.long[]
getEvictions()
Get number of evictions performed due to cache being full, per pack file extension.long
getFillPercentage()
Get 0..100, defining how full the cache is.long[]
getHitCount()
Get number of requests for items in the cache, per pack file extension.long[]
getHitRatio()
Get hit ratiosstatic DfsBlockCache
getInstance()
Get the currently active DfsBlockCache.long[]
getMissCount()
Get number of requests for items not in the cache, per pack file extension.(package private) DfsBlock
getOrLoad(BlockBasedFile file, long position, DfsReader ctx, DfsBlockCache.ReadableChannelSupplier fileChannel)
Look up a cached object, creating and loading it if it doesn't exist.(package private) <T> DfsBlockCache.Ref<T>
getOrLoadRef(DfsStreamKey key, long position, DfsBlockCache.RefLoader<T> loader)
Look up a cached object, creating and loading it if it doesn't exist.private static java.util.concurrent.atomic.AtomicLong
getStat(java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> stats, DfsStreamKey key)
private static long[]
getStatVals(java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> stat)
long[]
getTotalRequestCount()
Get total number of requests (hit + miss), per pack file extension.boolean
hasBlock0(DfsStreamKey key)
Quickly check if the cache contains block 0 of the given stream.private int
hash(int packHash, long off)
private static boolean
isIndexOrBitmapExtPos(int packExtPos)
private java.util.concurrent.locks.ReentrantLock
lockFor(DfsStreamKey key, long position)
private java.util.concurrent.locks.ReentrantLock
lockForRef(DfsStreamKey key)
private static java.util.concurrent.atomic.AtomicLong[]
newCounters()
(package private) void
put(DfsBlock v)
(package private) <T> DfsBlockCache.Ref<T>
put(DfsStreamKey key, long pos, long size, T v)
(package private) <T> DfsBlockCache.Ref<T>
putRef(DfsStreamKey key, long size, T v)
static void
reconfigure(DfsBlockCacheConfig cfg)
Modify the configuration of the window cache.private void
reportIndexEvicted(DfsBlockCache.Ref<?> dead)
private void
reportIndexRequested(DfsBlockCache.Ref<?> ref, boolean cacheHit, long start)
private void
reserveSpace(long reserve, DfsStreamKey key)
private <T> T
scan(DfsBlockCache.HashEntry n, DfsStreamKey key, long position)
private <T> DfsBlockCache.Ref<T>
scanRef(DfsBlockCache.HashEntry n, DfsStreamKey key, long position)
(package private) boolean
shouldCopyThroughCache(long length)
private int
slot(DfsStreamKey key, long position)
private static int
tableSize(DfsBlockCacheConfig cfg)
-
-
-
Field Detail
-
cache
private static volatile DfsBlockCache cache
-
tableSize
private final int tableSize
Number of entries intable
.
-
table
private final java.util.concurrent.atomic.AtomicReferenceArray<DfsBlockCache.HashEntry> table
Hash bucket directory; entries are chained below.
-
loadLocks
private final java.util.concurrent.locks.ReentrantLock[] loadLocks
Locks to prevent concurrent loads for same (PackFile,position) block. The number of locks isDfsBlockCacheConfig.getConcurrencyLevel()
to cap the overall concurrent block loads.
-
refLocks
private final java.util.concurrent.locks.ReentrantLock[][] refLocks
A separate pool of locks per pack extension to prevent concurrent loads for same index or bitmap from PackFile.
-
maxBytes
private final long maxBytes
Maximum number of bytes the cache should hold.
-
maxStreamThroughCache
private final long maxStreamThroughCache
Pack files smaller than this size can be copied through the cache.
-
blockSize
private final int blockSize
Suggested block size to read from pack files in.If a pack file does not have a native block size, this size will be used.
If a pack file has a native size, a whole multiple of the native size will be used until it matches this size.
The value for blockSize must be a power of 2.
-
blockSizeShift
private final int blockSizeShift
AsblockSize
is a power of 2, bits to shift for a / blockSize.
-
statHit
private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> statHit
Number of times a block was found in the cache, per pack file extension.
-
statMiss
private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> statMiss
Number of times a block was not found, and had to be loaded, per pack file extension.
-
statEvict
private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> statEvict
Number of blocks evicted due to cache being full, per pack file extension.
-
liveBytes
private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> liveBytes
Number of bytes currently loaded in the cache, per pack file extension.
-
clockLock
private final java.util.concurrent.locks.ReentrantLock clockLock
Protects the clock and its related data.
-
refLockWaitTime
private final java.util.function.Consumer<java.lang.Long> refLockWaitTime
A consumer of object reference lock wait time milliseconds. May be used to build a metric.
-
clockHand
private DfsBlockCache.Ref clockHand
Current position of the clock.
-
cacheHotLimits
private final int[] cacheHotLimits
Limits of cache hot count per pack file extension.
-
indexEventConsumer
private final DfsBlockCacheConfig.IndexEventConsumer indexEventConsumer
Consumer of loading and eviction events of indexes.
-
indexEvictionMap
private final java.util.Map<DfsBlockCache.EvictKey,java.lang.Long> indexEvictionMap
Stores timestamps of the last eviction of indexes.
-
-
Constructor Detail
-
DfsBlockCache
private DfsBlockCache(DfsBlockCacheConfig cfg)
-
-
Method Detail
-
reconfigure
public static void reconfigure(DfsBlockCacheConfig cfg)
Modify the configuration of the window cache.The new configuration is applied immediately, and the existing cache is cleared.
- Parameters:
cfg
- the new window cache configuration.- Throws:
java.lang.IllegalArgumentException
- the cache configuration contains one or more invalid settings, usually too low of a limit.
-
getInstance
public static DfsBlockCache getInstance()
Get the currently active DfsBlockCache.- Returns:
- the currently active DfsBlockCache.
-
shouldCopyThroughCache
boolean shouldCopyThroughCache(long length)
-
getCurrentSize
public long[] getCurrentSize()
Get total number of bytes in the cache, per pack file extension.- Returns:
- total number of bytes in the cache, per pack file extension.
-
getFillPercentage
public long getFillPercentage()
Get 0..100, defining how full the cache is.- Returns:
- 0..100, defining how full the cache is.
-
getHitCount
public long[] getHitCount()
Get number of requests for items in the cache, per pack file extension.- Returns:
- number of requests for items in the cache, per pack file extension.
-
getMissCount
public long[] getMissCount()
Get number of requests for items not in the cache, per pack file extension.- Returns:
- number of requests for items not in the cache, per pack file extension.
-
getTotalRequestCount
public long[] getTotalRequestCount()
Get total number of requests (hit + miss), per pack file extension.- Returns:
- total number of requests (hit + miss), per pack file extension.
-
getHitRatio
public long[] getHitRatio()
Get hit ratios- Returns:
- hit ratios
-
getEvictions
public long[] getEvictions()
Get number of evictions performed due to cache being full, per pack file extension.- Returns:
- number of evictions performed due to cache being full, per pack file extension.
-
hasBlock0
public boolean hasBlock0(DfsStreamKey key)
Quickly check if the cache contains block 0 of the given stream.This can be useful for sophisticated pre-read algorithms to quickly determine if a file is likely already in cache, especially small reftables which may be smaller than a typical DFS block size.
- Parameters:
key
- the file to check.- Returns:
- true if block 0 (the first block) is in the cache.
-
hash
private int hash(int packHash, long off)
-
getBlockSize
int getBlockSize()
-
tableSize
private static int tableSize(DfsBlockCacheConfig cfg)
-
getOrLoad
DfsBlock getOrLoad(BlockBasedFile file, long position, DfsReader ctx, DfsBlockCache.ReadableChannelSupplier fileChannel) throws java.io.IOException
Look up a cached object, creating and loading it if it doesn't exist.- Parameters:
file
- the pack that "contains" the cached object.position
- offset withinpack
of the object.ctx
- current thread's reader.fileChannel
- supplier for channel to readpack
.- Returns:
- the object reference.
- Throws:
java.io.IOException
- the reference was not in the cache and could not be loaded.
-
reserveSpace
private void reserveSpace(long reserve, DfsStreamKey key)
-
creditSpace
private void creditSpace(long credit, DfsStreamKey key)
-
addToClock
private void addToClock(DfsBlockCache.Ref ref, long credit)
-
put
void put(DfsBlock v)
-
getOrLoadRef
<T> DfsBlockCache.Ref<T> getOrLoadRef(DfsStreamKey key, long position, DfsBlockCache.RefLoader<T> loader) throws java.io.IOException
Look up a cached object, creating and loading it if it doesn't exist.- Parameters:
key
- the stream key of the pack.position
- the position in the key. The default should be 0.loader
- the function to load the reference.- Returns:
- the object reference.
- Throws:
java.io.IOException
- the reference was not in the cache and could not be loaded.
-
putRef
<T> DfsBlockCache.Ref<T> putRef(DfsStreamKey key, long size, T v)
-
put
<T> DfsBlockCache.Ref<T> put(DfsStreamKey key, long pos, long size, T v)
-
contains
boolean contains(DfsStreamKey key, long position)
-
get
<T> T get(DfsStreamKey key, long position)
-
scan
private <T> T scan(DfsBlockCache.HashEntry n, DfsStreamKey key, long position)
-
scanRef
private <T> DfsBlockCache.Ref<T> scanRef(DfsBlockCache.HashEntry n, DfsStreamKey key, long position)
-
slot
private int slot(DfsStreamKey key, long position)
-
lockFor
private java.util.concurrent.locks.ReentrantLock lockFor(DfsStreamKey key, long position)
-
lockForRef
private java.util.concurrent.locks.ReentrantLock lockForRef(DfsStreamKey key)
-
newCounters
private static java.util.concurrent.atomic.AtomicLong[] newCounters()
-
getStat
private static java.util.concurrent.atomic.AtomicLong getStat(java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> stats, DfsStreamKey key)
-
getStatVals
private static long[] getStatVals(java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicLong[]> stat)
-
clean
private static DfsBlockCache.HashEntry clean(DfsBlockCache.HashEntry top)
-
reportIndexRequested
private void reportIndexRequested(DfsBlockCache.Ref<?> ref, boolean cacheHit, long start)
-
reportIndexEvicted
private void reportIndexEvicted(DfsBlockCache.Ref<?> dead)
-
isIndexOrBitmapExtPos
private static boolean isIndexOrBitmapExtPos(int packExtPos)
-
-