Skip to content

Commit 83c7983

Browse files
author
huiruan
committed
Use ReadWriteLock for region scanner readpoint map
1 parent 748cad6 commit 83c7983

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,9 @@ public void setRestoredRegion(boolean restoredRegion) {
399399
private final int miniBatchSize;
400400

401401
final ConcurrentHashMap<RegionScanner, Long> scannerReadPoints;
402+
// Lock to manage concurrency between RegionScanner and getSmallestReadPoint
403+
final ReentrantReadWriteLock scannerReadPointsLock = new ReentrantReadWriteLock();
404+
final boolean useReadWriteLockForReadPoints;
402405

403406
/**
404407
* The sequence ID that was enLongAddered when this region was opened.
@@ -446,18 +449,26 @@ public long getSmallestReadPoint() {
446449
long minimumReadPoint;
447450
// We need to ensure that while we are calculating the smallestReadPoint
448451
// no new RegionScanners can grab a readPoint that we are unaware of.
449-
// We achieve this by synchronizing on the scannerReadPoints object.
450-
synchronized (scannerReadPoints) {
451-
minimumReadPoint = mvcc.getReadPoint();
452-
for (Long readPoint : this.scannerReadPoints.values()) {
453-
if (readPoint < minimumReadPoint) {
454-
minimumReadPoint = readPoint;
455-
}
452+
if (useReadWriteLockForReadPoints) {
453+
scannerReadPointsLock.writeLock().lock();
454+
try {
455+
minimumReadPoint = calculateSmallestReadPoint();
456+
} finally {
457+
scannerReadPointsLock.writeLock().unlock();
458+
}
459+
} else {
460+
// We achieve this by synchronizing on the scannerReadPoints object.
461+
synchronized (scannerReadPoints) {
462+
minimumReadPoint = calculateSmallestReadPoint();
456463
}
457464
}
458465
return minimumReadPoint;
459466
}
460467

468+
private long calculateSmallestReadPoint() {
469+
return scannerReadPoints.values().stream().mapToLong(Long::longValue).min().orElse(0L);
470+
}
471+
461472
/*
462473
* Data structure of write state flags used coordinating flushes, compactions and closes.
463474
*/
@@ -798,6 +809,13 @@ public HRegion(final HRegionFileSystem fs, final WAL wal, final Configuration co
798809
}
799810
this.rowLockWaitDuration = tmpRowLockDuration;
800811

812+
this.useReadWriteLockForReadPoints =
813+
conf.getBoolean("hbase.readpoints.read.write.lock.enable", false);
814+
if (LOG.isDebugEnabled()) {
815+
LOG.debug("region = {}, useReadWriteLockForReadPoints = {}", getRegionInfo(),
816+
useReadWriteLockForReadPoints);
817+
}
818+
801819
this.isLoadingCfsOnDemandDefault = conf.getBoolean(LOAD_CFS_ON_DEMAND_CONFIG_KEY, true);
802820
this.htableDescriptor = htd;
803821
Set<byte[]> families = this.htableDescriptor.getColumnFamilyNames();
@@ -8145,7 +8163,7 @@ private void doAttachReplicateRegionReplicaAction(WALKeyImpl walKey, WALEdit wal
81458163
(3 * ClassSize.CONCURRENT_HASHMAP) + // lockedRows, scannerReadPoints, regionLockHolders
81468164
WriteState.HEAP_SIZE + // writestate
81478165
ClassSize.CONCURRENT_SKIPLISTMAP + ClassSize.CONCURRENT_SKIPLISTMAP_ENTRY + // stores
8148-
(2 * ClassSize.REENTRANT_LOCK) + // lock, updatesLock
8166+
(3 * ClassSize.REENTRANT_LOCK) + // lock, updatesLock, scannerReadPointsLock
81498167
MultiVersionConcurrencyControl.FIXED_SIZE // mvcc
81508168
+ 2 * ClassSize.TREEMAP // maxSeqIdInStores, replicationScopes
81518169
+ 2 * ClassSize.ATOMIC_INTEGER // majorInProgress, minorInProgress

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,34 @@ private static boolean hasNonce(HRegion region, long nonce) {
130130
long mvccReadPoint = PackagePrivateFieldAccessor.getMvccReadPoint(scan);
131131
this.scannerReadPoints = region.scannerReadPoints;
132132
this.rsServices = region.getRegionServerServices();
133-
synchronized (scannerReadPoints) {
134-
if (mvccReadPoint > 0) {
135-
this.readPt = mvccReadPoint;
136-
} else if (hasNonce(region, nonce)) {
137-
this.readPt = rsServices.getNonceManager().getMvccFromOperationContext(nonceGroup, nonce);
138-
} else {
139-
this.readPt = region.getReadPoint(isolationLevel);
133+
if (region.useReadWriteLockForReadPoints) {
134+
region.scannerReadPointsLock.readLock().lock();
135+
try {
136+
this.readPt = calculateReadPoint(isolationLevel, mvccReadPoint, nonceGroup, nonce);
137+
scannerReadPoints.put(this, this.readPt);
138+
} finally {
139+
region.scannerReadPointsLock.readLock().unlock();
140+
}
141+
} else {
142+
synchronized (scannerReadPoints) {
143+
this.readPt = calculateReadPoint(isolationLevel, mvccReadPoint, nonceGroup, nonce);
144+
scannerReadPoints.put(this, this.readPt);
140145
}
141-
scannerReadPoints.put(this, this.readPt);
142146
}
143147
initializeScanners(scan, additionalScanners);
144148
}
145149

150+
private long calculateReadPoint(IsolationLevel isolationLevel, long mvccReadPoint,
151+
long nonceGroup, long nonce) {
152+
if (mvccReadPoint > 0) {
153+
return mvccReadPoint;
154+
}
155+
if (hasNonce(region, nonce)) {
156+
return rsServices.getNonceManager().getMvccFromOperationContext(nonceGroup, nonce);
157+
}
158+
return region.getReadPoint(isolationLevel);
159+
}
160+
146161
private void initializeScanners(Scan scan, List<KeyValueScanner> additionalScanners)
147162
throws IOException {
148163
// Here we separate all scanners into two lists - scanner that provide data required

0 commit comments

Comments
 (0)