diff --git a/src/main/java/com/tidesdb/CacheStats.java b/src/main/java/com/tidesdb/CacheStats.java index cc846f4..de29061 100644 --- a/src/main/java/com/tidesdb/CacheStats.java +++ b/src/main/java/com/tidesdb/CacheStats.java @@ -19,7 +19,8 @@ package com.tidesdb; /** - * Statistics about the block cache. + * Statistics about the block cache in a {@link TidesDB} instance. Created by + * the native library and returned from {@link TidesDB#getCacheStats()}. */ public class CacheStats { @@ -31,6 +32,18 @@ public class CacheStats { private final double hitRate; private final long numPartitions; + /** + * Creates a new {@code CacheStats} instance. Typically called by the JNI + * bridge rather than application code. + * + * @param enabled whether the cache is enabled + * @param totalEntries the total number of cache entries + * @param totalBytes the total bytes used by the cache + * @param hits the number of cache hits + * @param misses the number of cache misses + * @param hitRate the cache hit rate (0.0 to 1.0) + * @param numPartitions the number of cache partitions + */ public CacheStats(boolean enabled, long totalEntries, long totalBytes, long hits, long misses, double hitRate, long numPartitions) { this.enabled = enabled; this.totalEntries = totalEntries; @@ -42,63 +55,63 @@ public CacheStats(boolean enabled, long totalEntries, long totalBytes, long hits } /** - * Returns whether the cache is enabled. + * Returns whether the block cache is enabled. * - * @return true if enabled + * @return {@code true} if the cache is enabled */ public boolean isEnabled() { return enabled; } /** - * Gets the total number of entries in the cache. + * Returns the total number of entries currently in the cache. * - * @return total entries + * @return the total entry count */ public long getTotalEntries() { return totalEntries; } /** - * Gets the total bytes used by the cache. + * Returns the total bytes used by the cache. * - * @return total bytes + * @return the total bytes */ public long getTotalBytes() { return totalBytes; } /** - * Gets the number of cache hits. + * Returns the cumulative number of cache hits. * - * @return cache hits + * @return the cache hit count */ public long getHits() { return hits; } /** - * Gets the number of cache misses. + * Returns the cumulative number of cache misses. * - * @return cache misses + * @return the cache miss count */ public long getMisses() { return misses; } /** - * Gets the cache hit rate. + * Returns the cache hit rate. * - * @return hit rate (0.0 to 1.0) + * @return the hit rate, typically in the range 0.0 to 1.0 */ public double getHitRate() { return hitRate; } /** - * Gets the number of cache partitions. + * Returns the number of cache partitions. * - * @return number of partitions + * @return the partition count */ public long getNumPartitions() { return numPartitions; diff --git a/src/main/java/com/tidesdb/ColumnFamily.java b/src/main/java/com/tidesdb/ColumnFamily.java index e07a57f..aee3f8f 100644 --- a/src/main/java/com/tidesdb/ColumnFamily.java +++ b/src/main/java/com/tidesdb/ColumnFamily.java @@ -19,8 +19,15 @@ package com.tidesdb; /** - * Represents a column family in TidesDB. - * Column families are isolated key-value stores with independent configuration. + * Represents a column family in TidesDB. A column family is an isolated + * key-value store within a database, with its own independent configuration. + * + *
A {@code ColumnFamily} is a handle returned by {@link TidesDB#getColumnFamily(String)} + * and is not independently closeable. There is no Java-side guard against using + * a column family after its owning database has been closed; callers must manage + * the lifecycle externally. + * + *
This class is not guaranteed to be thread-safe. */ public class ColumnFamily { @@ -40,7 +47,7 @@ public class ColumnFamily { /** * Gets the name of this column family. * - * @return the column family name + * @return the column family name, never {@code null} */ public String getName() { return name; @@ -49,8 +56,8 @@ public String getName() { /** * Retrieves statistics about this column family. * - * @return column family statistics - * @throws TidesDBException if the stats cannot be retrieved + * @return column family statistics, never {@code null} + * @throws TidesDBException if the native stats retrieval fails */ public Stats getStats() throws TidesDBException { return nativeGetStats(nativeHandle); @@ -59,7 +66,7 @@ public Stats getStats() throws TidesDBException { /** * Manually triggers compaction for this column family. * - * @throws TidesDBException if compaction fails + * @throws TidesDBException if the native compaction fails */ public void compact() throws TidesDBException { nativeCompact(nativeHandle); @@ -84,9 +91,9 @@ public void compactRange(byte[] startKey, byte[] endKey) throws TidesDBException } /** - * Manually triggers memtable flush for this column family. + * Manually triggers a memtable flush for this column family. * - * @throws TidesDBException if flush fails + * @throws TidesDBException if the native flush fails */ public void flushMemtable() throws TidesDBException { nativeFlushMemtable(nativeHandle); diff --git a/src/main/java/com/tidesdb/ColumnFamilyConfig.java b/src/main/java/com/tidesdb/ColumnFamilyConfig.java index ff7c4a6..2271876 100644 --- a/src/main/java/com/tidesdb/ColumnFamilyConfig.java +++ b/src/main/java/com/tidesdb/ColumnFamilyConfig.java @@ -19,7 +19,13 @@ package com.tidesdb; /** - * Configuration for a column family. + * Configuration for a column family. Use {@link #builder()} to construct a + * configuration with custom values, or {@link #defaultConfig()} to obtain a + * configuration with default settings. + * + *
Instances are immutable once built. Unlike {@link Config.Builder}, the + * {@link Builder#build()} method does not perform Java-side validation of the + * field values. */ public class ColumnFamilyConfig { @@ -86,7 +92,7 @@ private ColumnFamilyConfig(Builder builder) { * are sourced from the underlying C library so that this binding tracks the * engine's defaults automatically. * - * @return a new ColumnFamilyConfig with default values + * @return a new {@code ColumnFamilyConfig} with default values */ public static ColumnFamilyConfig defaultConfig() { return new Builder() @@ -119,9 +125,9 @@ public static ColumnFamilyConfig defaultConfig() { } /** - * Creates a new builder for ColumnFamilyConfig. + * Creates a new builder with default values. * - * @return a new Builder + * @return a new {@code Builder} */ public static Builder builder() { return new Builder(); @@ -173,25 +179,150 @@ static ColumnFamilyConfig fromNative(long writeBufferSize, long levelSizeRatio, .build(); } + /** + * Returns the write-buffer (memtable) size in bytes. + * + * @return the write-buffer size in bytes + */ public long getWriteBufferSize() { return writeBufferSize; } + + /** + * Returns the size ratio between adjacent LSM levels. + * + * @return the level size ratio + */ public long getLevelSizeRatio() { return levelSizeRatio; } + + /** + * Returns the minimum number of LSM levels. + * + * @return the minimum level count + */ public int getMinLevels() { return minLevels; } + + /** + * Returns the dividing level offset used for tiering decisions. + * + * @return the dividing level offset + */ public int getDividingLevelOffset() { return dividingLevelOffset; } + + /** + * Returns the key-log value threshold in bytes. Values at or below this + * size are stored inline in the key log. + * + * @return the key-log value threshold in bytes + */ public long getKlogValueThreshold() { return klogValueThreshold; } + + /** + * Returns the compression algorithm used for SSTables. + * + * @return the compression algorithm + */ public CompressionAlgorithm getCompressionAlgorithm() { return compressionAlgorithm; } + + /** + * Returns whether Bloom filters are enabled for this column family. + * + * @return {@code true} if Bloom filters are enabled + */ public boolean isEnableBloomFilter() { return enableBloomFilter; } + + /** + * Returns the Bloom filter false-positive rate. + * + * @return the false-positive rate + */ public double getBloomFPR() { return bloomFPR; } + + /** + * Returns whether block indexes are enabled for this column family. + * + * @return {@code true} if block indexes are enabled + */ public boolean isEnableBlockIndexes() { return enableBlockIndexes; } + + /** + * Returns the index sample ratio for block indexes. + * + * @return the index sample ratio + */ public int getIndexSampleRatio() { return indexSampleRatio; } + + /** + * Returns the block index prefix length. + * + * @return the block index prefix length + */ public int getBlockIndexPrefixLen() { return blockIndexPrefixLen; } + + /** + * Returns the sync mode for durability control. + * + * @return the sync mode + */ public SyncMode getSyncMode() { return syncMode; } + + /** + * Returns the sync interval in microseconds. Only meaningful when + * {@code syncMode} is {@link SyncMode#SYNC_INTERVAL}. + * + * @return the sync interval in microseconds + */ public long getSyncIntervalUs() { return syncIntervalUs; } + + /** + * Returns the custom comparator name. An empty string indicates the + * default lexicographic comparator. + * + * @return the comparator name, or an empty string for the default + */ public String getComparatorName() { return comparatorName; } + + /** + * Returns the maximum level for the skip-list memtable. + * + * @return the skip-list maximum level + */ public int getSkipListMaxLevel() { return skipListMaxLevel; } + + /** + * Returns the probability parameter for skip-list level promotion. + * + * @return the skip-list probability + */ public float getSkipListProbability() { return skipListProbability; } + + /** + * Returns the default isolation level for transactions on this column + * family. + * + * @return the default isolation level + */ public IsolationLevel getDefaultIsolationLevel() { return defaultIsolationLevel; } + + /** + * Returns the minimum disk space in bytes required before writes are + * rejected. + * + * @return the minimum disk space in bytes + */ public long getMinDiskSpace() { return minDiskSpace; } + + /** + * Returns the L1 file-count trigger for compaction. + * + * @return the L1 file count trigger + */ public int getL1FileCountTrigger() { return l1FileCountTrigger; } + + /** + * Returns the L0 queue stall threshold. When the L0 file count reaches + * this threshold, writes are stalled until compaction reduces the count. + * + * @return the L0 queue stall threshold + */ public int getL0QueueStallThreshold() { return l0QueueStallThreshold; } public double getTombstoneDensityTrigger() { return tombstoneDensityTrigger; } public long getTombstoneDensityMinEntries() { return tombstoneDensityMinEntries; } @@ -265,10 +396,21 @@ private static native void nativeSaveToIni(String iniFile, String sectionName, private static native ColumnFamilyConfig nativeLoadFromIni(String iniFile, String sectionName) throws TidesDBException; /** - * Builder for ColumnFamilyConfig. + * Builder for {@link ColumnFamilyConfig}. All fields have sensible + * defaults matching {@link #defaultConfig()}. Call {@link #build()} to + * create the immutable configuration. + * + *
Unlike {@link Config.Builder}, the {@link #build()} method does not + * perform Java-side validation of field values. */ public static class Builder { private long writeBufferSize = 128 * 1024 * 1024; + + /** + * Creates a new builder with default values. + */ + public Builder() { + } private long levelSizeRatio = 10; private int minLevels = 5; private int dividingLevelOffset = 2; @@ -294,101 +436,228 @@ public static class Builder { private boolean objectLazyCompaction = false; private boolean objectPrefetchCompaction = true; + /** + * Sets the write-buffer (memtable) size in bytes. + * + * @param writeBufferSize the size in bytes + * @return this builder + */ public Builder writeBufferSize(long writeBufferSize) { this.writeBufferSize = writeBufferSize; return this; } + /** + * Sets the size ratio between adjacent LSM levels. + * + * @param levelSizeRatio the level size ratio + * @return this builder + */ public Builder levelSizeRatio(long levelSizeRatio) { this.levelSizeRatio = levelSizeRatio; return this; } + /** + * Sets the minimum number of LSM levels. + * + * @param minLevels the minimum level count + * @return this builder + */ public Builder minLevels(int minLevels) { this.minLevels = minLevels; return this; } + /** + * Sets the dividing level offset for tiering decisions. + * + * @param dividingLevelOffset the offset + * @return this builder + */ public Builder dividingLevelOffset(int dividingLevelOffset) { this.dividingLevelOffset = dividingLevelOffset; return this; } + /** + * Sets the key-log value threshold in bytes. + * + * @param klogValueThreshold the threshold in bytes + * @return this builder + */ public Builder klogValueThreshold(long klogValueThreshold) { this.klogValueThreshold = klogValueThreshold; return this; } + /** + * Sets the compression algorithm for SSTables. + * + * @param compressionAlgorithm the compression algorithm; must not be + * {@code null} + * @return this builder + */ public Builder compressionAlgorithm(CompressionAlgorithm compressionAlgorithm) { this.compressionAlgorithm = compressionAlgorithm; return this; } + /** + * Enables or disables Bloom filters. + * + * @param enableBloomFilter {@code true} to enable + * @return this builder + */ public Builder enableBloomFilter(boolean enableBloomFilter) { this.enableBloomFilter = enableBloomFilter; return this; } + /** + * Sets the Bloom filter false-positive rate. + * + * @param bloomFPR the false-positive rate + * @return this builder + */ public Builder bloomFPR(double bloomFPR) { this.bloomFPR = bloomFPR; return this; } + /** + * Enables or disables block indexes. + * + * @param enableBlockIndexes {@code true} to enable + * @return this builder + */ public Builder enableBlockIndexes(boolean enableBlockIndexes) { this.enableBlockIndexes = enableBlockIndexes; return this; } + /** + * Sets the index sample ratio for block indexes. + * + * @param indexSampleRatio the sample ratio + * @return this builder + */ public Builder indexSampleRatio(int indexSampleRatio) { this.indexSampleRatio = indexSampleRatio; return this; } + /** + * Sets the block index prefix length. + * + * @param blockIndexPrefixLen the prefix length + * @return this builder + */ public Builder blockIndexPrefixLen(int blockIndexPrefixLen) { this.blockIndexPrefixLen = blockIndexPrefixLen; return this; } + /** + * Sets the sync mode for durability control. + * + * @param syncMode the sync mode; must not be {@code null} + * @return this builder + */ public Builder syncMode(SyncMode syncMode) { this.syncMode = syncMode; return this; } + /** + * Sets the sync interval in microseconds. Only meaningful when + * {@code syncMode} is {@link SyncMode#SYNC_INTERVAL}. + * + * @param syncIntervalUs the interval in microseconds + * @return this builder + */ public Builder syncIntervalUs(long syncIntervalUs) { this.syncIntervalUs = syncIntervalUs; return this; } + /** + * Sets the custom comparator name. An empty string selects the + * default lexicographic comparator. + * + * @param comparatorName the comparator name + * @return this builder + */ public Builder comparatorName(String comparatorName) { this.comparatorName = comparatorName; return this; } + /** + * Sets the maximum level for the skip-list memtable. + * + * @param skipListMaxLevel the maximum level + * @return this builder + */ public Builder skipListMaxLevel(int skipListMaxLevel) { this.skipListMaxLevel = skipListMaxLevel; return this; } + /** + * Sets the probability parameter for skip-list level promotion. + * + * @param skipListProbability the promotion probability + * @return this builder + */ public Builder skipListProbability(float skipListProbability) { this.skipListProbability = skipListProbability; return this; } + /** + * Sets the default isolation level for transactions on this column + * family. + * + * @param defaultIsolationLevel the isolation level; must not be + * {@code null} + * @return this builder + */ public Builder defaultIsolationLevel(IsolationLevel defaultIsolationLevel) { this.defaultIsolationLevel = defaultIsolationLevel; return this; } + /** + * Sets the minimum disk space in bytes required before writes are + * rejected. + * + * @param minDiskSpace the minimum disk space in bytes + * @return this builder + */ public Builder minDiskSpace(long minDiskSpace) { this.minDiskSpace = minDiskSpace; return this; } + /** + * Sets the L1 file-count trigger for compaction. + * + * @param l1FileCountTrigger the file count trigger + * @return this builder + */ public Builder l1FileCountTrigger(int l1FileCountTrigger) { this.l1FileCountTrigger = l1FileCountTrigger; return this; } + /** + * Sets the L0 queue stall threshold. When the L0 file count reaches + * this threshold, writes are stalled until compaction reduces the count. + * + * @param l0QueueStallThreshold the stall threshold + * @return this builder + */ public Builder l0QueueStallThreshold(int l0QueueStallThreshold) { this.l0QueueStallThreshold = l0QueueStallThreshold; return this; @@ -419,6 +688,12 @@ public Builder objectPrefetchCompaction(boolean objectPrefetchCompaction) { return this; } + /** + * Creates the immutable {@link ColumnFamilyConfig}. No Java-side + * validation of field values is performed. + * + * @return a new {@code ColumnFamilyConfig} + */ public ColumnFamilyConfig build() { return new ColumnFamilyConfig(this); } diff --git a/src/main/java/com/tidesdb/CompressionAlgorithm.java b/src/main/java/com/tidesdb/CompressionAlgorithm.java index 24cd8a0..1d2f58f 100644 --- a/src/main/java/com/tidesdb/CompressionAlgorithm.java +++ b/src/main/java/com/tidesdb/CompressionAlgorithm.java @@ -19,13 +19,34 @@ package com.tidesdb; /** - * Compression algorithm for column families. + * Compression algorithm for column family SSTables. Each constant maps to an + * integer used by the JNI bridge. */ public enum CompressionAlgorithm { + + /** + * No compression. + */ NO_COMPRESSION(0), + + /** + * Snappy compression. + */ SNAPPY_COMPRESSION(1), + + /** + * LZ4 compression with default settings. + */ LZ4_COMPRESSION(2), + + /** + * Zstandard compression. + */ ZSTD_COMPRESSION(3), + + /** + * LZ4 compression optimized for speed. + */ LZ4_FAST_COMPRESSION(4); private final int value; @@ -34,10 +55,24 @@ public enum CompressionAlgorithm { this.value = value; } + /** + * Returns the JNI numeric mapping for this compression algorithm. + * + * @return the integer value passed to the native library + */ public int getValue() { return value; } + /** + * Returns the {@link CompressionAlgorithm} constant matching the given + * JNI integer value. + * + * @param value the JNI integer value + * @return the matching constant + * @throws IllegalArgumentException if {@code value} does not map to any + * known constant + */ public static CompressionAlgorithm fromValue(int value) { for (CompressionAlgorithm algo : values()) { if (algo.value == value) { diff --git a/src/main/java/com/tidesdb/Config.java b/src/main/java/com/tidesdb/Config.java index 16d9d2c..87eef5e 100644 --- a/src/main/java/com/tidesdb/Config.java +++ b/src/main/java/com/tidesdb/Config.java @@ -19,7 +19,12 @@ package com.tidesdb; /** - * Configuration for opening a TidesDB instance. + * Configuration for opening a {@link TidesDB} instance. Use {@link #builder(String)} + * to construct a configuration with custom values, or {@link #defaultConfig()} to + * obtain a configuration with default settings. + * + *
Instances are immutable once built. {@link Builder#build()} validates all fields + * and throws {@link IllegalArgumentException} for invalid values. */ public class Config { @@ -72,11 +77,18 @@ private Config(Builder builder) { } /** - * Creates a default configuration. The {@code maxConcurrentFlushes} default is - * sourced from the underlying C library via {@code tidesdb_default_config()} so - * the binding tracks the engine's defaults automatically. + * Creates a default configuration with the following values: + *
The {@link #load()} method is idempotent and {@code synchronized}; it + * attempts to load the native library through the following ordered lookup + * paths: + *
If all paths fail, {@link UnsatisfiedLinkError} is thrown with details + * about the attempted paths. + * + *
{@link #isLoaded()} reports whether the library has been loaded + * successfully; it does not synchronize or guarantee availability. */ public class NativeLibrary { + private NativeLibrary() { + // utility class + } + private static final String LIBRARY_NAME = "tidesdb_jni"; private static volatile boolean loaded = false; private static final Object lock = new Object(); /** - * Loads the native library. - * This method is idempotent and thread-safe. + * Loads the native TidesDB JNI library. This method is idempotent and + * synchronized; concurrent calls are safe and only the first call + * performs the actual loading. + * + *
The lookup order is: + *
This method reports the current loaded flag without synchronizing; + * it does not guarantee that subsequent {@link #load()} calls will + * succeed or that the library is usable from the calling thread. * - * @return true if loaded + * @return {@code true} if the library has been loaded */ public static boolean isLoaded() { return loaded; diff --git a/src/main/java/com/tidesdb/Stats.java b/src/main/java/com/tidesdb/Stats.java index 44aa721..5122b1c 100644 --- a/src/main/java/com/tidesdb/Stats.java +++ b/src/main/java/com/tidesdb/Stats.java @@ -19,7 +19,13 @@ package com.tidesdb; /** - * Statistics about a column family. + * Statistics about a column family, including level structure, memtable size, + * and the active configuration. Created by the native library and returned + * from {@link ColumnFamily#getStats()}. + * + *
The arrays returned by {@link #getLevelSizes()} and + * {@link #getLevelNumSSTables()} are the internal stored references, not + * defensive copies. Callers must not modify them. */ public class Stats { @@ -52,6 +58,39 @@ public class Stats { private final long flushCount; private final long compactionCount; + /** + * Creates a new {@code Stats} instance. Typically called by the JNI + * bridge rather than application code. + * + * @param numLevels the number of LSM levels + * @param memtableSize the current memtable size in bytes + * @param levelSizes the size in bytes for each level (stored by reference) + * @param levelNumSSTables the SSTable count for each level (stored by reference) + * @param config the column family configuration + * @param totalKeys total number of keys across memtable and all SSTables + * @param totalDataSize total data size (klog + vlog) across all SSTables + * @param avgKeySize average key size in bytes + * @param avgValueSize average value size in bytes + * @param levelKeyCounts number of keys per level + * @param readAmp read amplification (point lookup cost multiplier) + * @param hitRate cache hit rate (0.0 to 1.0) + * @param useBtree whether this column family uses B+tree format + * @param btreeTotalNodes total number of B+tree nodes across all SSTables + * @param btreeMaxHeight maximum B+tree height across all SSTables + * @param btreeAvgHeight average B+tree height across all SSTables + * @param totalTombstones total number of tombstones across every SSTable + * @param tombstoneRatio tombstone ratio (totalTombstones / totalKeys) + * @param levelTombstoneCounts per-level tombstone counts + * @param maxSstDensity worst per-SSTable tombstone density + * @param maxSstDensityLevel 1-based level index where worst density was observed + * @param walBytesWritten framed bytes appended to WAL (lifetime since open) + * @param flushBytesWritten on-disk bytes flushes wrote to L0 SSTables + * @param compactionBytesWritten on-disk bytes compactions wrote + * @param compactionBytesRead on-disk bytes compactions read as input + * @param userBytesWritten logical key+value bytes committed + * @param flushCount number of flushed SSTables produced + * @param compactionCount number of compaction output SSTables produced + */ public Stats(int numLevels, long memtableSize, long[] levelSizes, int[] levelNumSSTables, ColumnFamilyConfig config, long totalKeys, long totalDataSize, double avgKeySize, double avgValueSize, long[] levelKeyCounts, @@ -93,7 +132,7 @@ public Stats(int numLevels, long memtableSize, long[] levelSizes, int[] levelNum } /** - * Gets the number of levels. + * Returns the number of LSM levels. * * @return the number of levels */ @@ -102,36 +141,43 @@ public int getNumLevels() { } /** - * Gets the memtable size in bytes. + * Returns the current memtable size in bytes. * - * @return the memtable size + * @return the memtable size in bytes */ public long getMemtableSize() { return memtableSize; } /** - * Gets the sizes of each level in bytes. + * Returns the sizes of each LSM level in bytes. + * + *
The returned array is the stored internal reference, not a + * defensive copy. Callers must not modify it. * - * @return array of level sizes + * @return the level sizes in bytes (internal reference) */ public long[] getLevelSizes() { return levelSizes; } /** - * Gets the number of SSTables at each level. + * Returns the number of SSTables at each LSM level. + * + *
The returned array is the stored internal reference, not a + * defensive copy. Callers must not modify it. * - * @return array of SSTable counts per level + * @return the SSTable counts per level (internal reference) */ public int[] getLevelNumSSTables() { return levelNumSSTables; } /** - * Gets the column family configuration. + * Returns the column family configuration active when these statistics + * were captured. * - * @return the configuration + * @return the configuration, never {@code null} */ public ColumnFamilyConfig getConfig() { return config; diff --git a/src/main/java/com/tidesdb/SyncMode.java b/src/main/java/com/tidesdb/SyncMode.java index 0958b21..9e6894b 100644 --- a/src/main/java/com/tidesdb/SyncMode.java +++ b/src/main/java/com/tidesdb/SyncMode.java @@ -19,11 +19,26 @@ package com.tidesdb; /** - * Sync mode for durability. + * Sync mode for durability control. Each constant maps to an integer used by + * the JNI bridge. */ public enum SyncMode { + + /** + * No synchronous writes; fastest, but uncommitted data may be lost on + * crash. + */ SYNC_NONE(0), + + /** + * Synchronous writes on every operation; slowest, but most durable. + */ SYNC_FULL(1), + + /** + * Synchronous writes at a configured interval (see + * {@code syncIntervalUs} in {@link ColumnFamilyConfig}). + */ SYNC_INTERVAL(2); private final int value; @@ -32,10 +47,24 @@ public enum SyncMode { this.value = value; } + /** + * Returns the JNI numeric mapping for this sync mode. + * + * @return the integer value passed to the native library + */ public int getValue() { return value; } + /** + * Returns the {@link SyncMode} constant matching the given JNI integer + * value. + * + * @param value the JNI integer value + * @return the matching constant + * @throws IllegalArgumentException if {@code value} does not map to any + * known constant + */ public static SyncMode fromValue(int value) { for (SyncMode mode : values()) { if (mode.value == value) { diff --git a/src/main/java/com/tidesdb/TidesDB.java b/src/main/java/com/tidesdb/TidesDB.java index c5a10c7..a001b73 100644 --- a/src/main/java/com/tidesdb/TidesDB.java +++ b/src/main/java/com/tidesdb/TidesDB.java @@ -21,8 +21,17 @@ import java.io.Closeable; /** - * TidesDB is the main database class providing access to TidesDB functionality. - * This class wraps the native TidesDB library through JNI. + * Main entry point for a TidesDB database. This class wraps the native TidesDB + * library through JNI and owns the underlying database handle. + * + *
{@code TidesDB} implements {@link java.io.Closeable} and should be used with + * try-with-resources. Callers must close any {@link TidesDBIterator} and + * {@link Transaction} instances before closing the database. Closing an already-closed + * instance is a no-op. + * + *
Operations on a closed instance throw {@link IllegalStateException}. + * + *
This class is not guaranteed to be thread-safe. */ public class TidesDB implements Closeable { @@ -40,9 +49,11 @@ private TidesDB(long nativeHandle) { /** * Opens a TidesDB instance with the given configuration. * - * @param config the database configuration + * @param config the database configuration; must not be {@code null} * @return a new TidesDB instance - * @throws TidesDBException if the database cannot be opened + * @throws IllegalArgumentException if {@code config} is {@code null} or its + * {@code dbPath} is {@code null} or empty + * @throws TidesDBException if the native database cannot be opened */ public static TidesDB open(Config config) throws TidesDBException { if (config == null) { @@ -129,7 +140,13 @@ public static boolean isS3Available() { } /** - * Closes the database instance and releases all resources. + * Closes the database and releases all native resources. + * + *
This method is idempotent; subsequent calls are no-ops. After closing, + * all other operations on this instance throw {@link IllegalStateException}. + * + *
Callers should close all {@link TidesDBIterator} and {@link Transaction} + * instances before calling this method. */ @Override public void close() { @@ -143,9 +160,12 @@ public void close() { /** * Creates a new column family with the given configuration. * - * @param name the column family name - * @param config the column family configuration - * @throws TidesDBException if the column family cannot be created + * @param name the column family name; must not be {@code null} or empty + * @param config the column family configuration; must not be {@code null} + * @throws IllegalArgumentException if {@code name} is {@code null} or empty, + * or {@code config} is {@code null} + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the native column family cannot be created */ public void createColumnFamily(String name, ColumnFamilyConfig config) throws TidesDBException { checkNotClosed(); @@ -188,8 +208,10 @@ public void createColumnFamily(String name, ColumnFamilyConfig config) throws Ti /** * Drops a column family and all associated data. * - * @param name the column family name - * @throws TidesDBException if the column family cannot be dropped + * @param name the column family name; must not be {@code null} or empty + * @throws IllegalArgumentException if {@code name} is {@code null} or empty + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the native column family cannot be dropped */ public void dropColumnFamily(String name) throws TidesDBException { checkNotClosed(); @@ -202,9 +224,12 @@ public void dropColumnFamily(String name) throws TidesDBException { /** * Retrieves a column family by name. * - * @param name the column family name - * @return the column family - * @throws TidesDBException if the column family is not found + * @param name the column family name; must not be {@code null} or empty + * @return the column family handle + * @throws IllegalArgumentException if {@code name} is {@code null} or empty + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the column family is not found or a native + * error occurs */ public ColumnFamily getColumnFamily(String name) throws TidesDBException { checkNotClosed(); @@ -218,8 +243,9 @@ public ColumnFamily getColumnFamily(String name) throws TidesDBException { /** * Lists all column families in the database. * - * @return array of column family names - * @throws TidesDBException if the list cannot be retrieved + * @return array of column family names, never {@code null} + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the native list operation fails */ public String[] listColumnFamilies() throws TidesDBException { checkNotClosed(); @@ -227,10 +253,13 @@ public String[] listColumnFamilies() throws TidesDBException { } /** - * Begins a new transaction with default isolation level. + * Begins a new transaction with the default isolation level. + * + *
Close the returned transaction before closing this database. * * @return a new transaction - * @throws TidesDBException if the transaction cannot be started + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the native transaction cannot be started */ public Transaction beginTransaction() throws TidesDBException { checkNotClosed(); @@ -241,9 +270,13 @@ public Transaction beginTransaction() throws TidesDBException { /** * Begins a new transaction with the specified isolation level. * - * @param isolationLevel the isolation level + *
Close the returned transaction before closing this database. + * + * @param isolationLevel the isolation level; must not be {@code null} * @return a new transaction - * @throws TidesDBException if the transaction cannot be started + * @throws IllegalArgumentException if {@code isolationLevel} is {@code null} + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the native transaction cannot be started */ public Transaction beginTransaction(IsolationLevel isolationLevel) throws TidesDBException { checkNotClosed(); @@ -257,8 +290,9 @@ public Transaction beginTransaction(IsolationLevel isolationLevel) throws TidesD /** * Retrieves statistics about the block cache. * - * @return cache statistics - * @throws TidesDBException if the stats cannot be retrieved + * @return cache statistics, never {@code null} + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the native stats retrieval fails */ public CacheStats getCacheStats() throws TidesDBException { checkNotClosed(); @@ -268,9 +302,11 @@ public CacheStats getCacheStats() throws TidesDBException { /** * Registers a custom comparator with the database. * - * @param name the comparator name - * @param context optional context string - * @throws TidesDBException if the comparator cannot be registered + * @param name the comparator name; must not be {@code null} or empty + * @param context optional context string, may be {@code null} + * @throws IllegalArgumentException if {@code name} is {@code null} or empty + * @throws IllegalStateException if this database is closed + * @throws TidesDBException if the native comparator registration fails */ public void registerComparator(String name, String context) throws TidesDBException { checkNotClosed(); diff --git a/src/main/java/com/tidesdb/TidesDBException.java b/src/main/java/com/tidesdb/TidesDBException.java index 7bc2d53..3cfaf03 100644 --- a/src/main/java/com/tidesdb/TidesDBException.java +++ b/src/main/java/com/tidesdb/TidesDBException.java @@ -19,62 +19,142 @@ package com.tidesdb; /** - * Exception thrown by TidesDB operations. + * Checked exception thrown by TidesDB native operations. Each exception carries + * an integer error code corresponding to a TidesDB status. Use {@link #getErrorCode()} + * to retrieve the numeric code and {@link #getErrorMessage()} for a human-readable + * description. */ public class TidesDBException extends Exception { + /** + * The error code. + */ private final int errorCode; /** - * Error codes from TidesDB. + * Error codes returned by the native TidesDB library. */ public static final int ERR_SUCCESS = 0; + + /** + * Memory allocation failure. + */ public static final int ERR_MEMORY = -1; + + /** + * Invalid arguments passed to a native operation. + */ public static final int ERR_INVALID_ARGS = -2; + + /** + * Requested entry not found. + */ public static final int ERR_NOT_FOUND = -3; + + /** + * I/O error during a native operation. + */ public static final int ERR_IO = -4; + + /** + * Data corruption detected. + */ public static final int ERR_CORRUPTION = -5; + + /** + * Entry already exists. + */ public static final int ERR_EXISTS = -6; + + /** + * Transaction conflict. + */ public static final int ERR_CONFLICT = -7; + + /** + * Key or value exceeds size limits. + */ public static final int ERR_TOO_LARGE = -8; + + /** + * Memory limit exceeded. + */ public static final int ERR_MEMORY_LIMIT = -9; + + /** + * Invalid database handle. + */ public static final int ERR_INVALID_DB = -10; + + /** + * Unknown error. + */ public static final int ERR_UNKNOWN = -11; + + /** + * Database is locked by another process. + */ public static final int ERR_LOCKED = -12; + /** + * Creates an exception with a message and an unknown error code. + * + * @param message the detail message + */ public TidesDBException(String message) { super(message); this.errorCode = ERR_UNKNOWN; } + /** + * Creates an exception with a message and a specific error code. + * + * @param message the detail message + * @param errorCode the TidesDB error code + */ public TidesDBException(String message, int errorCode) { super(message); this.errorCode = errorCode; } + /** + * Creates an exception with a message and a cause, using an unknown + * error code. + * + * @param message the detail message + * @param cause the underlying cause + */ public TidesDBException(String message, Throwable cause) { super(message, cause); this.errorCode = ERR_UNKNOWN; } + /** + * Creates an exception with a message, error code, and cause. + * + * @param message the detail message + * @param errorCode the TidesDB error code + * @param cause the underlying cause + */ public TidesDBException(String message, int errorCode, Throwable cause) { super(message, cause); this.errorCode = errorCode; } /** - * Gets the error code. + * Returns the TidesDB error code. * - * @return the error code + * @return one of the {@code ERR_*} constants defined in this class */ public int getErrorCode() { return errorCode; } /** - * Returns a human-readable error message for the error code. + * Returns a human-readable description of the error code. The mapping is + * local to this class and does not invoke any native method. * - * @return error message + * @return the error description */ public String getErrorMessage() { switch (errorCode) { diff --git a/src/main/java/com/tidesdb/TidesDBIterator.java b/src/main/java/com/tidesdb/TidesDBIterator.java index 66900c6..b750dfb 100644 --- a/src/main/java/com/tidesdb/TidesDBIterator.java +++ b/src/main/java/com/tidesdb/TidesDBIterator.java @@ -22,7 +22,15 @@ /** * Iterator for traversing key-value pairs in a column family. - * Provides efficient bidirectional traversal. + * Provides efficient bidirectional traversal over the underlying storage. + * + *
{@code TidesDBIterator} implements {@link java.io.Closeable} for use with + * try-with-resources. Close iterators before the owning {@link Transaction} is + * freed. After this iterator is freed, all operations except {@link #isValid()} + * and {@code close()} throw {@link IllegalStateException}. The {@link #isValid()} + * method returns {@code false} on a freed iterator. + * + *
This class is not guaranteed to be thread-safe. */ public class TidesDBIterator implements Closeable { @@ -40,7 +48,8 @@ public class TidesDBIterator implements Closeable { /** * Positions the iterator at the first key. * - * @throws TidesDBException if the seek fails + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native seek fails */ public void seekToFirst() throws TidesDBException { checkNotFreed(); @@ -50,7 +59,8 @@ public void seekToFirst() throws TidesDBException { /** * Positions the iterator at the last key. * - * @throws TidesDBException if the seek fails + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native seek fails */ public void seekToLast() throws TidesDBException { checkNotFreed(); @@ -58,10 +68,13 @@ public void seekToLast() throws TidesDBException { } /** - * Positions the iterator at the first key >= target key. + * Positions the iterator at the first key greater than or equal to the + * target key. * - * @param key the target key - * @throws TidesDBException if the seek fails + * @param key the target key; must not be {@code null} or empty + * @throws IllegalArgumentException if {@code key} is {@code null} or empty + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native seek fails */ public void seek(byte[] key) throws TidesDBException { checkNotFreed(); @@ -72,10 +85,13 @@ public void seek(byte[] key) throws TidesDBException { } /** - * Positions the iterator at the last key {@code <=} target key. + * Positions the iterator at the last key less than or equal to the target + * key. * - * @param key the target key - * @throws TidesDBException if the seek fails + * @param key the target key; must not be {@code null} or empty + * @throws IllegalArgumentException if {@code key} is {@code null} or empty + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native seek fails */ public void seekForPrev(byte[] key) throws TidesDBException { checkNotFreed(); @@ -86,9 +102,12 @@ public void seekForPrev(byte[] key) throws TidesDBException { } /** - * Returns true if the iterator is positioned at a valid entry. + * Returns whether the iterator is positioned at a valid entry. * - * @return true if valid + *
Returns {@code false} if this iterator has been freed, without + * throwing an exception. + * + * @return {@code true} if the iterator is at a valid entry */ public boolean isValid() { if (freed) { @@ -100,7 +119,8 @@ public boolean isValid() { /** * Moves the iterator to the next entry. * - * @throws TidesDBException if the move fails + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native next operation fails */ public void next() throws TidesDBException { checkNotFreed(); @@ -110,7 +130,8 @@ public void next() throws TidesDBException { /** * Moves the iterator to the previous entry. * - * @throws TidesDBException if the move fails + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native prev operation fails */ public void prev() throws TidesDBException { checkNotFreed(); @@ -118,10 +139,11 @@ public void prev() throws TidesDBException { } /** - * Retrieves the current key from the iterator. + * Retrieves the key at the current iterator position. * * @return the current key - * @throws TidesDBException if the key cannot be retrieved + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native key retrieval fails */ public byte[] key() throws TidesDBException { checkNotFreed(); @@ -129,10 +151,11 @@ public byte[] key() throws TidesDBException { } /** - * Retrieves the current value from the iterator. + * Retrieves the value at the current iterator position. * * @return the current value - * @throws TidesDBException if the value cannot be retrieved + * @throws IllegalStateException if this iterator is freed + * @throws TidesDBException if the native value retrieval fails */ public byte[] value() throws TidesDBException { checkNotFreed(); @@ -153,7 +176,10 @@ public KeyValue keyValue() throws TidesDBException { } /** - * Frees the iterator resources. + * Frees the iterator and releases all native resources. + * + *
This method is idempotent; subsequent calls are no-ops. After freeing, + * all operations except {@link #isValid()} throw {@link IllegalStateException}. */ public void free() { if (!freed && nativeHandle != 0) { @@ -164,7 +190,7 @@ public void free() { } /** - * Closes the iterator (same as free). + * Closes this iterator. Equivalent to {@link #free()}. */ @Override public void close() { diff --git a/src/main/java/com/tidesdb/Transaction.java b/src/main/java/com/tidesdb/Transaction.java index a6baadc..58bfff8 100644 --- a/src/main/java/com/tidesdb/Transaction.java +++ b/src/main/java/com/tidesdb/Transaction.java @@ -21,8 +21,15 @@ import java.io.Closeable; /** - * Represents a transaction in TidesDB. - * Transactions provide atomic operations on the database. + * Represents a transaction in TidesDB. Transactions provide atomic + * operations on the database and implement {@link java.io.Closeable} for + * use with try-with-resources. + * + *
Close transactions before closing the owning {@link TidesDB} instance. + * After this transaction is freed, all operations except {@code close()} throw + * {@link IllegalStateException}. + * + *
This class is not guaranteed to be thread-safe. */ public class Transaction implements Closeable { @@ -40,11 +47,15 @@ public class Transaction implements Closeable { /** * Adds a key-value pair to the transaction. * - * @param cf the column family - * @param key the key - * @param value the value - * @param ttl Unix timestamp (seconds since epoch) for expiration, or -1 for no expiration - * @throws TidesDBException if the put fails + * @param cf the column family; must not be {@code null} + * @param key the key; must not be {@code null} or empty + * @param value the value; must not be {@code null} + * @param ttl expiration as seconds since the Unix epoch, or {@code -1} for + * no expiration + * @throws IllegalArgumentException if {@code cf} is {@code null}, {@code key} + * is {@code null} or empty, or {@code value} is {@code null} + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native put fails */ public void put(ColumnFamily cf, byte[] key, byte[] value, long ttl) throws TidesDBException { checkNotFreed(); @@ -63,10 +74,15 @@ public void put(ColumnFamily cf, byte[] key, byte[] value, long ttl) throws Tide /** * Adds a key-value pair to the transaction with no expiration. * - * @param cf the column family - * @param key the key - * @param value the value - * @throws TidesDBException if the put fails + *
Equivalent to {@code put(cf, key, value, -1)}. + * + * @param cf the column family; must not be {@code null} + * @param key the key; must not be {@code null} or empty + * @param value the value; must not be {@code null} + * @throws IllegalArgumentException if {@code cf} is {@code null}, {@code key} + * is {@code null} or empty, or {@code value} is {@code null} + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native put fails */ public void put(ColumnFamily cf, byte[] key, byte[] value) throws TidesDBException { put(cf, key, value, -1); @@ -75,10 +91,13 @@ public void put(ColumnFamily cf, byte[] key, byte[] value) throws TidesDBExcepti /** * Retrieves a value from the transaction. * - * @param cf the column family - * @param key the key - * @return the value, or null if not found - * @throws TidesDBException if the get fails + * @param cf the column family; must not be {@code null} + * @param key the key; must not be {@code null} or empty + * @return the value, or {@code null} if not found + * @throws IllegalArgumentException if {@code cf} is {@code null}, or + * {@code key} is {@code null} or empty + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native get fails */ public byte[] get(ColumnFamily cf, byte[] key) throws TidesDBException { checkNotFreed(); @@ -94,9 +113,12 @@ public byte[] get(ColumnFamily cf, byte[] key) throws TidesDBException { /** * Removes a key-value pair from the transaction. * - * @param cf the column family - * @param key the key - * @throws TidesDBException if the delete fails + * @param cf the column family; must not be {@code null} + * @param key the key; must not be {@code null} or empty + * @throws IllegalArgumentException if {@code cf} is {@code null}, or + * {@code key} is {@code null} or empty + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native delete fails */ public void delete(ColumnFamily cf, byte[] key) throws TidesDBException { checkNotFreed(); @@ -136,9 +158,10 @@ public void singleDelete(ColumnFamily cf, byte[] key) throws TidesDBException { } /** - * Commits the transaction. + * Commits the transaction, making all pending operations durable. * - * @throws TidesDBException if the commit fails + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native commit fails */ public void commit() throws TidesDBException { checkNotFreed(); @@ -146,9 +169,10 @@ public void commit() throws TidesDBException { } /** - * Rolls back the transaction. + * Rolls back all operations in the transaction. * - * @throws TidesDBException if the rollback fails + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native rollback fails */ public void rollback() throws TidesDBException { checkNotFreed(); @@ -156,10 +180,12 @@ public void rollback() throws TidesDBException { } /** - * Creates a savepoint within the transaction. + * Creates a named savepoint within the transaction. * - * @param name the savepoint name - * @throws TidesDBException if the savepoint cannot be created + * @param name the savepoint name; must not be {@code null} or empty + * @throws IllegalArgumentException if {@code name} is {@code null} or empty + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native savepoint creation fails */ public void savepoint(String name) throws TidesDBException { checkNotFreed(); @@ -170,10 +196,12 @@ public void savepoint(String name) throws TidesDBException { } /** - * Rolls back the transaction to a savepoint. + * Rolls back the transaction to a named savepoint. * - * @param name the savepoint name - * @throws TidesDBException if the rollback fails + * @param name the savepoint name; must not be {@code null} or empty + * @throws IllegalArgumentException if {@code name} is {@code null} or empty + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native rollback fails */ public void rollbackToSavepoint(String name) throws TidesDBException { checkNotFreed(); @@ -184,10 +212,12 @@ public void rollbackToSavepoint(String name) throws TidesDBException { } /** - * Releases a savepoint without rolling back. + * Releases a named savepoint without rolling back. * - * @param name the savepoint name - * @throws TidesDBException if the release fails + * @param name the savepoint name; must not be {@code null} or empty + * @throws IllegalArgumentException if {@code name} is {@code null} or empty + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native release fails */ public void releaseSavepoint(String name) throws TidesDBException { checkNotFreed(); @@ -198,11 +228,15 @@ public void releaseSavepoint(String name) throws TidesDBException { } /** - * Creates a new iterator for a column family within this transaction. + * Creates a new iterator for the given column family within this transaction. * - * @param cf the column family + *
Close the returned iterator before freeing this transaction. + * + * @param cf the column family; must not be {@code null} * @return a new iterator - * @throws TidesDBException if the iterator cannot be created + * @throws IllegalArgumentException if {@code cf} is {@code null} + * @throws IllegalStateException if this transaction is freed + * @throws TidesDBException if the native iterator cannot be created */ public TidesDBIterator newIterator(ColumnFamily cf) throws TidesDBException { checkNotFreed(); @@ -229,7 +263,10 @@ public void reset(IsolationLevel isolation) throws TidesDBException { } /** - * Frees the transaction resources. + * Frees the transaction and releases all native resources. + * + *
This method is idempotent; subsequent calls are no-ops. After freeing, + * all other operations on this instance throw {@link IllegalStateException}. */ public void free() { if (!freed && nativeHandle != 0) { @@ -240,7 +277,7 @@ public void free() { } /** - * Closes the transaction (same as free). + * Closes this transaction. Equivalent to {@link #free()}. */ @Override public void close() {