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.4 LANGUAGES CXX)
project(tidesdb_cpp VERSION 2.3.5 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down
36 changes: 36 additions & 0 deletions include/tidesdb/tidesdb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,28 @@ struct Stats
double btreeAvgHeight = 0.0; ///< Average tree height across all SSTables
};

/**
* @brief Database-level statistics
*/
struct DbStats
{
int numColumnFamilies = 0;
std::uint64_t totalMemory = 0;
std::uint64_t availableMemory = 0;
std::size_t resolvedMemoryLimit = 0;
int memoryPressureLevel = 0;
int flushPendingCount = 0;
std::int64_t totalMemtableBytes = 0;
int totalImmutableCount = 0;
int totalSstableCount = 0;
std::uint64_t totalDataSizeBytes = 0;
int numOpenSstables = 0;
std::uint64_t globalSeq = 0;
std::int64_t txnMemoryBytes = 0;
std::size_t compactionQueueSize = 0;
std::size_t flushQueueSize = 0;
};

/**
* @brief Block cache statistics
*/
Expand Down Expand Up @@ -306,6 +328,14 @@ class ColumnFamily
*/
void purge();

/**
* @brief Force an immediate fsync of the active write-ahead log
*
* Useful for explicit durability control when using SyncMode::None or
* SyncMode::Interval. Thread-safe.
*/
void syncWal();

/**
* @brief Check if a flush operation is in progress
* @return true if flushing, false otherwise
Expand Down Expand Up @@ -596,6 +626,12 @@ class TidesDB
*/
[[nodiscard]] Transaction beginTransaction(IsolationLevel isolation);

/**
* @brief Get database-level statistics
* @return Aggregate statistics across the entire database instance
*/
[[nodiscard]] DbStats getDbStats();

/**
* @brief Get block cache statistics
* @return Cache statistics
Expand Down
32 changes: 32 additions & 0 deletions src/tidesdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,12 @@ void ColumnFamily::purge()
checkResult(result, "failed to purge column family");
}

void ColumnFamily::syncWal()
{
int result = tidesdb_sync_wal(cf_);
checkResult(result, "failed to sync WAL");
}

bool ColumnFamily::isFlushing() const
{
return tidesdb_is_flushing(cf_) != 0;
Expand Down Expand Up @@ -738,6 +744,32 @@ Transaction TidesDB::beginTransaction(IsolationLevel isolation)
return Transaction(txn);
}

DbStats TidesDB::getDbStats()
{
tidesdb_db_stats_t cStats;
int result = tidesdb_get_db_stats(db_, &cStats);
checkResult(result, "failed to get database stats");

DbStats stats;
stats.numColumnFamilies = cStats.num_column_families;
stats.totalMemory = cStats.total_memory;
stats.availableMemory = cStats.available_memory;
stats.resolvedMemoryLimit = cStats.resolved_memory_limit;
stats.memoryPressureLevel = cStats.memory_pressure_level;
stats.flushPendingCount = cStats.flush_pending_count;
stats.totalMemtableBytes = cStats.total_memtable_bytes;
stats.totalImmutableCount = cStats.total_immutable_count;
stats.totalSstableCount = cStats.total_sstable_count;
stats.totalDataSizeBytes = cStats.total_data_size_bytes;
stats.numOpenSstables = cStats.num_open_sstables;
stats.globalSeq = cStats.global_seq;
stats.txnMemoryBytes = cStats.txn_memory_bytes;
stats.compactionQueueSize = cStats.compaction_queue_size;
stats.flushQueueSize = cStats.flush_queue_size;

return stats;
}

CacheStats TidesDB::getCacheStats()
{
tidesdb_cache_stats_t cStats;
Expand Down
94 changes: 94 additions & 0 deletions tests/tidesdb_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,100 @@ TEST_F(TidesDBTest, TransactionResetAfterRollback)
}
}

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

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

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

// Write some data
{
auto txn = db.beginTransaction();
for (int i = 0; i < 10; ++i)
{
std::string key = "key" + std::to_string(i);
std::string value = "value" + std::to_string(i);
txn.put(cf, key, value, -1);
}
txn.commit();
}

// Force WAL sync -- should not throw
ASSERT_NO_THROW(cf.syncWal());
}

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

auto cfConfig = tidesdb::ColumnFamilyConfig::defaultConfig();
cfConfig.syncMode = tidesdb::SyncMode::Interval;
cfConfig.syncIntervalUs = 1000000; // 1 second
db.createColumnFamily("test_cf", cfConfig);

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

// Write data
{
auto txn = db.beginTransaction();
txn.put(cf, "key1", "value1", -1);
txn.commit();
}

// Explicit sync before the interval fires
ASSERT_NO_THROW(cf.syncWal());
}

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

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

auto cf1 = db.getColumnFamily("cf1");
auto cf2 = db.getColumnFamily("cf2");

// Write some data
{
auto txn = db.beginTransaction();
for (int i = 0; i < 20; ++i)
{
txn.put(cf1, "k1_" + std::to_string(i), "v1_" + std::to_string(i), -1);
txn.put(cf2, "k2_" + std::to_string(i), "v2_" + std::to_string(i), -1);
}
txn.commit();
}

auto dbStats = db.getDbStats();

ASSERT_EQ(dbStats.numColumnFamilies, 2);
ASSERT_GT(dbStats.totalMemory, 0u);
ASSERT_GE(dbStats.resolvedMemoryLimit, 0u);
ASSERT_GE(dbStats.memoryPressureLevel, 0);
ASSERT_GE(dbStats.globalSeq, 0u);
ASSERT_GE(dbStats.flushQueueSize, 0u);
ASSERT_GE(dbStats.compactionQueueSize, 0u);
}

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

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

auto dbStats = db.getDbStats();

ASSERT_EQ(dbStats.numColumnFamilies, 1);
ASSERT_GT(dbStats.totalMemory, 0u);
}

// Commit hook test helpers
struct HookTestCtx
{
Expand Down
Loading