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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(tidesdb_cpp VERSION 2.3.7 LANGUAGES CXX)
project(tidesdb_cpp VERSION 2.4.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,3 @@ Multiple licenses apply:
## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

- [Discord](https://discord.gg/tWEmjR66cy)
- [GitHub Issues](https://github.com/tidesdb/tidesdb-cpp/issues)
65 changes: 42 additions & 23 deletions include/tidesdb/tidesdb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,10 @@ struct ColumnFamilyConfig
int l0QueueStallThreshold = 20;
bool useBtree = false; ///< Use B+tree format for klog (default: false = block-based)
tidesdb_commit_hook_fn commitHookFn =
nullptr; ///< Optional commit hook callback (runtime-only)
void* commitHookCtx = nullptr; ///< Optional user context for commit hook (runtime-only)
std::size_t objectTargetFileSize = 0; ///< Target SSTable size in object store mode (0=auto)
int objectLazyCompaction = 0; ///< 1 = compact less aggressively in object store mode
int objectPrefetchCompaction = 1; ///< 1 = download all inputs before merge
nullptr; ///< Optional commit hook callback (runtime-only)
void* commitHookCtx = nullptr; ///< Optional user context for commit hook (runtime-only)
int objectLazyCompaction = 0; ///< 1 = compact less aggressively in object store mode
int objectPrefetchCompaction = 1; ///< 1 = download all inputs before merge

/**
* @brief Get default column family configuration from TidesDB
Expand Down Expand Up @@ -229,22 +228,25 @@ struct ColumnFamilyConfig
*/
struct ObjectStoreConfig
{
std::string localCachePath; ///< Local directory for cached SSTable files (empty = use db_path)
std::size_t localCacheMaxBytes = 0; ///< Max local cache size in bytes (0 = unlimited)
bool cacheOnRead = true; ///< Cache downloaded files locally
bool cacheOnWrite = true; ///< Keep local copy after upload
int maxConcurrentUploads = 4; ///< Parallel upload threads
int maxConcurrentDownloads = 8; ///< Parallel download threads
std::size_t multipartThreshold = 64 * 1024 * 1024; ///< Use multipart upload above this size (64MB)
std::size_t multipartPartSize = 8 * 1024 * 1024; ///< Chunk size for multipart uploads (8MB)
bool syncManifestToObject = true; ///< Upload MANIFEST after each compaction
bool replicateWal = true; ///< Upload closed WAL segments for replication
bool walUploadSync = false; ///< false = background WAL upload, true = block flush until uploaded
std::size_t walSyncThresholdBytes = 1048576; ///< Sync active WAL when it grows by this many bytes (0 = off)
bool walSyncOnCommit = false; ///< Upload WAL after every txn commit for RPO=0
bool replicaMode = false; ///< Enable read-only replica mode
std::uint64_t replicaSyncIntervalUs = 5000000; ///< MANIFEST poll interval in microseconds (5s)
bool replicaReplayWal = true; ///< Replay WAL from object store for near-real-time reads
std::string localCachePath; ///< Local directory for cached SSTable files (empty = use db_path)
std::size_t localCacheMaxBytes = 0; ///< Max local cache size in bytes (0 = unlimited)
bool cacheOnRead = true; ///< Cache downloaded files locally
bool cacheOnWrite = true; ///< Keep local copy after upload
int maxConcurrentUploads = 4; ///< Parallel upload threads
int maxConcurrentDownloads = 8; ///< Parallel download threads
std::size_t multipartThreshold =
64 * 1024 * 1024; ///< Use multipart upload above this size (64MB)
std::size_t multipartPartSize = 8 * 1024 * 1024; ///< Chunk size for multipart uploads (8MB)
bool syncManifestToObject = true; ///< Upload MANIFEST after each compaction
bool replicateWal = true; ///< Upload closed WAL segments for replication
bool walUploadSync =
false; ///< false = background WAL upload, true = block flush until uploaded
std::size_t walSyncThresholdBytes =
1048576; ///< Sync active WAL when it grows by this many bytes (0 = off)
bool walSyncOnCommit = false; ///< Upload WAL after every txn commit for RPO=0
bool replicaMode = false; ///< Enable read-only replica mode
std::uint64_t replicaSyncIntervalUs = 5000000; ///< MANIFEST poll interval in microseconds (5s)
bool replicaReplayWal = true; ///< Replay WAL from object store for near-real-time reads

/**
* @brief Get default object store configuration from TidesDB
Expand Down Expand Up @@ -273,8 +275,10 @@ struct Config
float unifiedMemtableSkipListProbability = 0; ///< Skip list probability (0 = default 0.25)
SyncMode unifiedMemtableSyncMode = SyncMode::None; ///< Sync mode for unified WAL
std::uint64_t unifiedMemtableSyncIntervalUs = 0; ///< Sync interval for unified WAL
tidesdb_objstore_t* objectStore = nullptr; ///< Pluggable object store connector (nullptr = local only)
std::optional<ObjectStoreConfig> objectStoreConfig; ///< Object store behavior config (nullopt = defaults)
tidesdb_objstore_t* objectStore =
nullptr; ///< Pluggable object store connector (nullptr = local only)
std::optional<ObjectStoreConfig>
objectStoreConfig; ///< Object store behavior config (nullopt = defaults)
};

/**
Expand Down Expand Up @@ -582,6 +586,21 @@ class Transaction
*/
void del(ColumnFamily& cf, const std::vector<std::uint8_t>& key);

/**
* @brief Single-delete a key
*
* Emits a single-delete tombstone. When a single-delete meets exactly one
* prior put for the same key during compaction, both records are dropped.
* Use only when the caller guarantees at most one put precedes the delete;
* otherwise prefer del(). See tidesdb_txn_single_delete (TDB v9.1.0).
*/
void singleDelete(ColumnFamily& cf, std::string_view key);

/**
* @brief Single-delete a key (byte vector overload)
*/
void singleDelete(ColumnFamily& cf, const std::vector<std::uint8_t>& key);

/**
* @brief Commit the transaction
*/
Expand Down
25 changes: 17 additions & 8 deletions src/tidesdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ ColumnFamilyConfig ColumnFamilyConfig::defaultConfig()
config.l1FileCountTrigger = cConfig.l1_file_count_trigger;
config.l0QueueStallThreshold = cConfig.l0_queue_stall_threshold;
config.useBtree = cConfig.use_btree != 0;
config.objectTargetFileSize = cConfig.object_target_file_size;
config.objectLazyCompaction = cConfig.object_lazy_compaction;
config.objectPrefetchCompaction = cConfig.object_prefetch_compaction;

Expand Down Expand Up @@ -106,7 +105,6 @@ ColumnFamilyConfig ColumnFamilyConfig::loadFromIni(const std::string& iniFile,
config.l1FileCountTrigger = cConfig.l1_file_count_trigger;
config.l0QueueStallThreshold = cConfig.l0_queue_stall_threshold;
config.useBtree = cConfig.use_btree != 0;
config.objectTargetFileSize = cConfig.object_target_file_size;
config.objectLazyCompaction = cConfig.object_lazy_compaction;
config.objectPrefetchCompaction = cConfig.object_prefetch_compaction;

Expand Down Expand Up @@ -139,7 +137,7 @@ void ColumnFamilyConfig::saveToIni(const std::string& iniFile, const std::string
cConfig.l1_file_count_trigger = config.l1FileCountTrigger;
cConfig.l0_queue_stall_threshold = config.l0QueueStallThreshold;
cConfig.use_btree = config.useBtree ? 1 : 0;
cConfig.object_target_file_size = config.objectTargetFileSize;
cConfig.object_target_file_size = 0; /* retired, reserved in C for ABI compatibility */
cConfig.object_lazy_compaction = config.objectLazyCompaction;
cConfig.object_prefetch_compaction = config.objectPrefetchCompaction;

Expand Down Expand Up @@ -255,7 +253,6 @@ Stats ColumnFamily::getStats() const
cfConfig.l1FileCountTrigger = cStats->config->l1_file_count_trigger;
cfConfig.l0QueueStallThreshold = cStats->config->l0_queue_stall_threshold;
cfConfig.useBtree = cStats->config->use_btree != 0;
cfConfig.objectTargetFileSize = cStats->config->object_target_file_size;
cfConfig.objectLazyCompaction = cStats->config->object_lazy_compaction;
cfConfig.objectPrefetchCompaction = cStats->config->object_prefetch_compaction;
stats.config = cfConfig;
Expand Down Expand Up @@ -355,7 +352,7 @@ void ColumnFamily::updateRuntimeConfig(const ColumnFamilyConfig& config, bool pe
cConfig.l1_file_count_trigger = config.l1FileCountTrigger;
cConfig.l0_queue_stall_threshold = config.l0QueueStallThreshold;
cConfig.use_btree = config.useBtree ? 1 : 0;
cConfig.object_target_file_size = config.objectTargetFileSize;
cConfig.object_target_file_size = 0; /* retired, reserved in C for ABI compatibility */
cConfig.object_lazy_compaction = config.objectLazyCompaction;
cConfig.object_prefetch_compaction = config.objectPrefetchCompaction;

Expand Down Expand Up @@ -584,6 +581,19 @@ void Transaction::del(ColumnFamily& cf, const std::vector<std::uint8_t>& key)
checkResult(result, "failed to delete key");
}

void Transaction::singleDelete(ColumnFamily& cf, std::string_view key)
{
int result = tidesdb_txn_single_delete(
txn_, cf.handle(), reinterpret_cast<const uint8_t*>(key.data()), key.size());
checkResult(result, "failed to single-delete key");
}

void Transaction::singleDelete(ColumnFamily& cf, const std::vector<std::uint8_t>& key)
{
int result = tidesdb_txn_single_delete(txn_, cf.handle(), key.data(), key.size());
checkResult(result, "failed to single-delete key");
}

void Transaction::commit()
{
int result = tidesdb_txn_commit(txn_);
Expand Down Expand Up @@ -656,8 +666,7 @@ TidesDB::TidesDB(const Config& config)
if (config.objectStoreConfig.has_value())
{
const auto& os = config.objectStoreConfig.value();
osCfg.local_cache_path =
os.localCachePath.empty() ? nullptr : os.localCachePath.c_str();
osCfg.local_cache_path = os.localCachePath.empty() ? nullptr : os.localCachePath.c_str();
osCfg.local_cache_max_bytes = os.localCacheMaxBytes;
osCfg.cache_on_read = os.cacheOnRead ? 1 : 0;
osCfg.cache_on_write = os.cacheOnWrite ? 1 : 0;
Expand Down Expand Up @@ -737,7 +746,7 @@ void TidesDB::createColumnFamily(const std::string& name, const ColumnFamilyConf
cConfig.l1_file_count_trigger = config.l1FileCountTrigger;
cConfig.l0_queue_stall_threshold = config.l0QueueStallThreshold;
cConfig.use_btree = config.useBtree ? 1 : 0;
cConfig.object_target_file_size = config.objectTargetFileSize;
cConfig.object_target_file_size = 0; /* retired, reserved in C for ABI compatibility */
cConfig.object_lazy_compaction = config.objectLazyCompaction;
cConfig.object_prefetch_compaction = config.objectPrefetchCompaction;

Expand Down
50 changes: 48 additions & 2 deletions tests/tidesdb_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,54 @@ TEST_F(TidesDBTest, TransactionPutGetDelete)
}
}

TEST_F(TidesDBTest, TransactionSingleDelete)
{
tidesdb::TidesDB db(getConfig());

auto cfConfig = tidesdb::ColumnFamilyConfig::defaultConfig();
db.createColumnFamily("test_cf", cfConfig);

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

{
auto txn = db.beginTransaction();
txn.put(cf, "sd_key", "sd_value", -1);
txn.commit();
}

{
auto txn = db.beginTransaction();
txn.singleDelete(cf, "sd_key");
txn.commit();
}

{
auto txn = db.beginTransaction();
EXPECT_THROW(txn.get(cf, "sd_key"), tidesdb::Exception);
}

// byte vector overload
std::vector<std::uint8_t> key{'b', 'y', 't', 'e', 'k', 'e', 'y'};
std::vector<std::uint8_t> value{'v', 'a', 'l'};

{
auto txn = db.beginTransaction();
txn.put(cf, key, value, -1);
txn.commit();
}

{
auto txn = db.beginTransaction();
txn.singleDelete(cf, key);
txn.commit();
}

{
auto txn = db.beginTransaction();
EXPECT_THROW(txn.get(cf, key), tidesdb::Exception);
}
}

TEST_F(TidesDBTest, TransactionWithTTL)
{
tidesdb::TidesDB db(getConfig());
Expand Down Expand Up @@ -1630,8 +1678,6 @@ TEST_F(TidesDBTest, ColumnFamilyConfigObjectStoreFields)
{
auto cfConfig = tidesdb::ColumnFamilyConfig::defaultConfig();

// Verify object store fields are populated from C defaults
ASSERT_GT(cfConfig.objectTargetFileSize, 0u); // default 256MB
ASSERT_GE(cfConfig.objectLazyCompaction, 0);
ASSERT_GE(cfConfig.objectPrefetchCompaction, 0);
}
Expand Down
Loading