commit
ea189b320c
@ -0,0 +1,115 @@ |
|||||||
|
package org.rocksdb; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>A TransactionLogIterator is used to iterate over the transactions in a db. |
||||||
|
* One run of the iterator is continuous, i.e. the iterator will stop at the |
||||||
|
* beginning of any gap in sequences.</p> |
||||||
|
*/ |
||||||
|
public class TransactionLogIterator extends RocksObject { |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>An iterator is either positioned at a WriteBatch |
||||||
|
* or not valid. This method returns true if the iterator |
||||||
|
* is valid. Can read data from a valid iterator.</p> |
||||||
|
* |
||||||
|
* @return true if iterator position is valid. |
||||||
|
*/ |
||||||
|
public boolean isValid() { |
||||||
|
return isValid(nativeHandle_); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Moves the iterator to the next WriteBatch. |
||||||
|
* <strong>REQUIRES</strong>: Valid() to be true.</p> |
||||||
|
*/ |
||||||
|
public void next() { |
||||||
|
next(nativeHandle_); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Throws RocksDBException if something went wrong.</p> |
||||||
|
* |
||||||
|
* @throws org.rocksdb.RocksDBException if something went |
||||||
|
* wrong in the underlying C++ code. |
||||||
|
*/ |
||||||
|
public void status() throws RocksDBException { |
||||||
|
status(nativeHandle_); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>If iterator position is valid, return the current |
||||||
|
* write_batch and the sequence number of the earliest |
||||||
|
* transaction contained in the batch.</p> |
||||||
|
* |
||||||
|
* <p>ONLY use if Valid() is true and status() is OK.</p> |
||||||
|
* |
||||||
|
* @return {@link org.rocksdb.TransactionLogIterator.BatchResult} |
||||||
|
* instance. |
||||||
|
*/ |
||||||
|
public BatchResult getBatch() { |
||||||
|
assert(isValid()); |
||||||
|
return getBatch(nativeHandle_); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>TransactionLogIterator constructor.</p> |
||||||
|
* |
||||||
|
* @param nativeHandle address to native address. |
||||||
|
*/ |
||||||
|
TransactionLogIterator(long nativeHandle) { |
||||||
|
super(); |
||||||
|
nativeHandle_ = nativeHandle; |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void disposeInternal() { |
||||||
|
disposeInternal(nativeHandle_); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>BatchResult represents a data structure returned |
||||||
|
* by a TransactionLogIterator containing a sequence |
||||||
|
* number and a {@link WriteBatch} instance.</p> |
||||||
|
*/ |
||||||
|
public class BatchResult { |
||||||
|
/** |
||||||
|
* <p>Constructor of BatchResult class.</p> |
||||||
|
* |
||||||
|
* @param sequenceNumber related to this BatchResult instance. |
||||||
|
* @param nativeHandle to {@link org.rocksdb.WriteBatch} |
||||||
|
* native instance. |
||||||
|
*/ |
||||||
|
public BatchResult(long sequenceNumber, long nativeHandle) { |
||||||
|
sequenceNumber_ = sequenceNumber; |
||||||
|
writeBatch_ = new WriteBatch(nativeHandle); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Return sequence number related to this BatchResult.</p> |
||||||
|
* |
||||||
|
* @return Sequence number. |
||||||
|
*/ |
||||||
|
public long sequenceNumber() { |
||||||
|
return sequenceNumber_; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Return contained {@link org.rocksdb.WriteBatch} |
||||||
|
* instance</p> |
||||||
|
* |
||||||
|
* @return {@link org.rocksdb.WriteBatch} instance. |
||||||
|
*/ |
||||||
|
public WriteBatch writeBatch() { |
||||||
|
return writeBatch_; |
||||||
|
} |
||||||
|
|
||||||
|
private final long sequenceNumber_; |
||||||
|
private final WriteBatch writeBatch_; |
||||||
|
} |
||||||
|
|
||||||
|
private native void disposeInternal(long handle); |
||||||
|
private native boolean isValid(long handle); |
||||||
|
private native void next(long handle); |
||||||
|
private native void status(long handle) |
||||||
|
throws RocksDBException; |
||||||
|
private native BatchResult getBatch(long handle); |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
package org.rocksdb.test; |
||||||
|
|
||||||
|
import org.junit.ClassRule; |
||||||
|
import org.junit.Rule; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.rules.TemporaryFolder; |
||||||
|
import org.rocksdb.*; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
public class TransactionLogIteratorTest { |
||||||
|
@ClassRule |
||||||
|
public static final RocksMemoryResource rocksMemoryResource = |
||||||
|
new RocksMemoryResource(); |
||||||
|
|
||||||
|
@Rule |
||||||
|
public TemporaryFolder dbFolder = new TemporaryFolder(); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void transactionLogIterator() throws RocksDBException { |
||||||
|
RocksDB db = null; |
||||||
|
Options options = null; |
||||||
|
TransactionLogIterator transactionLogIterator = null; |
||||||
|
try { |
||||||
|
options = new Options(). |
||||||
|
setCreateIfMissing(true); |
||||||
|
db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); |
||||||
|
transactionLogIterator = db.getUpdatesSince(0); |
||||||
|
} finally { |
||||||
|
if (transactionLogIterator != null) { |
||||||
|
transactionLogIterator.dispose(); |
||||||
|
} |
||||||
|
if (db != null) { |
||||||
|
db.close(); |
||||||
|
} |
||||||
|
if (options != null) { |
||||||
|
options.dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void getBatch() throws RocksDBException { |
||||||
|
final int numberOfPuts = 5; |
||||||
|
RocksDB db = null; |
||||||
|
Options options = null; |
||||||
|
ColumnFamilyHandle cfHandle = null; |
||||||
|
TransactionLogIterator transactionLogIterator = null; |
||||||
|
try { |
||||||
|
options = new Options(). |
||||||
|
setCreateIfMissing(true). |
||||||
|
setWalTtlSeconds(1000). |
||||||
|
setWalSizeLimitMB(10); |
||||||
|
|
||||||
|
db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); |
||||||
|
|
||||||
|
for (int i = 0; i < numberOfPuts; i++){ |
||||||
|
db.put(String.valueOf(i).getBytes(), |
||||||
|
String.valueOf(i).getBytes()); |
||||||
|
} |
||||||
|
db.flush(new FlushOptions().setWaitForFlush(true)); |
||||||
|
|
||||||
|
// the latest sequence number is 5 because 5 puts
|
||||||
|
// were written beforehand
|
||||||
|
assertThat(db.getLatestSequenceNumber()). |
||||||
|
isEqualTo(numberOfPuts); |
||||||
|
|
||||||
|
// insert 5 writes into a cf
|
||||||
|
cfHandle = db.createColumnFamily( |
||||||
|
new ColumnFamilyDescriptor("new_cf".getBytes())); |
||||||
|
|
||||||
|
for (int i = 0; i < numberOfPuts; i++){ |
||||||
|
db.put(cfHandle, String.valueOf(i).getBytes(), |
||||||
|
String.valueOf(i).getBytes()); |
||||||
|
} |
||||||
|
// the latest sequence number is 10 because
|
||||||
|
// (5 + 5) puts were written beforehand
|
||||||
|
assertThat(db.getLatestSequenceNumber()). |
||||||
|
isEqualTo(numberOfPuts + numberOfPuts); |
||||||
|
|
||||||
|
// Get updates since the beginning
|
||||||
|
transactionLogIterator = db.getUpdatesSince(0); |
||||||
|
assertThat(transactionLogIterator.isValid()).isTrue(); |
||||||
|
transactionLogIterator.status(); |
||||||
|
|
||||||
|
// The first sequence number is 1
|
||||||
|
TransactionLogIterator.BatchResult batchResult = |
||||||
|
transactionLogIterator.getBatch(); |
||||||
|
assertThat(batchResult.sequenceNumber()).isEqualTo(1); |
||||||
|
} finally { |
||||||
|
if (transactionLogIterator != null) { |
||||||
|
transactionLogIterator.dispose(); |
||||||
|
} |
||||||
|
if (cfHandle != null) { |
||||||
|
cfHandle.dispose(); |
||||||
|
} |
||||||
|
if (db != null) { |
||||||
|
db.close(); |
||||||
|
} |
||||||
|
if (options != null) { |
||||||
|
options.dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void transactionLogIteratorStallAtLastRecord() throws RocksDBException { |
||||||
|
RocksDB db = null; |
||||||
|
Options options = null; |
||||||
|
TransactionLogIterator transactionLogIterator = null; |
||||||
|
try { |
||||||
|
options = new Options(). |
||||||
|
setCreateIfMissing(true). |
||||||
|
setWalTtlSeconds(1000). |
||||||
|
setWalSizeLimitMB(10); |
||||||
|
|
||||||
|
db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); |
||||||
|
db.put("key1".getBytes(), "value1".getBytes()); |
||||||
|
// Get updates since the beginning
|
||||||
|
transactionLogIterator = db.getUpdatesSince(0); |
||||||
|
transactionLogIterator.status(); |
||||||
|
assertThat(transactionLogIterator.isValid()).isTrue(); |
||||||
|
transactionLogIterator.next(); |
||||||
|
assertThat(transactionLogIterator.isValid()).isFalse(); |
||||||
|
transactionLogIterator.status(); |
||||||
|
db.put("key2".getBytes(), "value2".getBytes()); |
||||||
|
transactionLogIterator.next(); |
||||||
|
transactionLogIterator.status(); |
||||||
|
assertThat(transactionLogIterator.isValid()).isTrue(); |
||||||
|
|
||||||
|
} finally { |
||||||
|
if (transactionLogIterator != null) { |
||||||
|
transactionLogIterator.dispose(); |
||||||
|
} |
||||||
|
if (db != null) { |
||||||
|
db.close(); |
||||||
|
} |
||||||
|
if (options != null) { |
||||||
|
options.dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void transactionLogIteratorCheckAfterRestart() throws RocksDBException { |
||||||
|
final int numberOfKeys = 2; |
||||||
|
RocksDB db = null; |
||||||
|
Options options = null; |
||||||
|
TransactionLogIterator transactionLogIterator = null; |
||||||
|
try { |
||||||
|
options = new Options(). |
||||||
|
setCreateIfMissing(true). |
||||||
|
setWalTtlSeconds(1000). |
||||||
|
setWalSizeLimitMB(10); |
||||||
|
|
||||||
|
db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); |
||||||
|
db.put("key1".getBytes(), "value1".getBytes()); |
||||||
|
db.put("key2".getBytes(), "value2".getBytes()); |
||||||
|
db.flush(new FlushOptions().setWaitForFlush(true)); |
||||||
|
// reopen
|
||||||
|
db.close(); |
||||||
|
db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); |
||||||
|
assertThat(db.getLatestSequenceNumber()).isEqualTo(numberOfKeys); |
||||||
|
|
||||||
|
transactionLogIterator = db.getUpdatesSince(0); |
||||||
|
for (int i = 0; i < numberOfKeys; i++) { |
||||||
|
transactionLogIterator.status(); |
||||||
|
assertThat(transactionLogIterator.isValid()).isTrue(); |
||||||
|
transactionLogIterator.next(); |
||||||
|
} |
||||||
|
} finally { |
||||||
|
if (transactionLogIterator != null) { |
||||||
|
transactionLogIterator.dispose(); |
||||||
|
} |
||||||
|
if (db != null) { |
||||||
|
db.close(); |
||||||
|
} |
||||||
|
if (options != null) { |
||||||
|
options.dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// This file implements the "bridge" between Java and C++ and enables
|
||||||
|
// calling c++ rocksdb::Iterator methods from Java side.
|
||||||
|
|
||||||
|
#include <jni.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include "include/org_rocksdb_TransactionLogIterator.h" |
||||||
|
#include "rocksdb/transaction_log.h" |
||||||
|
#include "rocksjni/portal.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionLogIterator |
||||||
|
* Method: disposeInternal |
||||||
|
* Signature: (J)V |
||||||
|
*/ |
||||||
|
void Java_org_rocksdb_TransactionLogIterator_disposeInternal( |
||||||
|
JNIEnv* env, jobject jobj, jlong handle) { |
||||||
|
delete reinterpret_cast<rocksdb::TransactionLogIterator*>(handle); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionLogIterator |
||||||
|
* Method: isValid |
||||||
|
* Signature: (J)Z |
||||||
|
*/ |
||||||
|
jboolean Java_org_rocksdb_TransactionLogIterator_isValid( |
||||||
|
JNIEnv* env, jobject jobj, jlong handle) { |
||||||
|
return reinterpret_cast<rocksdb::TransactionLogIterator*>(handle)->Valid(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionLogIterator |
||||||
|
* Method: next |
||||||
|
* Signature: (J)V |
||||||
|
*/ |
||||||
|
void Java_org_rocksdb_TransactionLogIterator_next( |
||||||
|
JNIEnv* env, jobject jobj, jlong handle) { |
||||||
|
reinterpret_cast<rocksdb::TransactionLogIterator*>(handle)->Next(); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionLogIterator |
||||||
|
* Method: status |
||||||
|
* Signature: (J)V |
||||||
|
*/ |
||||||
|
void Java_org_rocksdb_TransactionLogIterator_status( |
||||||
|
JNIEnv* env, jobject jobj, jlong handle) { |
||||||
|
rocksdb::Status s = reinterpret_cast< |
||||||
|
rocksdb::TransactionLogIterator*>(handle)->status(); |
||||||
|
if (!s.ok()) { |
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, s); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionLogIterator |
||||||
|
* Method: getBatch |
||||||
|
* Signature: (J)Lorg/rocksdb/TransactionLogIterator$BatchResult |
||||||
|
*/ |
||||||
|
jobject Java_org_rocksdb_TransactionLogIterator_getBatch( |
||||||
|
JNIEnv* env, jobject jobj, jlong handle) { |
||||||
|
rocksdb::BatchResult batch_result = |
||||||
|
reinterpret_cast<rocksdb::TransactionLogIterator*>(handle)->GetBatch(); |
||||||
|
jclass jclazz = env->FindClass( |
||||||
|
"org/rocksdb/TransactionLogIterator$BatchResult"); |
||||||
|
assert(jclazz != nullptr); |
||||||
|
jmethodID mid = env->GetMethodID( |
||||||
|
jclazz, "<init>", "(Lorg/rocksdb/TransactionLogIterator;JJ)V"); |
||||||
|
assert(mid != nullptr); |
||||||
|
return env->NewObject(jclazz, mid, jobj, |
||||||
|
batch_result.sequence, batch_result.writeBatchPtr.release()); |
||||||
|
} |
Loading…
Reference in new issue