5252import java .util .concurrent .ExecutorService ;
5353import java .util .concurrent .TimeUnit ;
5454import java .util .concurrent .atomic .AtomicLong ;
55+ import java .util .concurrent .atomic .LongAdder ;
5556import java .util .concurrent .locks .ReentrantReadWriteLock ;
57+ import java .util .function .LongSupplier ;
5658
5759/**
5860 * This is a cache for {@link BitSet} instances that are used with the {@link DocumentSubsetReader}.
@@ -122,18 +124,27 @@ public final class DocumentSubsetBitsetCache implements IndexReader.ClosedListen
122124 private final Cache <BitsetCacheKey , BitSet > bitsetCache ;
123125 private final Map <IndexReader .CacheKey , Set <BitsetCacheKey >> keysByIndex ;
124126 private final AtomicLong cacheFullWarningTime ;
127+ private final LongSupplier relativeNanoTimeProvider ;
128+ private final LongAdder hitsTimeInNanos = new LongAdder ();
129+ private final LongAdder missesTimeInNanos = new LongAdder ();
125130
126131 public DocumentSubsetBitsetCache (Settings settings , ThreadPool threadPool ) {
127132 this (settings , threadPool .executor (ThreadPool .Names .GENERIC ));
128133 }
129134
135+ // visible for testing
136+ DocumentSubsetBitsetCache (Settings settings , ExecutorService cleanupExecutor ) {
137+ this (settings , cleanupExecutor , System ::nanoTime );
138+ }
139+
130140 /**
131141 * @param settings The global settings object for this node
132142 * @param cleanupExecutor An executor on which the cache cleanup tasks can be run. Due to the way the cache is structured internally,
133143 * it is sometimes necessary to run an asynchronous task to synchronize the internal state.
144+ * @param relativeNanoTimeProvider Provider of nanos for code that needs to measure relative time.
134145 */
135146 // visible for testing
136- DocumentSubsetBitsetCache (Settings settings , ExecutorService cleanupExecutor ) {
147+ DocumentSubsetBitsetCache (Settings settings , ExecutorService cleanupExecutor , LongSupplier relativeNanoTimeProvider ) {
137148 final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock ();
138149 this .cacheEvictionLock = new ReleasableLock (readWriteLock .writeLock ());
139150 this .cacheModificationLock = new ReleasableLock (readWriteLock .readLock ());
@@ -150,6 +161,7 @@ public DocumentSubsetBitsetCache(Settings settings, ThreadPool threadPool) {
150161
151162 this .keysByIndex = new ConcurrentHashMap <>();
152163 this .cacheFullWarningTime = new AtomicLong (0 );
164+ this .relativeNanoTimeProvider = Objects .requireNonNull (relativeNanoTimeProvider );
153165 }
154166
155167 @ Override
@@ -222,6 +234,8 @@ public long ramBytesUsed() {
222234 */
223235 @ Nullable
224236 public BitSet getBitSet (final Query query , final LeafReaderContext context ) throws ExecutionException {
237+ final long cacheStart = relativeNanoTimeProvider .getAsLong ();
238+
225239 final IndexReader .CacheHelper coreCacheHelper = context .reader ().getCoreCacheHelper ();
226240 if (coreCacheHelper == null ) {
227241 try {
@@ -235,7 +249,9 @@ public BitSet getBitSet(final Query query, final LeafReaderContext context) thro
235249 final BitsetCacheKey cacheKey = new BitsetCacheKey (indexKey , query );
236250
237251 try (ReleasableLock ignored = cacheModificationLock .acquire ()) {
252+ final boolean [] cacheKeyWasPresent = new boolean [] { true };
238253 final BitSet bitSet = bitsetCache .computeIfAbsent (cacheKey , ignore1 -> {
254+ cacheKeyWasPresent [0 ] = false ;
239255 // This ensures all insertions into the set are guarded by ConcurrentHashMap's atomicity guarantees.
240256 keysByIndex .compute (indexKey , (ignore2 , set ) -> {
241257 if (set == null ) {
@@ -264,6 +280,11 @@ public BitSet getBitSet(final Query query, final LeafReaderContext context) thro
264280 }
265281 return result ;
266282 });
283+ if (cacheKeyWasPresent [0 ]) {
284+ hitsTimeInNanos .add (relativeNanoTimeProvider .getAsLong () - cacheStart );
285+ } else {
286+ missesTimeInNanos .add (relativeNanoTimeProvider .getAsLong () - cacheStart );
287+ }
267288 if (bitSet == NULL_MARKER ) {
268289 return null ;
269290 } else {
@@ -331,6 +352,8 @@ public Map<String, Object> usageStats() {
331352 stats .put ("hits" , cacheStats .getHits ());
332353 stats .put ("misses" , cacheStats .getMisses ());
333354 stats .put ("evictions" , cacheStats .getEvictions ());
355+ stats .put ("hits_time_in_millis" , TimeValue .nsecToMSec (hitsTimeInNanos .sum ()));
356+ stats .put ("misses_time_in_millis" , TimeValue .nsecToMSec (missesTimeInNanos .sum ()));
334357 return Collections .unmodifiableMap (stats );
335358 }
336359
0 commit comments