Skip to content

Commit 4a9cf99

Browse files
authored
HBASE-27644 Should not return false when WALKey has no following KVs while reading WAL file (#5032)
Signed-off-by: Viraj Jasani <vjasani@apache.org>
1 parent 4b7815d commit 4a9cf99

File tree

2 files changed

+69
-26
lines changed

2 files changed

+69
-26
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,9 @@ protected boolean readNext(Entry entry) throws IOException {
408408
WALKey walKey = builder.build();
409409
entry.getKey().readFieldsFromPb(walKey, this.byteStringUncompressor);
410410
if (!walKey.hasFollowingKvCount() || 0 == walKey.getFollowingKvCount()) {
411-
LOG.trace("WALKey has no KVs that follow it; trying the next one. current offset={}",
411+
LOG.debug("WALKey has no KVs that follow it; trying the next one. current offset={}",
412412
this.inputStream.getPos());
413-
seekOnFs(originalPosition);
414-
return false;
413+
return true;
415414
}
416415
int expectedCells = walKey.getFollowingKvCount();
417416
long posBefore = this.inputStream.getPos();

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

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ public Integer run() throws Exception {
276276
* log entry. Does its writing as an alternate user in another filesystem instance to simulate
277277
* better it being a regionserver.
278278
*/
279-
class ZombieLastLogWriterRegionServer extends Thread {
279+
private class ZombieLastLogWriterRegionServer extends Thread {
280280
final AtomicLong editsCount;
281281
final AtomicBoolean stop;
282282
final int numOfWriters;
@@ -402,10 +402,6 @@ public void testOldRecoveredEditsFileSidelined() throws IOException {
402402

403403
private Path createRecoveredEditsPathForRegion() throws IOException {
404404
byte[] encoded = RegionInfoBuilder.FIRST_META_REGIONINFO.getEncodedNameAsBytes();
405-
long now = EnvironmentEdgeManager.currentTime();
406-
Entry entry = new Entry(
407-
new WALKeyImpl(encoded, TableName.META_TABLE_NAME, 1, now, HConstants.DEFAULT_CLUSTER_ID),
408-
new WALEdit());
409405
Path p = WALSplitUtil.getRegionSplitEditsPath(TableName.META_TABLE_NAME, encoded, 1,
410406
FILENAME_BEING_SPLIT, TMPDIRNAME, conf);
411407
return p;
@@ -491,10 +487,10 @@ public void testSplitLeavesCompactionEventsEdits() throws IOException {
491487
assertEquals(11, countWAL(splitLog[0]));
492488
}
493489

494-
/*
490+
/**
495491
* Tests that WalSplitter ignores replication marker edits.
496492
*/
497-
@Test(timeout = 30000)
493+
@Test
498494
public void testSplitRemovesReplicationMarkerEdits() throws IOException {
499495
RegionInfo regionInfo = ReplicationMarkerChore.REGION_INFO;
500496
Path path = new Path(WALDIR, WAL_FILE_PREFIX + "1");
@@ -1206,7 +1202,44 @@ public void testRecoveredEditsStoragePolicy() throws IOException {
12061202
} finally {
12071203
conf.unset(HConstants.WAL_STORAGE_POLICY);
12081204
}
1205+
}
1206+
1207+
/**
1208+
* See HBASE-27644, typically we should not have empty WALEdit but we should be able to process
1209+
* it, instead of losing data after it.
1210+
*/
1211+
@Test
1212+
public void testEmptyWALEdit() throws IOException {
1213+
final String region = "region__5";
1214+
REGIONS.clear();
1215+
REGIONS.add(region);
1216+
makeRegionDirs(REGIONS);
1217+
fs.mkdirs(WALDIR);
1218+
Path path = new Path(WALDIR, WAL_FILE_PREFIX + 5);
1219+
generateEmptyEditWAL(path, Bytes.toBytes(region));
1220+
useDifferentDFSClient();
1221+
1222+
Path regiondir = new Path(TABLEDIR, region);
1223+
fs.mkdirs(regiondir);
1224+
List<Path> splitPaths = WALSplitter.split(HBASELOGDIR, WALDIR, OLDLOGDIR, fs, conf, wals);
1225+
// Make sure that WALSplitter generate the split file
1226+
assertEquals(1, splitPaths.size());
1227+
1228+
Path originalLog = (fs.listStatus(OLDLOGDIR))[0].getPath();
1229+
assertEquals(11, countWAL(originalLog));
1230+
// we will skip the empty WAL when splitting
1231+
assertEquals(10, countWAL(splitPaths.get(0)));
1232+
}
12091233

1234+
private void generateEmptyEditWAL(Path path, byte[] region) throws IOException {
1235+
fs.mkdirs(WALDIR);
1236+
try (Writer writer = wals.createWALWriter(fs, path)) {
1237+
long seq = 0;
1238+
appendEmptyEntry(writer, TABLE_NAME, region, seq++);
1239+
for (int i = 0; i < 10; i++) {
1240+
appendEntry(writer, TABLE_NAME, region, Bytes.toBytes(i), FAMILY, QUALIFIER, VALUE, seq++);
1241+
}
1242+
}
12101243
}
12111244

12121245
private Writer generateWALs(int leaveOpen) throws IOException {
@@ -1399,7 +1432,7 @@ private static void appendRegionEvent(Writer w, String region) throws IOExceptio
13991432
w.sync(false);
14001433
}
14011434

1402-
public static long appendEntry(Writer writer, TableName table, byte[] region, byte[] row,
1435+
private static long appendEntry(Writer writer, TableName table, byte[] region, byte[] row,
14031436
byte[] family, byte[] qualifier, byte[] value, long seq) throws IOException {
14041437
LOG.info(Thread.currentThread().getName() + " append");
14051438
writer.append(createTestEntry(table, region, row, family, qualifier, value, seq));
@@ -1412,13 +1445,27 @@ private static Entry createTestEntry(TableName table, byte[] region, byte[] row,
14121445
byte[] qualifier, byte[] value, long seq) {
14131446
long time = System.nanoTime();
14141447

1415-
seq++;
14161448
final KeyValue cell = new KeyValue(row, family, qualifier, time, KeyValue.Type.Put, value);
14171449
WALEdit edit = new WALEdit();
14181450
edit.add(cell);
14191451
return new Entry(new WALKeyImpl(region, table, seq, time, HConstants.DEFAULT_CLUSTER_ID), edit);
14201452
}
14211453

1454+
private static long appendEmptyEntry(Writer writer, TableName table, byte[] region, long seq)
1455+
throws IOException {
1456+
LOG.info(Thread.currentThread().getName() + " append");
1457+
writer.append(createEmptyEntry(table, region, seq));
1458+
LOG.info(Thread.currentThread().getName() + " sync");
1459+
writer.sync(false);
1460+
return seq;
1461+
}
1462+
1463+
private static Entry createEmptyEntry(TableName table, byte[] region, long seq) {
1464+
long time = System.nanoTime();
1465+
return new Entry(new WALKeyImpl(region, table, seq, time, HConstants.DEFAULT_CLUSTER_ID),
1466+
new WALEdit());
1467+
}
1468+
14221469
private void injectEmptyFile(String suffix, boolean closeFile) throws IOException {
14231470
Writer writer =
14241471
WALFactory.createWALWriter(fs, new Path(WALDIR, WAL_FILE_PREFIX + suffix), conf);
@@ -1428,22 +1475,19 @@ private void injectEmptyFile(String suffix, boolean closeFile) throws IOExceptio
14281475
}
14291476

14301477
private boolean logsAreEqual(Path p1, Path p2) throws IOException {
1431-
Reader in1, in2;
1432-
in1 = wals.createReader(fs, p1);
1433-
in2 = wals.createReader(fs, p2);
1434-
Entry entry1;
1435-
Entry entry2;
1436-
while ((entry1 = in1.next()) != null) {
1437-
entry2 = in2.next();
1438-
if (
1439-
(entry1.getKey().compareTo(entry2.getKey()) != 0)
1440-
|| (!entry1.getEdit().toString().equals(entry2.getEdit().toString()))
1441-
) {
1442-
return false;
1478+
try (Reader in1 = wals.createReader(fs, p1); Reader in2 = wals.createReader(fs, p2)) {
1479+
Entry entry1;
1480+
Entry entry2;
1481+
while ((entry1 = in1.next()) != null) {
1482+
entry2 = in2.next();
1483+
if (
1484+
(entry1.getKey().compareTo(entry2.getKey()) != 0)
1485+
|| (!entry1.getEdit().toString().equals(entry2.getEdit().toString()))
1486+
) {
1487+
return false;
1488+
}
14431489
}
14441490
}
1445-
in1.close();
1446-
in2.close();
14471491
return true;
14481492
}
14491493
}

0 commit comments

Comments
 (0)