From de06e20e0a2f1a4cc79be48c8c4204d2df770992 Mon Sep 17 00:00:00 2001 From: Peter Somogyi Date: Sat, 5 Jun 2021 08:57:26 +0200 Subject: [PATCH] HBASE-25970 MOB data loss - incorrect concatenation of MOB_FILE_REFS (#3355) Signed-off-by: Wellington Chevreuil Signed-off-by: Pankaj Kumar --- .../org/apache/hadoop/hbase/mob/MobUtils.java | 12 +-- .../apache/hadoop/hbase/mob/TestMobUtils.java | 94 +++++++++++++++++++ 2 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobUtils.java diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/mob/MobUtils.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/mob/MobUtils.java index 2ae29385eb4..2bce365d260 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/mob/MobUtils.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/mob/MobUtils.java @@ -720,20 +720,20 @@ public final class MobUtils { StringBuilder sb = new StringBuilder(100 + mobRefSet.size() * 105); boolean doubleSlash = false; for (TableName tableName : mobRefSet.keySet()) { + if (doubleSlash) { + sb.append("//"); + } else { + doubleSlash = true; + } sb.append(tableName).append("/"); boolean comma = false; for (String refs : mobRefSet.get(tableName)) { - sb.append(refs); if (comma) { sb.append(","); } else { comma = true; } - } - if (doubleSlash) { - sb.append("//"); - } else { - doubleSlash = true; + sb.append(refs); } } return Bytes.toBytes(sb.toString()); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobUtils.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobUtils.java new file mode 100644 index 00000000000..9bb003cb95a --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/mob/TestMobUtils.java @@ -0,0 +1,94 @@ +/** + * 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.mob; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSet; +import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSetMultimap; + +@Category(SmallTests.class) +public class TestMobUtils { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestMobUtils.class); + public static final TableName TEST_TABLE_1 = TableName.valueOf("testTable1"); + public static final TableName TEST_TABLE_2 = TableName.valueOf("testTable2"); + public static final TableName TEST_TABLE_3 = TableName.valueOf("testTable3"); + + @Test + public void serializeSingleMobFileRefs() { + ImmutableSetMultimap mobRefSet = + ImmutableSetMultimap.builder() + .putAll(TEST_TABLE_1, "file1a") + .build(); + byte[] result = MobUtils.serializeMobFileRefs(mobRefSet); + assertEquals("testTable1/file1a", Bytes.toString(result)); + } + + @Test + public void serializeMultipleMobFileRefs() { + ImmutableSetMultimap mobRefSet = + ImmutableSetMultimap.builder() + .putAll(TEST_TABLE_1, "file1a", "file1b") + .putAll(TEST_TABLE_2, "file2a") + .putAll(TEST_TABLE_3, "file3a", "file3b") + .build(); + byte[] result = MobUtils.serializeMobFileRefs(mobRefSet); + assertEquals("testTable1/file1a,file1b//testTable2/file2a//testTable3/file3a,file3b", + Bytes.toString(result)); + } + + @Test + public void deserializeSingleMobFileRefs() { + ImmutableSetMultimap mobRefSet = + MobUtils.deserializeMobFileRefs(Bytes.toBytes("testTable1/file1a")).build(); + assertEquals(1, mobRefSet.size()); + ImmutableSet testTable1Refs = mobRefSet.get(TEST_TABLE_1); + assertEquals(1, testTable1Refs.size()); + assertTrue(testTable1Refs.contains("file1a")); + } + + @Test + public void deserializeMultipleMobFileRefs() { + ImmutableSetMultimap mobRefSet = + MobUtils.deserializeMobFileRefs(Bytes.toBytes( + "testTable1/file1a,file1b//testTable2/file2a//testTable3/file3a,file3b")).build(); + assertEquals(5, mobRefSet.size()); + ImmutableSet testTable1Refs = mobRefSet.get(TEST_TABLE_1); + ImmutableSet testTable2Refs = mobRefSet.get(TEST_TABLE_2); + ImmutableSet testTable3Refs = mobRefSet.get(TEST_TABLE_3); + assertEquals(2, testTable1Refs.size()); + assertEquals(1, testTable2Refs.size()); + assertEquals(2, testTable3Refs.size()); + assertTrue(testTable1Refs.contains("file1a")); + assertTrue(testTable1Refs.contains("file1b")); + assertTrue(testTable2Refs.contains("file2a")); + assertTrue(testTable3Refs.contains("file3a")); + assertTrue(testTable3Refs.contains("file3b")); + } +}