Skip to content

Commit 66a2fc5

Browse files
authored
HBASE-22539 WAL corruption due to early DBBs re-use when Durability.ASYNC_WAL is used (#437)
Signed-off-by: Zheng Hu <[email protected]>
1 parent f6ece8d commit 66a2fc5

File tree

11 files changed

+382
-32
lines changed

11 files changed

+382
-32
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.util.ArrayList;
2424
import java.util.List;
2525
import java.util.Optional;
26-
26+
import java.util.concurrent.atomic.AtomicInteger;
2727
import org.apache.hadoop.hbase.CellScanner;
2828
import org.apache.hadoop.hbase.DoNotRetryIOException;
2929
import org.apache.hadoop.hbase.io.ByteBuffAllocator;
@@ -51,7 +51,7 @@
5151
* the result.
5252
*/
5353
@InterfaceAudience.Private
54-
abstract class ServerCall<T extends ServerRpcConnection> implements RpcCall, RpcResponse {
54+
public abstract class ServerCall<T extends ServerRpcConnection> implements RpcCall, RpcResponse {
5555

5656
protected final int id; // the client's call id
5757
protected final BlockingService service;
@@ -91,6 +91,12 @@ abstract class ServerCall<T extends ServerRpcConnection> implements RpcCall, Rpc
9191
private long exceptionSize = 0;
9292
private final boolean retryImmediatelySupported;
9393

94+
// This is a dirty hack to address HBASE-22539. The lowest bit is for normal rpc cleanup, and the
95+
// second bit is for WAL reference. We can only call release if both of them are zero. The reason
96+
// why we can not use a general reference counting is that, we may call cleanup multiple times in
97+
// the current implementation. We should fix this in the future.
98+
private final AtomicInteger reference = new AtomicInteger(0b01);
99+
94100
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_NULL_ON_SOME_PATH",
95101
justification = "Can't figure why this complaint is happening... see below")
96102
ServerCall(int id, BlockingService service, MethodDescriptor md, RequestHeader header,
@@ -141,14 +147,43 @@ public void done() {
141147
cleanup();
142148
}
143149

150+
private void release(int mask) {
151+
for (;;) {
152+
int ref = reference.get();
153+
if ((ref & mask) == 0) {
154+
return;
155+
}
156+
int nextRef = ref & (~mask);
157+
if (reference.compareAndSet(ref, nextRef)) {
158+
if (nextRef == 0) {
159+
if (this.reqCleanup != null) {
160+
this.reqCleanup.run();
161+
}
162+
}
163+
return;
164+
}
165+
}
166+
}
167+
144168
@Override
145169
public void cleanup() {
146-
if (this.reqCleanup != null) {
147-
this.reqCleanup.run();
148-
this.reqCleanup = null;
170+
release(0b01);
171+
}
172+
173+
public void retainByWAL() {
174+
for (;;) {
175+
int ref = reference.get();
176+
int nextRef = ref | 0b10;
177+
if (reference.compareAndSet(ref, nextRef)) {
178+
return;
179+
}
149180
}
150181
}
151182

183+
public void releaseByWAL() {
184+
release(0b10);
185+
}
186+
152187
@Override
153188
public String toString() {
154189
return toShortString() + " param: " +

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
import org.apache.hadoop.hbase.client.RegionInfo;
6060
import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
6161
import org.apache.hadoop.hbase.io.util.MemorySizeUtil;
62+
import org.apache.hadoop.hbase.ipc.RpcServer;
63+
import org.apache.hadoop.hbase.ipc.ServerCall;
6264
import org.apache.hadoop.hbase.log.HBaseMarkers;
6365
import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl;
6466
import org.apache.hadoop.hbase.trace.TraceUtil;
@@ -971,7 +973,7 @@ boolean isUnflushedEntries() {
971973
* Exposed for testing only. Use to tricks like halt the ring buffer appending.
972974
*/
973975
@VisibleForTesting
974-
void atHeadOfRingBufferEventHandlerAppend() {
976+
protected void atHeadOfRingBufferEventHandlerAppend() {
975977
// Noop
976978
}
977979

@@ -1061,8 +1063,10 @@ protected final long stampSequenceIdAndPublishToRingBuffer(RegionInfo hri, WALKe
10611063
txidHolder.setValue(ringBuffer.next());
10621064
});
10631065
long txid = txidHolder.longValue();
1066+
ServerCall<?> rpcCall = RpcServer.getCurrentCall().filter(c -> c instanceof ServerCall)
1067+
.filter(c -> c.getCellScanner() != null).map(c -> (ServerCall) c).orElse(null);
10641068
try (TraceScope scope = TraceUtil.createTrace(implClassName + ".append")) {
1065-
FSWALEntry entry = new FSWALEntry(txid, key, edits, hri, inMemstore);
1069+
FSWALEntry entry = new FSWALEntry(txid, key, edits, hri, inMemstore, rpcCall);
10661070
entry.stampRegionSequenceId(we);
10671071
ringBuffer.get(txid).load(entry);
10681072
} finally {

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ private void syncFailed(long epochWhenSync, Throwable error) {
323323
private void syncCompleted(AsyncWriter writer, long processedTxid, long startTimeNs) {
324324
highestSyncedTxid.set(processedTxid);
325325
for (Iterator<FSWALEntry> iter = unackedAppends.iterator(); iter.hasNext();) {
326-
if (iter.next().getTxid() <= processedTxid) {
326+
FSWALEntry entry = iter.next();
327+
if (entry.getTxid() <= processedTxid) {
328+
entry.release();
327329
iter.remove();
328330
} else {
329331
break;
@@ -487,6 +489,7 @@ private void drainNonMarkerEditsAndFailSyncs() {
487489
while (iter.hasNext()) {
488490
FSWALEntry entry = iter.next();
489491
if (!entry.getEdit().isMetaEdit()) {
492+
entry.release();
490493
hasNonMarkerEdits = true;
491494
break;
492495
}
@@ -497,7 +500,10 @@ private void drainNonMarkerEditsAndFailSyncs() {
497500
if (!iter.hasNext()) {
498501
break;
499502
}
500-
iter.next();
503+
iter.next().release();
504+
}
505+
for (FSWALEntry entry : unackedAppends) {
506+
entry.release();
501507
}
502508
unackedAppends.clear();
503509
// fail the sync futures which are under the txid of the first remaining edit, if none, fail

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import com.lmax.disruptor.TimeoutException;
3030
import com.lmax.disruptor.dsl.Disruptor;
3131
import com.lmax.disruptor.dsl.ProducerType;
32-
3332
import java.io.IOException;
3433
import java.io.OutputStream;
3534
import java.util.Arrays;
@@ -39,7 +38,6 @@
3938
import java.util.concurrent.LinkedBlockingQueue;
4039
import java.util.concurrent.TimeUnit;
4140
import java.util.concurrent.atomic.AtomicInteger;
42-
4341
import org.apache.hadoop.conf.Configuration;
4442
import org.apache.hadoop.fs.FSDataOutputStream;
4543
import org.apache.hadoop.fs.FileSystem;
@@ -64,6 +62,7 @@
6462
import org.apache.yetus.audience.InterfaceAudience;
6563
import org.slf4j.Logger;
6664
import org.slf4j.LoggerFactory;
65+
6766
import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
6867

6968
/**
@@ -985,7 +984,6 @@ public void onEvent(final RingBufferTruck truck, final long sequence, boolean en
985984
//TODO handle htrace API change, see HBASE-18895
986985
//TraceScope scope = Trace.continueSpan(entry.detachSpan());
987986
try {
988-
989987
if (this.exception != null) {
990988
// Return to keep processing events coming off the ringbuffer
991989
return;
@@ -1002,6 +1000,8 @@ public void onEvent(final RingBufferTruck truck, final long sequence, boolean en
10021000
: new DamagedWALException("On sync", this.exception));
10031001
// Return to keep processing events coming off the ringbuffer
10041002
return;
1003+
} finally {
1004+
entry.release();
10051005
}
10061006
} else {
10071007
// What is this if not an append or sync. Fail all up to this!!!

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

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,17 @@
1717
*/
1818
package org.apache.hadoop.hbase.regionserver.wal;
1919

20-
import static java.util.stream.Collectors.toCollection;
21-
2220
import java.io.IOException;
2321
import java.util.Collections;
2422
import java.util.List;
23+
import java.util.Optional;
2524
import java.util.Set;
2625
import java.util.TreeSet;
27-
2826
import org.apache.hadoop.hbase.Cell;
29-
import org.apache.hadoop.hbase.CellComparator;
3027
import org.apache.hadoop.hbase.CellUtil;
3128
import org.apache.hadoop.hbase.PrivateCellUtil;
3229
import org.apache.hadoop.hbase.client.RegionInfo;
30+
import org.apache.hadoop.hbase.ipc.ServerCall;
3331
import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl;
3432
import org.apache.hadoop.hbase.util.Bytes;
3533
import org.apache.hadoop.hbase.wal.WAL.Entry;
@@ -56,19 +54,24 @@ class FSWALEntry extends Entry {
5654
private final transient boolean inMemstore;
5755
private final transient RegionInfo regionInfo;
5856
private final transient Set<byte[]> familyNames;
57+
private final transient Optional<ServerCall<?>> rpcCall;
5958

60-
FSWALEntry(final long txid, final WALKeyImpl key, final WALEdit edit,
61-
final RegionInfo regionInfo, final boolean inMemstore) {
59+
FSWALEntry(final long txid, final WALKeyImpl key, final WALEdit edit, final RegionInfo regionInfo,
60+
final boolean inMemstore, ServerCall<?> rpcCall) {
6261
super(key, edit);
6362
this.inMemstore = inMemstore;
6463
this.regionInfo = regionInfo;
6564
this.txid = txid;
6665
if (inMemstore) {
6766
// construct familyNames here to reduce the work of log sinker.
68-
Set<byte []> families = edit.getFamilies();
69-
this.familyNames = families != null? families: collectFamilies(edit.getCells());
67+
Set<byte[]> families = edit.getFamilies();
68+
this.familyNames = families != null ? families : collectFamilies(edit.getCells());
7069
} else {
71-
this.familyNames = Collections.<byte[]>emptySet();
70+
this.familyNames = Collections.<byte[]> emptySet();
71+
}
72+
this.rpcCall = Optional.ofNullable(rpcCall);
73+
if (rpcCall != null) {
74+
rpcCall.retainByWAL();
7275
}
7376
}
7477

@@ -77,12 +80,13 @@ static Set<byte[]> collectFamilies(List<Cell> cells) {
7780
if (CollectionUtils.isEmpty(cells)) {
7881
return Collections.emptySet();
7982
} else {
80-
return cells.stream()
81-
.filter(v -> !CellUtil.matchingFamily(v, WALEdit.METAFAMILY))
82-
.collect(toCollection(() -> new TreeSet<>(CellComparator.getInstance()::compareFamilies)))
83-
.stream()
84-
.map(CellUtil::cloneFamily)
85-
.collect(toCollection(() -> new TreeSet<>(Bytes.BYTES_COMPARATOR)));
83+
Set<byte[]> set = new TreeSet<>(Bytes.BYTES_COMPARATOR);
84+
for (Cell cell: cells) {
85+
if (!CellUtil.matchingFamily(cell, WALEdit.METAFAMILY)) {
86+
set.add(CellUtil.cloneFamily(cell));
87+
}
88+
}
89+
return set;
8690
}
8791
}
8892

@@ -129,4 +133,8 @@ long stampRegionSequenceId(MultiVersionConcurrencyControl.WriteEntry we) throws
129133
Set<byte[]> getFamilyNames() {
130134
return familyNames;
131135
}
136+
137+
void release() {
138+
rpcCall.ifPresent(ServerCall::releaseByWAL);
139+
}
132140
}

hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/AbstractTestWALReplay.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,9 +1156,8 @@ private WALEdit createWALEdit(final byte[] rowName, final byte[] family, Environ
11561156
private FSWALEntry createFSWALEntry(HTableDescriptor htd, HRegionInfo hri, long sequence,
11571157
byte[] rowName, byte[] family, EnvironmentEdge ee, MultiVersionConcurrencyControl mvcc,
11581158
int index, NavigableMap<byte[], Integer> scopes) throws IOException {
1159-
FSWALEntry entry =
1160-
new FSWALEntry(sequence, createWALKey(htd.getTableName(), hri, mvcc, scopes), createWALEdit(
1161-
rowName, family, ee, index), hri, true);
1159+
FSWALEntry entry = new FSWALEntry(sequence, createWALKey(htd.getTableName(), hri, mvcc, scopes),
1160+
createWALEdit(rowName, family, ee, index), hri, true, null);
11621161
entry.stampRegionSequenceId(mvcc.begin());
11631162
return entry;
11641163
}

hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/TestAsyncFSWAL.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ protected AbstractFSWAL<?> newSlowWAL(FileSystem fs, Path rootDir, String logDir
113113
failIfWALExists, prefix, suffix, GROUP, CHANNEL_CLASS) {
114114

115115
@Override
116-
void atHeadOfRingBufferEventHandlerAppend() {
116+
protected void atHeadOfRingBufferEventHandlerAppend() {
117117
action.run();
118118
super.atHeadOfRingBufferEventHandlerAppend();
119119
}

hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/wal/TestFSHLog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ protected AbstractFSWAL<?> newSlowWAL(FileSystem fs, Path rootDir, String walDir
8989
prefix, suffix) {
9090

9191
@Override
92-
void atHeadOfRingBufferEventHandlerAppend() {
92+
protected void atHeadOfRingBufferEventHandlerAppend() {
9393
action.run();
9494
super.atHeadOfRingBufferEventHandlerAppend();
9595
}

0 commit comments

Comments
 (0)