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 6 package org.rocksdb; 7 8 import java.nio.ByteBuffer; 9 import java.util.Arrays; 10 import java.util.List; 11 import java.util.ArrayList; 12 13 import org.junit.ClassRule; 14 import org.junit.Rule; 15 import org.junit.Test; 16 import org.junit.rules.TemporaryFolder; 17 18 import static org.assertj.core.api.Assertions.assertThat; 19 20 public class MergeTest { 21 22 @ClassRule 23 public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE = 24 new RocksNativeLibraryResource(); 25 26 @Rule 27 public TemporaryFolder dbFolder = new TemporaryFolder(); 28 29 @Test stringOption()30 public void stringOption() 31 throws InterruptedException, RocksDBException { 32 try (final Options opt = new Options() 33 .setCreateIfMissing(true) 34 .setMergeOperatorName("stringappend"); 35 final RocksDB db = RocksDB.open(opt, 36 dbFolder.getRoot().getAbsolutePath())) { 37 // writing aa under key 38 db.put("key".getBytes(), "aa".getBytes()); 39 // merge bb under key 40 db.merge("key".getBytes(), "bb".getBytes()); 41 42 final byte[] value = db.get("key".getBytes()); 43 final String strValue = new String(value); 44 assertThat(strValue).isEqualTo("aa,bb"); 45 } 46 } 47 longToByteArray(long l)48 private byte[] longToByteArray(long l) { 49 ByteBuffer buf = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); 50 buf.putLong(l); 51 return buf.array(); 52 } 53 longFromByteArray(byte[] a)54 private long longFromByteArray(byte[] a) { 55 ByteBuffer buf = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); 56 buf.put(a); 57 buf.flip(); 58 return buf.getLong(); 59 } 60 61 @Test uint64AddOption()62 public void uint64AddOption() 63 throws InterruptedException, RocksDBException { 64 try (final Options opt = new Options() 65 .setCreateIfMissing(true) 66 .setMergeOperatorName("uint64add"); 67 final RocksDB db = RocksDB.open(opt, 68 dbFolder.getRoot().getAbsolutePath())) { 69 // writing (long)100 under key 70 db.put("key".getBytes(), longToByteArray(100)); 71 // merge (long)1 under key 72 db.merge("key".getBytes(), longToByteArray(1)); 73 74 final byte[] value = db.get("key".getBytes()); 75 final long longValue = longFromByteArray(value); 76 assertThat(longValue).isEqualTo(101); 77 } 78 } 79 80 @Test cFStringOption()81 public void cFStringOption() 82 throws InterruptedException, RocksDBException { 83 84 try (final ColumnFamilyOptions cfOpt1 = new ColumnFamilyOptions() 85 .setMergeOperatorName("stringappend"); 86 final ColumnFamilyOptions cfOpt2 = new ColumnFamilyOptions() 87 .setMergeOperatorName("stringappend") 88 ) { 89 final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList( 90 new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt1), 91 new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt2) 92 ); 93 94 final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>(); 95 try (final DBOptions opt = new DBOptions() 96 .setCreateIfMissing(true) 97 .setCreateMissingColumnFamilies(true); 98 final RocksDB db = RocksDB.open(opt, 99 dbFolder.getRoot().getAbsolutePath(), cfDescriptors, 100 columnFamilyHandleList)) { 101 try { 102 // writing aa under key 103 db.put(columnFamilyHandleList.get(1), 104 "cfkey".getBytes(), "aa".getBytes()); 105 // merge bb under key 106 db.merge(columnFamilyHandleList.get(1), 107 "cfkey".getBytes(), "bb".getBytes()); 108 109 byte[] value = db.get(columnFamilyHandleList.get(1), 110 "cfkey".getBytes()); 111 String strValue = new String(value); 112 assertThat(strValue).isEqualTo("aa,bb"); 113 } finally { 114 for (final ColumnFamilyHandle handle : columnFamilyHandleList) { 115 handle.close(); 116 } 117 } 118 } 119 } 120 } 121 122 @Test cFUInt64AddOption()123 public void cFUInt64AddOption() 124 throws InterruptedException, RocksDBException { 125 126 try (final ColumnFamilyOptions cfOpt1 = new ColumnFamilyOptions() 127 .setMergeOperatorName("uint64add"); 128 final ColumnFamilyOptions cfOpt2 = new ColumnFamilyOptions() 129 .setMergeOperatorName("uint64add") 130 ) { 131 final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList( 132 new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt1), 133 new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt2) 134 ); 135 136 final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>(); 137 try (final DBOptions opt = new DBOptions() 138 .setCreateIfMissing(true) 139 .setCreateMissingColumnFamilies(true); 140 final RocksDB db = RocksDB.open(opt, 141 dbFolder.getRoot().getAbsolutePath(), cfDescriptors, 142 columnFamilyHandleList)) { 143 try { 144 // writing (long)100 under key 145 db.put(columnFamilyHandleList.get(1), 146 "cfkey".getBytes(), longToByteArray(100)); 147 // merge (long)1 under key 148 db.merge(columnFamilyHandleList.get(1), 149 "cfkey".getBytes(), longToByteArray(1)); 150 151 byte[] value = db.get(columnFamilyHandleList.get(1), 152 "cfkey".getBytes()); 153 long longValue = longFromByteArray(value); 154 assertThat(longValue).isEqualTo(101); 155 } finally { 156 for (final ColumnFamilyHandle handle : columnFamilyHandleList) { 157 handle.close(); 158 } 159 } 160 } 161 } 162 } 163 164 @Test operatorOption()165 public void operatorOption() 166 throws InterruptedException, RocksDBException { 167 try (final StringAppendOperator stringAppendOperator = new StringAppendOperator(); 168 final Options opt = new Options() 169 .setCreateIfMissing(true) 170 .setMergeOperator(stringAppendOperator); 171 final RocksDB db = RocksDB.open(opt, 172 dbFolder.getRoot().getAbsolutePath())) { 173 // Writing aa under key 174 db.put("key".getBytes(), "aa".getBytes()); 175 176 // Writing bb under key 177 db.merge("key".getBytes(), "bb".getBytes()); 178 179 final byte[] value = db.get("key".getBytes()); 180 final String strValue = new String(value); 181 182 assertThat(strValue).isEqualTo("aa,bb"); 183 } 184 } 185 186 @Test uint64AddOperatorOption()187 public void uint64AddOperatorOption() 188 throws InterruptedException, RocksDBException { 189 try (final UInt64AddOperator uint64AddOperator = new UInt64AddOperator(); 190 final Options opt = new Options() 191 .setCreateIfMissing(true) 192 .setMergeOperator(uint64AddOperator); 193 final RocksDB db = RocksDB.open(opt, 194 dbFolder.getRoot().getAbsolutePath())) { 195 // Writing (long)100 under key 196 db.put("key".getBytes(), longToByteArray(100)); 197 198 // Writing (long)1 under key 199 db.merge("key".getBytes(), longToByteArray(1)); 200 201 final byte[] value = db.get("key".getBytes()); 202 final long longValue = longFromByteArray(value); 203 204 assertThat(longValue).isEqualTo(101); 205 } 206 } 207 208 @Test cFOperatorOption()209 public void cFOperatorOption() 210 throws InterruptedException, RocksDBException { 211 try (final StringAppendOperator stringAppendOperator = new StringAppendOperator(); 212 final ColumnFamilyOptions cfOpt1 = new ColumnFamilyOptions() 213 .setMergeOperator(stringAppendOperator); 214 final ColumnFamilyOptions cfOpt2 = new ColumnFamilyOptions() 215 .setMergeOperator(stringAppendOperator) 216 ) { 217 final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList( 218 new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt1), 219 new ColumnFamilyDescriptor("new_cf".getBytes(), cfOpt2) 220 ); 221 final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>(); 222 try (final DBOptions opt = new DBOptions() 223 .setCreateIfMissing(true) 224 .setCreateMissingColumnFamilies(true); 225 final RocksDB db = RocksDB.open(opt, 226 dbFolder.getRoot().getAbsolutePath(), cfDescriptors, 227 columnFamilyHandleList) 228 ) { 229 try { 230 // writing aa under key 231 db.put(columnFamilyHandleList.get(1), 232 "cfkey".getBytes(), "aa".getBytes()); 233 // merge bb under key 234 db.merge(columnFamilyHandleList.get(1), 235 "cfkey".getBytes(), "bb".getBytes()); 236 byte[] value = db.get(columnFamilyHandleList.get(1), 237 "cfkey".getBytes()); 238 String strValue = new String(value); 239 240 // Test also with createColumnFamily 241 try (final ColumnFamilyOptions cfHandleOpts = 242 new ColumnFamilyOptions() 243 .setMergeOperator(stringAppendOperator); 244 final ColumnFamilyHandle cfHandle = 245 db.createColumnFamily( 246 new ColumnFamilyDescriptor("new_cf2".getBytes(), 247 cfHandleOpts)) 248 ) { 249 // writing xx under cfkey2 250 db.put(cfHandle, "cfkey2".getBytes(), "xx".getBytes()); 251 // merge yy under cfkey2 252 db.merge(cfHandle, new WriteOptions(), "cfkey2".getBytes(), 253 "yy".getBytes()); 254 value = db.get(cfHandle, "cfkey2".getBytes()); 255 String strValueTmpCf = new String(value); 256 257 assertThat(strValue).isEqualTo("aa,bb"); 258 assertThat(strValueTmpCf).isEqualTo("xx,yy"); 259 } 260 } finally { 261 for (final ColumnFamilyHandle columnFamilyHandle : 262 columnFamilyHandleList) { 263 columnFamilyHandle.close(); 264 } 265 } 266 } 267 } 268 } 269 270 @Test cFUInt64AddOperatorOption()271 public void cFUInt64AddOperatorOption() 272 throws InterruptedException, RocksDBException { 273 try (final UInt64AddOperator uint64AddOperator = new UInt64AddOperator(); 274 final ColumnFamilyOptions cfOpt1 = new ColumnFamilyOptions() 275 .setMergeOperator(uint64AddOperator); 276 final ColumnFamilyOptions cfOpt2 = new ColumnFamilyOptions() 277 .setMergeOperator(uint64AddOperator) 278 ) { 279 final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList( 280 new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt1), 281 new ColumnFamilyDescriptor("new_cf".getBytes(), cfOpt2) 282 ); 283 final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>(); 284 try (final DBOptions opt = new DBOptions() 285 .setCreateIfMissing(true) 286 .setCreateMissingColumnFamilies(true); 287 final RocksDB db = RocksDB.open(opt, 288 dbFolder.getRoot().getAbsolutePath(), cfDescriptors, 289 columnFamilyHandleList) 290 ) { 291 try { 292 // writing (long)100 under key 293 db.put(columnFamilyHandleList.get(1), 294 "cfkey".getBytes(), longToByteArray(100)); 295 // merge (long)1 under key 296 db.merge(columnFamilyHandleList.get(1), 297 "cfkey".getBytes(), longToByteArray(1)); 298 byte[] value = db.get(columnFamilyHandleList.get(1), 299 "cfkey".getBytes()); 300 long longValue = longFromByteArray(value); 301 302 // Test also with createColumnFamily 303 try (final ColumnFamilyOptions cfHandleOpts = 304 new ColumnFamilyOptions() 305 .setMergeOperator(uint64AddOperator); 306 final ColumnFamilyHandle cfHandle = 307 db.createColumnFamily( 308 new ColumnFamilyDescriptor("new_cf2".getBytes(), 309 cfHandleOpts)) 310 ) { 311 // writing (long)200 under cfkey2 312 db.put(cfHandle, "cfkey2".getBytes(), longToByteArray(200)); 313 // merge (long)50 under cfkey2 314 db.merge(cfHandle, new WriteOptions(), "cfkey2".getBytes(), 315 longToByteArray(50)); 316 value = db.get(cfHandle, "cfkey2".getBytes()); 317 long longValueTmpCf = longFromByteArray(value); 318 319 assertThat(longValue).isEqualTo(101); 320 assertThat(longValueTmpCf).isEqualTo(250); 321 } 322 } finally { 323 for (final ColumnFamilyHandle columnFamilyHandle : 324 columnFamilyHandleList) { 325 columnFamilyHandle.close(); 326 } 327 } 328 } 329 } 330 } 331 332 @Test operatorGcBehaviour()333 public void operatorGcBehaviour() 334 throws RocksDBException { 335 try (final StringAppendOperator stringAppendOperator = new StringAppendOperator()) { 336 try (final Options opt = new Options() 337 .setCreateIfMissing(true) 338 .setMergeOperator(stringAppendOperator); 339 final RocksDB db = RocksDB.open(opt, 340 dbFolder.getRoot().getAbsolutePath())) { 341 //no-op 342 } 343 344 // test reuse 345 try (final Options opt = new Options() 346 .setMergeOperator(stringAppendOperator); 347 final RocksDB db = RocksDB.open(opt, 348 dbFolder.getRoot().getAbsolutePath())) { 349 //no-op 350 } 351 352 // test param init 353 try (final StringAppendOperator stringAppendOperator2 = new StringAppendOperator(); 354 final Options opt = new Options() 355 .setMergeOperator(stringAppendOperator2); 356 final RocksDB db = RocksDB.open(opt, 357 dbFolder.getRoot().getAbsolutePath())) { 358 //no-op 359 } 360 361 // test replace one with another merge operator instance 362 try (final Options opt = new Options() 363 .setMergeOperator(stringAppendOperator); 364 final StringAppendOperator newStringAppendOperator = new StringAppendOperator()) { 365 opt.setMergeOperator(newStringAppendOperator); 366 try (final RocksDB db = RocksDB.open(opt, 367 dbFolder.getRoot().getAbsolutePath())) { 368 //no-op 369 } 370 } 371 } 372 } 373 374 @Test uint64AddOperatorGcBehaviour()375 public void uint64AddOperatorGcBehaviour() 376 throws RocksDBException { 377 try (final UInt64AddOperator uint64AddOperator = new UInt64AddOperator()) { 378 try (final Options opt = new Options() 379 .setCreateIfMissing(true) 380 .setMergeOperator(uint64AddOperator); 381 final RocksDB db = RocksDB.open(opt, 382 dbFolder.getRoot().getAbsolutePath())) { 383 //no-op 384 } 385 386 // test reuse 387 try (final Options opt = new Options() 388 .setMergeOperator(uint64AddOperator); 389 final RocksDB db = RocksDB.open(opt, 390 dbFolder.getRoot().getAbsolutePath())) { 391 //no-op 392 } 393 394 // test param init 395 try (final UInt64AddOperator uint64AddOperator2 = new UInt64AddOperator(); 396 final Options opt = new Options() 397 .setMergeOperator(uint64AddOperator2); 398 final RocksDB db = RocksDB.open(opt, 399 dbFolder.getRoot().getAbsolutePath())) { 400 //no-op 401 } 402 403 // test replace one with another merge operator instance 404 try (final Options opt = new Options() 405 .setMergeOperator(uint64AddOperator); 406 final UInt64AddOperator newUInt64AddOperator = new UInt64AddOperator()) { 407 opt.setMergeOperator(newUInt64AddOperator); 408 try (final RocksDB db = RocksDB.open(opt, 409 dbFolder.getRoot().getAbsolutePath())) { 410 //no-op 411 } 412 } 413 } 414 } 415 416 @Test emptyStringInSetMergeOperatorByName()417 public void emptyStringInSetMergeOperatorByName() { 418 try (final Options opt = new Options() 419 .setMergeOperatorName(""); 420 final ColumnFamilyOptions cOpt = new ColumnFamilyOptions() 421 .setMergeOperatorName("")) { 422 //no-op 423 } 424 } 425 426 @Test(expected = IllegalArgumentException.class) nullStringInSetMergeOperatorByNameOptions()427 public void nullStringInSetMergeOperatorByNameOptions() { 428 try (final Options opt = new Options()) { 429 opt.setMergeOperatorName(null); 430 } 431 } 432 433 @Test(expected = IllegalArgumentException.class) 434 public void nullStringInSetMergeOperatorByNameColumnFamilyOptions()435 nullStringInSetMergeOperatorByNameColumnFamilyOptions() { 436 try (final ColumnFamilyOptions opt = new ColumnFamilyOptions()) { 437 opt.setMergeOperatorName(null); 438 } 439 } 440 } 441