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
29 changes: 28 additions & 1 deletion src/tidesdb.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ ffi.cdef[[
// Structures
static const int TDB_MAX_CF_NAME_LEN = 128;

typedef struct {
const uint8_t *key;
size_t key_size;
const uint8_t *value;
size_t value_size;
int64_t ttl;
int is_delete;
} tidesdb_commit_op_t;

typedef int (*tidesdb_commit_hook_fn)(const tidesdb_commit_op_t *ops, int num_ops,
uint64_t commit_seq, void *ctx);

typedef struct {
char name[128];
size_t write_buffer_size;
Expand All @@ -68,6 +80,8 @@ ffi.cdef[[
int l1_file_count_trigger;
int l0_queue_stall_threshold;
int use_btree;
tidesdb_commit_hook_fn commit_hook_fn;
void *commit_hook_ctx;
} tidesdb_column_family_config_t;

typedef struct {
Expand Down Expand Up @@ -161,6 +175,9 @@ ffi.cdef[[
int tidesdb_get_cache_stats(void* db, tidesdb_cache_stats_t* stats);
int tidesdb_range_cost(void* cf, const uint8_t* key_a, size_t key_a_size, const uint8_t* key_b, size_t key_b_size, double* cost);

// Commit hook operations
int tidesdb_cf_set_commit_hook(void* cf, tidesdb_commit_hook_fn fn, void* ctx);

// Backup operations
int tidesdb_backup(void* db, const char* dir);
int tidesdb_checkpoint(void* db, const char* checkpoint_dir);
Expand Down Expand Up @@ -553,6 +570,16 @@ function ColumnFamily:update_runtime_config(config, persist_to_disk)
check_result(result, "failed to update runtime config")
end

function ColumnFamily:set_commit_hook(fn, ctx)
local result = lib.tidesdb_cf_set_commit_hook(self._cf, fn, ctx)
check_result(result, "failed to set commit hook")
end

function ColumnFamily:clear_commit_hook()
local result = lib.tidesdb_cf_set_commit_hook(self._cf, nil, nil)
check_result(result, "failed to clear commit hook")
end

function ColumnFamily:range_cost(key_a, key_b)
local cost = ffi.new("double[1]")
local result = lib.tidesdb_range_cost(self._cf, key_a, #key_a, key_b, #key_b, cost)
Expand Down Expand Up @@ -1061,6 +1088,6 @@ function tidesdb.save_config_to_ini(ini_file, section_name, config)
end

-- Version
tidesdb._VERSION = "0.5.3"
tidesdb._VERSION = "0.5.4"

return tidesdb
86 changes: 86 additions & 0 deletions tests/test_tidesdb.lua
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,92 @@ function tests.test_range_cost()
print("PASS: test_range_cost")
end

function tests.test_commit_hook()
local ffi = require("ffi")
local path = "./test_db_commit_hook"
cleanup_db(path)

local db = tidesdb.TidesDB.open(path)
db:create_column_family("test_cf")
local cf = db:get_column_family("test_cf")

-- Track hook invocations
local hook_called = 0
local hook_ops_count = 0
local hook_seq = 0
local hook_keys = {}
local hook_had_delete = false

local hook = ffi.cast("tidesdb_commit_hook_fn", function(ops, num_ops, commit_seq, ctx)
hook_called = hook_called + 1
hook_ops_count = hook_ops_count + num_ops
hook_seq = tonumber(commit_seq)
for i = 0, num_ops - 1 do
local key = ffi.string(ops[i].key, ops[i].key_size)
table.insert(hook_keys, key)
if ops[i].is_delete ~= 0 then
hook_had_delete = true
end
end
return 0
end)

-- Set commit hook
cf:set_commit_hook(hook, nil)

-- Write data (should trigger the hook)
local txn = db:begin_txn()
txn:put(cf, "key1", "value1")
txn:put(cf, "key2", "value2")
txn:commit()
txn:free()

assert_true(hook_called >= 1, "commit hook should have been called")
assert_true(hook_ops_count >= 2, "hook should have received at least 2 ops")
assert_true(hook_seq > 0, "commit_seq should be > 0")

-- Verify keys were captured
local found_key1 = false
local found_key2 = false
for _, k in ipairs(hook_keys) do
if k == "key1" then found_key1 = true end
if k == "key2" then found_key2 = true end
end
assert_true(found_key1, "hook should have received key1")
assert_true(found_key2, "hook should have received key2")

-- Test delete operation fires hook
hook_had_delete = false
local del_txn = db:begin_txn()
del_txn:delete(cf, "key1")
del_txn:commit()
del_txn:free()

assert_true(hook_had_delete, "hook should have received a delete operation")

-- Save hook_called count before clearing
local calls_before_clear = hook_called

-- Clear commit hook
cf:clear_commit_hook()

-- Write more data (should NOT trigger the hook)
local txn2 = db:begin_txn()
txn2:put(cf, "key3", "value3")
txn2:commit()
txn2:free()

assert_eq(hook_called, calls_before_clear, "hook should not fire after clearing")

-- Clean up the callback to prevent GC issues
hook:free()

db:drop_column_family("test_cf")
db:close()
cleanup_db(path)
print("PASS: test_commit_hook")
end

-- Run all tests
local function run_tests()
print("Running TidesDB Lua tests...")
Expand Down
4 changes: 2 additions & 2 deletions tidesdb-0.5.3-1.rockspec → tidesdb-0.5.4-1.rockspec
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package = "tidesdb"
version = "0.5.3-1"
version = "0.5.4-1"
source = {
url = "git://github.com/tidesdb/tidesdb-lua.git",
tag = "v0.5.3"
tag = "v0.5.4"
}
description = {
summary = "Official Lua bindings for TidesDB - A high-performance embedded key-value storage engine",
Expand Down
Loading