Skip to content

Commit 6467d0c

Browse files
authored
Merge branch 'main' into buffered-murmur3
2 parents 9b645ba + e308abb commit 6467d0c

File tree

7 files changed

+218
-4
lines changed

7 files changed

+218
-4
lines changed

docs/changelog/133314.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 133314
2+
summary: Add hits and misses timing stats to DLS cache
3+
area: Authorization
4+
type: enhancement
5+
issues: []

modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/GeoIpCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ public String toString() {
4242
}
4343
};
4444

45-
private final LongSupplier relativeNanoTimeProvider;
4645
private final Cache<CacheKey, Object> cache;
46+
private final LongSupplier relativeNanoTimeProvider;
4747
private final LongAdder hitsTimeInNanos = new LongAdder();
4848
private final LongAdder missesTimeInNanos = new LongAdder();
4949

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.cluster.routing.allocation.allocator;
11+
12+
import org.elasticsearch.common.bytes.BytesReference;
13+
import org.elasticsearch.common.io.stream.Writeable;
14+
import org.elasticsearch.common.xcontent.XContentHelper;
15+
import org.elasticsearch.test.AbstractWireSerializingTestCase;
16+
import org.elasticsearch.xcontent.ToXContent;
17+
import org.elasticsearch.xcontent.XContentBuilder;
18+
import org.elasticsearch.xcontent.XContentFactory;
19+
20+
import java.io.IOException;
21+
import java.util.Map;
22+
23+
import static org.hamcrest.Matchers.equalTo;
24+
25+
public class MetricStatsTests extends AbstractWireSerializingTestCase<ClusterBalanceStats.MetricStats> {
26+
27+
@Override
28+
protected Writeable.Reader<ClusterBalanceStats.MetricStats> instanceReader() {
29+
return ClusterBalanceStats.MetricStats::readFrom;
30+
}
31+
32+
@Override
33+
protected ClusterBalanceStats.MetricStats createTestInstance() {
34+
return createRandomMetricStats();
35+
}
36+
37+
public static ClusterBalanceStats.MetricStats createRandomMetricStats() {
38+
return new ClusterBalanceStats.MetricStats(randomDouble(), randomDouble(), randomDouble(), randomDouble(), randomDouble());
39+
}
40+
41+
@Override
42+
protected ClusterBalanceStats.MetricStats mutateInstance(ClusterBalanceStats.MetricStats instance) throws IOException {
43+
return createTestInstance();
44+
}
45+
46+
public void testToXContent() throws IOException {
47+
ClusterBalanceStats.MetricStats stats = createRandomMetricStats();
48+
49+
XContentBuilder builder = XContentFactory.jsonBuilder();
50+
builder = stats.toXContent(builder, ToXContent.EMPTY_PARAMS);
51+
// Convert to map for easy assertions
52+
Map<String, Object> map = XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2();
53+
54+
assertThat(map.get("total"), equalTo(stats.total()));
55+
assertThat(map.get("min"), equalTo(stats.min()));
56+
assertThat(map.get("max"), equalTo(stats.max()));
57+
assertThat(map.get("average"), equalTo(stats.average()));
58+
assertThat(map.get("std_dev"), equalTo(stats.stdDev()));
59+
}
60+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.cluster.routing.allocation.allocator;
11+
12+
import org.elasticsearch.common.bytes.BytesReference;
13+
import org.elasticsearch.common.io.stream.Writeable;
14+
import org.elasticsearch.common.xcontent.XContentHelper;
15+
import org.elasticsearch.test.AbstractWireSerializingTestCase;
16+
import org.elasticsearch.xcontent.ToXContent;
17+
import org.elasticsearch.xcontent.XContentBuilder;
18+
import org.elasticsearch.xcontent.XContentFactory;
19+
20+
import java.io.IOException;
21+
import java.util.Map;
22+
23+
import static org.elasticsearch.cluster.routing.allocation.allocator.MetricStatsTests.createRandomMetricStats;
24+
import static org.hamcrest.Matchers.containsInAnyOrder;
25+
26+
public class TierBalanceStatsTests extends AbstractWireSerializingTestCase<ClusterBalanceStats.TierBalanceStats> {
27+
28+
@Override
29+
protected Writeable.Reader<ClusterBalanceStats.TierBalanceStats> instanceReader() {
30+
return ClusterBalanceStats.TierBalanceStats::readFrom;
31+
}
32+
33+
@Override
34+
protected ClusterBalanceStats.TierBalanceStats createTestInstance() {
35+
return createRandomTierBalanceStats();
36+
}
37+
38+
private ClusterBalanceStats.TierBalanceStats createRandomTierBalanceStats() {
39+
return new ClusterBalanceStats.TierBalanceStats(
40+
createRandomMetricStats(),
41+
createRandomMetricStats(),
42+
createRandomMetricStats(),
43+
createRandomMetricStats(),
44+
createRandomMetricStats()
45+
);
46+
}
47+
48+
@Override
49+
protected ClusterBalanceStats.TierBalanceStats mutateInstance(ClusterBalanceStats.TierBalanceStats instance) throws IOException {
50+
return createTestInstance();
51+
}
52+
53+
@SuppressWarnings("unchecked")
54+
public void testToXContent() throws IOException {
55+
ClusterBalanceStats.TierBalanceStats tierBalanceStats = createRandomTierBalanceStats();
56+
57+
XContentBuilder builder = XContentFactory.jsonBuilder();
58+
builder = tierBalanceStats.toXContent(builder, ToXContent.EMPTY_PARAMS);
59+
// Convert to map for easy assertions
60+
Map<String, Object> map = XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2();
61+
62+
assertThat(
63+
map.keySet(),
64+
containsInAnyOrder(
65+
"shard_count",
66+
"undesired_shard_allocation_count",
67+
"forecast_write_load",
68+
"forecast_disk_usage",
69+
"actual_disk_usage"
70+
)
71+
);
72+
73+
Map<String, Object> shardCountStats = (Map<String, Object>) map.get("shard_count");
74+
assertThat(shardCountStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev"));
75+
assertEquals(shardCountStats.get("total"), tierBalanceStats.shardCount().total());
76+
assertEquals(shardCountStats.get("average"), tierBalanceStats.shardCount().average());
77+
assertEquals(shardCountStats.get("min"), tierBalanceStats.shardCount().min());
78+
assertEquals(shardCountStats.get("max"), tierBalanceStats.shardCount().max());
79+
assertEquals(shardCountStats.get("std_dev"), tierBalanceStats.shardCount().stdDev());
80+
81+
Map<String, Object> undesiredShardAllocationCountStats = (Map<String, Object>) map.get("undesired_shard_allocation_count");
82+
assertThat(undesiredShardAllocationCountStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev"));
83+
assertEquals(undesiredShardAllocationCountStats.get("total"), tierBalanceStats.undesiredShardAllocations().total());
84+
assertEquals(undesiredShardAllocationCountStats.get("average"), tierBalanceStats.undesiredShardAllocations().average());
85+
assertEquals(undesiredShardAllocationCountStats.get("min"), tierBalanceStats.undesiredShardAllocations().min());
86+
assertEquals(undesiredShardAllocationCountStats.get("max"), tierBalanceStats.undesiredShardAllocations().max());
87+
assertEquals(undesiredShardAllocationCountStats.get("std_dev"), tierBalanceStats.undesiredShardAllocations().stdDev());
88+
89+
Map<String, Object> forecastWriteLoadStats = (Map<String, Object>) map.get("forecast_write_load");
90+
assertThat(forecastWriteLoadStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev"));
91+
assertEquals(forecastWriteLoadStats.get("total"), tierBalanceStats.forecastWriteLoad().total());
92+
assertEquals(forecastWriteLoadStats.get("average"), tierBalanceStats.forecastWriteLoad().average());
93+
assertEquals(forecastWriteLoadStats.get("min"), tierBalanceStats.forecastWriteLoad().min());
94+
assertEquals(forecastWriteLoadStats.get("max"), tierBalanceStats.forecastWriteLoad().max());
95+
assertEquals(forecastWriteLoadStats.get("std_dev"), tierBalanceStats.forecastWriteLoad().stdDev());
96+
97+
Map<String, Object> forecastDiskUsageStats = (Map<String, Object>) map.get("forecast_disk_usage");
98+
assertThat(forecastDiskUsageStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev"));
99+
assertEquals(forecastDiskUsageStats.get("total"), tierBalanceStats.forecastShardSize().total());
100+
assertEquals(forecastDiskUsageStats.get("average"), tierBalanceStats.forecastShardSize().average());
101+
assertEquals(forecastDiskUsageStats.get("min"), tierBalanceStats.forecastShardSize().min());
102+
assertEquals(forecastDiskUsageStats.get("max"), tierBalanceStats.forecastShardSize().max());
103+
assertEquals(forecastDiskUsageStats.get("std_dev"), tierBalanceStats.forecastShardSize().stdDev());
104+
105+
Map<String, Object> actualDiskUsageStats = (Map<String, Object>) map.get("actual_disk_usage");
106+
assertThat(actualDiskUsageStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev"));
107+
assertEquals(actualDiskUsageStats.get("total"), tierBalanceStats.actualShardSize().total());
108+
assertEquals(actualDiskUsageStats.get("average"), tierBalanceStats.actualShardSize().average());
109+
assertEquals(actualDiskUsageStats.get("min"), tierBalanceStats.actualShardSize().min());
110+
assertEquals(actualDiskUsageStats.get("max"), tierBalanceStats.actualShardSize().max());
111+
assertEquals(actualDiskUsageStats.get("std_dev"), tierBalanceStats.actualShardSize().stdDev());
112+
}
113+
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@
5252
import java.util.concurrent.ExecutorService;
5353
import java.util.concurrent.TimeUnit;
5454
import java.util.concurrent.atomic.AtomicLong;
55+
import java.util.concurrent.atomic.LongAdder;
5556
import 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

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
}

x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ public final class EnrichCache {
4646
private static final CacheValue EMPTY_CACHE_VALUE = new CacheValue(List.of(), CacheKey.CACHE_KEY_SIZE);
4747

4848
private final Cache<CacheKey, CacheValue> cache;
49+
private final LongAdder sizeInBytes = new LongAdder();
4950
private final LongSupplier relativeNanoTimeProvider;
5051
private final LongAdder hitsTimeInNanos = new LongAdder();
5152
private final LongAdder missesTimeInNanos = new LongAdder();
52-
private final LongAdder sizeInBytes = new LongAdder();
5353

5454
EnrichCache(long maxSize) {
5555
this(maxSize, System::nanoTime);

0 commit comments

Comments
 (0)