Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
872f13a
Add row-level cache for the get operation
terence-yoo Aug 19, 2025
80b728c
Resolve compile error caused by ReturnValueIgnored
terence-yoo Sep 11, 2025
a6a8465
Separate RowCache from BlockCache
terence-yoo Sep 22, 2025
7429c3b
Invalidate only the row cache of regions that were bulkloaded
terence-yoo Sep 25, 2025
6df768a
Minor fix
terence-yoo Sep 25, 2025
e33db29
Do not use the row cache when the data exists only in the MemStore
terence-yoo Sep 25, 2025
7091abe
Some cosmetic fix related to RowCache
terence-yoo Oct 4, 2025
d54fff4
Bump Caffeine to the latest version
terence-yoo Oct 4, 2025
99ce0f0
Retrigger
terence-yoo Oct 5, 2025
e3a8af8
Enable setting ROW_CACHE_ENABLED via HBase Shell
terence-yoo Oct 14, 2025
ffba417
Replace manual hit/miss counting with Cache.stats()
terence-yoo Oct 14, 2025
95d7892
Rename method for RowCache
terence-yoo Oct 14, 2025
859f6b0
Fix spotless violation
terence-yoo Oct 15, 2025
cceda48
Modify comment for region level invalidation
terence-yoo Oct 15, 2025
6545e31
Add ROW_CACHE_EVICT_ON_CLOSE configuration option
terence-yoo Oct 16, 2025
113445d
Add RowCache interface
terence-yoo Oct 16, 2025
5dad021
Minor fix
terence-yoo Oct 16, 2025
4db38bd
Support row cache when off heap cache is enabled
terence-yoo Oct 20, 2025
24a7ba8
Add global row.cache.enabled configuration
terence-yoo Oct 20, 2025
7cc5d2a
Merge remote-tracking branch 'up/master' into HBASE-29585
terence-yoo Oct 21, 2025
7e2718a
Enhance TestRowCache to ensure eviction count increments only for act…
terence-yoo Oct 21, 2025
7b4ddd9
Fix incorrect changes from previous merge
terence-yoo Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,11 @@ default boolean matchReplicationScope(boolean enabled) {
}
return !enabled;
}

/**
* Checks whether row caching is enabled for this table. Note that row caching applies only at the
* entire row level, not at the column family level.
* @return {@code true} if row cache is enabled, otherwise {@code false}
*/
boolean isRowCacheEnabled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ public class TableDescriptorBuilder {
private final static Map<String, String> DEFAULT_VALUES = new HashMap<>();
private final static Set<Bytes> RESERVED_KEYWORDS = new HashSet<>();

/**
* Used by HBase Shell interface to access this metadata attribute which denotes if the row cache
* is enabled.
*/
@InterfaceAudience.Private
public static final String ROW_CACHE_ENABLED = "ROW_CACHE_ENABLED";
private static final Bytes ROW_CACHE_ENABLED_KEY = new Bytes(Bytes.toBytes(ROW_CACHE_ENABLED));
private static final boolean DEFAULT_ROW_CACHE_ENABLED = false;

static {
DEFAULT_VALUES.put(MAX_FILESIZE, String.valueOf(HConstants.DEFAULT_MAX_FILE_SIZE));
DEFAULT_VALUES.put(READONLY, String.valueOf(DEFAULT_READONLY));
Expand Down Expand Up @@ -565,6 +574,11 @@ public TableDescriptor build() {
return new ModifyableTableDescriptor(desc);
}

public TableDescriptorBuilder setRowCacheEnabled(boolean rowCacheEnabled) {
desc.setRowCacheEnabled(rowCacheEnabled);
return this;
}

private static final class ModifyableTableDescriptor
implements TableDescriptor, Comparable<ModifyableTableDescriptor> {

Expand Down Expand Up @@ -1510,6 +1524,15 @@ public Optional<String> getRegionServerGroup() {
return Optional.empty();
}
}

@Override
public boolean isRowCacheEnabled() {
return getOrDefault(ROW_CACHE_ENABLED_KEY, Boolean::valueOf, DEFAULT_ROW_CACHE_ENABLED);
}

public ModifyableTableDescriptor setRowCacheEnabled(boolean enabled) {
return setValue(ROW_CACHE_ENABLED_KEY, Boolean.toString(enabled));
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,14 @@ public enum OperationStatusCode {
public static final long HBASE_CLIENT_SCANNER_ONHEAP_BLOCK_CACHE_FIXED_SIZE_DEFAULT =
32 * 1024 * 1024L;

/**
* Configuration key for the minimum number of HFiles required to activate the Row Cache. If the
* number of HFiles is less than this value, the Row Cache does not operate even if it is enabled
* at the table level.
*/
public static final String ROW_CACHE_ACTIVATE_MIN_HFILES_KEY = "row.cache.activate.min.hfiles";
public static final int ROW_CACHE_ACTIVATE_MIN_HFILES_DEFAULT = 2;

/**
* Configuration key for setting pread must read both necessaryLen and extraLen, default is
* disabled. This is an optimized flag for reading HFile from blob storage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,8 @@ public void setTimestamp(byte[] ts) throws IOException {

@Override
public ExtendedCell deepClone() {
// This is not used in actual flow. Throwing UnsupportedOperationException
throw new UnsupportedOperationException();
// To garbage collect the objects referenced by this cell, we need to deep clone it
return ExtendedCell.super.deepClone();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ public int getId() {
/** Fixed file trailer, both versions (always just a magic string) */
TRAILER("TRABLK\"$", BlockCategory.META),

// Pseudo block

/**
* Cells of a row for row cache. This is a pseudo block type. It only exists to share the
* BlockCache interface.
*/
ROW_CELLS("ROWCELLS", BlockCategory.ROW),

// Legacy blocks

/** Block index magic string in version 1 */
Expand All @@ -91,6 +99,7 @@ public enum BlockCategory {
INDEX,
BLOOM,
ALL_CATEGORIES,
ROW,
UNKNOWN;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ public interface MetricsRegionServerSource extends BaseSource, JvmPauseMonitorSo
String BLOCK_CACHE_GENERAL_BLOOM_META_MISS_COUNT = "blockCacheGeneralBloomMetaMissCount";
String BLOCK_CACHE_DELETE_FAMILY_BLOOM_MISS_COUNT = "blockCacheDeleteFamilyBloomMissCount";
String BLOCK_CACHE_TRAILER_MISS_COUNT = "blockCacheTrailerMissCount";
String BLOCK_CACHE_ROW_MISS_COUNT = "blockCacheRowMissCount";
String BLOCK_CACHE_DATA_HIT_COUNT = "blockCacheDataHitCount";
String BLOCK_CACHE_ENCODED_DATA_HIT_COUNT = "blockCacheEncodedDataHitCount";
String BLOCK_CACHE_LEAF_INDEX_HIT_COUNT = "blockCacheLeafIndexHitCount";
Expand All @@ -397,6 +398,7 @@ public interface MetricsRegionServerSource extends BaseSource, JvmPauseMonitorSo
String BLOCK_CACHE_GENERAL_BLOOM_META_HIT_COUNT = "blockCacheGeneralBloomMetaHitCount";
String BLOCK_CACHE_DELETE_FAMILY_BLOOM_HIT_COUNT = "blockCacheDeleteFamilyBloomHitCount";
String BLOCK_CACHE_TRAILER_HIT_COUNT = "blockCacheTrailerHitCount";
String BLOCK_CACHE_ROW_HIT_COUNT = "blockCacheRowHitCount";
String L1_CACHE_FREE_SIZE = "l1CacheFreeSize";
String L1_CACHE_FREE_SIZE_DESC = "Amount of free bytes in the L1 cache";
String L1_CACHE_SIZE = "l1CacheSize";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ public void getMetrics(MetricsCollector metricsCollector, boolean all) {
.addCounter(Interns.info(BLOCK_CACHE_DELETE_FAMILY_BLOOM_MISS_COUNT, ""),
rsWrap.getDeleteFamilyBloomMissCount())
.addCounter(Interns.info(BLOCK_CACHE_TRAILER_MISS_COUNT, ""), rsWrap.getTrailerMissCount())
.addCounter(Interns.info(BLOCK_CACHE_ROW_MISS_COUNT, ""), rsWrap.getRowMissCount())
.addCounter(Interns.info(BLOCK_CACHE_DATA_HIT_COUNT, ""), rsWrap.getDataHitCount())
.addCounter(Interns.info(BLOCK_CACHE_LEAF_INDEX_HIT_COUNT, ""),
rsWrap.getLeafIndexHitCount())
Expand All @@ -452,6 +453,7 @@ public void getMetrics(MetricsCollector metricsCollector, boolean all) {
.addCounter(Interns.info(BLOCK_CACHE_DELETE_FAMILY_BLOOM_HIT_COUNT, ""),
rsWrap.getDeleteFamilyBloomHitCount())
.addCounter(Interns.info(BLOCK_CACHE_TRAILER_HIT_COUNT, ""), rsWrap.getTrailerHitCount())
.addCounter(Interns.info(BLOCK_CACHE_ROW_HIT_COUNT, ""), rsWrap.getRowHitCount())
.addCounter(Interns.info(UPDATES_BLOCKED_TIME, UPDATES_BLOCKED_DESC),
rsWrap.getUpdatesBlockedTime())
.addCounter(Interns.info(FLUSHED_CELLS, FLUSHED_CELLS_DESC), rsWrap.getFlushedCellsCount())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,8 @@ public interface MetricsRegionServerWrapper {

long getTrailerMissCount();

long getRowMissCount();

long getDataHitCount();

long getLeafIndexHitCount();
Expand All @@ -635,6 +637,8 @@ public interface MetricsRegionServerWrapper {

long getTrailerHitCount();

long getRowHitCount();

long getTotalRowActionRequestCount();

long getByteBuffAllocatorHeapAllocationBytes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public class CacheStats {
private final LongAdder generalBloomMetaMissCount = new LongAdder();
private final LongAdder deleteFamilyBloomMissCount = new LongAdder();
private final LongAdder trailerMissCount = new LongAdder();
private final LongAdder rowMissCount = new LongAdder();

private final LongAdder dataHitCount = new LongAdder();
private final LongAdder leafIndexHitCount = new LongAdder();
Expand All @@ -102,6 +103,7 @@ public class CacheStats {
private final LongAdder generalBloomMetaHitCount = new LongAdder();
private final LongAdder deleteFamilyBloomHitCount = new LongAdder();
private final LongAdder trailerHitCount = new LongAdder();
private final LongAdder rowHitCount = new LongAdder();

// Executor for periodic cache stats rolling
private ScheduledExecutorService metricsRollerScheduler;
Expand Down Expand Up @@ -219,6 +221,9 @@ public void miss(boolean caching, boolean primary, BlockType type) {
case TRAILER:
trailerMissCount.increment();
break;
case ROW_CELLS:
rowMissCount.increment();
break;
default:
// If there's a new type that's fine
// Ignore it for now. This is metrics don't exception.
Expand Down Expand Up @@ -266,6 +271,9 @@ public void hit(boolean caching, boolean primary, BlockType type) {
case TRAILER:
trailerHitCount.increment();
break;
case ROW_CELLS:
rowHitCount.increment();
break;
default:
// If there's a new type that's fine
// Ignore it for now. This is metrics don't exception.
Expand Down Expand Up @@ -376,6 +384,14 @@ public long getTrailerHitCount() {
return trailerHitCount.sum();
}

public long getRowHitCount() {
return rowHitCount.sum();
}

public long getRowMissCount() {
return rowMissCount.sum();
}

public long getRequestCount() {
return getHitCount() + getMissCount();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.io.hfile;

import java.util.Arrays;
import java.util.Objects;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.ClassSize;
import org.apache.yetus.audience.InterfaceAudience;

/**
* Cache Key for use with implementations of {@link BlockCache}
*/
@InterfaceAudience.Private
public class RowCacheKey extends BlockCacheKey {
private static final long serialVersionUID = -686874540957524887L;
public static final long FIXED_OVERHEAD = ClassSize.estimateBase(RowCacheKey.class, false);

private final byte[] rowKey;
// Row cache keys should not be evicted on close, since the cache may contain many entries and
// eviction would be slow. Instead, the region’s rowCacheSeqNum is used to generate new keys that
// ignore the existing cache when the region is reopened or bulk-loaded.
private final long rowCacheSeqNum;

public RowCacheKey(HRegion region, byte[] rowKey) {
super(region.getRegionInfo().getEncodedName(), 0, region.getRegionInfo().getReplicaId() == 0,
BlockType.ROW_CELLS);

this.rowKey = Objects.requireNonNull(rowKey, "rowKey cannot be null");
this.rowCacheSeqNum = region.getRowCacheSeqNum();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
RowCacheKey that = (RowCacheKey) o;
return rowCacheSeqNum == that.rowCacheSeqNum && Arrays.equals(rowKey, that.rowKey);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), Arrays.hashCode(rowKey), Long.hashCode(rowCacheSeqNum));
}

@Override
public String toString() {
return super.toString() + '_' + Bytes.toStringBinary(this.rowKey) + '_' + rowCacheSeqNum;
}

@Override
public long heapSize() {
return FIXED_OVERHEAD + ClassSize.align(rowKey.length);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
Expand Down Expand Up @@ -433,6 +434,11 @@ public MetricsTableRequests getMetricsTableRequests() {
*/
private long openSeqNum = HConstants.NO_SEQNUM;

/**
* Basically the same as openSeqNum, but it is updated when bulk load is done.
*/
private final AtomicLong rowCacheSeqNum = new AtomicLong(HConstants.NO_SEQNUM);

/**
* The default setting for whether to enable on-demand CF loading for scan requests to this
* region. Requests can override it.
Expand Down Expand Up @@ -7868,6 +7874,7 @@ private HRegion openHRegion(final CancelableProgressable reporter) throws IOExce
LOG.debug("checking classloading for " + this.getRegionInfo().getEncodedName());
TableDescriptorChecker.checkClassLoading(cConfig, htableDescriptor);
this.openSeqNum = initialize(reporter);
this.rowCacheSeqNum.set(this.openSeqNum);
this.mvcc.advanceTo(openSeqNum);
// The openSeqNum must be increased every time when a region is assigned, as we rely on it to
// determine whether a region has been successfully reopened. So here we always write open
Expand Down Expand Up @@ -8696,6 +8703,17 @@ public long getOpenSeqNum() {
return this.openSeqNum;
}

public long getRowCacheSeqNum() {
return this.rowCacheSeqNum.get();
}

/**
* This is used to invalidate the entire row cache after bulk loading.
*/
Comment on lines 8763 to 8765
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment correct? I thought we would be invalidating only the rows for the given regions. Rows from regions not touched by bulkload would stay valid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I'll fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public void increaseRowCacheSeqNum() {
this.rowCacheSeqNum.incrementAndGet();
}

@Override
public Map<byte[], Long> getMaxStoreSeqId() {
return this.maxSeqIdInStores;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3408,6 +3408,9 @@ CacheEvictionStats clearRegionBlockCache(Region region) {
}
}

// evict the entire row cache
evictedBlocks += blockCache.evictBlocksByHfileName(region.getRegionInfo().getEncodedName());

return CacheEvictionStats.builder().withEvictedBlocks(evictedBlocks).build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,11 @@ public long getTrailerMissCount() {
return this.cacheStats != null ? this.cacheStats.getTrailerMissCount() : 0L;
}

@Override
public long getRowMissCount() {
return this.cacheStats != null ? this.cacheStats.getRowMissCount() : 0L;
}

@Override
public long getDataHitCount() {
return this.cacheStats != null ? this.cacheStats.getDataHitCount() : 0L;
Expand Down Expand Up @@ -1194,6 +1199,11 @@ public long getTrailerHitCount() {
return this.cacheStats != null ? this.cacheStats.getTrailerHitCount() : 0L;
}

@Override
public long getRowHitCount() {
return this.cacheStats != null ? this.cacheStats.getRowHitCount() : 0L;
}

@Override
public long getByteBuffAllocatorHeapAllocationBytes() {
return ByteBuffAllocator.getHeapAllocationBytes(allocator, ByteBuffAllocator.HEAP);
Expand Down
Loading