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).
5 package org.rocksdb;
6 
7 import org.junit.*;
8 import org.junit.rules.ExpectedException;
9 import org.junit.rules.TemporaryFolder;
10 
11 import java.nio.ByteBuffer;
12 import java.util.*;
13 
14 import static java.nio.charset.StandardCharsets.UTF_8;
15 import static org.assertj.core.api.Assertions.assertThat;
16 import static org.junit.Assert.fail;
17 
18 public class RocksDBTest {
19 
20   @ClassRule
21   public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE =
22       new RocksNativeLibraryResource();
23 
24   @Rule
25   public TemporaryFolder dbFolder = new TemporaryFolder();
26 
27   public static final Random rand = PlatformRandomHelper.
28       getPlatformSpecificRandomFactory();
29 
30   @Test
open()31   public void open() throws RocksDBException {
32     try (final RocksDB db =
33              RocksDB.open(dbFolder.getRoot().getAbsolutePath())) {
34       assertThat(db).isNotNull();
35     }
36   }
37 
38   @Test
open_opt()39   public void open_opt() throws RocksDBException {
40     try (final Options opt = new Options().setCreateIfMissing(true);
41          final RocksDB db = RocksDB.open(opt,
42              dbFolder.getRoot().getAbsolutePath())) {
43       assertThat(db).isNotNull();
44     }
45   }
46 
47   @Test
openWhenOpen()48   public void openWhenOpen() throws RocksDBException {
49     final String dbPath = dbFolder.getRoot().getAbsolutePath();
50 
51     try (final RocksDB db1 = RocksDB.open(dbPath)) {
52       try (final RocksDB db2 = RocksDB.open(dbPath)) {
53         fail("Should have thrown an exception when opening the same db twice");
54       } catch (final RocksDBException e) {
55         assertThat(e.getStatus().getCode()).isEqualTo(Status.Code.IOError);
56         assertThat(e.getStatus().getSubCode()).isEqualTo(Status.SubCode.None);
57         assertThat(e.getStatus().getState()).contains("lock ");
58       }
59     }
60   }
61 
62   @Test
createColumnFamily()63   public void createColumnFamily() throws RocksDBException {
64       final byte[] col1Name = "col1".getBytes(UTF_8);
65 
66       try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
67            final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions()
68       ) {
69         try (final ColumnFamilyHandle col1 =
70             db.createColumnFamily(new ColumnFamilyDescriptor(col1Name, cfOpts))) {
71           assertThat(col1).isNotNull();
72           assertThat(col1.getName()).isEqualTo(col1Name);
73         }
74       }
75 
76       final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
77       try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath(),
78         Arrays.asList(
79             new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
80             new ColumnFamilyDescriptor(col1Name)),
81             cfHandles)) {
82         try {
83           assertThat(cfHandles.size()).isEqualTo(2);
84           assertThat(cfHandles.get(1)).isNotNull();
85           assertThat(cfHandles.get(1).getName()).isEqualTo(col1Name);
86         } finally {
87           for (final ColumnFamilyHandle cfHandle :
88               cfHandles) {
89             cfHandle.close();
90           }
91         }
92       }
93   }
94 
95 
96   @Test
createColumnFamilies()97   public void createColumnFamilies() throws RocksDBException {
98     final byte[] col1Name = "col1".getBytes(UTF_8);
99     final byte[] col2Name = "col2".getBytes(UTF_8);
100 
101     List<ColumnFamilyHandle> cfHandles;
102     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
103          final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions()
104     ) {
105       cfHandles =
106           db.createColumnFamilies(cfOpts, Arrays.asList(col1Name, col2Name));
107       try {
108         assertThat(cfHandles).isNotNull();
109         assertThat(cfHandles.size()).isEqualTo(2);
110         assertThat(cfHandles.get(0).getName()).isEqualTo(col1Name);
111         assertThat(cfHandles.get(1).getName()).isEqualTo(col2Name);
112       } finally {
113         for (final ColumnFamilyHandle cfHandle : cfHandles) {
114           cfHandle.close();
115         }
116       }
117     }
118 
119     cfHandles = new ArrayList<>();
120     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath(),
121         Arrays.asList(
122             new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
123             new ColumnFamilyDescriptor(col1Name),
124             new ColumnFamilyDescriptor(col2Name)),
125         cfHandles)) {
126       try {
127         assertThat(cfHandles.size()).isEqualTo(3);
128         assertThat(cfHandles.get(1)).isNotNull();
129         assertThat(cfHandles.get(1).getName()).isEqualTo(col1Name);
130         assertThat(cfHandles.get(2)).isNotNull();
131         assertThat(cfHandles.get(2).getName()).isEqualTo(col2Name);
132       } finally {
133         for (final ColumnFamilyHandle cfHandle : cfHandles) {
134           cfHandle.close();
135         }
136       }
137     }
138   }
139 
140   @Test
createColumnFamiliesfromDescriptors()141   public void createColumnFamiliesfromDescriptors() throws RocksDBException {
142     final byte[] col1Name = "col1".getBytes(UTF_8);
143     final byte[] col2Name = "col2".getBytes(UTF_8);
144 
145     List<ColumnFamilyHandle> cfHandles;
146     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
147          final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions()
148     ) {
149       cfHandles =
150           db.createColumnFamilies(Arrays.asList(
151               new ColumnFamilyDescriptor(col1Name, cfOpts),
152               new ColumnFamilyDescriptor(col2Name, cfOpts)));
153       try {
154         assertThat(cfHandles).isNotNull();
155         assertThat(cfHandles.size()).isEqualTo(2);
156         assertThat(cfHandles.get(0).getName()).isEqualTo(col1Name);
157         assertThat(cfHandles.get(1).getName()).isEqualTo(col2Name);
158       } finally {
159         for (final ColumnFamilyHandle cfHandle : cfHandles) {
160           cfHandle.close();
161         }
162       }
163     }
164 
165     cfHandles = new ArrayList<>();
166     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath(),
167         Arrays.asList(
168             new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
169             new ColumnFamilyDescriptor(col1Name),
170             new ColumnFamilyDescriptor(col2Name)),
171         cfHandles)) {
172       try {
173         assertThat(cfHandles.size()).isEqualTo(3);
174         assertThat(cfHandles.get(1)).isNotNull();
175         assertThat(cfHandles.get(1).getName()).isEqualTo(col1Name);
176         assertThat(cfHandles.get(2)).isNotNull();
177         assertThat(cfHandles.get(2).getName()).isEqualTo(col2Name);
178       } finally {
179         for (final ColumnFamilyHandle cfHandle : cfHandles) {
180           cfHandle.close();
181         }
182       }
183     }
184   }
185 
186   @Test
put()187   public void put() throws RocksDBException {
188     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
189          final WriteOptions opt = new WriteOptions(); final ReadOptions optr = new ReadOptions()) {
190       db.put("key1".getBytes(), "value".getBytes());
191       db.put(opt, "key2".getBytes(), "12345678".getBytes());
192       assertThat(db.get("key1".getBytes())).isEqualTo(
193           "value".getBytes());
194       assertThat(db.get("key2".getBytes())).isEqualTo(
195           "12345678".getBytes());
196 
197       ByteBuffer key = ByteBuffer.allocateDirect(12);
198       ByteBuffer value = ByteBuffer.allocateDirect(12);
199       key.position(4);
200       key.put("key3".getBytes());
201       key.position(4).limit(8);
202       value.position(4);
203       value.put("val3".getBytes());
204       value.position(4).limit(8);
205 
206       db.put(opt, key, value);
207 
208       assertThat(key.position()).isEqualTo(8);
209       assertThat(key.limit()).isEqualTo(8);
210 
211       assertThat(value.position()).isEqualTo(8);
212       assertThat(value.limit()).isEqualTo(8);
213 
214       key.position(4);
215 
216       ByteBuffer result = ByteBuffer.allocateDirect(12);
217       assertThat(db.get(optr, key, result)).isEqualTo(4);
218       assertThat(result.position()).isEqualTo(0);
219       assertThat(result.limit()).isEqualTo(4);
220       assertThat(key.position()).isEqualTo(8);
221       assertThat(key.limit()).isEqualTo(8);
222 
223       byte[] tmp = new byte[4];
224       result.get(tmp);
225       assertThat(tmp).isEqualTo("val3".getBytes());
226 
227       key.position(4);
228 
229       result.clear().position(9);
230       assertThat(db.get(optr, key, result)).isEqualTo(4);
231       assertThat(result.position()).isEqualTo(9);
232       assertThat(result.limit()).isEqualTo(12);
233       assertThat(key.position()).isEqualTo(8);
234       assertThat(key.limit()).isEqualTo(8);
235       byte[] tmp2 = new byte[3];
236       result.get(tmp2);
237       assertThat(tmp2).isEqualTo("val".getBytes());
238 
239       // put
240       Segment key3 = sliceSegment("key3");
241       Segment key4 = sliceSegment("key4");
242       Segment value0 = sliceSegment("value 0");
243       Segment value1 = sliceSegment("value 1");
244       db.put(key3.data, key3.offset, key3.len, value0.data, value0.offset, value0.len);
245       db.put(opt, key4.data, key4.offset, key4.len, value1.data, value1.offset, value1.len);
246 
247       // compare
248       Assert.assertTrue(value0.isSamePayload(db.get(key3.data, key3.offset, key3.len)));
249       Assert.assertTrue(value1.isSamePayload(db.get(key4.data, key4.offset, key4.len)));
250     }
251   }
252 
sliceSegment(String key)253   private static Segment sliceSegment(String key) {
254     ByteBuffer rawKey = ByteBuffer.allocate(key.length() + 4);
255     rawKey.put((byte)0);
256     rawKey.put((byte)0);
257     rawKey.put(key.getBytes());
258 
259     return new Segment(rawKey.array(), 2, key.length());
260   }
261 
262   private static class Segment {
263     final byte[] data;
264     final int offset;
265     final int len;
266 
isSamePayload(byte[] value)267     public boolean isSamePayload(byte[] value) {
268       if (value == null) {
269         return false;
270       }
271       if (value.length != len) {
272         return false;
273       }
274 
275       for (int i = 0; i < value.length; i++) {
276         if (data[i + offset] != value[i]) {
277           return false;
278         }
279       }
280 
281       return true;
282     }
283 
Segment(byte[] value, int offset, int len)284     public Segment(byte[] value, int offset, int len) {
285       this.data = value;
286       this.offset = offset;
287       this.len = len;
288     }
289   }
290 
291   @Test
write()292   public void write() throws RocksDBException {
293     try (final StringAppendOperator stringAppendOperator = new StringAppendOperator();
294          final Options options = new Options()
295              .setMergeOperator(stringAppendOperator)
296              .setCreateIfMissing(true);
297          final RocksDB db = RocksDB.open(options,
298              dbFolder.getRoot().getAbsolutePath());
299          final WriteOptions opts = new WriteOptions()) {
300 
301       try (final WriteBatch wb1 = new WriteBatch()) {
302         wb1.put("key1".getBytes(), "aa".getBytes());
303         wb1.merge("key1".getBytes(), "bb".getBytes());
304 
305         try (final WriteBatch wb2 = new WriteBatch()) {
306           wb2.put("key2".getBytes(), "xx".getBytes());
307           wb2.merge("key2".getBytes(), "yy".getBytes());
308           db.write(opts, wb1);
309           db.write(opts, wb2);
310         }
311       }
312 
313       assertThat(db.get("key1".getBytes())).isEqualTo(
314           "aa,bb".getBytes());
315       assertThat(db.get("key2".getBytes())).isEqualTo(
316           "xx,yy".getBytes());
317     }
318   }
319 
320   @Test
getWithOutValue()321   public void getWithOutValue() throws RocksDBException {
322     try (final RocksDB db =
323              RocksDB.open(dbFolder.getRoot().getAbsolutePath())) {
324       db.put("key1".getBytes(), "value".getBytes());
325       db.put("key2".getBytes(), "12345678".getBytes());
326       byte[] outValue = new byte[5];
327       // not found value
328       int getResult = db.get("keyNotFound".getBytes(), outValue);
329       assertThat(getResult).isEqualTo(RocksDB.NOT_FOUND);
330       // found value which fits in outValue
331       getResult = db.get("key1".getBytes(), outValue);
332       assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND);
333       assertThat(outValue).isEqualTo("value".getBytes());
334       // found value which fits partially
335       getResult = db.get("key2".getBytes(), outValue);
336       assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND);
337       assertThat(outValue).isEqualTo("12345".getBytes());
338     }
339   }
340 
341   @Test
getWithOutValueReadOptions()342   public void getWithOutValueReadOptions() throws RocksDBException {
343     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
344          final ReadOptions rOpt = new ReadOptions()) {
345       db.put("key1".getBytes(), "value".getBytes());
346       db.put("key2".getBytes(), "12345678".getBytes());
347       byte[] outValue = new byte[5];
348       // not found value
349       int getResult = db.get(rOpt, "keyNotFound".getBytes(),
350           outValue);
351       assertThat(getResult).isEqualTo(RocksDB.NOT_FOUND);
352       // found value which fits in outValue
353       getResult = db.get(rOpt, "key1".getBytes(), outValue);
354       assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND);
355       assertThat(outValue).isEqualTo("value".getBytes());
356       // found value which fits partially
357       getResult = db.get(rOpt, "key2".getBytes(), outValue);
358       assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND);
359       assertThat(outValue).isEqualTo("12345".getBytes());
360     }
361   }
362 
363   @Rule
364   public ExpectedException thrown = ExpectedException.none();
365 
366   @Test
getOutOfArrayMaxSizeValue()367   public void getOutOfArrayMaxSizeValue() throws RocksDBException {
368     final int numberOfValueSplits = 10;
369     final int splitSize = Integer.MAX_VALUE / numberOfValueSplits;
370 
371     Runtime runtime = Runtime.getRuntime();
372     long neededMemory = ((long)(splitSize)) * (((long)numberOfValueSplits) + 3);
373     boolean isEnoughMemory = runtime.maxMemory() - runtime.totalMemory() > neededMemory;
374     Assume.assumeTrue(isEnoughMemory);
375 
376     final byte[] valueSplit = new byte[splitSize];
377     final byte[] key = "key".getBytes();
378 
379     thrown.expect(RocksDBException.class);
380     thrown.expectMessage("Requested array size exceeds VM limit");
381 
382     // merge (numberOfValueSplits + 1) valueSplit's to get value size exceeding Integer.MAX_VALUE
383     try (final StringAppendOperator stringAppendOperator = new StringAppendOperator();
384          final Options opt = new Options()
385                  .setCreateIfMissing(true)
386                  .setMergeOperator(stringAppendOperator);
387          final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) {
388       db.put(key, valueSplit);
389       for (int i = 0; i < numberOfValueSplits; i++) {
390         db.merge(key, valueSplit);
391       }
392       db.get(key);
393     }
394   }
395 
396   @SuppressWarnings("deprecated")
397   @Test
multiGet()398   public void multiGet() throws RocksDBException {
399     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
400          final ReadOptions rOpt = new ReadOptions()) {
401       db.put("key1".getBytes(), "value".getBytes());
402       db.put("key2".getBytes(), "12345678".getBytes());
403       List<byte[]> lookupKeys = new ArrayList<>();
404       lookupKeys.add("key1".getBytes());
405       lookupKeys.add("key2".getBytes());
406       Map<byte[], byte[]> results = db.multiGet(lookupKeys);
407       assertThat(results).isNotNull();
408       assertThat(results.values()).isNotNull();
409       assertThat(results.values()).
410           contains("value".getBytes(), "12345678".getBytes());
411       // test same method with ReadOptions
412       results = db.multiGet(rOpt, lookupKeys);
413       assertThat(results).isNotNull();
414       assertThat(results.values()).isNotNull();
415       assertThat(results.values()).
416           contains("value".getBytes(), "12345678".getBytes());
417 
418       // remove existing key
419       lookupKeys.remove("key2".getBytes());
420       // add non existing key
421       lookupKeys.add("key3".getBytes());
422       results = db.multiGet(lookupKeys);
423       assertThat(results).isNotNull();
424       assertThat(results.values()).isNotNull();
425       assertThat(results.values()).
426           contains("value".getBytes());
427       // test same call with readOptions
428       results = db.multiGet(rOpt, lookupKeys);
429       assertThat(results).isNotNull();
430       assertThat(results.values()).isNotNull();
431       assertThat(results.values()).
432           contains("value".getBytes());
433     }
434   }
435 
436   @Test
multiGetAsList()437   public void multiGetAsList() throws RocksDBException {
438     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
439          final ReadOptions rOpt = new ReadOptions()) {
440       db.put("key1".getBytes(), "value".getBytes());
441       db.put("key2".getBytes(), "12345678".getBytes());
442       List<byte[]> lookupKeys = new ArrayList<>();
443       lookupKeys.add("key1".getBytes());
444       lookupKeys.add("key2".getBytes());
445       List<byte[]> results = db.multiGetAsList(lookupKeys);
446       assertThat(results).isNotNull();
447       assertThat(results).hasSize(lookupKeys.size());
448       assertThat(results).
449           containsExactly("value".getBytes(), "12345678".getBytes());
450       // test same method with ReadOptions
451       results = db.multiGetAsList(rOpt, lookupKeys);
452       assertThat(results).isNotNull();
453       assertThat(results).
454           contains("value".getBytes(), "12345678".getBytes());
455 
456       // remove existing key
457       lookupKeys.remove(1);
458       // add non existing key
459       lookupKeys.add("key3".getBytes());
460       results = db.multiGetAsList(lookupKeys);
461       assertThat(results).isNotNull();
462       assertThat(results).
463           containsExactly("value".getBytes(), null);
464       // test same call with readOptions
465       results = db.multiGetAsList(rOpt, lookupKeys);
466       assertThat(results).isNotNull();
467       assertThat(results).contains("value".getBytes());
468     }
469   }
470 
471   @Test
merge()472   public void merge() throws RocksDBException {
473     try (final StringAppendOperator stringAppendOperator = new StringAppendOperator();
474          final Options opt = new Options()
475             .setCreateIfMissing(true)
476             .setMergeOperator(stringAppendOperator);
477          final WriteOptions wOpt = new WriteOptions();
478          final RocksDB db = RocksDB.open(opt,
479              dbFolder.getRoot().getAbsolutePath())
480     ) {
481       db.put("key1".getBytes(), "value".getBytes());
482       assertThat(db.get("key1".getBytes())).isEqualTo(
483           "value".getBytes());
484       // merge key1 with another value portion
485       db.merge("key1".getBytes(), "value2".getBytes());
486       assertThat(db.get("key1".getBytes())).isEqualTo(
487           "value,value2".getBytes());
488       // merge key1 with another value portion
489       db.merge(wOpt, "key1".getBytes(), "value3".getBytes());
490       assertThat(db.get("key1".getBytes())).isEqualTo(
491           "value,value2,value3".getBytes());
492       // merge on non existent key shall insert the value
493       db.merge(wOpt, "key2".getBytes(), "xxxx".getBytes());
494       assertThat(db.get("key2".getBytes())).isEqualTo(
495           "xxxx".getBytes());
496 
497       Segment key3 = sliceSegment("key3");
498       Segment key4 = sliceSegment("key4");
499       Segment value0 = sliceSegment("value 0");
500       Segment value1 = sliceSegment("value 1");
501 
502       db.merge(key3.data, key3.offset, key3.len, value0.data, value0.offset, value0.len);
503       db.merge(wOpt, key4.data, key4.offset, key4.len, value1.data, value1.offset, value1.len);
504 
505       // compare
506       Assert.assertTrue(value0.isSamePayload(db.get(key3.data, key3.offset, key3.len)));
507       Assert.assertTrue(value1.isSamePayload(db.get(key4.data, key4.offset, key4.len)));
508     }
509   }
510 
511   @Test
delete()512   public void delete() throws RocksDBException {
513     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
514          final WriteOptions wOpt = new WriteOptions()) {
515       db.put("key1".getBytes(), "value".getBytes());
516       db.put("key2".getBytes(), "12345678".getBytes());
517       db.put("key3".getBytes(), "33".getBytes());
518       assertThat(db.get("key1".getBytes())).isEqualTo(
519           "value".getBytes());
520       assertThat(db.get("key2".getBytes())).isEqualTo(
521           "12345678".getBytes());
522       assertThat(db.get("key3".getBytes())).isEqualTo("33".getBytes());
523       db.delete("key1".getBytes());
524       db.delete(wOpt, "key2".getBytes());
525       ByteBuffer key = ByteBuffer.allocateDirect(16);
526       key.put("key3".getBytes()).flip();
527       db.delete(wOpt, key);
528       assertThat(key.position()).isEqualTo(4);
529       assertThat(key.limit()).isEqualTo(4);
530 
531       assertThat(db.get("key1".getBytes())).isNull();
532       assertThat(db.get("key2".getBytes())).isNull();
533 
534       Segment key3 = sliceSegment("key3");
535       Segment key4 = sliceSegment("key4");
536       db.put("key3".getBytes(), "key3 value".getBytes());
537       db.put("key4".getBytes(), "key4 value".getBytes());
538 
539       db.delete(key3.data, key3.offset, key3.len);
540       db.delete(wOpt, key4.data, key4.offset, key4.len);
541 
542       assertThat(db.get("key3".getBytes())).isNull();
543       assertThat(db.get("key4".getBytes())).isNull();
544     }
545   }
546 
547   @Test
singleDelete()548   public void singleDelete() throws RocksDBException {
549     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
550          final WriteOptions wOpt = new WriteOptions()) {
551       db.put("key1".getBytes(), "value".getBytes());
552       db.put("key2".getBytes(), "12345678".getBytes());
553       assertThat(db.get("key1".getBytes())).isEqualTo(
554           "value".getBytes());
555       assertThat(db.get("key2".getBytes())).isEqualTo(
556           "12345678".getBytes());
557       db.singleDelete("key1".getBytes());
558       db.singleDelete(wOpt, "key2".getBytes());
559       assertThat(db.get("key1".getBytes())).isNull();
560       assertThat(db.get("key2".getBytes())).isNull();
561     }
562   }
563 
564   @Test
singleDelete_nonExisting()565   public void singleDelete_nonExisting() throws RocksDBException {
566     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
567          final WriteOptions wOpt = new WriteOptions()) {
568       db.singleDelete("key1".getBytes());
569       db.singleDelete(wOpt, "key2".getBytes());
570       assertThat(db.get("key1".getBytes())).isNull();
571       assertThat(db.get("key2".getBytes())).isNull();
572     }
573   }
574 
575   @Test
deleteRange()576   public void deleteRange() throws RocksDBException {
577     try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath())) {
578       db.put("key1".getBytes(), "value".getBytes());
579       db.put("key2".getBytes(), "12345678".getBytes());
580       db.put("key3".getBytes(), "abcdefg".getBytes());
581       db.put("key4".getBytes(), "xyz".getBytes());
582       assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes());
583       assertThat(db.get("key2".getBytes())).isEqualTo("12345678".getBytes());
584       assertThat(db.get("key3".getBytes())).isEqualTo("abcdefg".getBytes());
585       assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes());
586       db.deleteRange("key2".getBytes(), "key4".getBytes());
587       assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes());
588       assertThat(db.get("key2".getBytes())).isNull();
589       assertThat(db.get("key3".getBytes())).isNull();
590       assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes());
591     }
592   }
593 
594   @Test
getIntProperty()595   public void getIntProperty() throws RocksDBException {
596     try (
597         final Options options = new Options()
598             .setCreateIfMissing(true)
599             .setMaxWriteBufferNumber(10)
600             .setMinWriteBufferNumberToMerge(10);
601         final RocksDB db = RocksDB.open(options,
602             dbFolder.getRoot().getAbsolutePath());
603         final WriteOptions wOpt = new WriteOptions().setDisableWAL(true)
604     ) {
605       db.put(wOpt, "key1".getBytes(), "value1".getBytes());
606       db.put(wOpt, "key2".getBytes(), "value2".getBytes());
607       db.put(wOpt, "key3".getBytes(), "value3".getBytes());
608       db.put(wOpt, "key4".getBytes(), "value4".getBytes());
609       assertThat(db.getLongProperty("rocksdb.num-entries-active-mem-table"))
610           .isGreaterThan(0);
611       assertThat(db.getLongProperty("rocksdb.cur-size-active-mem-table"))
612           .isGreaterThan(0);
613     }
614   }
615 
616   @Test
fullCompactRange()617   public void fullCompactRange() throws RocksDBException {
618     try (final Options opt = new Options().
619         setCreateIfMissing(true).
620         setDisableAutoCompactions(true).
621         setCompactionStyle(CompactionStyle.LEVEL).
622         setNumLevels(4).
623         setWriteBufferSize(100 << 10).
624         setLevelZeroFileNumCompactionTrigger(3).
625         setTargetFileSizeBase(200 << 10).
626         setTargetFileSizeMultiplier(1).
627         setMaxBytesForLevelBase(500 << 10).
628         setMaxBytesForLevelMultiplier(1).
629         setDisableAutoCompactions(false);
630          final RocksDB db = RocksDB.open(opt,
631              dbFolder.getRoot().getAbsolutePath())) {
632       // fill database with key/value pairs
633       byte[] b = new byte[10000];
634       for (int i = 0; i < 200; i++) {
635         rand.nextBytes(b);
636         db.put((String.valueOf(i)).getBytes(), b);
637       }
638       db.compactRange();
639     }
640   }
641 
642   @Test
fullCompactRangeColumnFamily()643   public void fullCompactRangeColumnFamily()
644       throws RocksDBException {
645     try (
646         final DBOptions opt = new DBOptions().
647             setCreateIfMissing(true).
648             setCreateMissingColumnFamilies(true);
649         final ColumnFamilyOptions new_cf_opts = new ColumnFamilyOptions().
650             setDisableAutoCompactions(true).
651             setCompactionStyle(CompactionStyle.LEVEL).
652             setNumLevels(4).
653             setWriteBufferSize(100 << 10).
654             setLevelZeroFileNumCompactionTrigger(3).
655             setTargetFileSizeBase(200 << 10).
656             setTargetFileSizeMultiplier(1).
657             setMaxBytesForLevelBase(500 << 10).
658             setMaxBytesForLevelMultiplier(1).
659             setDisableAutoCompactions(false)
660     ) {
661       final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
662           Arrays.asList(
663               new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
664               new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts));
665 
666       // open database
667       final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
668       try (final RocksDB db = RocksDB.open(opt,
669           dbFolder.getRoot().getAbsolutePath(),
670           columnFamilyDescriptors,
671           columnFamilyHandles)) {
672         try {
673           // fill database with key/value pairs
674           byte[] b = new byte[10000];
675           for (int i = 0; i < 200; i++) {
676             rand.nextBytes(b);
677             db.put(columnFamilyHandles.get(1),
678                 String.valueOf(i).getBytes(), b);
679           }
680           db.compactRange(columnFamilyHandles.get(1));
681         } finally {
682           for (final ColumnFamilyHandle handle : columnFamilyHandles) {
683             handle.close();
684           }
685         }
686       }
687     }
688   }
689 
690   @Test
compactRangeWithKeys()691   public void compactRangeWithKeys()
692       throws RocksDBException {
693     try (final Options opt = new Options().
694         setCreateIfMissing(true).
695         setDisableAutoCompactions(true).
696         setCompactionStyle(CompactionStyle.LEVEL).
697         setNumLevels(4).
698         setWriteBufferSize(100 << 10).
699         setLevelZeroFileNumCompactionTrigger(3).
700         setTargetFileSizeBase(200 << 10).
701         setTargetFileSizeMultiplier(1).
702         setMaxBytesForLevelBase(500 << 10).
703         setMaxBytesForLevelMultiplier(1).
704         setDisableAutoCompactions(false);
705          final RocksDB db = RocksDB.open(opt,
706              dbFolder.getRoot().getAbsolutePath())) {
707       // fill database with key/value pairs
708       byte[] b = new byte[10000];
709       for (int i = 0; i < 200; i++) {
710         rand.nextBytes(b);
711         db.put((String.valueOf(i)).getBytes(), b);
712       }
713       db.compactRange("0".getBytes(), "201".getBytes());
714     }
715   }
716 
717   @Test
compactRangeWithKeysReduce()718   public void compactRangeWithKeysReduce()
719       throws RocksDBException {
720     try (
721         final Options opt = new Options().
722             setCreateIfMissing(true).
723             setDisableAutoCompactions(true).
724             setCompactionStyle(CompactionStyle.LEVEL).
725             setNumLevels(4).
726             setWriteBufferSize(100 << 10).
727             setLevelZeroFileNumCompactionTrigger(3).
728             setTargetFileSizeBase(200 << 10).
729             setTargetFileSizeMultiplier(1).
730             setMaxBytesForLevelBase(500 << 10).
731             setMaxBytesForLevelMultiplier(1).
732             setDisableAutoCompactions(false);
733         final RocksDB db = RocksDB.open(opt,
734             dbFolder.getRoot().getAbsolutePath())) {
735       // fill database with key/value pairs
736       byte[] b = new byte[10000];
737       for (int i = 0; i < 200; i++) {
738         rand.nextBytes(b);
739         db.put((String.valueOf(i)).getBytes(), b);
740       }
741       db.flush(new FlushOptions().setWaitForFlush(true));
742       try (final CompactRangeOptions compactRangeOpts = new CompactRangeOptions()
743             .setChangeLevel(true)
744             .setTargetLevel(-1)
745             .setTargetPathId(0)) {
746         db.compactRange(null, "0".getBytes(), "201".getBytes(),
747             compactRangeOpts);
748       }
749     }
750   }
751 
752   @Test
compactRangeWithKeysColumnFamily()753   public void compactRangeWithKeysColumnFamily()
754       throws RocksDBException {
755     try (final DBOptions opt = new DBOptions().
756         setCreateIfMissing(true).
757         setCreateMissingColumnFamilies(true);
758          final ColumnFamilyOptions new_cf_opts = new ColumnFamilyOptions().
759              setDisableAutoCompactions(true).
760              setCompactionStyle(CompactionStyle.LEVEL).
761              setNumLevels(4).
762              setWriteBufferSize(100 << 10).
763              setLevelZeroFileNumCompactionTrigger(3).
764              setTargetFileSizeBase(200 << 10).
765              setTargetFileSizeMultiplier(1).
766              setMaxBytesForLevelBase(500 << 10).
767              setMaxBytesForLevelMultiplier(1).
768              setDisableAutoCompactions(false)
769     ) {
770       final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
771           Arrays.asList(
772               new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
773               new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts)
774           );
775 
776       // open database
777       final List<ColumnFamilyHandle> columnFamilyHandles =
778           new ArrayList<>();
779       try (final RocksDB db = RocksDB.open(opt,
780           dbFolder.getRoot().getAbsolutePath(),
781           columnFamilyDescriptors,
782           columnFamilyHandles)) {
783         try {
784           // fill database with key/value pairs
785           byte[] b = new byte[10000];
786           for (int i = 0; i < 200; i++) {
787             rand.nextBytes(b);
788             db.put(columnFamilyHandles.get(1),
789                 String.valueOf(i).getBytes(), b);
790           }
791           db.compactRange(columnFamilyHandles.get(1),
792               "0".getBytes(), "201".getBytes());
793         } finally {
794           for (final ColumnFamilyHandle handle : columnFamilyHandles) {
795             handle.close();
796           }
797         }
798       }
799     }
800   }
801 
802   @Test
compactRangeWithKeysReduceColumnFamily()803   public void compactRangeWithKeysReduceColumnFamily()
804       throws RocksDBException {
805     try (final DBOptions opt = new DBOptions().
806         setCreateIfMissing(true).
807         setCreateMissingColumnFamilies(true);
808          final ColumnFamilyOptions new_cf_opts = new ColumnFamilyOptions().
809              setDisableAutoCompactions(true).
810              setCompactionStyle(CompactionStyle.LEVEL).
811              setNumLevels(4).
812              setWriteBufferSize(100 << 10).
813              setLevelZeroFileNumCompactionTrigger(3).
814              setTargetFileSizeBase(200 << 10).
815              setTargetFileSizeMultiplier(1).
816              setMaxBytesForLevelBase(500 << 10).
817              setMaxBytesForLevelMultiplier(1).
818              setDisableAutoCompactions(false)
819     ) {
820       final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
821           Arrays.asList(
822               new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
823               new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts)
824           );
825 
826       final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
827       // open database
828       try (final RocksDB db = RocksDB.open(opt,
829           dbFolder.getRoot().getAbsolutePath(),
830           columnFamilyDescriptors,
831           columnFamilyHandles)) {
832         try (final CompactRangeOptions compactRangeOpts = new CompactRangeOptions()
833             .setChangeLevel(true)
834             .setTargetLevel(-1)
835             .setTargetPathId(0)) {
836           // fill database with key/value pairs
837           byte[] b = new byte[10000];
838           for (int i = 0; i < 200; i++) {
839             rand.nextBytes(b);
840             db.put(columnFamilyHandles.get(1),
841                 String.valueOf(i).getBytes(), b);
842           }
843           db.compactRange(columnFamilyHandles.get(1), "0".getBytes(),
844               "201".getBytes(), compactRangeOpts);
845         } finally {
846           for (final ColumnFamilyHandle handle : columnFamilyHandles) {
847             handle.close();
848           }
849         }
850       }
851     }
852   }
853 
854   @Test
compactRangeToLevel()855   public void compactRangeToLevel()
856       throws RocksDBException, InterruptedException {
857     final int NUM_KEYS_PER_L0_FILE = 100;
858     final int KEY_SIZE = 20;
859     final int VALUE_SIZE = 300;
860     final int L0_FILE_SIZE =
861         NUM_KEYS_PER_L0_FILE * (KEY_SIZE + VALUE_SIZE);
862     final int NUM_L0_FILES = 10;
863     final int TEST_SCALE = 5;
864     final int KEY_INTERVAL = 100;
865     try (final Options opt = new Options().
866         setCreateIfMissing(true).
867         setCompactionStyle(CompactionStyle.LEVEL).
868         setNumLevels(5).
869         // a slightly bigger write buffer than L0 file
870         // so that we can ensure manual flush always
871         // go before background flush happens.
872             setWriteBufferSize(L0_FILE_SIZE * 2).
873         // Disable auto L0 -> L1 compaction
874             setLevelZeroFileNumCompactionTrigger(20).
875             setTargetFileSizeBase(L0_FILE_SIZE * 100).
876             setTargetFileSizeMultiplier(1).
877         // To disable auto compaction
878             setMaxBytesForLevelBase(NUM_L0_FILES * L0_FILE_SIZE * 100).
879             setMaxBytesForLevelMultiplier(2).
880             setDisableAutoCompactions(true);
881          final RocksDB db = RocksDB.open(opt,
882              dbFolder.getRoot().getAbsolutePath())
883     ) {
884       // fill database with key/value pairs
885       byte[] value = new byte[VALUE_SIZE];
886       int int_key = 0;
887       for (int round = 0; round < 5; ++round) {
888         int initial_key = int_key;
889         for (int f = 1; f <= NUM_L0_FILES; ++f) {
890           for (int i = 0; i < NUM_KEYS_PER_L0_FILE; ++i) {
891             int_key += KEY_INTERVAL;
892             rand.nextBytes(value);
893 
894             db.put(String.format("%020d", int_key).getBytes(),
895                 value);
896           }
897           db.flush(new FlushOptions().setWaitForFlush(true));
898           // Make sure we do create one more L0 files.
899           assertThat(
900               db.getProperty("rocksdb.num-files-at-level0")).
901               isEqualTo("" + f);
902         }
903 
904         // Compact all L0 files we just created
905         db.compactRange(
906             String.format("%020d", initial_key).getBytes(),
907             String.format("%020d", int_key - 1).getBytes());
908         // Making sure there isn't any L0 files.
909         assertThat(
910             db.getProperty("rocksdb.num-files-at-level0")).
911             isEqualTo("0");
912         // Making sure there are some L1 files.
913         // Here we only use != 0 instead of a specific number
914         // as we don't want the test make any assumption on
915         // how compaction works.
916         assertThat(
917             db.getProperty("rocksdb.num-files-at-level1")).
918             isNotEqualTo("0");
919         // Because we only compacted those keys we issued
920         // in this round, there shouldn't be any L1 -> L2
921         // compaction.  So we expect zero L2 files here.
922         assertThat(
923             db.getProperty("rocksdb.num-files-at-level2")).
924             isEqualTo("0");
925       }
926     }
927   }
928 
929   @Test
deleteFilesInRange()930   public void deleteFilesInRange() throws RocksDBException, InterruptedException {
931     final int KEY_SIZE = 20;
932     final int VALUE_SIZE = 1000;
933     final int FILE_SIZE = 64000;
934     final int NUM_FILES = 10;
935 
936     final int KEY_INTERVAL = 10000;
937     /*
938      * Intention of these options is to end up reliably with 10 files
939      * we will be deleting using deleteFilesInRange.
940      * It is writing roughly number of keys that will fit in 10 files (target size)
941      * It is writing interleaved so that files from memory on L0 will overlap
942      * Then compaction cleans everything and we should end up with 10 files
943      */
944     try (final Options opt = new Options()
945                                  .setCreateIfMissing(true)
946                                  .setCompressionType(CompressionType.NO_COMPRESSION)
947                                  .setTargetFileSizeBase(FILE_SIZE)
948                                  .setWriteBufferSize(FILE_SIZE / 2)
949                                  .setDisableAutoCompactions(true);
950          final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) {
951       int records = FILE_SIZE / (KEY_SIZE + VALUE_SIZE);
952 
953       // fill database with key/value pairs
954       byte[] value = new byte[VALUE_SIZE];
955       int key_init = 0;
956       for (int o = 0; o < NUM_FILES; ++o) {
957         int int_key = key_init++;
958         for (int i = 0; i < records; ++i) {
959           int_key += KEY_INTERVAL;
960           rand.nextBytes(value);
961 
962           db.put(String.format("%020d", int_key).getBytes(), value);
963         }
964       }
965       db.flush(new FlushOptions().setWaitForFlush(true));
966       db.compactRange();
967       // Make sure we do create one more L0 files.
968       assertThat(db.getProperty("rocksdb.num-files-at-level0")).isEqualTo("0");
969 
970       // Should be 10, but we are OK with asserting +- 2
971       int files = Integer.parseInt(db.getProperty("rocksdb.num-files-at-level1"));
972       assertThat(files).isBetween(8, 12);
973 
974       // Delete lower 60% (roughly). Result should be 5, but we are OK with asserting +- 2
975       // Important is that we know something was deleted (JNI call did something)
976       // Exact assertions are done in C++ unit tests
977       db.deleteFilesInRanges(null,
978           Arrays.asList(null, String.format("%020d", records * KEY_INTERVAL * 6 / 10).getBytes()),
979           false);
980       files = Integer.parseInt(db.getProperty("rocksdb.num-files-at-level1"));
981       assertThat(files).isBetween(3, 7);
982     }
983   }
984 
985   @Test
compactRangeToLevelColumnFamily()986   public void compactRangeToLevelColumnFamily()
987       throws RocksDBException {
988     final int NUM_KEYS_PER_L0_FILE = 100;
989     final int KEY_SIZE = 20;
990     final int VALUE_SIZE = 300;
991     final int L0_FILE_SIZE =
992         NUM_KEYS_PER_L0_FILE * (KEY_SIZE + VALUE_SIZE);
993     final int NUM_L0_FILES = 10;
994     final int TEST_SCALE = 5;
995     final int KEY_INTERVAL = 100;
996 
997     try (final DBOptions opt = new DBOptions().
998         setCreateIfMissing(true).
999         setCreateMissingColumnFamilies(true);
1000          final ColumnFamilyOptions new_cf_opts = new ColumnFamilyOptions().
1001              setCompactionStyle(CompactionStyle.LEVEL).
1002              setNumLevels(5).
1003              // a slightly bigger write buffer than L0 file
1004              // so that we can ensure manual flush always
1005              // go before background flush happens.
1006                  setWriteBufferSize(L0_FILE_SIZE * 2).
1007              // Disable auto L0 -> L1 compaction
1008                  setLevelZeroFileNumCompactionTrigger(20).
1009                  setTargetFileSizeBase(L0_FILE_SIZE * 100).
1010                  setTargetFileSizeMultiplier(1).
1011              // To disable auto compaction
1012                  setMaxBytesForLevelBase(NUM_L0_FILES * L0_FILE_SIZE * 100).
1013                  setMaxBytesForLevelMultiplier(2).
1014                  setDisableAutoCompactions(true)
1015     ) {
1016       final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
1017           Arrays.asList(
1018               new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
1019               new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts)
1020           );
1021 
1022       final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
1023       // open database
1024       try (final RocksDB db = RocksDB.open(opt,
1025           dbFolder.getRoot().getAbsolutePath(),
1026           columnFamilyDescriptors,
1027           columnFamilyHandles)) {
1028         try {
1029           // fill database with key/value pairs
1030           byte[] value = new byte[VALUE_SIZE];
1031           int int_key = 0;
1032           for (int round = 0; round < 5; ++round) {
1033             int initial_key = int_key;
1034             for (int f = 1; f <= NUM_L0_FILES; ++f) {
1035               for (int i = 0; i < NUM_KEYS_PER_L0_FILE; ++i) {
1036                 int_key += KEY_INTERVAL;
1037                 rand.nextBytes(value);
1038 
1039                 db.put(columnFamilyHandles.get(1),
1040                     String.format("%020d", int_key).getBytes(),
1041                     value);
1042               }
1043               db.flush(new FlushOptions().setWaitForFlush(true),
1044                   columnFamilyHandles.get(1));
1045               // Make sure we do create one more L0 files.
1046               assertThat(
1047                   db.getProperty(columnFamilyHandles.get(1),
1048                       "rocksdb.num-files-at-level0")).
1049                   isEqualTo("" + f);
1050             }
1051 
1052             // Compact all L0 files we just created
1053             db.compactRange(
1054                 columnFamilyHandles.get(1),
1055                 String.format("%020d", initial_key).getBytes(),
1056                 String.format("%020d", int_key - 1).getBytes());
1057             // Making sure there isn't any L0 files.
1058             assertThat(
1059                 db.getProperty(columnFamilyHandles.get(1),
1060                     "rocksdb.num-files-at-level0")).
1061                 isEqualTo("0");
1062             // Making sure there are some L1 files.
1063             // Here we only use != 0 instead of a specific number
1064             // as we don't want the test make any assumption on
1065             // how compaction works.
1066             assertThat(
1067                 db.getProperty(columnFamilyHandles.get(1),
1068                     "rocksdb.num-files-at-level1")).
1069                 isNotEqualTo("0");
1070             // Because we only compacted those keys we issued
1071             // in this round, there shouldn't be any L1 -> L2
1072             // compaction.  So we expect zero L2 files here.
1073             assertThat(
1074                 db.getProperty(columnFamilyHandles.get(1),
1075                     "rocksdb.num-files-at-level2")).
1076                 isEqualTo("0");
1077           }
1078         } finally {
1079           for (final ColumnFamilyHandle handle : columnFamilyHandles) {
1080             handle.close();
1081           }
1082         }
1083       }
1084     }
1085   }
1086 
1087   @Test
pauseContinueBackgroundWork()1088   public void pauseContinueBackgroundWork() throws RocksDBException {
1089     try (final Options options = new Options().setCreateIfMissing(true);
1090          final RocksDB db = RocksDB.open(options,
1091              dbFolder.getRoot().getAbsolutePath())
1092     ) {
1093       db.pauseBackgroundWork();
1094       db.continueBackgroundWork();
1095       db.pauseBackgroundWork();
1096       db.continueBackgroundWork();
1097     }
1098   }
1099 
1100   @Test
enableDisableFileDeletions()1101   public void enableDisableFileDeletions() throws RocksDBException {
1102     try (final Options options = new Options().setCreateIfMissing(true);
1103          final RocksDB db = RocksDB.open(options,
1104              dbFolder.getRoot().getAbsolutePath())
1105     ) {
1106       db.disableFileDeletions();
1107       db.enableFileDeletions(false);
1108       db.disableFileDeletions();
1109       db.enableFileDeletions(true);
1110     }
1111   }
1112 
1113   @Test
setOptions()1114   public void setOptions() throws RocksDBException {
1115     try (final DBOptions options = new DBOptions()
1116              .setCreateIfMissing(true)
1117              .setCreateMissingColumnFamilies(true);
1118          final ColumnFamilyOptions new_cf_opts = new ColumnFamilyOptions()
1119              .setWriteBufferSize(4096)) {
1120 
1121       final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
1122           Arrays.asList(
1123               new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
1124               new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts));
1125 
1126       // open database
1127       final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
1128       try (final RocksDB db = RocksDB.open(options,
1129           dbFolder.getRoot().getAbsolutePath(), columnFamilyDescriptors, columnFamilyHandles)) {
1130         try {
1131           final MutableColumnFamilyOptions mutableOptions =
1132               MutableColumnFamilyOptions.builder()
1133                   .setWriteBufferSize(2048)
1134                   .build();
1135 
1136           db.setOptions(columnFamilyHandles.get(1), mutableOptions);
1137 
1138         } finally {
1139           for (final ColumnFamilyHandle handle : columnFamilyHandles) {
1140             handle.close();
1141           }
1142         }
1143       }
1144     }
1145   }
1146 
1147   @Test
destroyDB()1148   public void destroyDB() throws RocksDBException {
1149     try (final Options options = new Options().setCreateIfMissing(true)) {
1150       String dbPath = dbFolder.getRoot().getAbsolutePath();
1151       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1152         db.put("key1".getBytes(), "value".getBytes());
1153       }
1154       assertThat(dbFolder.getRoot().exists() && dbFolder.getRoot().listFiles().length != 0)
1155           .isTrue();
1156       RocksDB.destroyDB(dbPath, options);
1157       assertThat(dbFolder.getRoot().exists() && dbFolder.getRoot().listFiles().length != 0)
1158           .isFalse();
1159     }
1160   }
1161 
1162   @Test(expected = RocksDBException.class)
destroyDBFailIfOpen()1163   public void destroyDBFailIfOpen() throws RocksDBException {
1164     try (final Options options = new Options().setCreateIfMissing(true)) {
1165       String dbPath = dbFolder.getRoot().getAbsolutePath();
1166       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1167         // Fails as the db is open and locked.
1168         RocksDB.destroyDB(dbPath, options);
1169       }
1170     }
1171   }
1172 
1173   @Ignore("This test crashes. Re-enable after fixing.")
1174   @Test
getApproximateSizes()1175   public void getApproximateSizes() throws RocksDBException {
1176     final byte key1[] = "key1".getBytes(UTF_8);
1177     final byte key2[] = "key2".getBytes(UTF_8);
1178     final byte key3[] = "key3".getBytes(UTF_8);
1179     try (final Options options = new Options().setCreateIfMissing(true)) {
1180       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1181       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1182         db.put(key1, key1);
1183         db.put(key2, key2);
1184         db.put(key3, key3);
1185 
1186         final long[] sizes = db.getApproximateSizes(
1187             Arrays.asList(
1188                 new Range(new Slice(key1), new Slice(key2)),
1189                 new Range(new Slice(key2), new Slice(key3))
1190             ),
1191             SizeApproximationFlag.INCLUDE_FILES,
1192             SizeApproximationFlag.INCLUDE_MEMTABLES);
1193 
1194         assertThat(sizes.length).isEqualTo(2);
1195         assertThat(sizes[0]).isEqualTo(0);
1196         assertThat(sizes[1]).isGreaterThanOrEqualTo(1);
1197       }
1198     }
1199   }
1200 
1201   @Test
getApproximateMemTableStats()1202   public void getApproximateMemTableStats() throws RocksDBException {
1203     final byte key1[] = "key1".getBytes(UTF_8);
1204     final byte key2[] = "key2".getBytes(UTF_8);
1205     final byte key3[] = "key3".getBytes(UTF_8);
1206     try (final Options options = new Options().setCreateIfMissing(true)) {
1207       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1208       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1209         db.put(key1, key1);
1210         db.put(key2, key2);
1211         db.put(key3, key3);
1212 
1213         final RocksDB.CountAndSize stats =
1214             db.getApproximateMemTableStats(
1215                 new Range(new Slice(key1), new Slice(key3)));
1216 
1217         assertThat(stats).isNotNull();
1218         assertThat(stats.count).isGreaterThan(1);
1219         assertThat(stats.size).isGreaterThan(1);
1220       }
1221     }
1222   }
1223 
1224   @Ignore("TODO(AR) re-enable when ready!")
1225   @Test
compactFiles()1226   public void compactFiles() throws RocksDBException {
1227     final int kTestKeySize = 16;
1228     final int kTestValueSize = 984;
1229     final int kEntrySize = kTestKeySize + kTestValueSize;
1230     final int kEntriesPerBuffer = 100;
1231     final int writeBufferSize = kEntrySize * kEntriesPerBuffer;
1232     final byte[] cfName = "pikachu".getBytes(UTF_8);
1233 
1234     try (final Options options = new Options()
1235         .setCreateIfMissing(true)
1236         .setWriteBufferSize(writeBufferSize)
1237         .setCompactionStyle(CompactionStyle.LEVEL)
1238         .setTargetFileSizeBase(writeBufferSize)
1239         .setMaxBytesForLevelBase(writeBufferSize * 2)
1240         .setLevel0StopWritesTrigger(2)
1241         .setMaxBytesForLevelMultiplier(2)
1242         .setCompressionType(CompressionType.NO_COMPRESSION)
1243         .setMaxSubcompactions(4)) {
1244       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1245       try (final RocksDB db = RocksDB.open(options, dbPath);
1246            final ColumnFamilyOptions cfOptions = new ColumnFamilyOptions(options)) {
1247         db.createColumnFamily(new ColumnFamilyDescriptor(cfName,
1248             cfOptions)).close();
1249       }
1250 
1251       try (final ColumnFamilyOptions cfOptions = new ColumnFamilyOptions(options)) {
1252         final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList(
1253             new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOptions),
1254             new ColumnFamilyDescriptor(cfName, cfOptions)
1255         );
1256         final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
1257         try (final DBOptions dbOptions = new DBOptions(options);
1258             final RocksDB db = RocksDB.open(dbOptions, dbPath, cfDescriptors,
1259                 cfHandles);
1260         ) {
1261           try (final FlushOptions flushOptions = new FlushOptions()
1262                 .setWaitForFlush(true)
1263                 .setAllowWriteStall(true);
1264                final CompactionOptions compactionOptions = new CompactionOptions()) {
1265             final Random rnd = new Random(301);
1266             for (int key = 64 * kEntriesPerBuffer; key >= 0; --key) {
1267               final byte[] value = new byte[kTestValueSize];
1268               rnd.nextBytes(value);
1269               db.put(cfHandles.get(1), Integer.toString(key).getBytes(UTF_8),
1270                   value);
1271             }
1272             db.flush(flushOptions, cfHandles);
1273 
1274             final RocksDB.LiveFiles liveFiles = db.getLiveFiles();
1275             final List<String> compactedFiles =
1276                 db.compactFiles(compactionOptions, cfHandles.get(1),
1277                     liveFiles.files, 1, -1, null);
1278             assertThat(compactedFiles).isNotEmpty();
1279           } finally {
1280             for (final ColumnFamilyHandle cfHandle : cfHandles) {
1281               cfHandle.close();
1282             }
1283           }
1284         }
1285       }
1286     }
1287   }
1288 
1289   @Test
enableAutoCompaction()1290   public void enableAutoCompaction() throws RocksDBException {
1291     try (final DBOptions options = new DBOptions()
1292         .setCreateIfMissing(true)) {
1293       final List<ColumnFamilyDescriptor> cfDescs = Arrays.asList(
1294           new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)
1295       );
1296       final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
1297       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1298       try (final RocksDB db = RocksDB.open(options, dbPath, cfDescs, cfHandles)) {
1299         try {
1300           db.enableAutoCompaction(cfHandles);
1301         } finally {
1302           for (final ColumnFamilyHandle cfHandle : cfHandles) {
1303             cfHandle.close();
1304           }
1305         }
1306       }
1307     }
1308   }
1309 
1310   @Test
numberLevels()1311   public void numberLevels() throws RocksDBException {
1312     try (final Options options = new Options().setCreateIfMissing(true)) {
1313       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1314       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1315         assertThat(db.numberLevels()).isEqualTo(7);
1316       }
1317     }
1318   }
1319 
1320   @Test
maxMemCompactionLevel()1321   public void maxMemCompactionLevel() throws RocksDBException {
1322     try (final Options options = new Options().setCreateIfMissing(true)) {
1323       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1324       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1325         assertThat(db.maxMemCompactionLevel()).isEqualTo(0);
1326       }
1327     }
1328   }
1329 
1330   @Test
level0StopWriteTrigger()1331   public void level0StopWriteTrigger() throws RocksDBException {
1332     try (final Options options = new Options().setCreateIfMissing(true)) {
1333       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1334       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1335         assertThat(db.level0StopWriteTrigger()).isEqualTo(36);
1336       }
1337     }
1338   }
1339 
1340   @Test
getName()1341   public void getName() throws RocksDBException {
1342     try (final Options options = new Options().setCreateIfMissing(true)) {
1343       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1344       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1345         assertThat(db.getName()).isEqualTo(dbPath);
1346       }
1347     }
1348   }
1349 
1350   @Test
getEnv()1351   public void getEnv() throws RocksDBException {
1352     try (final Options options = new Options().setCreateIfMissing(true)) {
1353       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1354       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1355         assertThat(db.getEnv()).isEqualTo(Env.getDefault());
1356       }
1357     }
1358   }
1359 
1360   @Test
flush()1361   public void flush() throws RocksDBException {
1362     try (final Options options = new Options().setCreateIfMissing(true)) {
1363       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1364       try (final RocksDB db = RocksDB.open(options, dbPath);
1365         final FlushOptions flushOptions = new FlushOptions()) {
1366         db.flush(flushOptions);
1367       }
1368     }
1369   }
1370 
1371   @Test
flushWal()1372   public void flushWal() throws RocksDBException {
1373     try (final Options options = new Options().setCreateIfMissing(true)) {
1374       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1375       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1376         db.flushWal(true);
1377       }
1378     }
1379   }
1380 
1381   @Test
syncWal()1382   public void syncWal() throws RocksDBException {
1383     try (final Options options = new Options().setCreateIfMissing(true)) {
1384       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1385       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1386         db.syncWal();
1387       }
1388     }
1389   }
1390 
1391   @Test
setPreserveDeletesSequenceNumber()1392   public void setPreserveDeletesSequenceNumber() throws RocksDBException {
1393     try (final Options options = new Options().setCreateIfMissing(true)) {
1394       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1395       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1396         assertThat(db.setPreserveDeletesSequenceNumber(db.getLatestSequenceNumber()))
1397             .isFalse();
1398       }
1399     }
1400   }
1401 
1402   @Test
getLiveFiles()1403   public void getLiveFiles() throws RocksDBException {
1404     try (final Options options = new Options().setCreateIfMissing(true)) {
1405       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1406       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1407         final RocksDB.LiveFiles livefiles = db.getLiveFiles(true);
1408         assertThat(livefiles).isNotNull();
1409         assertThat(livefiles.manifestFileSize).isEqualTo(13);
1410         assertThat(livefiles.files.size()).isEqualTo(3);
1411         assertThat(livefiles.files.get(0)).isEqualTo("/CURRENT");
1412         assertThat(livefiles.files.get(1)).isEqualTo("/MANIFEST-000001");
1413         assertThat(livefiles.files.get(2)).isEqualTo("/OPTIONS-000005");
1414       }
1415     }
1416   }
1417 
1418   @Test
getSortedWalFiles()1419   public void getSortedWalFiles() throws RocksDBException {
1420     try (final Options options = new Options().setCreateIfMissing(true)) {
1421       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1422       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1423         db.put("key1".getBytes(UTF_8), "value1".getBytes(UTF_8));
1424         final List<LogFile> logFiles = db.getSortedWalFiles();
1425         assertThat(logFiles).isNotNull();
1426         assertThat(logFiles.size()).isEqualTo(1);
1427         assertThat(logFiles.get(0).type())
1428             .isEqualTo(WalFileType.kAliveLogFile);
1429       }
1430     }
1431   }
1432 
1433   @Test
deleteFile()1434   public void deleteFile() throws RocksDBException {
1435     try (final Options options = new Options().setCreateIfMissing(true)) {
1436       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1437       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1438         db.deleteFile("unknown");
1439       }
1440     }
1441   }
1442 
1443   @Test
getLiveFilesMetaData()1444   public void getLiveFilesMetaData() throws RocksDBException {
1445     try (final Options options = new Options().setCreateIfMissing(true)) {
1446       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1447       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1448         db.put("key1".getBytes(UTF_8), "value1".getBytes(UTF_8));
1449         final List<LiveFileMetaData> liveFilesMetaData
1450             = db.getLiveFilesMetaData();
1451         assertThat(liveFilesMetaData).isEmpty();
1452       }
1453     }
1454   }
1455 
1456   @Test
getColumnFamilyMetaData()1457   public void getColumnFamilyMetaData() throws RocksDBException {
1458     try (final DBOptions options = new DBOptions()
1459         .setCreateIfMissing(true)) {
1460       final List<ColumnFamilyDescriptor> cfDescs = Arrays.asList(
1461           new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)
1462       );
1463       final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
1464       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1465       try (final RocksDB db = RocksDB.open(options, dbPath, cfDescs, cfHandles)) {
1466         db.put(cfHandles.get(0), "key1".getBytes(UTF_8), "value1".getBytes(UTF_8));
1467         try {
1468           final ColumnFamilyMetaData cfMetadata =
1469               db.getColumnFamilyMetaData(cfHandles.get(0));
1470           assertThat(cfMetadata).isNotNull();
1471           assertThat(cfMetadata.name()).isEqualTo(RocksDB.DEFAULT_COLUMN_FAMILY);
1472           assertThat(cfMetadata.levels().size()).isEqualTo(7);
1473         } finally {
1474           for (final ColumnFamilyHandle cfHandle : cfHandles) {
1475             cfHandle.close();
1476           }
1477         }
1478       }
1479     }
1480   }
1481 
1482   @Test
verifyChecksum()1483   public void verifyChecksum() throws RocksDBException {
1484     try (final Options options = new Options().setCreateIfMissing(true)) {
1485       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1486       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1487         db.verifyChecksum();
1488       }
1489     }
1490   }
1491 
1492   @Test
getPropertiesOfAllTables()1493   public void getPropertiesOfAllTables() throws RocksDBException {
1494     try (final DBOptions options = new DBOptions()
1495         .setCreateIfMissing(true)) {
1496       final List<ColumnFamilyDescriptor> cfDescs = Arrays.asList(
1497           new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)
1498       );
1499       final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
1500       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1501       try (final RocksDB db = RocksDB.open(options, dbPath, cfDescs, cfHandles)) {
1502         db.put(cfHandles.get(0), "key1".getBytes(UTF_8), "value1".getBytes(UTF_8));
1503         try {
1504           final Map<String, TableProperties> properties =
1505               db.getPropertiesOfAllTables(cfHandles.get(0));
1506           assertThat(properties).isNotNull();
1507         } finally {
1508           for (final ColumnFamilyHandle cfHandle : cfHandles) {
1509             cfHandle.close();
1510           }
1511         }
1512       }
1513     }
1514   }
1515 
1516   @Test
getPropertiesOfTablesInRange()1517   public void getPropertiesOfTablesInRange() throws RocksDBException {
1518     try (final DBOptions options = new DBOptions()
1519         .setCreateIfMissing(true)) {
1520       final List<ColumnFamilyDescriptor> cfDescs = Arrays.asList(
1521           new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)
1522       );
1523       final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
1524       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1525       try (final RocksDB db = RocksDB.open(options, dbPath, cfDescs, cfHandles)) {
1526         db.put(cfHandles.get(0), "key1".getBytes(UTF_8), "value1".getBytes(UTF_8));
1527         db.put(cfHandles.get(0), "key2".getBytes(UTF_8), "value2".getBytes(UTF_8));
1528         db.put(cfHandles.get(0), "key3".getBytes(UTF_8), "value3".getBytes(UTF_8));
1529         try {
1530           final Range range = new Range(
1531               new Slice("key1".getBytes(UTF_8)),
1532               new Slice("key3".getBytes(UTF_8)));
1533           final Map<String, TableProperties> properties =
1534               db.getPropertiesOfTablesInRange(
1535                   cfHandles.get(0), Arrays.asList(range));
1536           assertThat(properties).isNotNull();
1537         } finally {
1538           for (final ColumnFamilyHandle cfHandle : cfHandles) {
1539             cfHandle.close();
1540           }
1541         }
1542       }
1543     }
1544   }
1545 
1546   @Test
suggestCompactRange()1547   public void suggestCompactRange() throws RocksDBException {
1548     try (final DBOptions options = new DBOptions()
1549         .setCreateIfMissing(true)) {
1550       final List<ColumnFamilyDescriptor> cfDescs = Arrays.asList(
1551           new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)
1552       );
1553       final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
1554       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1555       try (final RocksDB db = RocksDB.open(options, dbPath, cfDescs, cfHandles)) {
1556         db.put(cfHandles.get(0), "key1".getBytes(UTF_8), "value1".getBytes(UTF_8));
1557         db.put(cfHandles.get(0), "key2".getBytes(UTF_8), "value2".getBytes(UTF_8));
1558         db.put(cfHandles.get(0), "key3".getBytes(UTF_8), "value3".getBytes(UTF_8));
1559         try {
1560           final Range range =  db.suggestCompactRange(cfHandles.get(0));
1561           assertThat(range).isNotNull();
1562         } finally {
1563           for (final ColumnFamilyHandle cfHandle : cfHandles) {
1564             cfHandle.close();
1565           }
1566         }
1567       }
1568     }
1569   }
1570 
1571   @Test
promoteL0()1572   public void promoteL0() throws RocksDBException {
1573     try (final Options options = new Options().setCreateIfMissing(true)) {
1574       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1575       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1576         db.promoteL0(2);
1577       }
1578     }
1579   }
1580 
1581   @Test
startTrace()1582   public void startTrace() throws RocksDBException {
1583     try (final Options options = new Options().setCreateIfMissing(true)) {
1584       final String dbPath = dbFolder.getRoot().getAbsolutePath();
1585       try (final RocksDB db = RocksDB.open(options, dbPath)) {
1586         final TraceOptions traceOptions = new TraceOptions();
1587 
1588         try (final InMemoryTraceWriter traceWriter = new InMemoryTraceWriter()) {
1589           db.startTrace(traceOptions, traceWriter);
1590 
1591           db.put("key1".getBytes(UTF_8), "value1".getBytes(UTF_8));
1592 
1593           db.endTrace();
1594 
1595           final List<byte[]> writes = traceWriter.getWrites();
1596           assertThat(writes.size()).isGreaterThan(0);
1597         }
1598       }
1599     }
1600   }
1601 
1602   @Test
setDBOptions()1603   public void setDBOptions() throws RocksDBException {
1604     try (final DBOptions options = new DBOptions()
1605         .setCreateIfMissing(true)
1606         .setCreateMissingColumnFamilies(true);
1607          final ColumnFamilyOptions new_cf_opts = new ColumnFamilyOptions()
1608              .setWriteBufferSize(4096)) {
1609 
1610       final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
1611           Arrays.asList(
1612               new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
1613               new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts));
1614 
1615       // open database
1616       final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
1617       try (final RocksDB db = RocksDB.open(options,
1618           dbFolder.getRoot().getAbsolutePath(), columnFamilyDescriptors, columnFamilyHandles)) {
1619         try {
1620           final MutableDBOptions mutableOptions =
1621               MutableDBOptions.builder()
1622                   .setBytesPerSync(1024 * 1027 * 7)
1623                   .setAvoidFlushDuringShutdown(false)
1624                   .build();
1625 
1626           db.setDBOptions(mutableOptions);
1627         } finally {
1628           for (final ColumnFamilyHandle handle : columnFamilyHandles) {
1629             handle.close();
1630           }
1631         }
1632       }
1633     }
1634   }
1635 
1636   private static class InMemoryTraceWriter extends AbstractTraceWriter {
1637     private final List<byte[]> writes = new ArrayList<>();
1638     private volatile boolean closed = false;
1639 
1640     @Override
write(final Slice slice)1641     public void write(final Slice slice) {
1642       if (closed) {
1643         return;
1644       }
1645       final byte[] data = slice.data();
1646       final byte[] dataCopy = new byte[data.length];
1647       System.arraycopy(data, 0, dataCopy, 0, data.length);
1648       writes.add(dataCopy);
1649     }
1650 
1651     @Override
closeWriter()1652     public void closeWriter() {
1653       closed = true;
1654     }
1655 
1656     @Override
getFileSize()1657     public long getFileSize() {
1658       long size = 0;
1659       for (int i = 0; i < writes.size(); i++) {
1660         size += writes.get(i).length;
1661       }
1662       return size;
1663     }
1664 
getWrites()1665     public List<byte[]> getWrites() {
1666       return writes;
1667     }
1668   }
1669 }
1670