Skip to content

Commit 960dd66

Browse files
authored
[HUDI-9737] Add checksum checks while recovering from backup file for HoodieTableConfig (#13740)
1 parent 7da43d6 commit 960dd66

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

hudi-common/src/main/java/org/apache/hudi/common/util/ConfigUtils.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.slf4j.Logger;
3737
import org.slf4j.LoggerFactory;
3838

39+
import java.io.ByteArrayInputStream;
3940
import java.io.IOException;
4041
import java.io.InputStream;
4142
import java.io.OutputStream;
@@ -685,11 +686,37 @@ public static TypedProperties fetchConfigs(
685686

686687
public static void recoverIfNeeded(HoodieStorage storage, StoragePath cfgPath,
687688
StoragePath backupCfgPath) throws IOException {
689+
boolean needCopy = false;
688690
if (!storage.exists(cfgPath)) {
689-
// copy over from backup
690-
try (InputStream in = storage.open(backupCfgPath);
691-
OutputStream out = storage.create(cfgPath, false)) {
692-
FileIOUtils.copy(in, out);
691+
needCopy = true;
692+
} else {
693+
TypedProperties props = new TypedProperties();
694+
try (InputStream in = storage.open(cfgPath)) {
695+
props.load(in);
696+
if (!props.containsKey(TABLE_CHECKSUM.key()) || !HoodieTableConfig.validateChecksum(props)) {
697+
// the cfg file is invalid
698+
storage.deleteFile(cfgPath);
699+
needCopy = true;
700+
}
701+
}
702+
}
703+
if (needCopy && storage.exists(backupCfgPath)) {
704+
byte[] bytes = FileIOUtils.readAsByteArray(storage.open(backupCfgPath));
705+
// check whether existing backup file is valid or not
706+
try (InputStream backupStream = new ByteArrayInputStream(bytes)) {
707+
TypedProperties backupProps = new TypedProperties();
708+
backupProps.load(backupStream);
709+
if (!backupProps.containsKey(TABLE_CHECKSUM.key()) || !HoodieTableConfig.validateChecksum(backupProps)) {
710+
// need to delete the backup as anyway reads will also fail
711+
// subsequent writes will recover and update
712+
storage.deleteFile(backupCfgPath);
713+
LOG.warn("Invalid properties file {}: {}", backupCfgPath, backupProps);
714+
throw new IOException("Corrupted backup file");
715+
}
716+
// copy over from backup
717+
try (OutputStream out = storage.create(cfgPath, false)) {
718+
out.write(bytes);
719+
}
693720
}
694721
}
695722
// regardless, we don't need the backup anymore.

hudi-hadoop-common/src/test/java/org/apache/hudi/common/table/TestHoodieTableConfig.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,32 @@ void testUpdateRecovery(boolean shouldPropsFileExist) throws IOException {
240240
assertEquals(7, config.getProps().size());
241241
}
242242

243+
@Test
244+
void testUpdateRecoveryWithInvalidProps() throws IOException {
245+
// 1. Actual props is invalid, populate it from backup if they are valid
246+
HoodieTableConfig config = new HoodieTableConfig(storage, metaPath);
247+
try (OutputStream out = storage.create(backupCfgPath)) {
248+
config.getProps().store(out, "");
249+
}
250+
storage.deleteFile(cfgPath);
251+
// create the empty file
252+
storage.create(cfgPath);
253+
recoverIfNeeded(storage, cfgPath, backupCfgPath);
254+
assertTrue(storage.exists(cfgPath));
255+
assertFalse(storage.exists(backupCfgPath));
256+
config = new HoodieTableConfig(storage, metaPath);
257+
assertEquals(7, config.getProps().size());
258+
259+
// 2. Backup properties file is also invalid
260+
storage.deleteFile(cfgPath);
261+
// create the empty files
262+
storage.create(cfgPath);
263+
storage.create(backupCfgPath);
264+
assertThrows(IOException.class, () -> recoverIfNeeded(storage, cfgPath, backupCfgPath));
265+
assertFalse(storage.exists(backupCfgPath));
266+
assertFalse(storage.exists(cfgPath));
267+
}
268+
243269
@Test
244270
void testReadRetry() throws IOException {
245271
// When both the hoodie.properties and hoodie.properties.backup do not exist then the read fails

0 commit comments

Comments
 (0)