Skip to content

Commit 46f8632

Browse files
author
Hernan Gelaf-Romer
committed
Force CF compatibility during incremental backup
1 parent ef19929 commit 46f8632

File tree

3 files changed

+115
-4
lines changed

3 files changed

+115
-4
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.backup.impl;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
22+
import org.apache.yetus.audience.InterfaceAudience;
23+
24+
@InterfaceAudience.Private
25+
public class ColumnFamilyMismatchException extends BackupException {
26+
private ColumnFamilyMismatchException(String msg) {
27+
super(msg);
28+
}
29+
30+
public static ColumnFamilyMismatchException create(ColumnFamilyDescriptor[] currentCfs,
31+
ColumnFamilyDescriptor[] backupCfs) {
32+
String currentCfsParsed = StringUtils.join(currentCfs, ',');
33+
String backupCfsParsed = StringUtils.join(backupCfs, ',');
34+
35+
String msg = "Mismatch in column family descriptors. Current families: %s Backup families: %s"
36+
.formatted(currentCfsParsed, backupCfsParsed);
37+
return new ColumnFamilyMismatchException(msg);
38+
}
39+
}

hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/impl/IncrementalTableBackupClient.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,20 @@
3232
import org.apache.hadoop.fs.Path;
3333
import org.apache.hadoop.hbase.TableName;
3434
import org.apache.hadoop.hbase.backup.BackupCopyJob;
35+
import org.apache.hadoop.hbase.backup.BackupInfo;
3536
import org.apache.hadoop.hbase.backup.BackupInfo.BackupPhase;
3637
import org.apache.hadoop.hbase.backup.BackupRequest;
3738
import org.apache.hadoop.hbase.backup.BackupRestoreFactory;
3839
import org.apache.hadoop.hbase.backup.BackupType;
40+
import org.apache.hadoop.hbase.backup.HBackupFileSystem;
3941
import org.apache.hadoop.hbase.backup.mapreduce.MapReduceBackupCopyJob;
4042
import org.apache.hadoop.hbase.backup.util.BackupUtils;
4143
import org.apache.hadoop.hbase.client.Admin;
44+
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
4245
import org.apache.hadoop.hbase.client.Connection;
4346
import org.apache.hadoop.hbase.mapreduce.WALPlayer;
47+
import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
48+
import org.apache.hadoop.hbase.snapshot.SnapshotManifest;
4449
import org.apache.hadoop.hbase.util.Bytes;
4550
import org.apache.hadoop.hbase.util.CommonFSUtils;
4651
import org.apache.hadoop.hbase.util.HFileArchiveUtil;
@@ -51,6 +56,8 @@
5156
import org.slf4j.Logger;
5257
import org.slf4j.LoggerFactory;
5358

59+
import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
60+
5461
/**
5562
* Incremental backup implementation. See the {@link #execute() execute} method.
5663
*/
@@ -262,6 +269,20 @@ private void updateFileLists(List<String> activeFiles, List<String> archiveFiles
262269
@Override
263270
public void execute() throws IOException {
264271
try {
272+
String fullBackupId =
273+
getAncestors(backupInfo).stream().filter(bi -> bi.getType() == BackupType.FULL).findFirst()
274+
.orElseThrow(() -> new RuntimeException(
275+
"Could not find full backup for incremental backup: " + backupInfo.getBackupId()))
276+
.getBackupId();
277+
278+
try (BackupAdminImpl backupAdmin = new BackupAdminImpl(conn)) {
279+
BackupInfo fullBackupInfo = backupAdmin.getBackupInfo(fullBackupId);
280+
281+
for (TableName tn : backupInfo.getTables()) {
282+
verifyHtd(tn, fullBackupInfo);
283+
}
284+
}
285+
265286
// case PREPARE_INCREMENTAL:
266287
beginBackup(backupManager, backupInfo);
267288
backupInfo.setPhase(BackupPhase.PREPARE_INCREMENTAL);
@@ -434,4 +455,38 @@ protected Path getBulkOutputDir() {
434455
path = new Path(path, backupId);
435456
return path;
436457
}
458+
459+
private void verifyHtd(TableName tn, BackupInfo fullBackupInfo) throws IOException {
460+
try (Admin admin = conn.getAdmin()) {
461+
ColumnFamilyDescriptor[] currentCfs = admin.getDescriptor(tn).getColumnFamilies();
462+
String snapshotName = fullBackupInfo.getSnapshotName(tn);
463+
Path root = HBackupFileSystem.getTableBackupPath(tn,
464+
new Path(fullBackupInfo.getBackupRootDir()), fullBackupInfo.getBackupId());
465+
Path manifestDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, root);
466+
467+
FileSystem fs = FileSystem.get(conf);
468+
SnapshotProtos.SnapshotDescription snapshotDescription =
469+
SnapshotDescriptionUtils.readSnapshotInfo(fs, manifestDir);
470+
SnapshotManifest manifest = SnapshotManifest.open(conf, fs, manifestDir, snapshotDescription);
471+
472+
ColumnFamilyDescriptor[] backupCfs = manifest.getTableDescriptor().getColumnFamilies();
473+
verifyCfs(currentCfs, backupCfs);
474+
}
475+
}
476+
477+
private static void verifyCfs(ColumnFamilyDescriptor[] currentCfs,
478+
ColumnFamilyDescriptor[] backupCfs) throws IOException {
479+
if (currentCfs.length != backupCfs.length) {
480+
throw ColumnFamilyMismatchException.create(currentCfs, backupCfs);
481+
}
482+
483+
for (int i = 0; i < backupCfs.length; i++) {
484+
String currentCf = currentCfs[i].getNameAsString();
485+
String backupCf = backupCfs[i].getNameAsString();
486+
487+
if (!currentCf.equals(backupCf)) {
488+
throw ColumnFamilyMismatchException.create(currentCfs, backupCfs);
489+
}
490+
}
491+
}
437492
}

hbase-backup/src/test/java/org/apache/hadoop/hbase/backup/TestIncrementalBackup.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
package org.apache.hadoop.hbase.backup;
1919

2020
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertThrows;
2122
import static org.junit.Assert.assertTrue;
2223

24+
import java.io.IOException;
2325
import java.util.ArrayList;
2426
import java.util.Collection;
2527
import java.util.HashSet;
@@ -31,6 +33,7 @@
3133
import org.apache.hadoop.hbase.TableName;
3234
import org.apache.hadoop.hbase.backup.impl.BackupAdminImpl;
3335
import org.apache.hadoop.hbase.backup.impl.BackupManifest;
36+
import org.apache.hadoop.hbase.backup.impl.ColumnFamilyMismatchException;
3437
import org.apache.hadoop.hbase.backup.util.BackupUtils;
3538
import org.apache.hadoop.hbase.client.Admin;
3639
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
@@ -53,6 +56,7 @@
5356
import org.slf4j.Logger;
5457
import org.slf4j.LoggerFactory;
5558

59+
import org.apache.hbase.thirdparty.com.google.common.base.Throwables;
5660
import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
5761
import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
5862

@@ -102,9 +106,7 @@ public void TestIncBackupRestore() throws Exception {
102106
insertIntoTable(conn, table1, mobName, 3, NB_ROWS_FAM3).close();
103107
Admin admin = conn.getAdmin();
104108
BackupAdminImpl client = new BackupAdminImpl(conn);
105-
BackupRequest request = createBackupRequest(BackupType.FULL, tables, BACKUP_ROOT_DIR);
106-
String backupIdFull = client.backupTables(request);
107-
assertTrue(checkSucceeded(backupIdFull));
109+
String backupIdFull = takeFullBackup(tables, client);
108110

109111
// #2 - insert some data to table
110112
Table t1 = insertIntoTable(conn, table1, famName, 1, ADD_ROWS);
@@ -150,7 +152,7 @@ public void TestIncBackupRestore() throws Exception {
150152

151153
// #3 - incremental backup for multiple tables
152154
tables = Lists.newArrayList(table1, table2);
153-
request = createBackupRequest(BackupType.INCREMENTAL, tables, BACKUP_ROOT_DIR);
155+
BackupRequest request = createBackupRequest(BackupType.INCREMENTAL, tables, BACKUP_ROOT_DIR);
154156
String backupIdIncMultiple = client.backupTables(request);
155157
assertTrue(checkSucceeded(backupIdIncMultiple));
156158
BackupManifest manifest =
@@ -165,6 +167,13 @@ public void TestIncBackupRestore() throws Exception {
165167
.build();
166168
TEST_UTIL.getAdmin().modifyTable(newTable1Desc);
167169

170+
// check that an incremental backup fails because the CFs don't match
171+
final List<TableName> tablesCopy = tables;
172+
IOException ex = assertThrows(IOException.class, () -> client
173+
.backupTables(createBackupRequest(BackupType.INCREMENTAL, tablesCopy, BACKUP_ROOT_DIR)));
174+
assertEquals(ColumnFamilyMismatchException.class, Throwables.getRootCause(ex).getClass());
175+
takeFullBackup(tables, client);
176+
168177
int NB_ROWS_FAM2 = 7;
169178
Table t3 = insertIntoTable(conn, table1, fam2Name, 2, NB_ROWS_FAM2);
170179
t3.close();
@@ -227,4 +236,12 @@ public void TestIncBackupRestore() throws Exception {
227236
admin.close();
228237
}
229238
}
239+
240+
private String takeFullBackup(List<TableName> tables, BackupAdminImpl backupAdmin)
241+
throws IOException {
242+
BackupRequest req = createBackupRequest(BackupType.FULL, tables, BACKUP_ROOT_DIR);
243+
String backupId = backupAdmin.backupTables(req);
244+
checkSucceeded(backupId);
245+
return backupId;
246+
}
230247
}

0 commit comments

Comments
 (0)