Skip to content

Commit 3176c00

Browse files
Gabor Kaszabgaborkaszab
authored andcommitted
Core: Expose the stats of the manifest file content cache
For observability purposes clients could use the stats of the manifest file content cache to see for instance the cache hit/miss ratio so that users can fine tune the configuration of the cache.
1 parent f3949ce commit 3176c00

File tree

4 files changed

+138
-6
lines changed

4 files changed

+138
-6
lines changed

core/src/main/java/org/apache/iceberg/ManifestFiles.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.iceberg.io.FileIO;
3434
import org.apache.iceberg.io.InputFile;
3535
import org.apache.iceberg.io.OutputFile;
36+
import org.apache.iceberg.metrics.CacheMetrics;
3637
import org.apache.iceberg.relocated.com.google.common.annotations.VisibleForTesting;
3738
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
3839
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
@@ -86,6 +87,11 @@ public static void dropCache(FileIO fileIO) {
8687
CONTENT_CACHES.cleanUp();
8788
}
8889

90+
/** Get statistics of the manifest file content cache for a FileIO. */
91+
public static CacheMetrics contentCacheStats(FileIO io) {
92+
return CacheMetrics.of(contentCache(io).stats());
93+
}
94+
8995
/**
9096
* Returns a {@link CloseableIterable} of file paths in the {@link ManifestFile}.
9197
*
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.iceberg.metrics;
20+
21+
import com.github.benmanes.caffeine.cache.stats.CacheStats;
22+
import org.immutables.value.Value;
23+
24+
@Value.Immutable
25+
public abstract class CacheMetrics {
26+
public abstract long hitCount();
27+
28+
public abstract long missCount();
29+
30+
public abstract long loadSuccessCount();
31+
32+
public abstract long loadFailureCount();
33+
34+
public abstract long totalLoadTime();
35+
36+
public abstract long evictionCount();
37+
38+
public abstract long evictionWeight();
39+
40+
public static CacheMetrics of(CacheStats stats) {
41+
return ImmutableCacheMetrics.builder()
42+
.hitCount(stats.hitCount())
43+
.missCount(stats.missCount())
44+
.loadSuccessCount(stats.loadSuccessCount())
45+
.loadFailureCount(stats.loadFailureCount())
46+
.totalLoadTime(stats.totalLoadTime())
47+
.evictionCount(stats.evictionCount())
48+
.evictionWeight(stats.evictionWeight())
49+
.build();
50+
}
51+
}

core/src/test/java/org/apache/iceberg/TestManifestCaching.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.apache.iceberg.hadoop.HadoopFileIO;
3737
import org.apache.iceberg.io.ContentCache;
3838
import org.apache.iceberg.io.FileIO;
39+
import org.apache.iceberg.metrics.CacheMetrics;
3940
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
4041
import org.apache.iceberg.relocated.com.google.common.collect.Lists;
4142
import org.apache.iceberg.types.Types;
@@ -78,16 +79,16 @@ public void testPlanWithCache() throws Exception {
7879
assertThat(cache.estimatedCacheSize())
7980
.as("All manifest files should be cached")
8081
.isEqualTo(numFiles);
81-
assertThat(cache.stats().loadCount())
82+
assertThat(ManifestFiles.contentCacheStats(table.io()).loadSuccessCount())
8283
.as("All manifest files should be recently loaded")
8384
.isEqualTo(numFiles);
84-
long missCount = cache.stats().missCount();
85+
long missCount = ManifestFiles.contentCacheStats(table.io()).missCount();
8586

8687
// planFiles and verify that cache size still the same
8788
TableScan scan2 = table.newScan();
8889
assertThat(scan2.planFiles()).hasSize(numFiles);
8990
assertThat(cache.estimatedCacheSize()).isEqualTo(numFiles);
90-
assertThat(cache.stats().missCount())
91+
assertThat(ManifestFiles.contentCacheStats(table.io()).missCount())
9192
.as("All manifest file reads should hit cache")
9293
.isEqualTo(missCount);
9394

@@ -115,10 +116,14 @@ public void testPlanWithSmallCache() throws Exception {
115116
assertThat(cache.maxTotalBytes()).isEqualTo(1);
116117
assertThat(scan.planFiles()).hasSize(numFiles);
117118
assertThat(cache.estimatedCacheSize()).isEqualTo(0);
118-
assertThat(cache.stats().loadCount())
119+
assertThat(ManifestFiles.contentCacheStats(table.io()).loadSuccessCount())
119120
.as("File should not be loaded through cache")
120121
.isEqualTo(0);
121-
assertThat(cache.stats().requestCount()).as("Cache should not serve file").isEqualTo(0);
122+
assertThat(
123+
ManifestFiles.contentCacheStats(table.io()).hitCount()
124+
+ ManifestFiles.contentCacheStats(table.io()).missCount())
125+
.as("Cache should not serve file")
126+
.isEqualTo(0);
122127
ManifestFiles.dropCache(scan.table().io());
123128
}
124129

@@ -199,7 +204,7 @@ public void testWeakFileIOReferenceCleanUp() {
199204
assertThat(cache1).isSameAs(firstCache);
200205
assertThat(cacheN).isSameAs(lastCache);
201206
assertThat(manifestCache.stats().loadCount()).isEqualTo(maxIO);
202-
assertThat(manifestCache.stats().evictionCount()).isEqualTo(maxIO - 2);
207+
assertThat(CacheMetrics.of(manifestCache.stats()).evictionCount()).isEqualTo(maxIO - 2);
203208
}
204209

205210
/**
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.iceberg.metrics;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
import com.github.benmanes.caffeine.cache.Cache;
24+
import com.github.benmanes.caffeine.cache.Caffeine;
25+
import com.github.benmanes.caffeine.cache.Weigher;
26+
import org.junit.jupiter.api.Test;
27+
28+
public class TestCacheMetrics {
29+
@Test
30+
public void testNoInputStats() {
31+
CacheMetrics cacheMetrics = CacheMetrics.of(Caffeine.newBuilder().build().stats());
32+
33+
assertThat(cacheMetrics.hitCount()).isZero();
34+
assertThat(cacheMetrics.missCount()).isZero();
35+
assertThat(cacheMetrics.loadSuccessCount()).isZero();
36+
assertThat(cacheMetrics.loadFailureCount()).isZero();
37+
assertThat(cacheMetrics.totalLoadTime()).isZero();
38+
assertThat(cacheMetrics.evictionCount()).isZero();
39+
assertThat(cacheMetrics.evictionWeight()).isZero();
40+
}
41+
42+
@Test
43+
public void testCacheMetricsFromCaffeineCache() {
44+
int maxTotalWeight = 300;
45+
46+
Cache<Integer, Integer> inputCache =
47+
Caffeine.newBuilder()
48+
.maximumWeight(maxTotalWeight)
49+
.weigher((Weigher<Integer, Integer>) (key, value) -> value * 100)
50+
.recordStats()
51+
.build();
52+
53+
inputCache.get(1, key -> key);
54+
inputCache.get(1, key -> key);
55+
inputCache.get(2, key -> key);
56+
inputCache.get(3, key -> key); // This evicts the other entries due to max weight
57+
58+
inputCache.cleanUp();
59+
60+
CacheMetrics cacheMetrics = CacheMetrics.of(inputCache.stats());
61+
62+
assertThat(cacheMetrics.hitCount()).isOne();
63+
assertThat(cacheMetrics.missCount()).isEqualTo(3);
64+
assertThat(cacheMetrics.loadSuccessCount()).isEqualTo(3);
65+
assertThat(cacheMetrics.loadFailureCount()).isZero();
66+
assertThat(cacheMetrics.totalLoadTime()).isGreaterThan(0);
67+
assertThat(cacheMetrics.evictionCount()).isEqualTo(2);
68+
assertThat(cacheMetrics.evictionWeight()).isEqualTo(300);
69+
}
70+
}

0 commit comments

Comments
 (0)