Skip to content

Commit 721811a

Browse files
authored
Merge pull request #36 from tidesdb/tdb936
add missing db.h ffi bindings and align with tidesdb 9.3.6
2 parents 8df2ee7 + 4a31bbc commit 721811a

9 files changed

Lines changed: 727 additions & 16 deletions

File tree

pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.tidesdb</groupId>
88
<artifactId>tidesdb-java</artifactId>
9-
<version>0.8.0</version>
9+
<version>0.8.1</version>
1010
<packaging>jar</packaging>
1111

1212
<name>TidesDB Java</name>
@@ -142,6 +142,12 @@
142142
<groupId>org.apache.maven.plugins</groupId>
143143
<artifactId>maven-javadoc-plugin</artifactId>
144144
<version>3.12.0</version>
145+
<configuration>
146+
<!-- Keep doclint strict on genuine problems (malformed HTML, broken
147+
references, bad syntax) but do not require a comment on every trivial
148+
fluent builder setter / one-line accessor. -->
149+
<doclint>all,-missing</doclint>
150+
</configuration>
145151
<executions>
146152
<execution>
147153
<id>attach-javadocs</id>

src/main/c/com_tidesdb_TidesDB.c

Lines changed: 196 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ static const char *getErrorMessage(int code)
7070
return "database is locked";
7171
case TDB_ERR_READONLY:
7272
return "database is read-only";
73+
case TDB_ERR_BUSY:
74+
return "resource is busy";
7375
default:
7476
return "unknown error";
7577
}
@@ -87,7 +89,7 @@ JNIEXPORT jlong JNICALL Java_com_tidesdb_TidesDB_nativeOpen(
8789
jlong oscMultipartPartSize, jboolean oscSyncManifestToObject, jboolean oscReplicateWal,
8890
jboolean oscWalUploadSync, jlong oscWalSyncThresholdBytes, jboolean oscWalSyncOnCommit,
8991
jboolean oscReplicaMode, jlong oscReplicaSyncIntervalUs, jboolean oscReplicaReplayWal,
90-
jint maxConcurrentFlushes)
92+
jint maxConcurrentFlushes, jboolean finishCompactionsOnClose)
9193
{
9294
const char *path = (*env)->GetStringUTFChars(env, dbPath, NULL);
9395
if (path == NULL)
@@ -151,7 +153,8 @@ JNIEXPORT jlong JNICALL Java_com_tidesdb_TidesDB_nativeOpen(
151153
.unified_memtable_sync_interval_us = (uint64_t)unifiedMemtableSyncIntervalUs,
152154
.object_store = obj_store,
153155
.object_store_config = obj_store != NULL ? &os_cfg : NULL,
154-
.max_concurrent_flushes = maxConcurrentFlushes};
156+
.max_concurrent_flushes = maxConcurrentFlushes,
157+
.finish_compactions_on_close = finishCompactionsOnClose ? 1 : 0};
155158

156159
tidesdb_t *db = NULL;
157160
int result = tidesdb_open(&config, &db);
@@ -632,7 +635,7 @@ JNIEXPORT jobject JNICALL Java_com_tidesdb_ColumnFamily_nativeGetStats(JNIEnv *e
632635

633636
jclass statsClass = (*env)->FindClass(env, "com/tidesdb/Stats");
634637
jmethodID constructor = (*env)->GetMethodID(env, statsClass, "<init>",
635-
"(IJ[J[ILcom/tidesdb/ColumnFamilyConfig;JJDD[JDDZJIDJD[JDI)V");
638+
"(IJ[J[ILcom/tidesdb/ColumnFamilyConfig;JJDD[JDDZJIDJD[JDIJJJJJJJ)V");
636639

637640
jobject statsObj = (*env)->NewObject(env, statsClass, constructor, stats->num_levels,
638641
(jlong)stats->memtable_size, levelSizes, levelNumSSTables,
@@ -646,7 +649,14 @@ JNIEXPORT jobject JNICALL Java_com_tidesdb_ColumnFamily_nativeGetStats(JNIEnv *e
646649
(jdouble)stats->tombstone_ratio,
647650
levelTombstoneCounts,
648651
(jdouble)stats->max_sst_density,
649-
(jint)stats->max_sst_density_level);
652+
(jint)stats->max_sst_density_level,
653+
(jlong)stats->wal_bytes_written,
654+
(jlong)stats->flush_bytes_written,
655+
(jlong)stats->compaction_bytes_written,
656+
(jlong)stats->compaction_bytes_read,
657+
(jlong)stats->user_bytes_written,
658+
(jlong)stats->flush_count,
659+
(jlong)stats->compaction_count);
650660

651661
tidesdb_free_stats(stats);
652662

@@ -1323,7 +1333,7 @@ JNIEXPORT jobject JNICALL Java_com_tidesdb_TidesDB_nativeGetDbStats(JNIEnv *env,
13231333
boolean, String, long, long, int, long, long, long, long, boolean */
13241334
jmethodID constructor = (*env)->GetMethodID(
13251335
env, dbStatsClass, "<init>",
1326-
"(IJJJIIJIIJIJJJJZJIZIJZLjava/lang/String;JJIJJJJZ)V");
1336+
"(IJJJIIJIIJIJJJJZJIZIJZLjava/lang/String;JJIJJJJZJJJJJJJJ)V");
13271337

13281338
jstring connectorStr = NULL;
13291339
if (db_stats.object_store_connector != NULL)
@@ -1362,7 +1372,15 @@ JNIEXPORT jobject JNICALL Java_com_tidesdb_TidesDB_nativeGetDbStats(JNIEnv *env,
13621372
(jlong)db_stats.upload_queue_depth,
13631373
(jlong)db_stats.total_uploads,
13641374
(jlong)db_stats.total_upload_failures,
1365-
db_stats.replica_mode != 0);
1375+
db_stats.replica_mode != 0,
1376+
(jlong)db_stats.uwal_bytes_written,
1377+
(jlong)db_stats.wal_bytes_written,
1378+
(jlong)db_stats.flush_bytes_written,
1379+
(jlong)db_stats.compaction_bytes_written,
1380+
(jlong)db_stats.compaction_bytes_read,
1381+
(jlong)db_stats.user_bytes_written,
1382+
(jlong)db_stats.flush_count,
1383+
(jlong)db_stats.compaction_count);
13661384
}
13671385

13681386
JNIEXPORT jdouble JNICALL Java_com_tidesdb_ColumnFamily_nativeRangeCost(JNIEnv *env, jclass cls,
@@ -1422,6 +1440,25 @@ JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativePromoteToPrimary(JNIEnv *e
14221440
}
14231441
}
14241442

1443+
JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeCancelBackgroundWork(JNIEnv *env, jclass cls,
1444+
jlong handle)
1445+
{
1446+
tidesdb_t *db = (tidesdb_t *)(uintptr_t)handle;
1447+
1448+
int result = tidesdb_cancel_background_work(db);
1449+
1450+
if (result != TDB_SUCCESS)
1451+
{
1452+
throwTidesDBException(env, result, getErrorMessage(result));
1453+
}
1454+
}
1455+
1456+
JNIEXPORT jlong JNICALL Java_com_tidesdb_TidesDB_nativeRaiseOpenFileLimit(JNIEnv *env, jclass cls,
1457+
jlong desired)
1458+
{
1459+
return (jlong)tidesdb_raise_open_file_limit((long)desired);
1460+
}
1461+
14251462
JNIEXPORT jint JNICALL Java_com_tidesdb_Config_nativeDefaultMaxConcurrentFlushes(JNIEnv *env,
14261463
jclass cls)
14271464
{
@@ -1443,6 +1480,159 @@ JNIEXPORT jlong JNICALL Java_com_tidesdb_ColumnFamilyConfig_nativeDefaultTombsto
14431480
return (jlong)cfg.tombstone_density_min_entries;
14441481
}
14451482

1483+
/**
1484+
* Builds a com.tidesdb.ColumnFamilyConfig from a native config struct via the
1485+
* ColumnFamilyConfig.fromNative static factory. Returns a local ref, or NULL on error.
1486+
*/
1487+
static jobject buildCfConfigObject(JNIEnv *env, const tidesdb_column_family_config_t *cfg)
1488+
{
1489+
jclass cfConfigClass = (*env)->FindClass(env, "com/tidesdb/ColumnFamilyConfig");
1490+
if (cfConfigClass == NULL) return NULL;
1491+
1492+
jmethodID fromNative = (*env)->GetStaticMethodID(
1493+
env, cfConfigClass, "fromNative",
1494+
"(JJIIJIZDZIIIJLjava/lang/String;IFIJIIDJZZZ)Lcom/tidesdb/ColumnFamilyConfig;");
1495+
if (fromNative == NULL)
1496+
{
1497+
(*env)->DeleteLocalRef(env, cfConfigClass);
1498+
return NULL;
1499+
}
1500+
1501+
jstring comparatorName = (*env)->NewStringUTF(env, cfg->comparator_name);
1502+
1503+
jobject obj = (*env)->CallStaticObjectMethod(
1504+
env, cfConfigClass, fromNative,
1505+
(jlong)cfg->write_buffer_size, (jlong)cfg->level_size_ratio, (jint)cfg->min_levels,
1506+
(jint)cfg->dividing_level_offset, (jlong)cfg->klog_value_threshold,
1507+
(jint)cfg->compression_algorithm,
1508+
cfg->enable_bloom_filter != 0 ? JNI_TRUE : JNI_FALSE, (jdouble)cfg->bloom_fpr,
1509+
cfg->enable_block_indexes != 0 ? JNI_TRUE : JNI_FALSE, (jint)cfg->index_sample_ratio,
1510+
(jint)cfg->block_index_prefix_len, (jint)cfg->sync_mode, (jlong)cfg->sync_interval_us,
1511+
comparatorName, (jint)cfg->skip_list_max_level, (jfloat)cfg->skip_list_probability,
1512+
(jint)cfg->default_isolation_level, (jlong)cfg->min_disk_space,
1513+
(jint)cfg->l1_file_count_trigger, (jint)cfg->l0_queue_stall_threshold,
1514+
(jdouble)cfg->tombstone_density_trigger, (jlong)cfg->tombstone_density_min_entries,
1515+
cfg->use_btree != 0 ? JNI_TRUE : JNI_FALSE,
1516+
cfg->object_lazy_compaction != 0 ? JNI_TRUE : JNI_FALSE,
1517+
cfg->object_prefetch_compaction != 0 ? JNI_TRUE : JNI_FALSE);
1518+
1519+
(*env)->DeleteLocalRef(env, comparatorName);
1520+
(*env)->DeleteLocalRef(env, cfConfigClass);
1521+
return obj;
1522+
}
1523+
1524+
JNIEXPORT void JNICALL Java_com_tidesdb_ColumnFamilyConfig_nativeSaveToIni(
1525+
JNIEnv *env, jclass cls, jstring iniFile, jstring sectionName, jlong writeBufferSize,
1526+
jlong levelSizeRatio, jint minLevels, jint dividingLevelOffset, jlong klogValueThreshold,
1527+
jint compressionAlgorithm, jboolean enableBloomFilter, jdouble bloomFPR,
1528+
jboolean enableBlockIndexes, jint indexSampleRatio, jint blockIndexPrefixLen, jint syncMode,
1529+
jlong syncIntervalUs, jstring comparatorName, jint skipListMaxLevel, jfloat skipListProbability,
1530+
jint defaultIsolationLevel, jlong minDiskSpace, jint l1FileCountTrigger,
1531+
jint l0QueueStallThreshold, jdouble tombstoneDensityTrigger, jlong tombstoneDensityMinEntries,
1532+
jboolean useBtree, jboolean objectLazyCompaction, jboolean objectPrefetchCompaction)
1533+
{
1534+
const char *ini = (*env)->GetStringUTFChars(env, iniFile, NULL);
1535+
if (ini == NULL)
1536+
{
1537+
throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get INI file path");
1538+
return;
1539+
}
1540+
const char *section = (*env)->GetStringUTFChars(env, sectionName, NULL);
1541+
if (section == NULL)
1542+
{
1543+
(*env)->ReleaseStringUTFChars(env, iniFile, ini);
1544+
throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get section name");
1545+
return;
1546+
}
1547+
1548+
const char *compName = NULL;
1549+
if (comparatorName != NULL)
1550+
{
1551+
compName = (*env)->GetStringUTFChars(env, comparatorName, NULL);
1552+
}
1553+
1554+
tidesdb_column_family_config_t config = {
1555+
.write_buffer_size = (size_t)writeBufferSize,
1556+
.level_size_ratio = (size_t)levelSizeRatio,
1557+
.min_levels = minLevels,
1558+
.dividing_level_offset = dividingLevelOffset,
1559+
.klog_value_threshold = (size_t)klogValueThreshold,
1560+
.compression_algorithm = (compression_algorithm)compressionAlgorithm,
1561+
.enable_bloom_filter = enableBloomFilter ? 1 : 0,
1562+
.bloom_fpr = bloomFPR,
1563+
.enable_block_indexes = enableBlockIndexes ? 1 : 0,
1564+
.index_sample_ratio = indexSampleRatio,
1565+
.block_index_prefix_len = blockIndexPrefixLen,
1566+
.sync_mode = syncMode,
1567+
.sync_interval_us = (uint64_t)syncIntervalUs,
1568+
.skip_list_max_level = skipListMaxLevel,
1569+
.skip_list_probability = skipListProbability,
1570+
.default_isolation_level = (tidesdb_isolation_level_t)defaultIsolationLevel,
1571+
.min_disk_space = (uint64_t)minDiskSpace,
1572+
.l1_file_count_trigger = l1FileCountTrigger,
1573+
.l0_queue_stall_threshold = l0QueueStallThreshold,
1574+
.tombstone_density_trigger = tombstoneDensityTrigger,
1575+
.tombstone_density_min_entries = (uint64_t)tombstoneDensityMinEntries,
1576+
.use_btree = useBtree ? 1 : 0,
1577+
.object_lazy_compaction = objectLazyCompaction ? 1 : 0,
1578+
.object_prefetch_compaction = objectPrefetchCompaction ? 1 : 0};
1579+
1580+
memset(config.comparator_name, 0, TDB_MAX_COMPARATOR_NAME);
1581+
if (compName != NULL && strlen(compName) > 0)
1582+
{
1583+
strncpy(config.comparator_name, compName, TDB_MAX_COMPARATOR_NAME - 1);
1584+
}
1585+
memset(config.comparator_ctx_str, 0, TDB_MAX_COMPARATOR_CTX);
1586+
1587+
int result = tidesdb_cf_config_save_to_ini(ini, section, &config);
1588+
1589+
(*env)->ReleaseStringUTFChars(env, iniFile, ini);
1590+
(*env)->ReleaseStringUTFChars(env, sectionName, section);
1591+
if (compName != NULL)
1592+
{
1593+
(*env)->ReleaseStringUTFChars(env, comparatorName, compName);
1594+
}
1595+
1596+
if (result != TDB_SUCCESS)
1597+
{
1598+
throwTidesDBException(env, result, getErrorMessage(result));
1599+
}
1600+
}
1601+
1602+
JNIEXPORT jobject JNICALL Java_com_tidesdb_ColumnFamilyConfig_nativeLoadFromIni(
1603+
JNIEnv *env, jclass cls, jstring iniFile, jstring sectionName)
1604+
{
1605+
const char *ini = (*env)->GetStringUTFChars(env, iniFile, NULL);
1606+
if (ini == NULL)
1607+
{
1608+
throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get INI file path");
1609+
return NULL;
1610+
}
1611+
const char *section = (*env)->GetStringUTFChars(env, sectionName, NULL);
1612+
if (section == NULL)
1613+
{
1614+
(*env)->ReleaseStringUTFChars(env, iniFile, ini);
1615+
throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get section name");
1616+
return NULL;
1617+
}
1618+
1619+
/* start from engine defaults so fields absent from the INI section keep sane values */
1620+
tidesdb_column_family_config_t config = tidesdb_default_column_family_config();
1621+
1622+
int result = tidesdb_cf_config_load_from_ini(ini, section, &config);
1623+
1624+
(*env)->ReleaseStringUTFChars(env, iniFile, ini);
1625+
(*env)->ReleaseStringUTFChars(env, sectionName, section);
1626+
1627+
if (result != TDB_SUCCESS)
1628+
{
1629+
throwTidesDBException(env, result, getErrorMessage(result));
1630+
return NULL;
1631+
}
1632+
1633+
return buildCfConfigObject(env, &config);
1634+
}
1635+
14461636
JNIEXPORT jobject JNICALL Java_com_tidesdb_TidesDBIterator_nativeKeyValue(JNIEnv *env, jclass cls,
14471637
jlong handle)
14481638
{

src/main/java/com/tidesdb/ColumnFamilyConfig.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,70 @@ static ColumnFamilyConfig fromNative(long writeBufferSize, long levelSizeRatio,
199199
public boolean isObjectLazyCompaction() { return objectLazyCompaction; }
200200
public boolean isObjectPrefetchCompaction() { return objectPrefetchCompaction; }
201201

202+
/**
203+
* Saves this column family configuration to an INI file under the given section.
204+
* If the file already exists it is overwritten. The written file can be read back
205+
* with {@link #loadFromIni(String, String)}.
206+
*
207+
* <p>Note: not every field round-trips. The persisted fields are the ones the engine
208+
* stores in a column family's {@code config.ini} (write buffer size, level ratios,
209+
* compression, bloom/index settings, sync mode, skip list parameters, isolation level,
210+
* compaction triggers, tombstone density, B+tree and object-store flags, and the
211+
* comparator name). Runtime-only fields such as commit hooks are not persisted.</p>
212+
*
213+
* @param iniFile path to the INI file to write
214+
* @param sectionName section name to write the configuration under
215+
* @throws TidesDBException if the file cannot be written
216+
*/
217+
public void saveToIni(String iniFile, String sectionName) throws TidesDBException {
218+
if (iniFile == null || iniFile.isEmpty()) {
219+
throw new IllegalArgumentException("INI file path cannot be null or empty");
220+
}
221+
if (sectionName == null || sectionName.isEmpty()) {
222+
throw new IllegalArgumentException("Section name cannot be null or empty");
223+
}
224+
nativeSaveToIni(iniFile, sectionName,
225+
writeBufferSize, levelSizeRatio, minLevels, dividingLevelOffset, klogValueThreshold,
226+
compressionAlgorithm.getValue(), enableBloomFilter, bloomFPR, enableBlockIndexes,
227+
indexSampleRatio, blockIndexPrefixLen, syncMode.getValue(), syncIntervalUs,
228+
comparatorName, skipListMaxLevel, skipListProbability,
229+
defaultIsolationLevel.getValue(), minDiskSpace, l1FileCountTrigger,
230+
l0QueueStallThreshold, tombstoneDensityTrigger, tombstoneDensityMinEntries,
231+
useBtree, objectLazyCompaction, objectPrefetchCompaction);
232+
}
233+
234+
/**
235+
* Loads a column family configuration from an INI file section previously written by
236+
* {@link #saveToIni(String, String)} (or produced by the engine for an existing column
237+
* family). Fields absent from the section fall back to the engine defaults.
238+
*
239+
* @param iniFile path to the INI file to read
240+
* @param sectionName section name to read the configuration from
241+
* @return the loaded configuration
242+
* @throws TidesDBException if the file cannot be read or the section is missing
243+
*/
244+
public static ColumnFamilyConfig loadFromIni(String iniFile, String sectionName) throws TidesDBException {
245+
if (iniFile == null || iniFile.isEmpty()) {
246+
throw new IllegalArgumentException("INI file path cannot be null or empty");
247+
}
248+
if (sectionName == null || sectionName.isEmpty()) {
249+
throw new IllegalArgumentException("Section name cannot be null or empty");
250+
}
251+
return nativeLoadFromIni(iniFile, sectionName);
252+
}
253+
202254
private static native double nativeDefaultTombstoneDensityTrigger();
203255
private static native long nativeDefaultTombstoneDensityMinEntries();
256+
private static native void nativeSaveToIni(String iniFile, String sectionName,
257+
long writeBufferSize, long levelSizeRatio, int minLevels, int dividingLevelOffset,
258+
long klogValueThreshold, int compressionAlgorithm, boolean enableBloomFilter,
259+
double bloomFPR, boolean enableBlockIndexes, int indexSampleRatio, int blockIndexPrefixLen,
260+
int syncMode, long syncIntervalUs, String comparatorName, int skipListMaxLevel,
261+
float skipListProbability, int defaultIsolationLevel, long minDiskSpace,
262+
int l1FileCountTrigger, int l0QueueStallThreshold, double tombstoneDensityTrigger,
263+
long tombstoneDensityMinEntries, boolean useBtree, boolean objectLazyCompaction,
264+
boolean objectPrefetchCompaction) throws TidesDBException;
265+
private static native ColumnFamilyConfig nativeLoadFromIni(String iniFile, String sectionName) throws TidesDBException;
204266

205267
/**
206268
* Builder for ColumnFamilyConfig.

0 commit comments

Comments
 (0)