[RocksJava] TTL Support
This commit is contained in:
parent
ca47da9e63
commit
5ff8aec4db
@ -7,20 +7,85 @@ package org.rocksdb;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Database with TTL support.
|
||||
*
|
||||
* <p><strong>Use case</strong></p>
|
||||
* <p>This API should be used to open the db when key-values inserted are
|
||||
* meant to be removed from the db in a non-strict 'ttl' amount of time
|
||||
* Therefore, this guarantees that key-values inserted will remain in the
|
||||
* db for >= ttl amount of time and the db will make efforts to remove the
|
||||
* key-values as soon as possible after ttl seconds of their insertion.
|
||||
* </p>
|
||||
*
|
||||
* <p><strong>Behaviour</strong></p>
|
||||
* <p>TTL is accepted in seconds
|
||||
* (int32_t)Timestamp(creation) is suffixed to values in Put internally
|
||||
* Expired TTL values deleted in compaction only:(Timestamp+ttl<time_now)
|
||||
* Get/Iterator may return expired entries(compaction not run on them yet)
|
||||
* Different TTL may be used during different Opens
|
||||
* </p>
|
||||
*
|
||||
* <p><strong>Example</strong></p>
|
||||
* <ul>
|
||||
* <li>Open1 at t=0 with ttl=4 and insert k1,k2, close at t=2</li>
|
||||
* <li>Open2 at t=3 with ttl=5. Now k1,k2 should be deleted at t>=5</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* read_only=true opens in the usual read-only mode. Compactions will not be
|
||||
* triggered(neither manual nor automatic), so no expired entries removed
|
||||
* </p>
|
||||
*
|
||||
* <p><strong>Constraints</strong></p>
|
||||
* <p>Not specifying/passing or non-positive TTL behaves
|
||||
* like TTL = infinity</p>
|
||||
*
|
||||
* <p><strong>!!!WARNING!!!</strong></p>
|
||||
* <p>Calling DB::Open directly to re-open a db created by this API will get
|
||||
* corrupt values(timestamp suffixed) and no ttl effect will be there
|
||||
* during the second Open, so use this API consistently to open the db
|
||||
* Be careful when passing ttl with a small positive value because the
|
||||
* whole database may be deleted in a small amount of time.</p>
|
||||
*/
|
||||
public class TtlDB extends RocksDB {
|
||||
|
||||
//static Status Open(const Options& options, const std::string& dbname,
|
||||
// DBWithTTL** dbptr, int32_t ttl = 0,
|
||||
// bool read_only = false);
|
||||
/**
|
||||
* <p>Opens a TtlDB.</p>
|
||||
*
|
||||
* <p>Database is opened in read-write mode without default TTL.</p>
|
||||
*
|
||||
* @param options {@link org.rocksdb.Options} instance.
|
||||
* @param db_path path to database.
|
||||
*
|
||||
* @return TtlDB instance.
|
||||
*
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
public static TtlDB open(Options options, String db_path)
|
||||
throws RocksDBException {
|
||||
return open(options, db_path, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Opens a TtlDB.</p>
|
||||
*
|
||||
* @param options {@link org.rocksdb.Options} instance.
|
||||
* @param db_path path to database.
|
||||
* @param ttl time to live for new entries.
|
||||
* @param readOnly boolean value indicating if database if db is
|
||||
* opened read-only.
|
||||
*
|
||||
* @return TtlDB instance.
|
||||
*
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
public static TtlDB open(Options options, String db_path, int ttl,
|
||||
boolean readOnly) throws RocksDBException {
|
||||
TtlDB ttldb = new TtlDB();
|
||||
ttldb.open(options.nativeHandle_, db_path, ttl, readOnly);
|
||||
|
||||
// Prevent the RocksDB object from attempting to delete
|
||||
// the underly C++ DB object.
|
||||
//ttldb.disOwnNativeHandle();
|
||||
return ttldb;
|
||||
}
|
||||
|
||||
@ -38,17 +103,39 @@ public class TtlDB extends RocksDB {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new ttl based column family with a name defined
|
||||
* in given ColumnFamilyDescriptor and allocates a
|
||||
* ColumnFamilyHandle within an internal structure.</p>
|
||||
*
|
||||
* <p>The ColumnFamilyHandle is automatically disposed with DB
|
||||
* disposal.</p>
|
||||
*
|
||||
* @param columnFamilyDescriptor column family to be created.
|
||||
* @param ttl TTL to set for this column family.
|
||||
*
|
||||
* @return {@link org.rocksdb.ColumnFamilyHandle} instance.
|
||||
*
|
||||
* @throws RocksDBException thrown if error happens in underlying
|
||||
* native library.
|
||||
*/
|
||||
public ColumnFamilyHandle createColumnFamilyWithTtl(
|
||||
ColumnFamilyDescriptor columnFamilyDescriptor, int ttl) {
|
||||
return null;
|
||||
ColumnFamilyDescriptor columnFamilyDescriptor, int ttl)
|
||||
throws RocksDBException {
|
||||
assert(isInitialized());
|
||||
return new ColumnFamilyHandle(this,
|
||||
createColumnFamilyWithTtl(nativeHandle_,
|
||||
columnFamilyDescriptor, ttl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the TtlDB instance and release resource.
|
||||
* <p>Close the TtlDB instance and release resource.</p>
|
||||
*
|
||||
* Internally, TtlDB owns the {@code rocksdb::DB} pointer to its associated
|
||||
* {@link org.rocksdb.RocksDB}. The release of that RocksDB pointer is handled in the destructor
|
||||
* of the c++ {@code rocksdb::TtlDB} and should be transparent to Java developers.
|
||||
* <p>Internally, TtlDB owns the {@code rocksdb::DB} pointer
|
||||
* to its associated {@link org.rocksdb.RocksDB}. The release
|
||||
* of that RocksDB pointer is handled in the destructor of the
|
||||
* c++ {@code rocksdb::TtlDB} and should be transparent to
|
||||
* Java developers.</p>
|
||||
*/
|
||||
@Override public synchronized void close() {
|
||||
if (isInitialized()) {
|
||||
@ -57,9 +144,13 @@ public class TtlDB extends RocksDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* A protected construction that will be used in the static factory
|
||||
* method {@link #open(DBOptions, String, java.util.List, java.util.List)} and
|
||||
* {@link #open(DBOptions, String, java.util.List, java.util.List, java.util.List, boolean)}.
|
||||
* <p>A protected constructor that will be used in the static
|
||||
* factory method
|
||||
* {@link #open(DBOptions, String, java.util.List, java.util.List)}
|
||||
* and
|
||||
* {@link #open(DBOptions, String, java.util.List,
|
||||
* java.util.List, java.util.List, boolean)}.
|
||||
* </p>
|
||||
*/
|
||||
protected TtlDB() {
|
||||
super();
|
||||
@ -72,4 +163,7 @@ public class TtlDB extends RocksDB {
|
||||
|
||||
private native void open(long optionsHandle, String db_path, int ttl,
|
||||
boolean readOnly) throws RocksDBException;
|
||||
private native long createColumnFamilyWithTtl(long handle,
|
||||
ColumnFamilyDescriptor columnFamilyDescriptor, int ttl)
|
||||
throws RocksDBException;
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.rocksdb.Options;
|
||||
import org.rocksdb.RocksDBException;
|
||||
import org.rocksdb.TtlDB;
|
||||
import org.rocksdb.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ -25,19 +25,49 @@ public class TtlDBTest {
|
||||
public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void ttlDBOpen() throws RocksDBException, InterruptedException {
|
||||
public void ttlDBOpen() throws RocksDBException,
|
||||
InterruptedException {
|
||||
Options options = null;
|
||||
TtlDB ttlDB = null;
|
||||
try {
|
||||
options = new Options().setCreateIfMissing(true);
|
||||
options = new Options().
|
||||
setCreateIfMissing(true).
|
||||
setMaxGrandparentOverlapFactor(0).
|
||||
setMaxMemCompactionLevel(0);
|
||||
ttlDB = TtlDB.open(options,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
ttlDB.put("key".getBytes(), "value".getBytes());
|
||||
assertThat(ttlDB.get("key".getBytes())).
|
||||
isEqualTo("value".getBytes());
|
||||
assertThat(ttlDB.get("key".getBytes())).isNotNull();
|
||||
} finally {
|
||||
if (ttlDB != null) {
|
||||
ttlDB.close();
|
||||
}
|
||||
if (options != null) {
|
||||
options.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ttlDBOpenWithTtl() throws RocksDBException,
|
||||
InterruptedException {
|
||||
Options options = null;
|
||||
TtlDB ttlDB = null;
|
||||
try {
|
||||
options = new Options().
|
||||
setCreateIfMissing(true).
|
||||
setMaxGrandparentOverlapFactor(0).
|
||||
setMaxMemCompactionLevel(0);
|
||||
ttlDB = TtlDB.open(options, dbFolder.getRoot().getAbsolutePath(),
|
||||
1, false);
|
||||
ttlDB.put("key".getBytes(), "value".getBytes());
|
||||
assertThat(ttlDB.get("key".getBytes())).
|
||||
isEqualTo("value".getBytes());
|
||||
Thread.sleep(1250);
|
||||
ttlDB.compactRange();
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
|
||||
ttlDB.compactRange();
|
||||
assertThat(ttlDB.get("key".getBytes())).isNull();
|
||||
} finally {
|
||||
if (ttlDB != null) {
|
||||
@ -48,4 +78,36 @@ public class TtlDBTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTtlColumnFamily() throws RocksDBException,
|
||||
InterruptedException {
|
||||
Options options = null;
|
||||
TtlDB ttlDB = null;
|
||||
ColumnFamilyHandle columnFamilyHandle = null;
|
||||
try {
|
||||
options = new Options().setCreateIfMissing(true);
|
||||
ttlDB = TtlDB.open(options,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
columnFamilyHandle = ttlDB.createColumnFamilyWithTtl(
|
||||
new ColumnFamilyDescriptor("new_cf"), 1);
|
||||
ttlDB.put(columnFamilyHandle, "key".getBytes(),
|
||||
"value".getBytes());
|
||||
assertThat(ttlDB.get(columnFamilyHandle, "key".getBytes())).
|
||||
isEqualTo("value".getBytes());
|
||||
Thread.sleep(2500);
|
||||
ttlDB.compactRange(columnFamilyHandle);
|
||||
assertThat(ttlDB.get(columnFamilyHandle, "key".getBytes())).isNull();
|
||||
} finally {
|
||||
if (columnFamilyHandle != null) {
|
||||
columnFamilyHandle.dispose();
|
||||
}
|
||||
if (ttlDB != null) {
|
||||
ttlDB.close();
|
||||
}
|
||||
if (options != null) {
|
||||
options.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,3 +40,36 @@ void Java_org_rocksdb_TtlDB_open(JNIEnv* env, jobject jttldb,
|
||||
}
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_TtlDB
|
||||
* Method: createColumnFamilyWithTtl
|
||||
* Signature: (JLorg/rocksdb/ColumnFamilyDescriptor;I)J;
|
||||
*/
|
||||
jlong Java_org_rocksdb_TtlDB_createColumnFamilyWithTtl(
|
||||
JNIEnv* env, jobject jobj, jlong jdb_handle,
|
||||
jobject jcf_descriptor, jint jttl) {
|
||||
rocksdb::ColumnFamilyHandle* handle;
|
||||
auto db_handle = reinterpret_cast<rocksdb::DBWithTTL*>(jdb_handle);
|
||||
|
||||
jstring jstr = (jstring) env->CallObjectMethod(jcf_descriptor,
|
||||
rocksdb::ColumnFamilyDescriptorJni::getColumnFamilyNameMethod(
|
||||
env));
|
||||
// get CF Options
|
||||
jobject jcf_opt_obj = env->CallObjectMethod(jcf_descriptor,
|
||||
rocksdb::ColumnFamilyDescriptorJni::getColumnFamilyOptionsMethod(
|
||||
env));
|
||||
rocksdb::ColumnFamilyOptions* cfOptions =
|
||||
rocksdb::ColumnFamilyOptionsJni::getHandle(env, jcf_opt_obj);
|
||||
|
||||
const char* cfname = env->GetStringUTFChars(jstr, 0);
|
||||
rocksdb::Status s = db_handle->CreateColumnFamilyWithTtl(
|
||||
*cfOptions, cfname, &handle, jttl);
|
||||
env->ReleaseStringUTFChars(jstr, cfname);
|
||||
|
||||
if (s.ok()) {
|
||||
return reinterpret_cast<jlong>(handle);
|
||||
}
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user