Skip to content

Commit f4b234c

Browse files
committed
Add RowCache interface
1 parent 6545e31 commit f4b234c

File tree

3 files changed

+160
-77
lines changed

3 files changed

+160
-77
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RowCache.java

Lines changed: 52 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -17,96 +17,72 @@
1717
*/
1818
package org.apache.hadoop.hbase.regionserver;
1919

20-
import com.github.benmanes.caffeine.cache.Cache;
21-
import com.github.benmanes.caffeine.cache.Caffeine;
22-
import com.github.benmanes.caffeine.cache.Policy;
23-
import com.github.benmanes.caffeine.cache.RemovalCause;
24-
import com.github.benmanes.caffeine.cache.RemovalListener;
25-
import java.util.Optional;
26-
import java.util.OptionalLong;
27-
import java.util.concurrent.atomic.LongAdder;
28-
import org.checkerframework.checker.nullness.qual.NonNull;
29-
3020
/**
31-
* A cache that stores rows retrieved by Get operations, using Caffeine as the underlying cache
32-
* implementation.
21+
* Interface for caching rows retrieved by Get operations.
3322
*/
3423
@org.apache.yetus.audience.InterfaceAudience.Private
35-
public class RowCache {
36-
private final class EvictionListener
37-
implements RemovalListener<@NonNull RowCacheKey, @NonNull RowCells> {
38-
@Override
39-
public void onRemoval(RowCacheKey key, RowCells value, @NonNull RemovalCause cause) {
40-
evictedRowCount.increment();
41-
}
42-
}
43-
44-
private final Cache<@NonNull RowCacheKey, RowCells> cache;
45-
46-
// Cache.stats() does not provide eviction count for entries, so we maintain our own counter.
47-
private final LongAdder evictedRowCount = new LongAdder();
48-
49-
RowCache(long maxSizeBytes) {
50-
if (maxSizeBytes <= 0) {
51-
cache = Caffeine.newBuilder().maximumSize(0).build();
52-
return;
53-
}
54-
55-
cache =
56-
Caffeine.newBuilder().maximumWeight(maxSizeBytes).removalListener(new EvictionListener())
57-
.weigher((RowCacheKey key,
58-
RowCells value) -> (int) Math.min(key.heapSize() + value.heapSize(), Integer.MAX_VALUE))
59-
.recordStats().build();
60-
}
61-
62-
void cacheRow(RowCacheKey key, RowCells value) {
63-
cache.put(key, value);
64-
}
65-
66-
public RowCells getRow(RowCacheKey key, boolean caching) {
67-
if (!caching) {
68-
return null;
69-
}
70-
71-
return cache.getIfPresent(key);
72-
}
24+
public interface RowCache {
25+
/**
26+
* Cache the specified row.
27+
* @param key the key of the row to cache
28+
* @param value the cells of the row to cache
29+
*/
30+
void cacheRow(RowCacheKey key, RowCells value);
7331

74-
void evictRow(RowCacheKey key) {
75-
cache.asMap().remove(key);
76-
}
32+
/**
33+
* Evict the specified row.
34+
* @param key the key of the row to evict
35+
*/
36+
void evictRow(RowCacheKey key);
7737

7838
/**
7939
* Evict all rows belonging to the specified region. This is heavy operation as it iterates the
8040
* entire RowCache key set.
8141
* @param region the region whose rows should be evicted
8242
*/
83-
void evictRowsByRegion(HRegion region) {
84-
cache.asMap().keySet().removeIf(key -> key.isSameRegion(region));
85-
}
43+
void evictRowsByRegion(HRegion region);
8644

87-
public long getHitCount() {
88-
return cache.stats().hitCount();
89-
}
45+
/**
46+
* Get the number of rows in the cache.
47+
* @return the number of rows in the cache
48+
*/
49+
long getCount();
9050

91-
public long getMissCount() {
92-
return cache.stats().missCount();
93-
}
51+
/**
52+
* Get the number of rows evicted from the cache.
53+
* @return the number of rows evicted from the cache
54+
*/
55+
long getEvictedRowCount();
9456

95-
public long getEvictedRowCount() {
96-
return evictedRowCount.sum();
97-
}
57+
/**
58+
* Get the hit count.
59+
* @return the hit count
60+
*/
61+
long getHitCount();
9862

99-
public long getSize() {
100-
Optional<OptionalLong> result = cache.policy().eviction().map(Policy.Eviction::weightedSize);
101-
return result.orElse(OptionalLong.of(-1L)).orElse(-1L);
102-
}
63+
/**
64+
* Get the maximum size of the cache in bytes.
65+
* @return the maximum size of the cache in bytes
66+
*/
67+
long getMaxSize();
10368

104-
public long getMaxSize() {
105-
Optional<Long> result = cache.policy().eviction().map(Policy.Eviction::getMaximum);
106-
return result.orElse(-1L);
107-
}
69+
/**
70+
* Get the miss count.
71+
* @return the miss count
72+
*/
73+
long getMissCount();
74+
75+
/**
76+
* Get the specified row from the cache.
77+
* @param key the key of the row to get
78+
* @param caching whether caching is enabled for this request
79+
* @return the cells of the row, or null if not found or caching is disabled
80+
*/
81+
RowCells getRow(RowCacheKey key, boolean caching);
10882

109-
public long getCount() {
110-
return cache.estimatedSize();
111-
}
83+
/**
84+
* Get the current size of the cache in bytes.
85+
* @return the current size of the cache in bytes
86+
*/
87+
long getSize();
11288
}

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RowCacheService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ interface RowOperation<R> {
7878
RowCacheService(Configuration conf) {
7979
enabledByConf =
8080
conf.getFloat(HConstants.ROW_CACHE_SIZE_KEY, HConstants.ROW_CACHE_SIZE_DEFAULT) > 0;
81-
rowCache = new RowCache(MemorySizeUtil.getRowCacheSize(conf));
81+
// Currently we only support TinyLfu implementation
82+
rowCache = new TinyLfuRowCache(MemorySizeUtil.getRowCacheSize(conf));
8283
}
8384

8485
RegionScannerImpl getScanner(HRegion region, Get get, Scan scan, List<Cell> results,
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.regionserver;
19+
20+
import com.github.benmanes.caffeine.cache.Cache;
21+
import com.github.benmanes.caffeine.cache.Caffeine;
22+
import com.github.benmanes.caffeine.cache.Policy;
23+
import com.github.benmanes.caffeine.cache.RemovalCause;
24+
import com.github.benmanes.caffeine.cache.RemovalListener;
25+
import java.util.Optional;
26+
import java.util.OptionalLong;
27+
import java.util.concurrent.atomic.LongAdder;
28+
import org.checkerframework.checker.nullness.qual.NonNull;
29+
30+
/**
31+
* A {@link RowCache} implementation backed by Caffeine with a TinyLFU-based eviction policy.
32+
*/
33+
@org.apache.yetus.audience.InterfaceAudience.Private
34+
public class TinyLfuRowCache implements RowCache {
35+
private final class EvictionListener
36+
implements RemovalListener<@NonNull RowCacheKey, @NonNull RowCells> {
37+
@Override
38+
public void onRemoval(RowCacheKey key, RowCells value, @NonNull RemovalCause cause) {
39+
evictedRowCount.increment();
40+
}
41+
}
42+
43+
private final Cache<@NonNull RowCacheKey, RowCells> cache;
44+
45+
// Cache.stats() does not provide eviction count for entries, so we maintain our own counter.
46+
private final LongAdder evictedRowCount = new LongAdder();
47+
48+
TinyLfuRowCache(long maxSizeBytes) {
49+
if (maxSizeBytes <= 0) {
50+
cache = Caffeine.newBuilder().maximumSize(0).build();
51+
return;
52+
}
53+
54+
cache =
55+
Caffeine.newBuilder().maximumWeight(maxSizeBytes).removalListener(new EvictionListener())
56+
.weigher((RowCacheKey key,
57+
RowCells value) -> (int) Math.min(key.heapSize() + value.heapSize(), Integer.MAX_VALUE))
58+
.recordStats().build();
59+
}
60+
61+
public void cacheRow(RowCacheKey key, RowCells value) {
62+
cache.put(key, value);
63+
}
64+
65+
public RowCells getRow(RowCacheKey key, boolean caching) {
66+
if (!caching) {
67+
return null;
68+
}
69+
70+
return cache.getIfPresent(key);
71+
}
72+
73+
public void evictRow(RowCacheKey key) {
74+
cache.asMap().remove(key);
75+
}
76+
77+
public void evictRowsByRegion(HRegion region) {
78+
cache.asMap().keySet().removeIf(key -> key.isSameRegion(region));
79+
}
80+
81+
public long getHitCount() {
82+
return cache.stats().hitCount();
83+
}
84+
85+
public long getMissCount() {
86+
return cache.stats().missCount();
87+
}
88+
89+
public long getEvictedRowCount() {
90+
return evictedRowCount.sum();
91+
}
92+
93+
public long getSize() {
94+
Optional<OptionalLong> result = cache.policy().eviction().map(Policy.Eviction::weightedSize);
95+
return result.orElse(OptionalLong.of(-1L)).orElse(-1L);
96+
}
97+
98+
public long getMaxSize() {
99+
Optional<Long> result = cache.policy().eviction().map(Policy.Eviction::getMaximum);
100+
return result.orElse(-1L);
101+
}
102+
103+
public long getCount() {
104+
return cache.estimatedSize();
105+
}
106+
}

0 commit comments

Comments
 (0)