1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
8 import org
.junit
.ClassRule
;
10 import org
.junit
.Test
;
11 import org
.junit
.rules
.TemporaryFolder
;
13 import java
.util
.List
;
14 import java
.util
.concurrent
.ThreadLocalRandom
;
16 import static org
.assertj
.core
.api
.Assertions
.assertThat
;
18 public class BackupEngineTest
{
21 public static final RocksMemoryResource rocksMemoryResource
=
22 new RocksMemoryResource();
25 public TemporaryFolder dbFolder
= new TemporaryFolder();
28 public TemporaryFolder backupFolder
= new TemporaryFolder();
31 public void backupDb() throws RocksDBException
{
32 // Open empty database.
33 try(final Options opt
= new Options().setCreateIfMissing(true);
34 final RocksDB db
= RocksDB
.open(opt
,
35 dbFolder
.getRoot().getAbsolutePath())) {
37 // Fill database with some test values
41 try(final BackupableDBOptions bopt
= new BackupableDBOptions(
42 backupFolder
.getRoot().getAbsolutePath());
43 final BackupEngine be
= BackupEngine
.open(opt
.getEnv(), bopt
)) {
44 be
.createNewBackup(db
, false);
45 be
.createNewBackup(db
, true);
46 verifyNumberOfValidBackups(be
, 2);
52 public void deleteBackup() throws RocksDBException
{
53 // Open empty database.
54 try(final Options opt
= new Options().setCreateIfMissing(true);
55 final RocksDB db
= RocksDB
.open(opt
,
56 dbFolder
.getRoot().getAbsolutePath())) {
57 // Fill database with some test values
60 try(final BackupableDBOptions bopt
= new BackupableDBOptions(
61 backupFolder
.getRoot().getAbsolutePath());
62 final BackupEngine be
= BackupEngine
.open(opt
.getEnv(), bopt
)) {
63 be
.createNewBackup(db
, false);
64 be
.createNewBackup(db
, true);
65 final List
<BackupInfo
> backupInfo
=
66 verifyNumberOfValidBackups(be
, 2);
67 // Delete the first backup
68 be
.deleteBackup(backupInfo
.get(0).backupId());
69 final List
<BackupInfo
> newBackupInfo
=
70 verifyNumberOfValidBackups(be
, 1);
72 // The second backup must remain.
73 assertThat(newBackupInfo
.get(0).backupId()).
74 isEqualTo(backupInfo
.get(1).backupId());
80 public void purgeOldBackups() throws RocksDBException
{
81 // Open empty database.
82 try(final Options opt
= new Options().setCreateIfMissing(true);
83 final RocksDB db
= RocksDB
.open(opt
,
84 dbFolder
.getRoot().getAbsolutePath())) {
85 // Fill database with some test values
87 // Create four backups
88 try(final BackupableDBOptions bopt
= new BackupableDBOptions(
89 backupFolder
.getRoot().getAbsolutePath());
90 final BackupEngine be
= BackupEngine
.open(opt
.getEnv(), bopt
)) {
91 be
.createNewBackup(db
, false);
92 be
.createNewBackup(db
, true);
93 be
.createNewBackup(db
, true);
94 be
.createNewBackup(db
, true);
95 final List
<BackupInfo
> backupInfo
=
96 verifyNumberOfValidBackups(be
, 4);
97 // Delete everything except the latest backup
98 be
.purgeOldBackups(1);
99 final List
<BackupInfo
> newBackupInfo
=
100 verifyNumberOfValidBackups(be
, 1);
101 // The latest backup must remain.
102 assertThat(newBackupInfo
.get(0).backupId()).
103 isEqualTo(backupInfo
.get(3).backupId());
109 public void restoreLatestBackup() throws RocksDBException
{
110 try(final Options opt
= new Options().setCreateIfMissing(true)) {
111 // Open empty database.
114 db
= RocksDB
.open(opt
,
115 dbFolder
.getRoot().getAbsolutePath());
116 // Fill database with some test values
119 try (final BackupableDBOptions bopt
= new BackupableDBOptions(
120 backupFolder
.getRoot().getAbsolutePath());
121 final BackupEngine be
= BackupEngine
.open(opt
.getEnv(), bopt
)) {
122 be
.createNewBackup(db
, true);
123 verifyNumberOfValidBackups(be
, 1);
124 db
.put("key1".getBytes(), "valueV2".getBytes());
125 db
.put("key2".getBytes(), "valueV2".getBytes());
126 be
.createNewBackup(db
, true);
127 verifyNumberOfValidBackups(be
, 2);
128 db
.put("key1".getBytes(), "valueV3".getBytes());
129 db
.put("key2".getBytes(), "valueV3".getBytes());
130 assertThat(new String(db
.get("key1".getBytes()))).endsWith("V3");
131 assertThat(new String(db
.get("key2".getBytes()))).endsWith("V3");
136 verifyNumberOfValidBackups(be
, 2);
137 // restore db from latest backup
138 try(final RestoreOptions ropts
= new RestoreOptions(false)) {
139 be
.restoreDbFromLatestBackup(dbFolder
.getRoot().getAbsolutePath(),
140 dbFolder
.getRoot().getAbsolutePath(), ropts
);
143 // Open database again.
144 db
= RocksDB
.open(opt
, dbFolder
.getRoot().getAbsolutePath());
146 // Values must have suffix V2 because of restoring latest backup.
147 assertThat(new String(db
.get("key1".getBytes()))).endsWith("V2");
148 assertThat(new String(db
.get("key2".getBytes()))).endsWith("V2");
159 public void restoreFromBackup()
160 throws RocksDBException
{
161 try(final Options opt
= new Options().setCreateIfMissing(true)) {
164 // Open empty database.
165 db
= RocksDB
.open(opt
,
166 dbFolder
.getRoot().getAbsolutePath());
167 // Fill database with some test values
169 try (final BackupableDBOptions bopt
= new BackupableDBOptions(
170 backupFolder
.getRoot().getAbsolutePath());
171 final BackupEngine be
= BackupEngine
.open(opt
.getEnv(), bopt
)) {
172 be
.createNewBackup(db
, true);
173 verifyNumberOfValidBackups(be
, 1);
174 db
.put("key1".getBytes(), "valueV2".getBytes());
175 db
.put("key2".getBytes(), "valueV2".getBytes());
176 be
.createNewBackup(db
, true);
177 verifyNumberOfValidBackups(be
, 2);
178 db
.put("key1".getBytes(), "valueV3".getBytes());
179 db
.put("key2".getBytes(), "valueV3".getBytes());
180 assertThat(new String(db
.get("key1".getBytes()))).endsWith("V3");
181 assertThat(new String(db
.get("key2".getBytes()))).endsWith("V3");
188 final List
<BackupInfo
> backupInfo
= verifyNumberOfValidBackups(be
, 2);
189 // restore db from first backup
190 be
.restoreDbFromBackup(backupInfo
.get(0).backupId(),
191 dbFolder
.getRoot().getAbsolutePath(),
192 dbFolder
.getRoot().getAbsolutePath(),
193 new RestoreOptions(false));
194 // Open database again.
195 db
= RocksDB
.open(opt
,
196 dbFolder
.getRoot().getAbsolutePath());
197 // Values must have suffix V2 because of restoring latest backup.
198 assertThat(new String(db
.get("key1".getBytes()))).endsWith("V1");
199 assertThat(new String(db
.get("key2".getBytes()))).endsWith("V1");
210 public void backupDbWithMetadata() throws RocksDBException
{
211 // Open empty database.
212 try (final Options opt
= new Options().setCreateIfMissing(true);
213 final RocksDB db
= RocksDB
.open(opt
, dbFolder
.getRoot().getAbsolutePath())) {
214 // Fill database with some test values
217 // Create two backups
218 try (final BackupableDBOptions bopt
=
219 new BackupableDBOptions(backupFolder
.getRoot().getAbsolutePath());
220 final BackupEngine be
= BackupEngine
.open(opt
.getEnv(), bopt
)) {
221 final String metadata
= String
.valueOf(ThreadLocalRandom
.current().nextInt());
222 be
.createNewBackupWithMetadata(db
, metadata
, true);
223 final List
<BackupInfo
> backupInfoList
= verifyNumberOfValidBackups(be
, 1);
224 assertThat(backupInfoList
.get(0).appMetadata()).isEqualTo(metadata
);
232 * @param be {@link BackupEngine} instance.
233 * @param expectedNumberOfBackups numerical value
234 * @throws RocksDBException thrown if an error occurs within the native
235 * part of the library.
237 private List
<BackupInfo
> verifyNumberOfValidBackups(final BackupEngine be
,
238 final int expectedNumberOfBackups
) throws RocksDBException
{
239 // Verify that backups exist
240 assertThat(be
.getCorruptedBackups().length
).
243 final List
<BackupInfo
> backupInfo
= be
.getBackupInfo();
244 assertThat(backupInfo
.size()).
245 isEqualTo(expectedNumberOfBackups
);
250 * Fill database with some test values.
252 * @param db {@link RocksDB} instance.
253 * @throws RocksDBException thrown if an error occurs within the native
254 * part of the library.
256 private void prepareDatabase(final RocksDB db
)
257 throws RocksDBException
{
258 db
.put("key1".getBytes(), "valueV1".getBytes());
259 db
.put("key2".getBytes(), "valueV1".getBytes());