Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.tidesdb</groupId>
<artifactId>tidesdb-java</artifactId>
<version>0.6.0</version>
<version>0.6.1</version>
<packaging>jar</packaging>

<name>TidesDB Java</name>
Expand Down
21 changes: 21 additions & 0 deletions src/main/c/com_tidesdb_TidesDB.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,27 @@ JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeBackup(JNIEnv *env, jclass
}
}

JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeCheckpoint(JNIEnv *env, jclass cls,
jlong handle, jstring dir)
{
tidesdb_t *db = (tidesdb_t *)(uintptr_t)handle;
const char *checkpointDir = (*env)->GetStringUTFChars(env, dir, NULL);
if (checkpointDir == NULL)
{
throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get checkpoint directory");
return;
}

int result = tidesdb_checkpoint(db, checkpointDir);

(*env)->ReleaseStringUTFChars(env, dir, checkpointDir);

if (result != TDB_SUCCESS)
{
throwTidesDBException(env, result, getErrorMessage(result));
}
}

JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeRenameColumnFamily(JNIEnv *env, jclass cls,
jlong handle,
jstring oldName,
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/tidesdb/TidesDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,21 @@ public void backup(String dir) throws TidesDBException {
nativeBackup(nativeHandle, dir);
}

/**
* Creates a lightweight, near-instant snapshot of an open database using hard links
* instead of copying SSTable data.
*
* @param dir the checkpoint directory (must be non-existent or empty)
* @throws TidesDBException if the checkpoint fails
*/
public void checkpoint(String dir) throws TidesDBException {
checkNotClosed();
if (dir == null || dir.isEmpty()) {
throw new IllegalArgumentException("Checkpoint directory cannot be null or empty");
}
nativeCheckpoint(nativeHandle, dir);
}

/**
* Atomically renames a column family and its underlying directory.
* The operation waits for any in-progress flush or compaction to complete before renaming.
Expand Down Expand Up @@ -307,6 +322,8 @@ private static native void nativeCreateColumnFamily(long handle, String name,

private static native void nativeBackup(long handle, String dir) throws TidesDBException;

private static native void nativeCheckpoint(long handle, String dir) throws TidesDBException;

private static native void nativeRenameColumnFamily(long handle, String oldName, String newName) throws TidesDBException;

private static native void nativeCloneColumnFamily(long handle, String sourceName, String destName) throws TidesDBException;
Expand Down
76 changes: 73 additions & 3 deletions src/test/java/com/tidesdb/TidesDBTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,76 @@ void testCloneColumnFamily() throws TidesDBException {

@Test
@Order(16)
void testCheckpoint() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb16").toString())
.numFlushThreads(2)
.numCompactionThreads(2)
.logLevel(LogLevel.INFO)
.blockCacheSize(64 * 1024 * 1024)
.maxOpenSSTables(256)
.build();

try (TidesDB db = TidesDB.open(config)) {
ColumnFamilyConfig cfConfig = ColumnFamilyConfig.defaultConfig();
db.createColumnFamily("test_cf", cfConfig);

ColumnFamily cf = db.getColumnFamily("test_cf");

// Insert some data
try (Transaction txn = db.beginTransaction()) {
for (int i = 0; i < 10; i++) {
txn.put(cf, ("key" + i).getBytes(), ("value" + i).getBytes());
}
txn.commit();
}

// Create checkpoint
String checkpointDir = tempDir.resolve("testdb16_checkpoint").toString();
db.checkpoint(checkpointDir);

// Open the checkpoint as a separate database and verify data
Config checkpointConfig = Config.builder(checkpointDir)
.numFlushThreads(2)
.numCompactionThreads(2)
.logLevel(LogLevel.INFO)
.blockCacheSize(64 * 1024 * 1024)
.maxOpenSSTables(256)
.build();

try (TidesDB checkpointDb = TidesDB.open(checkpointConfig)) {
ColumnFamily checkpointCf = checkpointDb.getColumnFamily("test_cf");
assertNotNull(checkpointCf);

try (Transaction txn = checkpointDb.beginTransaction()) {
for (int i = 0; i < 10; i++) {
byte[] result = txn.get(checkpointCf, ("key" + i).getBytes());
assertNotNull(result);
assertArrayEquals(("value" + i).getBytes(), result);
}
}
}
}
}

@Test
@Order(17)
void testCheckpointNullDir() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb16b").toString())
.numFlushThreads(2)
.numCompactionThreads(2)
.logLevel(LogLevel.INFO)
.blockCacheSize(64 * 1024 * 1024)
.maxOpenSSTables(256)
.build();

try (TidesDB db = TidesDB.open(config)) {
assertThrows(IllegalArgumentException.class, () -> db.checkpoint(null));
assertThrows(IllegalArgumentException.class, () -> db.checkpoint(""));
}
}

@Test
@Order(18)
void testTransactionPutGetDeleteBadKey() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb3").toString())
.numFlushThreads(2)
Expand Down Expand Up @@ -656,7 +726,7 @@ void testTransactionPutGetDeleteBadKey() throws TidesDBException {
}

@Test
@Order(17)
@Order(19)
void testTransactionReset() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb17").toString())
.numFlushThreads(2)
Expand Down Expand Up @@ -701,7 +771,7 @@ void testTransactionReset() throws TidesDBException {
}

@Test
@Order(18)
@Order(20)
void testTransactionResetWithDifferentIsolation() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb18").toString())
.numFlushThreads(2)
Expand Down Expand Up @@ -746,7 +816,7 @@ void testTransactionResetWithDifferentIsolation() throws TidesDBException {
}

@Test
@Order(19)
@Order(21)
void testTransactionResetNullIsolation() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb19").toString())
.numFlushThreads(2)
Expand Down
Loading