From 92ee952db9cf6d1666d32b2398530737e95d208e Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Sat, 21 Feb 2026 00:20:23 -0500 Subject: [PATCH 1/2] column family commit hook implementation; updated rockspec --- src/tidesdb.lua | 29 ++++++- tests/test_tidesdb.lua | 86 +++++++++++++++++++ ...5.3-1.rockspec => tidesdb-0.5.4-1.rockspec | 4 +- 3 files changed, 116 insertions(+), 3 deletions(-) rename tidesdb-0.5.3-1.rockspec => tidesdb-0.5.4-1.rockspec (95%) diff --git a/src/tidesdb.lua b/src/tidesdb.lua index 9768f1f..d78560e 100644 --- a/src/tidesdb.lua +++ b/src/tidesdb.lua @@ -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; + long 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; @@ -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 { @@ -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); @@ -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) @@ -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 diff --git a/tests/test_tidesdb.lua b/tests/test_tidesdb.lua index fad1aff..6473374 100644 --- a/tests/test_tidesdb.lua +++ b/tests/test_tidesdb.lua @@ -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...") diff --git a/tidesdb-0.5.3-1.rockspec b/tidesdb-0.5.4-1.rockspec similarity index 95% rename from tidesdb-0.5.3-1.rockspec rename to tidesdb-0.5.4-1.rockspec index f8f0d59..b7bc028 100644 --- a/tidesdb-0.5.3-1.rockspec +++ b/tidesdb-0.5.4-1.rockspec @@ -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", From 9dfab11f049f6889207a4369115b1c3dd053b429 Mon Sep 17 00:00:00 2001 From: Alex Gaetano Padula Date: Sat, 21 Feb 2026 00:27:43 -0500 Subject: [PATCH 2/2] corrected ttl to int64_t --- src/tidesdb.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tidesdb.lua b/src/tidesdb.lua index d78560e..2678b2c 100644 --- a/src/tidesdb.lua +++ b/src/tidesdb.lua @@ -47,7 +47,7 @@ ffi.cdef[[ size_t key_size; const uint8_t *value; size_t value_size; - long ttl; + int64_t ttl; int is_delete; } tidesdb_commit_op_t;