Skip to content

Commit d06fc06

Browse files
committed
Expose existing DLS cache x-pack usage statistics
1 parent db89fd4 commit d06fc06

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
import java.io.Closeable;
4242
import java.io.IOException;
43+
import java.util.Collections;
44+
import java.util.LinkedHashMap;
4345
import java.util.List;
4446
import java.util.Map;
4547
import java.util.Objects;
@@ -320,7 +322,16 @@ public static List<Setting<?>> getSettings() {
320322

321323
public Map<String, Object> usageStats() {
322324
final ByteSizeValue ram = ByteSizeValue.ofBytes(ramBytesUsed());
323-
return Map.of("count", entryCount(), "memory", ram.toString(), "memory_in_bytes", ram.getBytes());
325+
final Cache.Stats cacheStats = bitsetCache.stats();
326+
327+
final Map<String, Object> stats = new LinkedHashMap<>();
328+
stats.put("count", entryCount());
329+
stats.put("memory", ram.toString());
330+
stats.put("memory_in_bytes", ram.getBytes());
331+
stats.put("hits", cacheStats.getHits());
332+
stats.put("misses", cacheStats.getMisses());
333+
stats.put("evictions", cacheStats.getEvictions());
334+
return Collections.unmodifiableMap(stats);
324335
}
325336

326337
private static final class BitsetCacheKey {

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

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.util.ArrayList;
5454
import java.util.Collections;
5555
import java.util.IdentityHashMap;
56+
import java.util.LinkedHashMap;
5657
import java.util.List;
5758
import java.util.Map;
5859
import java.util.Set;
@@ -64,6 +65,7 @@
6465
import java.util.concurrent.atomic.AtomicReference;
6566

6667
import static org.hamcrest.Matchers.equalTo;
68+
import static org.hamcrest.Matchers.greaterThan;
6769
import static org.hamcrest.Matchers.is;
6870
import static org.hamcrest.Matchers.not;
6971
import static org.hamcrest.Matchers.notNullValue;
@@ -396,9 +398,9 @@ public void testCacheUnderConcurrentAccess() throws Exception {
396398
cache.verifyInternalConsistency();
397399

398400
// Due to cache evictions, we must get more bitsets than fields
399-
assertThat(uniqueBitSets.size(), Matchers.greaterThan(FIELD_COUNT));
401+
assertThat(uniqueBitSets.size(), greaterThan(FIELD_COUNT));
400402
// Due to cache evictions, we must have seen more bitsets than the cache currently holds
401-
assertThat(uniqueBitSets.size(), Matchers.greaterThan(cache.entryCount()));
403+
assertThat(uniqueBitSets.size(), greaterThan(cache.entryCount()));
402404
// Even under concurrent pressure, the cache should hit the expected size
403405
assertThat(cache.entryCount(), is(maxCacheCount));
404406
assertThat(cache.ramBytesUsed(), is(maxCacheBytes));
@@ -517,6 +519,53 @@ public void testEquivalentMatchAllDocsQuery() {
517519
assertFalse(DocumentSubsetBitsetCache.isEffectiveMatchAllDocsQuery(new TermQuery(new Term("term"))));
518520
}
519521

522+
public void testHitsMissesAndEvictionsStats() throws Exception {
523+
// cache that will evict all-but-one element, to test evictions
524+
final long maxCacheBytes = EXPECTED_BYTES_PER_BIT_SET + (EXPECTED_BYTES_PER_BIT_SET / 2);
525+
final Settings settings = Settings.builder()
526+
.put(DocumentSubsetBitsetCache.CACHE_SIZE_SETTING.getKey(), maxCacheBytes + "b")
527+
.build();
528+
final DocumentSubsetBitsetCache cache = newCache(settings);
529+
530+
final Map<String, Object> expectedStats = new LinkedHashMap<>();
531+
expectedStats.put("count", 0);
532+
expectedStats.put("memory", "0b");
533+
expectedStats.put("memory_in_bytes", 0L);
534+
expectedStats.put("hits", 0L);
535+
expectedStats.put("misses", 0L);
536+
expectedStats.put("evictions", 0L);
537+
assertThat(cache.usageStats(), equalTo(expectedStats));
538+
539+
runTestOnIndex((searchExecutionContext, leafContext) -> {
540+
// first lookup - miss
541+
final Query query1 = QueryBuilders.termQuery("field-1", "value-1").toQuery(searchExecutionContext);
542+
final BitSet bitSet1 = cache.getBitSet(query1, leafContext);
543+
assertThat(bitSet1, notNullValue());
544+
545+
// second same lookup - hit
546+
final BitSet bitSet1Again = cache.getBitSet(query1, leafContext);
547+
assertThat(bitSet1Again, sameInstance(bitSet1));
548+
549+
expectedStats.put("hits", 1L);
550+
expectedStats.put("misses", 1L);
551+
expectedStats.put("count", 1);
552+
expectedStats.put("memory", EXPECTED_BYTES_PER_BIT_SET + "b");
553+
expectedStats.put("memory_in_bytes", EXPECTED_BYTES_PER_BIT_SET);
554+
assertThat(cache.usageStats(), equalTo(expectedStats));
555+
556+
// second query - miss, should evict the first one
557+
final Query query2 = QueryBuilders.termQuery("field-2", "value-2").toQuery(searchExecutionContext);
558+
final BitSet bitSet2 = cache.getBitSet(query2, leafContext);
559+
assertThat(bitSet2, notNullValue());
560+
561+
// szymon: bug I believe, created issue: https://github.com/elastic/elasticsearch/issues/132842
562+
// assertBusy can be removed once the bug is fixed.
563+
expectedStats.put("misses", 3L);
564+
expectedStats.put("evictions", 1L);
565+
assertBusy(() -> { assertThat(cache.usageStats(), equalTo(expectedStats)); }, 200, TimeUnit.MILLISECONDS);
566+
});
567+
}
568+
520569
private void runTestOnIndex(CheckedBiConsumer<SearchExecutionContext, LeafReaderContext, Exception> body) throws Exception {
521570
runTestOnIndices(1, ctx -> {
522571
final TestIndexContext indexContext = ctx.get(0);
@@ -638,5 +687,4 @@ private void runTestOnIndices(int numberIndices, CheckedConsumer<List<TestIndexC
638687
private DocumentSubsetBitsetCache newCache(Settings settings) {
639688
return new DocumentSubsetBitsetCache(settings, singleThreadExecutor);
640689
}
641-
642690
}

0 commit comments

Comments
 (0)