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):

+ * + * + * @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);