diff --git a/pom.xml b/pom.xml
index fdcfbfc..c39aac8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.tidesdb
tidesdb-java
- 0.2.0
+ 0.3.0
jar
TidesDB Java
diff --git a/src/main/c/CMakeLists.txt b/src/main/c/CMakeLists.txt
index b1736f1..258dc35 100644
--- a/src/main/c/CMakeLists.txt
+++ b/src/main/c/CMakeLists.txt
@@ -3,8 +3,8 @@ project(tidesdb_jni C)
set(CMAKE_C_STANDARD 11)
-# Find JNI
-find_package(JNI REQUIRED)
+# Find JNI (AWT not required for JNI bindings)
+find_package(JNI REQUIRED COMPONENTS JVM)
# Find TidesDB
find_library(TIDESDB_LIBRARY tidesdb HINTS /usr/local/lib /opt/tidesdb/lib ${CMAKE_PREFIX_PATH}/lib)
diff --git a/src/main/c/com_tidesdb_TidesDB.c b/src/main/c/com_tidesdb_TidesDB.c
index 15817c9..8238273 100644
--- a/src/main/c/com_tidesdb_TidesDB.c
+++ b/src/main/c/com_tidesdb_TidesDB.c
@@ -145,7 +145,7 @@ JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeCreateColumnFamily(
.min_levels = minLevels,
.dividing_level_offset = dividingLevelOffset,
.klog_value_threshold = (size_t)klogValueThreshold,
- .compression_algo = (compression_algorithm)compressionAlgorithm,
+ .compression_algorithm = (compression_algorithm)compressionAlgorithm,
.enable_bloom_filter = enableBloomFilter ? 1 : 0,
.bloom_fpr = bloomFPR,
.enable_block_indexes = enableBlockIndexes ? 1 : 0,
@@ -341,6 +341,59 @@ JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeRegisterComparator(JNIEnv
}
}
+JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeBackup(JNIEnv *env, jclass cls, jlong handle,
+ jstring dir)
+{
+ tidesdb_t *db = (tidesdb_t *)(uintptr_t)handle;
+ const char *backupDir = (*env)->GetStringUTFChars(env, dir, NULL);
+ if (backupDir == NULL)
+ {
+ throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get backup directory");
+ return;
+ }
+
+ int result = tidesdb_backup(db, (char *)backupDir);
+
+ (*env)->ReleaseStringUTFChars(env, dir, backupDir);
+
+ 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,
+ jstring newName)
+{
+ tidesdb_t *db = (tidesdb_t *)(uintptr_t)handle;
+ const char *oldCfName = (*env)->GetStringUTFChars(env, oldName, NULL);
+ if (oldCfName == NULL)
+ {
+ throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get old column family name");
+ return;
+ }
+
+ const char *newCfName = (*env)->GetStringUTFChars(env, newName, NULL);
+ if (newCfName == NULL)
+ {
+ (*env)->ReleaseStringUTFChars(env, oldName, oldCfName);
+ throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get new column family name");
+ return;
+ }
+
+ int result = tidesdb_rename_column_family(db, oldCfName, newCfName);
+
+ (*env)->ReleaseStringUTFChars(env, oldName, oldCfName);
+ (*env)->ReleaseStringUTFChars(env, newName, newCfName);
+
+ if (result != TDB_SUCCESS)
+ {
+ throwTidesDBException(env, result, getErrorMessage(result));
+ }
+}
+
JNIEXPORT jobject JNICALL Java_com_tidesdb_ColumnFamily_nativeGetStats(JNIEnv *env, jclass cls,
jlong handle)
{
@@ -415,6 +468,43 @@ JNIEXPORT void JNICALL Java_com_tidesdb_ColumnFamily_nativeFlushMemtable(JNIEnv
}
}
+JNIEXPORT jboolean JNICALL Java_com_tidesdb_ColumnFamily_nativeIsFlushing(JNIEnv *env, jclass cls,
+ jlong handle)
+{
+ tidesdb_column_family_t *cf = (tidesdb_column_family_t *)(uintptr_t)handle;
+ return tidesdb_is_flushing(cf) != 0;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_tidesdb_ColumnFamily_nativeIsCompacting(JNIEnv *env, jclass cls,
+ jlong handle)
+{
+ tidesdb_column_family_t *cf = (tidesdb_column_family_t *)(uintptr_t)handle;
+ return tidesdb_is_compacting(cf) != 0;
+}
+
+JNIEXPORT void JNICALL Java_com_tidesdb_ColumnFamily_nativeUpdateRuntimeConfig(
+ JNIEnv *env, jclass cls, jlong handle, jlong writeBufferSize, jint skipListMaxLevel,
+ jfloat skipListProbability, jdouble bloomFPR, jint indexSampleRatio, jint syncMode,
+ jlong syncIntervalUs, jboolean persistToDisk)
+{
+ tidesdb_column_family_t *cf = (tidesdb_column_family_t *)(uintptr_t)handle;
+
+ tidesdb_column_family_config_t config = {.write_buffer_size = (size_t)writeBufferSize,
+ .skip_list_max_level = skipListMaxLevel,
+ .skip_list_probability = skipListProbability,
+ .bloom_fpr = bloomFPR,
+ .index_sample_ratio = indexSampleRatio,
+ .sync_mode = syncMode,
+ .sync_interval_us = (uint64_t)syncIntervalUs};
+
+ int result = tidesdb_cf_update_runtime_config(cf, &config, persistToDisk ? 1 : 0);
+
+ if (result != TDB_SUCCESS)
+ {
+ throwTidesDBException(env, result, getErrorMessage(result));
+ }
+}
+
JNIEXPORT void JNICALL Java_com_tidesdb_Transaction_nativePut(JNIEnv *env, jclass cls, jlong handle,
jlong cfHandle, jbyteArray key,
jbyteArray value, jlong ttl)
diff --git a/src/main/java/com/tidesdb/ColumnFamily.java b/src/main/java/com/tidesdb/ColumnFamily.java
index a1203e8..57b91ad 100644
--- a/src/main/java/com/tidesdb/ColumnFamily.java
+++ b/src/main/java/com/tidesdb/ColumnFamily.java
@@ -73,6 +73,58 @@ public void flushMemtable() throws TidesDBException {
nativeFlushMemtable(nativeHandle);
}
+ /**
+ * Checks if a flush operation is currently in progress for this column family.
+ *
+ * @return true if flushing is in progress
+ */
+ public boolean isFlushing() {
+ return nativeIsFlushing(nativeHandle);
+ }
+
+ /**
+ * Checks if a compaction operation is currently in progress for this column family.
+ *
+ * @return true if compaction is in progress
+ */
+ public boolean isCompacting() {
+ return nativeIsCompacting(nativeHandle);
+ }
+
+ /**
+ * Updates runtime-safe configuration settings for this column family.
+ * Configuration changes are applied to new operations only.
+ *
+ *
Updatable settings (safe to change at runtime):
+ *
+ * - writeBufferSize - Memtable flush threshold
+ * - skipListMaxLevel - Skip list level for new memtables
+ * - skipListProbability - Skip list probability for new memtables
+ * - bloomFPR - False positive rate for new SSTables
+ * - indexSampleRatio - Index sampling ratio for new SSTables
+ * - syncMode - Durability mode
+ * - syncIntervalUs - Sync interval in microseconds
+ *
+ *
+ * @param config the new configuration
+ * @param persistToDisk if true, saves changes to config.ini
+ * @throws TidesDBException if the update fails
+ */
+ public void updateRuntimeConfig(ColumnFamilyConfig config, boolean persistToDisk) throws TidesDBException {
+ if (config == null) {
+ throw new IllegalArgumentException("Config cannot be null");
+ }
+ nativeUpdateRuntimeConfig(nativeHandle,
+ config.getWriteBufferSize(),
+ config.getSkipListMaxLevel(),
+ config.getSkipListProbability(),
+ config.getBloomFPR(),
+ config.getIndexSampleRatio(),
+ config.getSyncMode().getValue(),
+ config.getSyncIntervalUs(),
+ persistToDisk);
+ }
+
long getNativeHandle() {
return nativeHandle;
}
@@ -80,4 +132,9 @@ long getNativeHandle() {
private static native Stats nativeGetStats(long handle) throws TidesDBException;
private static native void nativeCompact(long handle) throws TidesDBException;
private static native void nativeFlushMemtable(long handle) throws TidesDBException;
+ private static native boolean nativeIsFlushing(long handle);
+ private static native boolean nativeIsCompacting(long handle);
+ private static native void nativeUpdateRuntimeConfig(long handle, long writeBufferSize,
+ int skipListMaxLevel, float skipListProbability, double bloomFPR, int indexSampleRatio,
+ int syncMode, long syncIntervalUs, boolean persistToDisk) throws TidesDBException;
}
diff --git a/src/main/java/com/tidesdb/ColumnFamilyConfig.java b/src/main/java/com/tidesdb/ColumnFamilyConfig.java
index 06d4a73..8b99be2 100644
--- a/src/main/java/com/tidesdb/ColumnFamilyConfig.java
+++ b/src/main/java/com/tidesdb/ColumnFamilyConfig.java
@@ -74,26 +74,26 @@ private ColumnFamilyConfig(Builder builder) {
*/
public static ColumnFamilyConfig defaultConfig() {
return new Builder()
- .writeBufferSize(64 * 1024 * 1024)
+ .writeBufferSize(128 * 1024 * 1024)
.levelSizeRatio(10)
- .minLevels(4)
+ .minLevels(5)
.dividingLevelOffset(2)
- .klogValueThreshold(1024)
- .compressionAlgorithm(CompressionAlgorithm.NO_COMPRESSION)
- .enableBloomFilter(false)
+ .klogValueThreshold(512)
+ .compressionAlgorithm(CompressionAlgorithm.LZ4_COMPRESSION)
+ .enableBloomFilter(true)
.bloomFPR(0.01)
- .enableBlockIndexes(false)
- .indexSampleRatio(16)
- .blockIndexPrefixLen(4)
- .syncMode(SyncMode.SYNC_NONE)
- .syncIntervalUs(0)
+ .enableBlockIndexes(true)
+ .indexSampleRatio(1)
+ .blockIndexPrefixLen(16)
+ .syncMode(SyncMode.SYNC_FULL)
+ .syncIntervalUs(1000000)
.comparatorName("")
.skipListMaxLevel(12)
.skipListProbability(0.25f)
.defaultIsolationLevel(IsolationLevel.READ_COMMITTED)
- .minDiskSpace(1024 * 1024 * 1024)
+ .minDiskSpace(100 * 1024 * 1024)
.l1FileCountTrigger(4)
- .l0QueueStallThreshold(8)
+ .l0QueueStallThreshold(20)
.build();
}
@@ -131,26 +131,26 @@ public static Builder builder() {
* Builder for ColumnFamilyConfig.
*/
public static class Builder {
- private long writeBufferSize = 64 * 1024 * 1024;
+ private long writeBufferSize = 128 * 1024 * 1024;
private long levelSizeRatio = 10;
- private int minLevels = 4;
+ private int minLevels = 5;
private int dividingLevelOffset = 2;
- private long klogValueThreshold = 1024;
- private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.NO_COMPRESSION;
- private boolean enableBloomFilter = false;
+ private long klogValueThreshold = 512;
+ private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.LZ4_COMPRESSION;
+ private boolean enableBloomFilter = true;
private double bloomFPR = 0.01;
- private boolean enableBlockIndexes = false;
- private int indexSampleRatio = 16;
- private int blockIndexPrefixLen = 4;
- private SyncMode syncMode = SyncMode.SYNC_NONE;
- private long syncIntervalUs = 0;
+ private boolean enableBlockIndexes = true;
+ private int indexSampleRatio = 1;
+ private int blockIndexPrefixLen = 16;
+ private SyncMode syncMode = SyncMode.SYNC_FULL;
+ private long syncIntervalUs = 1000000;
private String comparatorName = "";
private int skipListMaxLevel = 12;
private float skipListProbability = 0.25f;
private IsolationLevel defaultIsolationLevel = IsolationLevel.READ_COMMITTED;
- private long minDiskSpace = 1024 * 1024 * 1024;
+ private long minDiskSpace = 100 * 1024 * 1024;
private int l1FileCountTrigger = 4;
- private int l0QueueStallThreshold = 8;
+ private int l0QueueStallThreshold = 20;
public Builder writeBufferSize(long writeBufferSize) {
this.writeBufferSize = writeBufferSize;
diff --git a/src/main/java/com/tidesdb/CompressionAlgorithm.java b/src/main/java/com/tidesdb/CompressionAlgorithm.java
index 6552962..24cd8a0 100644
--- a/src/main/java/com/tidesdb/CompressionAlgorithm.java
+++ b/src/main/java/com/tidesdb/CompressionAlgorithm.java
@@ -23,9 +23,10 @@
*/
public enum CompressionAlgorithm {
NO_COMPRESSION(0),
- LZ4_COMPRESSION(1),
- ZSTD_COMPRESSION(2),
- LZ4_FAST_COMPRESSION(3);
+ SNAPPY_COMPRESSION(1),
+ LZ4_COMPRESSION(2),
+ ZSTD_COMPRESSION(3),
+ LZ4_FAST_COMPRESSION(4);
private final int value;
diff --git a/src/main/java/com/tidesdb/TidesDB.java b/src/main/java/com/tidesdb/TidesDB.java
index d63268e..8b984b3 100644
--- a/src/main/java/com/tidesdb/TidesDB.java
+++ b/src/main/java/com/tidesdb/TidesDB.java
@@ -211,6 +211,39 @@ public void registerComparator(String name, String context) throws TidesDBExcept
nativeRegisterComparator(nativeHandle, name, context);
}
+ /**
+ * Creates an on-disk snapshot of the database without blocking normal reads/writes.
+ *
+ * @param dir the backup directory (must be non-existent or empty)
+ * @throws TidesDBException if the backup fails
+ */
+ public void backup(String dir) throws TidesDBException {
+ checkNotClosed();
+ if (dir == null || dir.isEmpty()) {
+ throw new IllegalArgumentException("Backup directory cannot be null or empty");
+ }
+ nativeBackup(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.
+ *
+ * @param oldName the current column family name
+ * @param newName the new column family name
+ * @throws TidesDBException if the rename fails
+ */
+ public void renameColumnFamily(String oldName, String newName) throws TidesDBException {
+ checkNotClosed();
+ if (oldName == null || oldName.isEmpty()) {
+ throw new IllegalArgumentException("Old column family name cannot be null or empty");
+ }
+ if (newName == null || newName.isEmpty()) {
+ throw new IllegalArgumentException("New column family name cannot be null or empty");
+ }
+ nativeRenameColumnFamily(nativeHandle, oldName, newName);
+ }
+
private void checkNotClosed() {
if (closed) {
throw new IllegalStateException("TidesDB instance is closed");
@@ -247,4 +280,8 @@ private static native void nativeCreateColumnFamily(long handle, String name,
private static native CacheStats nativeGetCacheStats(long handle) throws TidesDBException;
private static native void nativeRegisterComparator(long handle, String name, String context) throws TidesDBException;
+
+ private static native void nativeBackup(long handle, String dir) throws TidesDBException;
+
+ private static native void nativeRenameColumnFamily(long handle, String oldName, String newName) throws TidesDBException;
}
diff --git a/src/main/java/com/tidesdb/TidesDBException.java b/src/main/java/com/tidesdb/TidesDBException.java
index d9a8ac3..7bc2d53 100644
--- a/src/main/java/com/tidesdb/TidesDBException.java
+++ b/src/main/java/com/tidesdb/TidesDBException.java
@@ -29,18 +29,18 @@ public class TidesDBException extends Exception {
* Error codes from TidesDB.
*/
public static final int ERR_SUCCESS = 0;
- public static final int ERR_MEMORY = 1;
- public static final int ERR_INVALID_ARGS = 2;
- public static final int ERR_NOT_FOUND = 3;
- public static final int ERR_IO = 4;
- public static final int ERR_CORRUPTION = 5;
- public static final int ERR_EXISTS = 6;
- public static final int ERR_CONFLICT = 7;
- public static final int ERR_TOO_LARGE = 8;
- public static final int ERR_MEMORY_LIMIT = 9;
- public static final int ERR_INVALID_DB = 10;
- public static final int ERR_UNKNOWN = 11;
- public static final int ERR_LOCKED = 12;
+ public static final int ERR_MEMORY = -1;
+ public static final int ERR_INVALID_ARGS = -2;
+ public static final int ERR_NOT_FOUND = -3;
+ public static final int ERR_IO = -4;
+ public static final int ERR_CORRUPTION = -5;
+ public static final int ERR_EXISTS = -6;
+ public static final int ERR_CONFLICT = -7;
+ public static final int ERR_TOO_LARGE = -8;
+ public static final int ERR_MEMORY_LIMIT = -9;
+ public static final int ERR_INVALID_DB = -10;
+ public static final int ERR_UNKNOWN = -11;
+ public static final int ERR_LOCKED = -12;
public TidesDBException(String message) {
super(message);