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).
6 package org
.rocksdb
.util
;
8 import org
.junit
.BeforeClass
;
9 import org
.junit
.ClassRule
;
10 import org
.junit
.Rule
;
11 import org
.junit
.Test
;
12 import org
.junit
.rules
.TemporaryFolder
;
13 import org
.junit
.runner
.RunWith
;
14 import org
.junit
.runners
.Parameterized
;
15 import org
.junit
.runners
.Parameterized
.Parameter
;
16 import org
.junit
.runners
.Parameterized
.Parameters
;
19 import java
.nio
.ByteBuffer
;
20 import java
.nio
.file
.*;
21 import java
.util
.ArrayList
;
22 import java
.util
.Arrays
;
23 import java
.util
.List
;
24 import java
.util
.Random
;
26 import static java
.nio
.charset
.StandardCharsets
.UTF_8
;
27 import static org
.assertj
.core
.api
.Assertions
.assertThat
;
30 * Tests for IntComparator, but more generally
31 * also for rocksdb::ComparatorJniCallback implementation.
33 @RunWith(Parameterized
.class)
34 public class IntComparatorTest
{
36 // test with 500 random integer keys
37 private static final int TOTAL_KEYS
= 500;
38 private static final byte[][] keys
= new byte[TOTAL_KEYS
][4];
41 public static void prepareKeys() {
42 final ByteBuffer buf
= ByteBuffer
.allocate(4);
43 final Random random
= new Random();
44 for (int i
= 0; i
< TOTAL_KEYS
; i
++) {
45 final int ri
= random
.nextInt();
48 final byte[] key
= buf
.array();
50 // does key already exist (avoid duplicates)
51 if (keyExists(key
, i
)) {
52 i
--; // loop round and generate a different key
54 System
.arraycopy(key
, 0, keys
[i
], 0, 4);
59 private static boolean keyExists(final byte[] key
, final int limit
) {
60 for (int j
= 0; j
< limit
; j
++) {
61 if (Arrays
.equals(key
, keys
[j
])) {
68 @Parameters(name
= "{0}")
69 public static Iterable
<Object
[]> parameters() {
70 return Arrays
.asList(new Object
[][] {
71 { "non-direct_reused64_mutex", false, 64, ReusedSynchronisationType
.MUTEX
},
72 { "direct_reused64_mutex", true, 64, ReusedSynchronisationType
.MUTEX
},
73 { "non-direct_reused64_adaptive-mutex", false, 64, ReusedSynchronisationType
.ADAPTIVE_MUTEX
},
74 { "direct_reused64_adaptive-mutex", true, 64, ReusedSynchronisationType
.ADAPTIVE_MUTEX
},
75 { "non-direct_reused64_thread-local", false, 64, ReusedSynchronisationType
.THREAD_LOCAL
},
76 { "direct_reused64_thread-local", true, 64, ReusedSynchronisationType
.THREAD_LOCAL
},
77 { "non-direct_noreuse", false, -1, null },
78 { "direct_noreuse", true, -1, null }
86 public boolean useDirectBuffer
;
89 public int maxReusedBufferSize
;
92 public ReusedSynchronisationType reusedSynchronisationType
;
95 public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE
=
96 new RocksNativeLibraryResource();
99 public TemporaryFolder dbFolder
= new TemporaryFolder();
103 public void javaComparatorDefaultCf() throws RocksDBException
{
104 try (final ComparatorOptions options
= new ComparatorOptions()
105 .setUseDirectBuffer(useDirectBuffer
)
106 .setMaxReusedBufferSize(maxReusedBufferSize
)
107 // if reusedSynchronisationType == null we assume that maxReusedBufferSize <= 0 and so we just set ADAPTIVE_MUTEX, even though it won't be used
108 .setReusedSynchronisationType(reusedSynchronisationType
== null ? ReusedSynchronisationType
.ADAPTIVE_MUTEX
: reusedSynchronisationType
);
109 final IntComparator comparator
= new IntComparator(options
)) {
111 // test the round-tripability of keys written and read with the Comparator
112 testRoundtrip(FileSystems
.getDefault().getPath(
113 dbFolder
.getRoot().getAbsolutePath()), comparator
);
118 public void javaComparatorNamedCf() throws RocksDBException
{
119 try (final ComparatorOptions options
= new ComparatorOptions()
120 .setUseDirectBuffer(useDirectBuffer
)
121 .setMaxReusedBufferSize(maxReusedBufferSize
)
122 // if reusedSynchronisationType == null we assume that maxReusedBufferSize <= 0 and so we just set ADAPTIVE_MUTEX, even though it won't be used
123 .setReusedSynchronisationType(reusedSynchronisationType
== null ? ReusedSynchronisationType
.ADAPTIVE_MUTEX
: reusedSynchronisationType
);
124 final IntComparator comparator
= new IntComparator(options
)) {
126 // test the round-tripability of keys written and read with the Comparator
127 testRoundtripCf(FileSystems
.getDefault().getPath(
128 dbFolder
.getRoot().getAbsolutePath()), comparator
);
133 * Test which stores random keys into the database
134 * using an {@link IntComparator}
135 * it then checks that these keys are read back in
138 * @param db_path A path where we can store database
141 * @param comparator the comparator
143 * @throws RocksDBException if a database error happens.
145 private void testRoundtrip(final Path db_path
,
146 final AbstractComparator comparator
) throws RocksDBException
{
147 try (final Options opt
= new Options()
148 .setCreateIfMissing(true)
149 .setComparator(comparator
)) {
151 // store TOTAL_KEYS into the db
152 try (final RocksDB db
= RocksDB
.open(opt
, db_path
.toString())) {
153 for (int i
= 0; i
< TOTAL_KEYS
; i
++) {
154 db
.put(keys
[i
], "value".getBytes(UTF_8
));
158 // re-open db and read from start to end
159 // integer keys should be in ascending
160 // order as defined by IntComparator
161 final ByteBuffer key
= ByteBuffer
.allocate(4);
162 try (final RocksDB db
= RocksDB
.open(opt
, db_path
.toString());
163 final RocksIterator it
= db
.newIterator()) {
165 int lastKey
= Integer
.MIN_VALUE
;
167 for (it
.seekToFirst(); it
.isValid(); it
.next()) {
170 final int thisKey
= key
.getInt();
172 assertThat(thisKey
).isGreaterThan(lastKey
);
176 assertThat(count
).isEqualTo(TOTAL_KEYS
);
182 * Test which stores random keys into a column family
184 * using an {@link IntComparator}
185 * it then checks that these keys are read back in
188 * @param db_path A path where we can store database
191 * @param comparator the comparator
193 * @throws RocksDBException if a database error happens.
195 private void testRoundtripCf(final Path db_path
,
196 final AbstractComparator comparator
) throws RocksDBException
{
198 final List
<ColumnFamilyDescriptor
> cfDescriptors
= Arrays
.asList(
199 new ColumnFamilyDescriptor(RocksDB
.DEFAULT_COLUMN_FAMILY
),
200 new ColumnFamilyDescriptor("new_cf".getBytes(),
201 new ColumnFamilyOptions()
202 .setComparator(comparator
))
205 final List
<ColumnFamilyHandle
> cfHandles
= new ArrayList
<>();
207 try (final DBOptions opt
= new DBOptions()
208 .setCreateIfMissing(true)
209 .setCreateMissingColumnFamilies(true)) {
211 try (final RocksDB db
= RocksDB
.open(opt
, db_path
.toString(),
212 cfDescriptors
, cfHandles
)) {
214 assertThat(cfDescriptors
.size()).isEqualTo(2);
215 assertThat(cfHandles
.size()).isEqualTo(2);
217 for (int i
= 0; i
< TOTAL_KEYS
; i
++) {
218 db
.put(cfHandles
.get(1), keys
[i
], "value".getBytes(UTF_8
));
221 for (final ColumnFamilyHandle cfHandle
: cfHandles
) {
228 // re-open db and read from start to end
229 // integer keys should be in ascending
230 // order as defined by SimpleIntComparator
231 final ByteBuffer key
= ByteBuffer
.allocate(4);
232 try (final RocksDB db
= RocksDB
.open(opt
, db_path
.toString(),
233 cfDescriptors
, cfHandles
);
234 final RocksIterator it
= db
.newIterator(cfHandles
.get(1))) {
236 assertThat(cfDescriptors
.size()).isEqualTo(2);
237 assertThat(cfHandles
.size()).isEqualTo(2);
240 int lastKey
= Integer
.MIN_VALUE
;
242 for (it
.seekToFirst(); it
.isValid(); it
.next()) {
245 final int thisKey
= key
.getInt();
247 assertThat(thisKey
).isGreaterThan(lastKey
);
252 assertThat(count
).isEqualTo(TOTAL_KEYS
);
255 for (final ColumnFamilyHandle cfHandle
: cfHandles
) {
259 for (final ColumnFamilyDescriptor cfDescriptor
: cfDescriptors
) {
260 cfDescriptor
.getOptions().close();