| // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
| // This source code is licensed under both the GPLv2 (found in the |
| // COPYING file in the root directory) and Apache 2.0 License |
| // (found in the LICENSE.Apache file in the root directory). |
| |
| package org.rocksdb; |
| |
| import org.junit.ClassRule; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TemporaryFolder; |
| |
| import java.util.List; |
| |
| import static org.assertj.core.api.Assertions.assertThat; |
| |
| public class BackupEngineTest { |
| |
| @ClassRule |
| public static final RocksMemoryResource rocksMemoryResource = |
| new RocksMemoryResource(); |
| |
| @Rule |
| public TemporaryFolder dbFolder = new TemporaryFolder(); |
| |
| @Rule |
| public TemporaryFolder backupFolder = new TemporaryFolder(); |
| |
| @Test |
| public void backupDb() throws RocksDBException { |
| // Open empty database. |
| try(final Options opt = new Options().setCreateIfMissing(true); |
| final RocksDB db = RocksDB.open(opt, |
| dbFolder.getRoot().getAbsolutePath())) { |
| |
| // Fill database with some test values |
| prepareDatabase(db); |
| |
| // Create two backups |
| try(final BackupableDBOptions bopt = new BackupableDBOptions( |
| backupFolder.getRoot().getAbsolutePath()); |
| final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) { |
| be.createNewBackup(db, false); |
| be.createNewBackup(db, true); |
| verifyNumberOfValidBackups(be, 2); |
| } |
| } |
| } |
| |
| @Test |
| public void deleteBackup() throws RocksDBException { |
| // Open empty database. |
| try(final Options opt = new Options().setCreateIfMissing(true); |
| final RocksDB db = RocksDB.open(opt, |
| dbFolder.getRoot().getAbsolutePath())) { |
| // Fill database with some test values |
| prepareDatabase(db); |
| // Create two backups |
| try(final BackupableDBOptions bopt = new BackupableDBOptions( |
| backupFolder.getRoot().getAbsolutePath()); |
| final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) { |
| be.createNewBackup(db, false); |
| be.createNewBackup(db, true); |
| final List<BackupInfo> backupInfo = |
| verifyNumberOfValidBackups(be, 2); |
| // Delete the first backup |
| be.deleteBackup(backupInfo.get(0).backupId()); |
| final List<BackupInfo> newBackupInfo = |
| verifyNumberOfValidBackups(be, 1); |
| |
| // The second backup must remain. |
| assertThat(newBackupInfo.get(0).backupId()). |
| isEqualTo(backupInfo.get(1).backupId()); |
| } |
| } |
| } |
| |
| @Test |
| public void purgeOldBackups() throws RocksDBException { |
| // Open empty database. |
| try(final Options opt = new Options().setCreateIfMissing(true); |
| final RocksDB db = RocksDB.open(opt, |
| dbFolder.getRoot().getAbsolutePath())) { |
| // Fill database with some test values |
| prepareDatabase(db); |
| // Create four backups |
| try(final BackupableDBOptions bopt = new BackupableDBOptions( |
| backupFolder.getRoot().getAbsolutePath()); |
| final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) { |
| be.createNewBackup(db, false); |
| be.createNewBackup(db, true); |
| be.createNewBackup(db, true); |
| be.createNewBackup(db, true); |
| final List<BackupInfo> backupInfo = |
| verifyNumberOfValidBackups(be, 4); |
| // Delete everything except the latest backup |
| be.purgeOldBackups(1); |
| final List<BackupInfo> newBackupInfo = |
| verifyNumberOfValidBackups(be, 1); |
| // The latest backup must remain. |
| assertThat(newBackupInfo.get(0).backupId()). |
| isEqualTo(backupInfo.get(3).backupId()); |
| } |
| } |
| } |
| |
| @Test |
| public void restoreLatestBackup() throws RocksDBException { |
| try(final Options opt = new Options().setCreateIfMissing(true)) { |
| // Open empty database. |
| RocksDB db = null; |
| try { |
| db = RocksDB.open(opt, |
| dbFolder.getRoot().getAbsolutePath()); |
| // Fill database with some test values |
| prepareDatabase(db); |
| |
| try (final BackupableDBOptions bopt = new BackupableDBOptions( |
| backupFolder.getRoot().getAbsolutePath()); |
| final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) { |
| be.createNewBackup(db, true); |
| verifyNumberOfValidBackups(be, 1); |
| db.put("key1".getBytes(), "valueV2".getBytes()); |
| db.put("key2".getBytes(), "valueV2".getBytes()); |
| be.createNewBackup(db, true); |
| verifyNumberOfValidBackups(be, 2); |
| db.put("key1".getBytes(), "valueV3".getBytes()); |
| db.put("key2".getBytes(), "valueV3".getBytes()); |
| assertThat(new String(db.get("key1".getBytes()))).endsWith("V3"); |
| assertThat(new String(db.get("key2".getBytes()))).endsWith("V3"); |
| |
| db.close(); |
| db = null; |
| |
| verifyNumberOfValidBackups(be, 2); |
| // restore db from latest backup |
| try(final RestoreOptions ropts = new RestoreOptions(false)) { |
| be.restoreDbFromLatestBackup(dbFolder.getRoot().getAbsolutePath(), |
| dbFolder.getRoot().getAbsolutePath(), ropts); |
| } |
| |
| // Open database again. |
| db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath()); |
| |
| // Values must have suffix V2 because of restoring latest backup. |
| assertThat(new String(db.get("key1".getBytes()))).endsWith("V2"); |
| assertThat(new String(db.get("key2".getBytes()))).endsWith("V2"); |
| } |
| } finally { |
| if(db != null) { |
| db.close(); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void restoreFromBackup() |
| throws RocksDBException { |
| try(final Options opt = new Options().setCreateIfMissing(true)) { |
| RocksDB db = null; |
| try { |
| // Open empty database. |
| db = RocksDB.open(opt, |
| dbFolder.getRoot().getAbsolutePath()); |
| // Fill database with some test values |
| prepareDatabase(db); |
| try (final BackupableDBOptions bopt = new BackupableDBOptions( |
| backupFolder.getRoot().getAbsolutePath()); |
| final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) { |
| be.createNewBackup(db, true); |
| verifyNumberOfValidBackups(be, 1); |
| db.put("key1".getBytes(), "valueV2".getBytes()); |
| db.put("key2".getBytes(), "valueV2".getBytes()); |
| be.createNewBackup(db, true); |
| verifyNumberOfValidBackups(be, 2); |
| db.put("key1".getBytes(), "valueV3".getBytes()); |
| db.put("key2".getBytes(), "valueV3".getBytes()); |
| assertThat(new String(db.get("key1".getBytes()))).endsWith("V3"); |
| assertThat(new String(db.get("key2".getBytes()))).endsWith("V3"); |
| |
| //close the database |
| db.close(); |
| db = null; |
| |
| //restore the backup |
| final List<BackupInfo> backupInfo = verifyNumberOfValidBackups(be, 2); |
| // restore db from first backup |
| be.restoreDbFromBackup(backupInfo.get(0).backupId(), |
| dbFolder.getRoot().getAbsolutePath(), |
| dbFolder.getRoot().getAbsolutePath(), |
| new RestoreOptions(false)); |
| // Open database again. |
| db = RocksDB.open(opt, |
| dbFolder.getRoot().getAbsolutePath()); |
| // Values must have suffix V2 because of restoring latest backup. |
| assertThat(new String(db.get("key1".getBytes()))).endsWith("V1"); |
| assertThat(new String(db.get("key2".getBytes()))).endsWith("V1"); |
| } |
| } finally { |
| if(db != null) { |
| db.close(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Verify backups. |
| * |
| * @param be {@link BackupEngine} instance. |
| * @param expectedNumberOfBackups numerical value |
| * @throws RocksDBException thrown if an error occurs within the native |
| * part of the library. |
| */ |
| private List<BackupInfo> verifyNumberOfValidBackups(final BackupEngine be, |
| final int expectedNumberOfBackups) throws RocksDBException { |
| // Verify that backups exist |
| assertThat(be.getCorruptedBackups().length). |
| isEqualTo(0); |
| be.garbageCollect(); |
| final List<BackupInfo> backupInfo = be.getBackupInfo(); |
| assertThat(backupInfo.size()). |
| isEqualTo(expectedNumberOfBackups); |
| return backupInfo; |
| } |
| |
| /** |
| * Fill database with some test values. |
| * |
| * @param db {@link RocksDB} instance. |
| * @throws RocksDBException thrown if an error occurs within the native |
| * part of the library. |
| */ |
| private void prepareDatabase(final RocksDB db) |
| throws RocksDBException { |
| db.put("key1".getBytes(), "valueV1".getBytes()); |
| db.put("key2".getBytes(), "valueV1".getBytes()); |
| } |
| } |