Skip to content

Commit 9f86148

Browse files
committed
HBASE-26036 DBB released too early dirty data for some operations
1 parent 22ec681 commit 9f86148

File tree

7 files changed

+386
-166
lines changed

7 files changed

+386
-166
lines changed

hbase-common/src/main/java/org/apache/hadoop/hbase/io/ByteBuffAllocator.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.hadoop.hbase.HConstants;
3030
import org.apache.hadoop.hbase.nio.ByteBuff;
3131
import org.apache.hadoop.hbase.nio.SingleByteBuff;
32+
import org.apache.hadoop.hbase.util.ReflectionUtils;
3233
import org.apache.yetus.audience.InterfaceAudience;
3334
import org.slf4j.Logger;
3435
import org.slf4j.LoggerFactory;
@@ -70,6 +71,13 @@ public class ByteBuffAllocator {
7071

7172
public static final String MIN_ALLOCATE_SIZE_KEY = "hbase.server.allocator.minimal.allocate.size";
7273

74+
/**
75+
* Set an alternate bytebuffallocator by setting this config,
76+
* e.g. we can config {@link DeallocateRewriteByteBuffAllocator} to find out
77+
* prematurely release issues
78+
*/
79+
public static final String BYTEBUFF_ALLOCATOR_CLASS = "hbase.bytebuff.allocator.class";
80+
7381
/**
7482
* @deprecated since 2.3.0 and will be removed in 4.0.0. Use
7583
* {@link ByteBuffAllocator#ALLOCATOR_POOL_ENABLED_KEY} instead.
@@ -117,8 +125,8 @@ public interface Recycler {
117125
void free();
118126
}
119127

120-
private final boolean reservoirEnabled;
121-
private final int bufSize;
128+
protected final boolean reservoirEnabled;
129+
protected final int bufSize;
122130
private final int maxBufCount;
123131
private final AtomicInteger usedBufCount = new AtomicInteger(0);
124132

@@ -169,7 +177,9 @@ public static ByteBuffAllocator create(Configuration conf, boolean reservoirEnab
169177
conf.getInt(MAX_BUFFER_COUNT_KEY, conf.getInt(HConstants.REGION_SERVER_HANDLER_COUNT,
170178
HConstants.DEFAULT_REGION_SERVER_HANDLER_COUNT) * bufsForTwoMB * 2);
171179
int minSizeForReservoirUse = conf.getInt(MIN_ALLOCATE_SIZE_KEY, poolBufSize / 6);
172-
return new ByteBuffAllocator(true, maxBuffCount, poolBufSize, minSizeForReservoirUse);
180+
Class<?> clazz = conf.getClass(BYTEBUFF_ALLOCATOR_CLASS, ByteBuffAllocator.class);
181+
return (ByteBuffAllocator) ReflectionUtils
182+
.newInstance(clazz, true, maxBuffCount, poolBufSize, minSizeForReservoirUse);
173183
} else {
174184
return HEAP;
175185
}
@@ -184,8 +194,8 @@ private static ByteBuffAllocator createOnHeap() {
184194
return new ByteBuffAllocator(false, 0, DEFAULT_BUFFER_SIZE, Integer.MAX_VALUE);
185195
}
186196

187-
ByteBuffAllocator(boolean reservoirEnabled, int maxBufCount, int bufSize,
188-
int minSizeForReservoirUse) {
197+
protected ByteBuffAllocator(boolean reservoirEnabled, int maxBufCount, int bufSize,
198+
int minSizeForReservoirUse) {
189199
this.reservoirEnabled = reservoirEnabled;
190200
this.maxBufCount = maxBufCount;
191201
this.bufSize = bufSize;
@@ -377,7 +387,7 @@ private ByteBuffer getBuffer() {
377387
* Return back a ByteBuffer after its use. Don't read/write the ByteBuffer after the returning.
378388
* @param buf ByteBuffer to return.
379389
*/
380-
private void putbackBuffer(ByteBuffer buf) {
390+
protected void putbackBuffer(ByteBuffer buf) {
381391
if (buf.capacity() != bufSize || (reservoirEnabled ^ buf.isDirect())) {
382392
LOG.warn("Trying to put a buffer, not created by this pool! Will be just ignored");
383393
return;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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.io;
19+
20+
import java.nio.ByteBuffer;
21+
import org.apache.hadoop.hbase.util.Bytes;
22+
import org.apache.yetus.audience.InterfaceAudience;
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
25+
26+
/**
27+
* A ByteBuffAllocator that rewrite the bytebuffers right after released.
28+
* It can be used for test whether there are prematurely releasing backing bytebuffers.
29+
*/
30+
@InterfaceAudience.Private
31+
public class DeallocateRewriteByteBuffAllocator extends ByteBuffAllocator {
32+
private static final Logger LOG = LoggerFactory.getLogger(
33+
DeallocateRewriteByteBuffAllocator.class);
34+
35+
DeallocateRewriteByteBuffAllocator(boolean reservoirEnabled, int maxBufCount, int bufSize,
36+
int minSizeForReservoirUse) {
37+
super(reservoirEnabled, maxBufCount, bufSize, minSizeForReservoirUse);
38+
}
39+
40+
@Override
41+
protected void putbackBuffer(ByteBuffer buf) {
42+
if (buf.capacity() != bufSize || (reservoirEnabled ^ buf.isDirect())) {
43+
LOG.warn("Trying to put a buffer, not created by this pool! Will be just ignored");
44+
return;
45+
}
46+
buf.clear();
47+
byte[] tmp = generateTmpBytes(buf.capacity());
48+
buf.put(tmp, 0, tmp.length);
49+
super.putbackBuffer(buf);
50+
}
51+
52+
private byte[] generateTmpBytes(int length) {
53+
StringBuilder result = new StringBuilder();
54+
while (result.length() < length) {
55+
result.append("-");
56+
}
57+
return Bytes.toBytes(result.substring(0, length));
58+
}
59+
}

hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MultiRowMutationEndpoint.java

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@
3333
import org.apache.hadoop.hbase.client.Get;
3434
import org.apache.hadoop.hbase.client.Mutation;
3535
import org.apache.hadoop.hbase.client.RegionInfo;
36+
import org.apache.hadoop.hbase.client.Scan;
3637
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
3738
import org.apache.hadoop.hbase.filter.Filter;
3839
import org.apache.hadoop.hbase.io.TimeRange;
3940
import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils;
4041
import org.apache.hadoop.hbase.regionserver.HRegion;
4142
import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
4243
import org.apache.hadoop.hbase.regionserver.Region;
44+
import org.apache.hadoop.hbase.regionserver.RegionScanner;
4345
import org.apache.hadoop.hbase.regionserver.WrongRegionException;
4446
import org.apache.hadoop.hbase.util.Bytes;
4547
import org.apache.yetus.audience.InterfaceAudience;
@@ -221,22 +223,27 @@ private boolean matches(Region region, ClientProtos.Condition condition) throws
221223
get.setTimeRange(timeRange.getMin(), timeRange.getMax());
222224
}
223225

224-
List<Cell> result = region.get(get, false);
225226
boolean matches = false;
226-
if (filter != null) {
227-
if (!result.isEmpty()) {
228-
matches = true;
229-
}
230-
} else {
231-
boolean valueIsNull = comparator.getValue() == null || comparator.getValue().length == 0;
232-
if (result.isEmpty() && valueIsNull) {
233-
matches = true;
234-
} else if (result.size() > 0 && result.get(0).getValueLength() == 0 && valueIsNull) {
235-
matches = true;
236-
} else if (result.size() == 1 && !valueIsNull) {
237-
Cell kv = result.get(0);
238-
int compareResult = PrivateCellUtil.compareValue(kv, comparator);
239-
matches = matches(op, compareResult);
227+
try (RegionScanner scanner = region.getScanner(new Scan(get))) {
228+
// NOTE: Please don't use HRegion.get() instead,
229+
// because it will copy cells to heap. See HBASE-26036
230+
List<Cell> result = new ArrayList<>();
231+
scanner.next(result);
232+
if (filter != null) {
233+
if (!result.isEmpty()) {
234+
matches = true;
235+
}
236+
} else {
237+
boolean valueIsNull = comparator.getValue() == null || comparator.getValue().length == 0;
238+
if (result.isEmpty() && valueIsNull) {
239+
matches = true;
240+
} else if (result.size() > 0 && result.get(0).getValueLength() == 0 && valueIsNull) {
241+
matches = true;
242+
} else if (result.size() == 1 && !valueIsNull) {
243+
Cell kv = result.get(0);
244+
int compareResult = PrivateCellUtil.compareValue(kv, comparator);
245+
matches = matches(op, compareResult);
246+
}
240247
}
241248
}
242249
return matches;

0 commit comments

Comments
 (0)