Skip to content

Commit f765766

Browse files
committed
Add hits and misses timing stats to DLS cache
1 parent 098eb51 commit f765766

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCache.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@
5151
import java.util.concurrent.ExecutionException;
5252
import java.util.concurrent.ExecutorService;
5353
import java.util.concurrent.TimeUnit;
54+
import java.util.concurrent.atomic.AtomicBoolean;
5455
import java.util.concurrent.atomic.AtomicLong;
56+
import java.util.concurrent.atomic.LongAdder;
5557
import java.util.concurrent.locks.ReentrantReadWriteLock;
58+
import java.util.function.LongSupplier;
5659

5760
/**
5861
* This is a cache for {@link BitSet} instances that are used with the {@link DocumentSubsetReader}.
@@ -122,18 +125,27 @@ public final class DocumentSubsetBitsetCache implements IndexReader.ClosedListen
122125
private final Cache<BitsetCacheKey, BitSet> bitsetCache;
123126
private final Map<IndexReader.CacheKey, Set<BitsetCacheKey>> keysByIndex;
124127
private final AtomicLong cacheFullWarningTime;
128+
private final LongAdder hitsTimeInNanos;
129+
private final LongAdder missesTimeInNanos;
130+
private final LongSupplier relativeNanoTimeProvider;
125131

126132
public DocumentSubsetBitsetCache(Settings settings, ThreadPool threadPool) {
127133
this(settings, threadPool.executor(ThreadPool.Names.GENERIC));
128134
}
129135

136+
// visible for testing
137+
DocumentSubsetBitsetCache(Settings settings, ExecutorService cleanupExecutor) {
138+
this(settings, cleanupExecutor, System::nanoTime);
139+
}
140+
130141
/**
131142
* @param settings The global settings object for this node
132143
* @param cleanupExecutor An executor on which the cache cleanup tasks can be run. Due to the way the cache is structured internally,
133144
* it is sometimes necessary to run an asynchronous task to synchronize the internal state.
145+
* @param relativeNanoTimeProvider Provider of nanos for code that needs to measure relative time.
134146
*/
135147
// visible for testing
136-
DocumentSubsetBitsetCache(Settings settings, ExecutorService cleanupExecutor) {
148+
DocumentSubsetBitsetCache(Settings settings, ExecutorService cleanupExecutor, LongSupplier relativeNanoTimeProvider) {
137149
final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
138150
this.cacheEvictionLock = new ReleasableLock(readWriteLock.writeLock());
139151
this.cacheModificationLock = new ReleasableLock(readWriteLock.readLock());
@@ -150,6 +162,9 @@ public DocumentSubsetBitsetCache(Settings settings, ThreadPool threadPool) {
150162

151163
this.keysByIndex = new ConcurrentHashMap<>();
152164
this.cacheFullWarningTime = new AtomicLong(0);
165+
this.hitsTimeInNanos = new LongAdder();
166+
this.missesTimeInNanos = new LongAdder();
167+
this.relativeNanoTimeProvider = Objects.requireNonNull(relativeNanoTimeProvider);
153168
}
154169

155170
@Override
@@ -222,6 +237,8 @@ public long ramBytesUsed() {
222237
*/
223238
@Nullable
224239
public BitSet getBitSet(final Query query, final LeafReaderContext context) throws ExecutionException {
240+
final long startTimeNs = relativeNanoTimeProvider.getAsLong();
241+
225242
final IndexReader.CacheHelper coreCacheHelper = context.reader().getCoreCacheHelper();
226243
if (coreCacheHelper == null) {
227244
try {
@@ -235,7 +252,9 @@ public BitSet getBitSet(final Query query, final LeafReaderContext context) thro
235252
final BitsetCacheKey cacheKey = new BitsetCacheKey(indexKey, query);
236253

237254
try (ReleasableLock ignored = cacheModificationLock.acquire()) {
255+
final AtomicBoolean cacheKeyWasPresent = new AtomicBoolean(true);
238256
final BitSet bitSet = bitsetCache.computeIfAbsent(cacheKey, ignore1 -> {
257+
cacheKeyWasPresent.set(false);
239258
// This ensures all insertions into the set are guarded by ConcurrentHashMap's atomicity guarantees.
240259
keysByIndex.compute(indexKey, (ignore2, set) -> {
241260
if (set == null) {
@@ -264,6 +283,11 @@ public BitSet getBitSet(final Query query, final LeafReaderContext context) thro
264283
}
265284
return result;
266285
});
286+
if (cacheKeyWasPresent.get()) {
287+
hitsTimeInNanos.add(relativeNanoTimeProvider.getAsLong() - startTimeNs);
288+
} else {
289+
missesTimeInNanos.add(relativeNanoTimeProvider.getAsLong() - startTimeNs);
290+
}
267291
if (bitSet == NULL_MARKER) {
268292
return null;
269293
} else {
@@ -331,6 +355,8 @@ public Map<String, Object> usageStats() {
331355
stats.put("hits", cacheStats.getHits());
332356
stats.put("misses", cacheStats.getMisses());
333357
stats.put("evictions", cacheStats.getEvictions());
358+
stats.put("hits_time_in_millis", TimeValue.nsecToMSec(hitsTimeInNanos.sum()));
359+
stats.put("misses_time_in_millis", TimeValue.nsecToMSec(missesTimeInNanos.sum()));
334360
return Collections.unmodifiableMap(stats);
335361
}
336362

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCacheTests.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@
6262
import java.util.concurrent.Executors;
6363
import java.util.concurrent.TimeUnit;
6464
import java.util.concurrent.atomic.AtomicInteger;
65+
import java.util.concurrent.atomic.AtomicLong;
6566
import java.util.concurrent.atomic.AtomicReference;
67+
import java.util.function.LongSupplier;
6668
import java.util.function.Supplier;
6769

6870
import static org.hamcrest.Matchers.equalTo;
@@ -536,6 +538,8 @@ public void testHitsMissesAndEvictionsStats() throws Exception {
536538
stats.put("hits", 0L);
537539
stats.put("misses", 0L);
538540
stats.put("evictions", 0L);
541+
stats.put("hits_time_in_millis", 0L);
542+
stats.put("misses_time_in_millis", 0L);
539543
return stats;
540544
};
541545

@@ -551,12 +555,14 @@ public void testHitsMissesAndEvictionsStats() throws Exception {
551555
expectedStats.put("misses", 1L);
552556
expectedStats.put("memory", EXPECTED_BYTES_PER_BIT_SET + "b");
553557
expectedStats.put("memory_in_bytes", EXPECTED_BYTES_PER_BIT_SET);
558+
expectedStats.put("misses_time_in_millis", 1L);
554559
assertThat(cache.usageStats(), equalTo(expectedStats));
555560

556561
// second same lookup - hit
557562
final BitSet bitSet1Again = cache.getBitSet(query1, leafContext);
558563
assertThat(bitSet1Again, sameInstance(bitSet1));
559564
expectedStats.put("hits", 1L);
565+
expectedStats.put("hits_time_in_millis", 1L);
560566
assertThat(cache.usageStats(), equalTo(expectedStats));
561567

562568
// second query - miss, should evict the first one
@@ -568,13 +574,18 @@ public void testHitsMissesAndEvictionsStats() throws Exception {
568574
// see https://github.com/elastic/elasticsearch/issues/132842
569575
expectedStats.put("misses", 3L);
570576
expectedStats.put("evictions", 1L);
577+
// underlying Cache class tracks hits/misses, but timing is in DLS cache, which is why we have `2L` here,
578+
// because DLS cache is only hit once
579+
expectedStats.put("misses_time_in_millis", 2L);
571580
assertBusy(() -> { assertThat(cache.usageStats(), equalTo(expectedStats)); }, 200, TimeUnit.MILLISECONDS);
572581
});
573582

574583
final Map<String, Object> finalStats = emptyStatsSupplier.get();
575584
finalStats.put("hits", 1L);
576585
finalStats.put("misses", 3L);
577586
finalStats.put("evictions", 2L);
587+
finalStats.put("hits_time_in_millis", 1L);
588+
finalStats.put("misses_time_in_millis", 2L);
578589
assertThat(cache.usageStats(), equalTo(finalStats));
579590
}
580591

@@ -697,6 +708,8 @@ private void runTestOnIndices(int numberIndices, CheckedConsumer<List<TestIndexC
697708
}
698709

699710
private DocumentSubsetBitsetCache newCache(Settings settings) {
700-
return new DocumentSubsetBitsetCache(settings, singleThreadExecutor);
711+
final AtomicLong increasingMillisTime = new AtomicLong();
712+
final LongSupplier relativeNanoTimeProvider = () -> TimeUnit.MILLISECONDS.toNanos(increasingMillisTime.getAndIncrement());
713+
return new DocumentSubsetBitsetCache(settings, singleThreadExecutor, relativeNanoTimeProvider);
701714
}
702715
}

0 commit comments

Comments
 (0)