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 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
11 import static java
.nio
.charset
.StandardCharsets
.UTF_8
;
12 import static org
.assertj
.core
.api
.Assertions
.assertThat
;
13 import static org
.rocksdb
.util
.CapturingWriteBatchHandler
.Action
.DELETE
;
14 import static org
.rocksdb
.util
.CapturingWriteBatchHandler
.Action
.DELETE_RANGE
;
15 import static org
.rocksdb
.util
.CapturingWriteBatchHandler
.Action
.LOG
;
16 import static org
.rocksdb
.util
.CapturingWriteBatchHandler
.Action
.MERGE
;
17 import static org
.rocksdb
.util
.CapturingWriteBatchHandler
.Action
.PUT
;
18 import static org
.rocksdb
.util
.CapturingWriteBatchHandler
.Action
.SINGLE_DELETE
;
20 import java
.io
.UnsupportedEncodingException
;
21 import java
.nio
.ByteBuffer
;
22 import org
.junit
.ClassRule
;
23 import org
.junit
.Rule
;
24 import org
.junit
.Test
;
25 import org
.junit
.rules
.TemporaryFolder
;
26 import org
.rocksdb
.util
.CapturingWriteBatchHandler
;
27 import org
.rocksdb
.util
.CapturingWriteBatchHandler
.Event
;
28 import org
.rocksdb
.util
.WriteBatchGetter
;
31 * This class mimics the db/write_batch_test.cc
32 * in the c++ rocksdb library.
34 public class WriteBatchTest
{
36 public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE
=
37 new RocksNativeLibraryResource();
40 public TemporaryFolder dbFolder
= new TemporaryFolder();
43 public void emptyWriteBatch() {
44 try (final WriteBatch batch
= new WriteBatch()) {
45 assertThat(batch
.count()).isEqualTo(0);
50 public void multipleBatchOperations()
51 throws RocksDBException
{
53 final byte[] foo
= "foo".getBytes(UTF_8
);
54 final byte[] bar
= "bar".getBytes(UTF_8
);
55 final byte[] box
= "box".getBytes(UTF_8
);
56 final byte[] baz
= "baz".getBytes(UTF_8
);
57 final byte[] boo
= "boo".getBytes(UTF_8
);
58 final byte[] hoo
= "hoo".getBytes(UTF_8
);
59 final byte[] hello
= "hello".getBytes(UTF_8
);
61 try (final WriteBatch batch
= new WriteBatch()) {
65 batch
.merge(baz
, hoo
);
66 batch
.singleDelete(foo
);
67 batch
.deleteRange(baz
, foo
);
68 batch
.putLogData(hello
);
70 try(final CapturingWriteBatchHandler handler
=
71 new CapturingWriteBatchHandler()) {
72 batch
.iterate(handler
);
74 assertThat(handler
.getEvents().size()).isEqualTo(7);
76 assertThat(handler
.getEvents().get(0)).isEqualTo(new Event(PUT
, foo
, bar
));
77 assertThat(handler
.getEvents().get(1)).isEqualTo(new Event(DELETE
, box
, null));
78 assertThat(handler
.getEvents().get(2)).isEqualTo(new Event(PUT
, baz
, boo
));
79 assertThat(handler
.getEvents().get(3)).isEqualTo(new Event(MERGE
, baz
, hoo
));
80 assertThat(handler
.getEvents().get(4)).isEqualTo(new Event(SINGLE_DELETE
, foo
, null));
81 assertThat(handler
.getEvents().get(5)).isEqualTo(new Event(DELETE_RANGE
, baz
, foo
));
82 assertThat(handler
.getEvents().get(6)).isEqualTo(new Event(LOG
, null, hello
));
88 public void multipleBatchOperationsDirect()
89 throws UnsupportedEncodingException
, RocksDBException
{
90 try (WriteBatch batch
= new WriteBatch()) {
91 ByteBuffer key
= ByteBuffer
.allocateDirect(16);
92 ByteBuffer value
= ByteBuffer
.allocateDirect(16);
93 key
.put("foo".getBytes("US-ASCII")).flip();
94 value
.put("bar".getBytes("US-ASCII")).flip();
95 batch
.put(key
, value
);
96 assertThat(key
.position()).isEqualTo(3);
97 assertThat(key
.limit()).isEqualTo(3);
98 assertThat(value
.position()).isEqualTo(3);
99 assertThat(value
.limit()).isEqualTo(3);
102 key
.put("box".getBytes("US-ASCII")).flip();
104 assertThat(key
.position()).isEqualTo(3);
105 assertThat(key
.limit()).isEqualTo(3);
107 batch
.put("baz".getBytes("US-ASCII"), "boo".getBytes("US-ASCII"));
109 WriteBatchTestInternalHelper
.setSequence(batch
, 100);
110 assertThat(WriteBatchTestInternalHelper
.sequence(batch
)).isNotNull().isEqualTo(100);
111 assertThat(batch
.count()).isEqualTo(3);
112 assertThat(new String(getContents(batch
), "US-ASCII"))
113 .isEqualTo("Put(baz, boo)@102"
115 + "Put(foo, bar)@100");
120 public void testAppendOperation()
121 throws RocksDBException
{
122 try (final WriteBatch b1
= new WriteBatch();
123 final WriteBatch b2
= new WriteBatch()) {
124 WriteBatchTestInternalHelper
.setSequence(b1
, 200);
125 WriteBatchTestInternalHelper
.setSequence(b2
, 300);
126 WriteBatchTestInternalHelper
.append(b1
, b2
);
127 assertThat(getContents(b1
).length
).isEqualTo(0);
128 assertThat(b1
.count()).isEqualTo(0);
129 b2
.put("a".getBytes(UTF_8
), "va".getBytes(UTF_8
));
130 WriteBatchTestInternalHelper
.append(b1
, b2
);
131 assertThat("Put(a, va)@200".equals(new String(getContents(b1
),
133 assertThat(b1
.count()).isEqualTo(1);
135 b2
.put("b".getBytes(UTF_8
), "vb".getBytes(UTF_8
));
136 WriteBatchTestInternalHelper
.append(b1
, b2
);
137 assertThat(("Put(a, va)@200" +
139 .equals(new String(getContents(b1
), UTF_8
)));
140 assertThat(b1
.count()).isEqualTo(2);
141 b2
.delete("foo".getBytes(UTF_8
));
142 WriteBatchTestInternalHelper
.append(b1
, b2
);
143 assertThat(("Put(a, va)@200" +
147 .equals(new String(getContents(b1
), UTF_8
)));
148 assertThat(b1
.count()).isEqualTo(4);
153 public void blobOperation()
154 throws RocksDBException
{
155 try (final WriteBatch batch
= new WriteBatch()) {
156 batch
.put("k1".getBytes(UTF_8
), "v1".getBytes(UTF_8
));
157 batch
.put("k2".getBytes(UTF_8
), "v2".getBytes(UTF_8
));
158 batch
.put("k3".getBytes(UTF_8
), "v3".getBytes(UTF_8
));
159 batch
.putLogData("blob1".getBytes(UTF_8
));
160 batch
.delete("k2".getBytes(UTF_8
));
161 batch
.putLogData("blob2".getBytes(UTF_8
));
162 batch
.merge("foo".getBytes(UTF_8
), "bar".getBytes(UTF_8
));
163 assertThat(batch
.count()).isEqualTo(5);
164 assertThat(("Merge(foo, bar)@4" +
169 .equals(new String(getContents(batch
), UTF_8
)));
174 public void savePoints()
175 throws RocksDBException
{
176 try (final WriteBatch batch
= new WriteBatch()) {
177 batch
.put("k1".getBytes(UTF_8
), "v1".getBytes(UTF_8
));
178 batch
.put("k2".getBytes(UTF_8
), "v2".getBytes(UTF_8
));
179 batch
.put("k3".getBytes(UTF_8
), "v3".getBytes(UTF_8
));
181 assertThat(getFromWriteBatch(batch
, "k1")).isEqualTo("v1");
182 assertThat(getFromWriteBatch(batch
, "k2")).isEqualTo("v2");
183 assertThat(getFromWriteBatch(batch
, "k3")).isEqualTo("v3");
185 batch
.setSavePoint();
187 batch
.delete("k2".getBytes(UTF_8
));
188 batch
.put("k3".getBytes(UTF_8
), "v3-2".getBytes(UTF_8
));
190 assertThat(getFromWriteBatch(batch
, "k2")).isNull();
191 assertThat(getFromWriteBatch(batch
, "k3")).isEqualTo("v3-2");
194 batch
.setSavePoint();
196 batch
.put("k3".getBytes(UTF_8
), "v3-3".getBytes(UTF_8
));
197 batch
.put("k4".getBytes(UTF_8
), "v4".getBytes(UTF_8
));
199 assertThat(getFromWriteBatch(batch
, "k3")).isEqualTo("v3-3");
200 assertThat(getFromWriteBatch(batch
, "k4")).isEqualTo("v4");
203 batch
.rollbackToSavePoint();
205 assertThat(getFromWriteBatch(batch
, "k2")).isNull();
206 assertThat(getFromWriteBatch(batch
, "k3")).isEqualTo("v3-2");
207 assertThat(getFromWriteBatch(batch
, "k4")).isNull();
210 batch
.rollbackToSavePoint();
212 assertThat(getFromWriteBatch(batch
, "k1")).isEqualTo("v1");
213 assertThat(getFromWriteBatch(batch
, "k2")).isEqualTo("v2");
214 assertThat(getFromWriteBatch(batch
, "k3")).isEqualTo("v3");
215 assertThat(getFromWriteBatch(batch
, "k4")).isNull();
220 public void deleteRange() throws RocksDBException
{
221 try (final RocksDB db
= RocksDB
.open(dbFolder
.getRoot().getAbsolutePath());
222 final WriteBatch batch
= new WriteBatch();
223 final WriteOptions wOpt
= new WriteOptions()) {
224 db
.put("key1".getBytes(), "value".getBytes());
225 db
.put("key2".getBytes(), "12345678".getBytes());
226 db
.put("key3".getBytes(), "abcdefg".getBytes());
227 db
.put("key4".getBytes(), "xyz".getBytes());
228 assertThat(db
.get("key1".getBytes())).isEqualTo("value".getBytes());
229 assertThat(db
.get("key2".getBytes())).isEqualTo("12345678".getBytes());
230 assertThat(db
.get("key3".getBytes())).isEqualTo("abcdefg".getBytes());
231 assertThat(db
.get("key4".getBytes())).isEqualTo("xyz".getBytes());
233 batch
.deleteRange("key2".getBytes(), "key4".getBytes());
234 db
.write(wOpt
, batch
);
236 assertThat(db
.get("key1".getBytes())).isEqualTo("value".getBytes());
237 assertThat(db
.get("key2".getBytes())).isNull();
238 assertThat(db
.get("key3".getBytes())).isNull();
239 assertThat(db
.get("key4".getBytes())).isEqualTo("xyz".getBytes());
244 public void restorePoints() throws RocksDBException
{
245 try (final WriteBatch batch
= new WriteBatch()) {
247 batch
.put("k1".getBytes(), "v1".getBytes());
248 batch
.put("k2".getBytes(), "v2".getBytes());
250 batch
.setSavePoint();
252 batch
.put("k1".getBytes(), "123456789".getBytes());
253 batch
.delete("k2".getBytes());
255 batch
.rollbackToSavePoint();
257 try(final CapturingWriteBatchHandler handler
= new CapturingWriteBatchHandler()) {
258 batch
.iterate(handler
);
260 assertThat(handler
.getEvents().size()).isEqualTo(2);
261 assertThat(handler
.getEvents().get(0)).isEqualTo(new Event(PUT
, "k1".getBytes(), "v1".getBytes()));
262 assertThat(handler
.getEvents().get(1)).isEqualTo(new Event(PUT
, "k2".getBytes(), "v2".getBytes()));
267 @Test(expected
= RocksDBException
.class)
268 public void restorePoints_withoutSavePoints() throws RocksDBException
{
269 try (final WriteBatch batch
= new WriteBatch()) {
270 batch
.rollbackToSavePoint();
274 @Test(expected
= RocksDBException
.class)
275 public void restorePoints_withoutSavePoints_nested() throws RocksDBException
{
276 try (final WriteBatch batch
= new WriteBatch()) {
278 batch
.setSavePoint();
279 batch
.rollbackToSavePoint();
281 // without previous corresponding setSavePoint
282 batch
.rollbackToSavePoint();
287 public void popSavePoint() throws RocksDBException
{
288 try (final WriteBatch batch
= new WriteBatch()) {
290 batch
.put("k1".getBytes(), "v1".getBytes());
291 batch
.put("k2".getBytes(), "v2".getBytes());
293 batch
.setSavePoint();
295 batch
.put("k1".getBytes(), "123456789".getBytes());
296 batch
.delete("k2".getBytes());
298 batch
.setSavePoint();
300 batch
.popSavePoint();
302 batch
.rollbackToSavePoint();
304 try(final CapturingWriteBatchHandler handler
= new CapturingWriteBatchHandler()) {
305 batch
.iterate(handler
);
307 assertThat(handler
.getEvents().size()).isEqualTo(2);
308 assertThat(handler
.getEvents().get(0)).isEqualTo(new Event(PUT
, "k1".getBytes(), "v1".getBytes()));
309 assertThat(handler
.getEvents().get(1)).isEqualTo(new Event(PUT
, "k2".getBytes(), "v2".getBytes()));
314 @Test(expected
= RocksDBException
.class)
315 public void popSavePoint_withoutSavePoints() throws RocksDBException
{
316 try (final WriteBatch batch
= new WriteBatch()) {
317 batch
.popSavePoint();
321 @Test(expected
= RocksDBException
.class)
322 public void popSavePoint_withoutSavePoints_nested() throws RocksDBException
{
323 try (final WriteBatch batch
= new WriteBatch()) {
325 batch
.setSavePoint();
326 batch
.popSavePoint();
328 // without previous corresponding setSavePoint
329 batch
.popSavePoint();
334 public void maxBytes() throws RocksDBException
{
335 try (final WriteBatch batch
= new WriteBatch()) {
336 batch
.setMaxBytes(19);
338 batch
.put("k1".getBytes(), "v1".getBytes());
342 @Test(expected
= RocksDBException
.class)
343 public void maxBytes_over() throws RocksDBException
{
344 try (final WriteBatch batch
= new WriteBatch()) {
345 batch
.setMaxBytes(1);
347 batch
.put("k1".getBytes(), "v1".getBytes());
352 public void data() throws RocksDBException
{
353 try (final WriteBatch batch1
= new WriteBatch()) {
354 batch1
.delete("k0".getBytes());
355 batch1
.put("k1".getBytes(), "v1".getBytes());
356 batch1
.put("k2".getBytes(), "v2".getBytes());
357 batch1
.put("k3".getBytes(), "v3".getBytes());
358 batch1
.putLogData("log1".getBytes());
359 batch1
.merge("k2".getBytes(), "v22".getBytes());
360 batch1
.delete("k3".getBytes());
362 final byte[] serialized
= batch1
.data();
364 try(final WriteBatch batch2
= new WriteBatch(serialized
)) {
365 assertThat(batch2
.count()).isEqualTo(batch1
.count());
367 try(final CapturingWriteBatchHandler handler1
= new CapturingWriteBatchHandler()) {
368 batch1
.iterate(handler1
);
370 try (final CapturingWriteBatchHandler handler2
= new CapturingWriteBatchHandler()) {
371 batch2
.iterate(handler2
);
373 assertThat(handler1
.getEvents().equals(handler2
.getEvents())).isTrue();
381 public void dataSize() throws RocksDBException
{
382 try (final WriteBatch batch
= new WriteBatch()) {
383 batch
.put("k1".getBytes(), "v1".getBytes());
385 assertThat(batch
.getDataSize()).isEqualTo(19);
390 public void hasPut() throws RocksDBException
{
391 try (final WriteBatch batch
= new WriteBatch()) {
392 assertThat(batch
.hasPut()).isFalse();
394 batch
.put("k1".getBytes(), "v1".getBytes());
396 assertThat(batch
.hasPut()).isTrue();
401 public void hasDelete() throws RocksDBException
{
402 try (final WriteBatch batch
= new WriteBatch()) {
403 assertThat(batch
.hasDelete()).isFalse();
405 batch
.delete("k1".getBytes());
407 assertThat(batch
.hasDelete()).isTrue();
412 public void hasSingleDelete() throws RocksDBException
{
413 try (final WriteBatch batch
= new WriteBatch()) {
414 assertThat(batch
.hasSingleDelete()).isFalse();
416 batch
.singleDelete("k1".getBytes());
418 assertThat(batch
.hasSingleDelete()).isTrue();
423 public void hasDeleteRange() throws RocksDBException
{
424 try (final WriteBatch batch
= new WriteBatch()) {
425 assertThat(batch
.hasDeleteRange()).isFalse();
427 batch
.deleteRange("k1".getBytes(), "k2".getBytes());
429 assertThat(batch
.hasDeleteRange()).isTrue();
434 public void hasBeginPrepareRange() throws RocksDBException
{
435 try (final WriteBatch batch
= new WriteBatch()) {
436 assertThat(batch
.hasBeginPrepare()).isFalse();
441 public void hasEndPrepareRange() throws RocksDBException
{
442 try (final WriteBatch batch
= new WriteBatch()) {
443 assertThat(batch
.hasEndPrepare()).isFalse();
448 public void hasCommit() throws RocksDBException
{
449 try (final WriteBatch batch
= new WriteBatch()) {
450 assertThat(batch
.hasCommit()).isFalse();
455 public void hasRollback() throws RocksDBException
{
456 try (final WriteBatch batch
= new WriteBatch()) {
457 assertThat(batch
.hasRollback()).isFalse();
462 public void walTerminationPoint() throws RocksDBException
{
463 try (final WriteBatch batch
= new WriteBatch()) {
464 WriteBatch
.SavePoint walTerminationPoint
= batch
.getWalTerminationPoint();
465 assertThat(walTerminationPoint
.isCleared()).isTrue();
467 batch
.put("k1".getBytes(UTF_8
), "v1".getBytes(UTF_8
));
469 batch
.markWalTerminationPoint();
471 walTerminationPoint
= batch
.getWalTerminationPoint();
472 assertThat(walTerminationPoint
.getSize()).isEqualTo(19);
473 assertThat(walTerminationPoint
.getCount()).isEqualTo(1);
474 assertThat(walTerminationPoint
.getContentFlags()).isEqualTo(2);
479 public void getWriteBatch() {
480 try (final WriteBatch batch
= new WriteBatch()) {
481 assertThat(batch
.getWriteBatch()).isEqualTo(batch
);
485 static byte[] getContents(final WriteBatch wb
) {
486 return getContents(wb
.nativeHandle_
);
489 static String
getFromWriteBatch(final WriteBatch wb
, final String key
)
490 throws RocksDBException
{
491 final WriteBatchGetter getter
=
492 new WriteBatchGetter(key
.getBytes(UTF_8
));
494 if(getter
.getValue() != null) {
495 return new String(getter
.getValue(), UTF_8
);
501 private static native byte[] getContents(final long writeBatchHandle
);
505 * Package-private class which provides java api to access
506 * c++ WriteBatchInternal.
508 class WriteBatchTestInternalHelper
{
509 static void setSequence(final WriteBatch wb
, final long sn
) {
510 setSequence(wb
.nativeHandle_
, sn
);
513 static long sequence(final WriteBatch wb
) {
514 return sequence(wb
.nativeHandle_
);
517 static void append(final WriteBatch wb1
, final WriteBatch wb2
) {
518 append(wb1
.nativeHandle_
, wb2
.nativeHandle_
);
521 private static native void setSequence(final long writeBatchHandle
,
524 private static native long sequence(final long writeBatchHandle
);
526 private static native void append(final long writeBatchHandle1
,
527 final long writeBatchHandle2
);