From 2e0dbcc7d2b2cb10b6b3f927d8264af304fcca73 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Mon, 30 Sep 2024 13:40:21 +0900 Subject: [PATCH 01/13] feat(digest): Added ValkeyModule_GetKeyNameFromDigest and ValkeyModule_GetDbIdFromDigest Signed-off-by: Andrew Hahn --- src/digest.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/digest.rs diff --git a/src/digest.rs b/src/digest.rs new file mode 100644 index 0000000..e9de896 --- /dev/null +++ b/src/digest.rs @@ -0,0 +1,23 @@ +use crate::raw; +use std::os::raw::{c_int}; + +/// `Digest` is a high-level rust interface to the Valkey module C API +/// abstracting away the raw C ffi calls. +pub struct Digest { + pub dig: *mut raw::RedisModuleDigest, +} + +impl Digest { + pub const fn new(dig: *mut raw::RedisModuleDigest) -> Self { + Self { dig } + } + + pub fn key_name(&self) -> *mut raw::RedisModuleString { + unsafe { (*self.dig).key } + } + + pub fn db_id(&self) -> c_int { + unsafe { (*self.dig).dbid } + } +} + From 2e4aa8e1e7deeeebfa7827eee454ad08159edc8f Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Tue, 15 Oct 2024 14:46:35 +0900 Subject: [PATCH 02/13] Added wrappers for following operations: ValkeyModule_DigestAddStringBuffer ValkeyModule_DigestAddLongLong ValkeyModule_DigestEndSequence ValkeyModule_GetKeyNameFromDigest ValkeyModule_GetDbIdFromDigest Signed-off-by: Andrew Hahn --- src/digest.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++----- src/lib.rs | 1 + 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/digest.rs b/src/digest.rs index e9de896..4d7dc82 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -1,7 +1,7 @@ use crate::raw; -use std::os::raw::{c_int}; +use std::os::raw::c_char; -/// `Digest` is a high-level rust interface to the Valkey module C API +/// `Digest` is a high-level rust interface to the Valkey module C API /// abstracting away the raw C ffi calls. pub struct Digest { pub dig: *mut raw::RedisModuleDigest, @@ -12,12 +12,75 @@ impl Digest { Self { dig } } - pub fn key_name(&self) -> *mut raw::RedisModuleString { - unsafe { (*self.dig).key } + /// Returns the key name of this [`Digest`]. + /// + /// # Panics + /// + /// Will panic if `RedisModule_GetKeyNameFromDigest` is missing in redismodule.h + pub fn get_key_name(&self) -> *const raw::RedisModuleString { + unsafe { + match raw::RedisModule_GetKeyNameFromDigest { + Some(get_key_name_from_digest) => get_key_name_from_digest(self.dig), + None => panic!("RedisModule_GetKeyNameFromDigest is not available."), + } + } } - pub fn db_id(&self) -> c_int { - unsafe { (*self.dig).dbid } + /// Returns the database id of this [`Digest`]. + /// + /// # Panics + /// + /// Will panic if `RedisModule_GetDbIdFromDigest` is missing in redismodule.h + pub fn get_db_id(&self) -> i32 { + unsafe { + match raw::RedisModule_GetDbIdFromDigest { + Some(get_db_id_from_digest) => get_db_id_from_digest(self.dig), + None => panic!("RedisModule_GetDbIdFromDigest is not available."), + } + } + } + + /// Converts the long long input to a string and adds it to this [`Digest`]. + /// + /// # Panics + /// + /// Will panic if `RedisModule_DigestAddLongLong` is missing in redismodule.h + pub fn add_long_long(&mut self, ll: i64) { + unsafe { + match raw::RedisModule_DigestAddLongLong { + Some(digest_add_long_long) => digest_add_long_long(self.dig, ll), + None => panic!("RedisModule_DigestAddLongLong is not available."), + } + } } -} + /// Add a new element to this [`Digest`]. + /// + /// # Panics + /// + /// Will panic if `RedisModule_DigestAddStringBuffer` is missing in redismodule.h + pub fn add_string_buffer(&mut self, ele: &[u8]) { + unsafe { + match raw::RedisModule_DigestAddStringBuffer { + Some(digest_add_string_buffer) => { + digest_add_string_buffer(self.dig, ele.as_ptr().cast::(), ele.len()) + } + None => panic!("RedisModule_DigestAddStringBuffer is not available."), + } + } + } + + /// Ends the current sequence in this [`Digest`]. + /// + /// # Panics + /// + /// Will panic if `RedisModule_DigestEndSequence` is missing in redismodule.h + pub fn end_sequence(&mut self) { + unsafe { + match raw::RedisModule_DigestEndSequence { + Some(digest_end_sequence) => digest_end_sequence(self.dig), + None => panic!("RedisModule_DigestEndSequence is not available."), + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 4946b94..9c7302d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ mod redismodule; pub mod redisraw; pub mod redisvalue; pub mod stream; +pub mod digest; pub mod configuration; mod context; From 02dc383e20728ee85327824f3cc29efb2c24290a Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 16 Oct 2024 11:36:34 +0900 Subject: [PATCH 03/13] Changed `get_key_name` return type and updated syntax for digest methods Signed-off-by: Andrew Hahn --- src/digest.rs | 60 +++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/src/digest.rs b/src/digest.rs index 4d7dc82..332fc6d 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -1,4 +1,5 @@ -use crate::raw; +use crate::{raw, ValkeyString}; + use std::os::raw::c_char; /// `Digest` is a high-level rust interface to the Valkey module C API @@ -17,56 +18,51 @@ impl Digest { /// # Panics /// /// Will panic if `RedisModule_GetKeyNameFromDigest` is missing in redismodule.h - pub fn get_key_name(&self) -> *const raw::RedisModuleString { - unsafe { - match raw::RedisModule_GetKeyNameFromDigest { - Some(get_key_name_from_digest) => get_key_name_from_digest(self.dig), - None => panic!("RedisModule_GetKeyNameFromDigest is not available."), - } - } + pub fn get_key_name(&self) -> ValkeyString { + ValkeyString::from_redis_module_string(std::ptr::null_mut(), unsafe { + raw::RedisModule_GetKeyNameFromDigest + .expect("RedisModule_GetKeyNameFromDigest is not available.")(self.dig) + .cast_mut() + }) } - /// Returns the database id of this [`Digest`]. + /// Returns the database ID of this [`Digest`]. /// /// # Panics /// /// Will panic if `RedisModule_GetDbIdFromDigest` is missing in redismodule.h pub fn get_db_id(&self) -> i32 { unsafe { - match raw::RedisModule_GetDbIdFromDigest { - Some(get_db_id_from_digest) => get_db_id_from_digest(self.dig), - None => panic!("RedisModule_GetDbIdFromDigest is not available."), - } + raw::RedisModule_GetDbIdFromDigest + .expect("RedisModule_GetDbIdFromDigest is not available.")(self.dig) } } - /// Converts the long long input to a string and adds it to this [`Digest`]. + /// Adds a new element to this [`Digest`]. /// /// # Panics /// - /// Will panic if `RedisModule_DigestAddLongLong` is missing in redismodule.h - pub fn add_long_long(&mut self, ll: i64) { + /// Will panic if `RedisModule_DigestAddStringBuffer` is missing in redismodule.h + pub fn add_string_buffer(&mut self, ele: &[u8]) { unsafe { - match raw::RedisModule_DigestAddLongLong { - Some(digest_add_long_long) => digest_add_long_long(self.dig, ll), - None => panic!("RedisModule_DigestAddLongLong is not available."), - } + raw::RedisModule_DigestAddStringBuffer + .expect("RedisModule_DigestAddStringBuffer is not available.")( + self.dig, + ele.as_ptr().cast::(), + ele.len(), + ) } } - /// Add a new element to this [`Digest`]. + /// Similar to [`Digest::add_string_buffer`], but takes [`i64`]. /// /// # Panics /// - /// Will panic if `RedisModule_DigestAddStringBuffer` is missing in redismodule.h - pub fn add_string_buffer(&mut self, ele: &[u8]) { + /// Will panic if `RedisModule_DigestAddLongLong` is missing in redismodule.h + pub fn add_long_long(&mut self, ll: i64) { unsafe { - match raw::RedisModule_DigestAddStringBuffer { - Some(digest_add_string_buffer) => { - digest_add_string_buffer(self.dig, ele.as_ptr().cast::(), ele.len()) - } - None => panic!("RedisModule_DigestAddStringBuffer is not available."), - } + raw::RedisModule_DigestAddLongLong + .expect("RedisModule_DigestAddLongLong is not available.")(self.dig, ll) } } @@ -77,10 +73,8 @@ impl Digest { /// Will panic if `RedisModule_DigestEndSequence` is missing in redismodule.h pub fn end_sequence(&mut self) { unsafe { - match raw::RedisModule_DigestEndSequence { - Some(digest_end_sequence) => digest_end_sequence(self.dig), - None => panic!("RedisModule_DigestEndSequence is not available."), - } + raw::RedisModule_DigestEndSequence + .expect("RedisModule_DigestEndSequence is not available.")(self.dig) } } } From 7d31c527891f96e3b2c383ef74ff6c9dd4dd1402 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 16 Oct 2024 20:30:56 +0900 Subject: [PATCH 04/13] (test): Added digest callback implementation Signed-off-by: Andrew Hahn --- examples/data_type.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/data_type.rs b/examples/data_type.rs index 6bc5e3f..f4d547c 100644 --- a/examples/data_type.rs +++ b/examples/data_type.rs @@ -1,5 +1,6 @@ use std::os::raw::c_void; use valkey_module::alloc::ValkeyAlloc; +use valkey_module::digest::Digest; use valkey_module::native_types::ValkeyType; use valkey_module::{raw, valkey_module, Context, NextArg, ValkeyResult, ValkeyString}; @@ -17,10 +18,10 @@ static MY_VALKEY_TYPE: ValkeyType = ValkeyType::new( rdb_save: None, aof_rewrite: None, free: Some(free), + digest: Some(digest), // Currently unused by Redis mem_usage: None, - digest: None, // Aux data aux_load: None, @@ -44,6 +45,13 @@ unsafe extern "C" fn free(value: *mut c_void) { drop(Box::from_raw(value.cast::())); } +unsafe extern "C" fn digest(md: *mut raw::RedisModuleDigest, value: *mut c_void) { + let mut dig = Digest::new(md); + let val = &*(value.cast::()); + dig.add_string_buffer(&val.data.as_bytes()); + dig.end_sequence(); +} + fn alloc_set(ctx: &Context, args: Vec) -> ValkeyResult { let mut args = args.into_iter().skip(1); let key = args.next_arg()?; From ca41b3a201d7242dee4fd9cbd2b5d2458707a107 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Fri, 25 Oct 2024 13:27:40 +0900 Subject: [PATCH 05/13] (tests): Add alloc module tests Signed-off-by: Andrew Hahn --- tests/integration.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/integration.rs b/tests/integration.rs index 935c527..d810105 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -736,3 +736,53 @@ fn test_expire() -> Result<()> { Ok(()) } + +#[test] +fn test_alloc_data_type() -> Result<()> { + let port: u16 = 6503; + let _guards = vec![start_valkey_server_with_module("data_type", port) + .with_context(|| FAILED_TO_START_SERVER)?]; + let mut con = get_valkey_connection(port).with_context(|| FAILED_TO_CONNECT_TO_SERVER)?; + + // Test set to verify allocation + let res: i64 = redis::cmd("alloc.set") + .arg(&["test_key", "10"]) + .query(&mut con) + .with_context(|| "failed to run alloc.set")?; + assert_eq!(res, 10); + + // Get value and verify content + let res: String = redis::cmd("alloc.get") + .arg(&["test_key"]) + .query(&mut con) + .with_context(|| "failed to run alloc.get")?; + assert_eq!(res, "A".repeat(10)); + + // Test set reallocation + let res: i64 = redis::cmd("alloc.set") + .arg(&["test_key", "5"]) + .query(&mut con) + .with_context(|| "failed to run alloc.set")?; + assert_eq!(res, 5); + + // Test get with reallocated key + let res: String = redis::cmd("alloc.get") + .arg(&["test_key"]) + .query(&mut con) + .with_context(|| "failed to run alloc.get")?; + assert_eq!(res, "B".repeat(5)); + + let _: i64 = redis::cmd("DEL") + .arg(&["test_key"]) + .query(&mut con) + .with_context(|| "failed to run DEL")?; + + // Test get with deleted key + let res: Option = redis::cmd("alloc.get") + .arg(&["test_key"]) + .query(&mut con) + .with_context(|| "failed to run alloc.get")?; + assert!(res.is_none()); + + Ok(()) +} From 46a1540610f6753f37eed01751ab3dd858315e6b Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 30 Oct 2024 19:27:10 +0900 Subject: [PATCH 06/13] tests: changed test name for clarity Signed-off-by: Andrew Hahn --- tests/integration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration.rs b/tests/integration.rs index d810105..f535061 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -738,7 +738,7 @@ fn test_expire() -> Result<()> { } #[test] -fn test_alloc_data_type() -> Result<()> { +fn test_alloc() -> Result<()> { let port: u16 = 6503; let _guards = vec![start_valkey_server_with_module("data_type", port) .with_context(|| FAILED_TO_START_SERVER)?]; From 3b8a0836f33b4c706d4315cae8d9c58765350128 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 30 Oct 2024 20:18:17 +0900 Subject: [PATCH 07/13] tests: Added DEBUG digest test Signed-off-by: Andrew Hahn --- tests/integration.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/integration.rs b/tests/integration.rs index f535061..3c2faf3 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -786,3 +786,43 @@ fn test_alloc() -> Result<()> { Ok(()) } + +#[test] +fn test_debug() -> Result<()> { + let port: u16 = 6504; + let _guards = vec![start_valkey_server_with_module("data_type", port) + .with_context(|| FAILED_TO_START_SERVER)?]; + let mut con = get_valkey_connection(port).with_context(|| FAILED_TO_CONNECT_TO_SERVER)?; + + let _: i64 = redis::cmd("alloc.set") + .arg(&["test_key", "10"]) + .query(&mut con) + .with_context(|| "failed to run alloc.set")?; + + // Test DEBUG digest command to verify digest callback + let res: String = redis::cmd("DEBUG") + .arg(&["digest"]) + .query(&mut con) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert!( + !res.is_empty(), + "DEBUG DIGEST should return a non-empty string" + ); + + let _: i64 = redis::cmd("DEL") + .arg(&["test_key"]) + .query(&mut con) + .with_context(|| "failed to run DEL")?; + + // Test DEBUG digest command to verify digest callback on unset key + let res: String = redis::cmd("DEBUG") + .arg(&["digest"]) + .query(&mut con) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert!( + !res.is_empty(), + "DEBUG DIGEST should return a non-empty string" + ); + + Ok(()) +} From c76de04bb36feaac58ec1ed769ad0999f5dad177 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 30 Oct 2024 11:11:46 -0700 Subject: [PATCH 08/13] test: Add DIGEST-VALUE cases Signed-off-by: Andrew Hahn --- tests/integration.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/integration.rs b/tests/integration.rs index 3c2faf3..79e6a43 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -799,9 +799,9 @@ fn test_debug() -> Result<()> { .query(&mut con) .with_context(|| "failed to run alloc.set")?; - // Test DEBUG digest command to verify digest callback + // Test DEBUG DIGEST command to verify digest callback let res: String = redis::cmd("DEBUG") - .arg(&["digest"]) + .arg("digest") .query(&mut con) .with_context(|| "failed to run DEBUG DIGEST")?; assert!( @@ -809,19 +809,26 @@ fn test_debug() -> Result<()> { "DEBUG DIGEST should return a non-empty string" ); + // Test DEBUG DIGEST-VALUE command to verify digest callback + let res: redis::Value = redis::cmd("DEBUG") + .arg(&["digest-value", "test_key"]) + .query(&mut con) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert!(!matches!(res, redis::Value::Nil), "DEBUG DIGEST-VALUE should not return nil"); + let _: i64 = redis::cmd("DEL") - .arg(&["test_key"]) + .arg("test_key") .query(&mut con) .with_context(|| "failed to run DEL")?; // Test DEBUG digest command to verify digest callback on unset key let res: String = redis::cmd("DEBUG") - .arg(&["digest"]) + .arg("digest") .query(&mut con) .with_context(|| "failed to run DEBUG DIGEST")?; - assert!( - !res.is_empty(), - "DEBUG DIGEST should return a non-empty string" + assert_eq!( + res, + "0".repeat(40) ); Ok(()) From acc22c8c7e8831b70579a446fa8b46f0c3c8890f Mon Sep 17 00:00:00 2001 From: Andrew Hahn <58017052+hahnandrew@users.noreply.github.com> Date: Thu, 31 Oct 2024 03:54:48 +0900 Subject: [PATCH 09/13] Test suite docs update Signed-off-by: Andrew Hahn <58017052+hahnandrew@users.noreply.github.com> --- tests/integration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration.rs b/tests/integration.rs index 79e6a43..7bc4a98 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -813,7 +813,7 @@ fn test_debug() -> Result<()> { let res: redis::Value = redis::cmd("DEBUG") .arg(&["digest-value", "test_key"]) .query(&mut con) - .with_context(|| "failed to run DEBUG DIGEST")?; + .with_context(|| "failed to run DEBUG DIGEST-VALUE")?; assert!(!matches!(res, redis::Value::Nil), "DEBUG DIGEST-VALUE should not return nil"); let _: i64 = redis::cmd("DEL") From ec7ec3a688e04acb04e879367d7766a9cdaa3358 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Mon, 25 Nov 2024 10:13:15 +0900 Subject: [PATCH 10/13] (test): Added integration tests for get_key_name, get_db_id, add_long_long Signed-off-by: Andrew Hahn --- Cargo.toml | 4 ++ examples/data_type2.rs | 107 +++++++++++++++++++++++++++++++++++++++++ src/digest.rs | 2 +- src/lib.rs | 2 +- tests/integration.rs | 103 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 examples/data_type2.rs diff --git a/Cargo.toml b/Cargo.toml index 3f7182d..ee804fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,10 @@ crate-type = ["cdylib"] name = "data_type" crate-type = ["cdylib"] +[[example]] +name = "data_type2" +crate-type = ["cdylib"] + [[example]] name = "load_unload" crate-type = ["cdylib"] diff --git a/examples/data_type2.rs b/examples/data_type2.rs new file mode 100644 index 0000000..0e00e28 --- /dev/null +++ b/examples/data_type2.rs @@ -0,0 +1,107 @@ +use raw::KeyType; +use std::collections::HashMap; +use std::os::raw::c_void; +use std::ptr::null; +use valkey_module::alloc::ValkeyAlloc; +use valkey_module::digest::Digest; +use valkey_module::key::{ValkeyKey, ValkeyKeyWritable}; +use valkey_module::native_types::ValkeyType; +use valkey_module::{raw, valkey_module, Context, NextArg, ValkeyResult, ValkeyString}; + +#[derive(Debug)] +struct MyType { + data: i64, +} + +static MY_VALKEY_TYPE: ValkeyType = ValkeyType::new( + "mytype123", + 0, + raw::RedisModuleTypeMethods { + version: raw::REDISMODULE_TYPE_METHOD_VERSION as u64, + rdb_load: None, + rdb_save: None, + aof_rewrite: None, + free: Some(free), + digest: Some(digest), + + // Currently unused by Redis + mem_usage: None, + + // Aux data + aux_load: None, + aux_save: None, + aux_save2: None, + aux_save_triggers: 0, + + free_effort: None, + unlink: None, + copy: None, + defrag: None, + + copy2: None, + free_effort2: None, + mem_usage2: None, + unlink2: None, + }, +); + +unsafe extern "C" fn free(value: *mut c_void) { + drop(Box::from_raw(value.cast::())); +} + +unsafe extern "C" fn digest(md: *mut raw::RedisModuleDigest, value: *mut c_void) { + let mut dig = Digest::new(md); + let val = &*(value.cast::()); + dig.add_long_long(val.data); + dig.get_db_id(); + let keyname = dig.get_key_name(); + assert!(!keyname.is_empty()); + dig.end_sequence(); +} + +fn alloc_set(ctx: &Context, args: Vec) -> ValkeyResult { + let mut args = args.into_iter().skip(1); + let key = args.next_arg()?; + let size = args.next_i64()?; + + ctx.log_debug(format!("key: {key}, size: {size}").as_str()); + + let key = ctx.open_key_writable(&key); + + if let Some(value) = key.get_value::(&MY_VALKEY_TYPE)? { + value.data = size; + } else { + let value = MyType { data: size }; + key.set_value(&MY_VALKEY_TYPE, value)?; + } + Ok(size.into()) +} + +fn alloc_get(ctx: &Context, args: Vec) -> ValkeyResult { + let mut args = args.into_iter().skip(1); + let key = args.next_arg()?; + + let key = ctx.open_key(&key); + + let value = match key.get_value::(&MY_VALKEY_TYPE)? { + Some(value) => value.data.into(), + None => ().into(), + }; + + Ok(value) +} + +////////////////////////////////////////////////////// + +valkey_module! { + name: "alloc2", + version: 1, + allocator: (ValkeyAlloc, ValkeyAlloc), + data_types: [ + MY_VALKEY_TYPE, + ], + commands: [ + ["alloc.set", alloc_set, "write", 1, 1, 1], + ["alloc.get", alloc_get, "readonly", 1, 1, 1], + ], +} diff --git a/src/digest.rs b/src/digest.rs index 332fc6d..650b80e 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -19,7 +19,7 @@ impl Digest { /// /// Will panic if `RedisModule_GetKeyNameFromDigest` is missing in redismodule.h pub fn get_key_name(&self) -> ValkeyString { - ValkeyString::from_redis_module_string(std::ptr::null_mut(), unsafe { + ValkeyString::new(None, unsafe { raw::RedisModule_GetKeyNameFromDigest .expect("RedisModule_GetKeyNameFromDigest is not available.")(self.dig) .cast_mut() diff --git a/src/lib.rs b/src/lib.rs index 1c03d59..ebb7580 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate num_traits; pub mod alloc; pub mod apierror; +pub mod digest; pub mod error; pub mod native_types; pub mod raw; @@ -11,7 +12,6 @@ mod redismodule; pub mod redisraw; pub mod redisvalue; pub mod stream; -pub mod digest; pub mod configuration; mod context; diff --git a/tests/integration.rs b/tests/integration.rs index b4196ec..ef2470c 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -739,7 +739,7 @@ fn test_expire() -> Result<()> { #[test] fn test_alloc() -> Result<()> { - let port: u16 = 6503; + let port: u16 = 6509; let _guards = vec![start_valkey_server_with_module("data_type", port) .with_context(|| FAILED_TO_START_SERVER)?]; let mut con = get_valkey_connection(port).with_context(|| FAILED_TO_CONNECT_TO_SERVER)?; @@ -834,6 +834,107 @@ fn test_debug() -> Result<()> { Ok(()) } +#[test] +fn test_debug2() -> Result<()> { + // DB1 + let port: u16 = 6505; + let _guards = vec![start_valkey_server_with_module("data_type2", port) + .with_context(|| FAILED_TO_START_SERVER)?]; + let mut con1 = get_valkey_connection(port).with_context(|| FAILED_TO_CONNECT_TO_SERVER)?; + + // DB2 + let port2: u16 = 6506; + let _guards = vec![start_valkey_server_with_module("data_type2", port2) + .with_context(|| FAILED_TO_START_SERVER)?]; + let mut con2 = get_valkey_connection(port2).with_context(|| FAILED_TO_CONNECT_TO_SERVER)?; + + // Set on DB1 + let _: i64 = redis::cmd("alloc.set") + .arg(&["k1", "3"]) + .query(&mut con1) + .with_context(|| "failed to run alloc.set")?; + + // Test DEBUG DIGEST command on DB1 to verify digest callback + let res: String = redis::cmd("DEBUG") + .arg("digest") + .query(&mut con1) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert!( + !res.is_empty(), + "DEBUG DIGEST should return a non-empty string" + ); + + // Get on DB1 + let get_res_db1: String = redis::cmd("alloc.get") + .arg("k1") + .query(&mut con1) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert!( + !get_res_db1.is_empty(), + "alloc.get should return a non-empty string" + ); + + // Set on DB2 + let _: i64 = redis::cmd("alloc.set") + .arg(&["k1", "3"]) + .query(&mut con2) + .with_context(|| "failed to run alloc.set")?; + + // Test DEBUG DIGEST command on DB2 to verify digest callback + let res: String = redis::cmd("DEBUG") + .arg("digest") + .query(&mut con2) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert!( + !res.is_empty(), + "DEBUG DIGEST should return a non-empty string" + ); + + // Get on DB2 + let get_res_db2: String = redis::cmd("alloc.get") + .arg("k1") + .query(&mut con2) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert!( + !get_res_db2.is_empty(), + "DEBUG DIGEST should return a non-empty string" + ); + + // Compare digested DB1 & DB2 + assert_eq!(get_res_db1, get_res_db2); + + // Delete key on DB1 + let _: i64 = redis::cmd("DEL") + .arg("k1") + .query(&mut con1) + .with_context(|| "failed to run DEL")?; + + // Test DEBUG DIGEST on DB1 to verify digest callback on unset key + let res_db1: String = redis::cmd("DEBUG") + .arg("digest") + .query(&mut con1) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert_eq!(res_db1, "0".repeat(40)); + + // Delete key on DB2 + let _: i64 = redis::cmd("DEL") + .arg("k1") + .query(&mut con2) + .with_context(|| "failed to run DEL")?; + + // Test DEBUG DIGEST command on DB2 to verify digest callback on unset key + let res_db2: String = redis::cmd("DEBUG") + .arg("digest") + .query(&mut con2) + .with_context(|| "failed to run DEBUG DIGEST")?; + assert_eq!(res_db2, "0".repeat(40)); + + // Compare empty DB1 & DB2 + assert_eq!(res_db1, res_db2); + + Ok(()) +} + #[test] fn test_acl_categories() -> Result<()> { let port = 6503; From 1baa7c558b6a2d8960d575b93784abae69dfc506 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Sun, 1 Dec 2024 16:06:12 +0900 Subject: [PATCH 11/13] feat(digest): Use type alias instead of primitive types reference #139, #143 Signed-off-by: Andrew Hahn --- src/digest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/digest.rs b/src/digest.rs index 650b80e..46db717 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -1,6 +1,6 @@ use crate::{raw, ValkeyString}; -use std::os::raw::c_char; +use std::os::raw::{c_char, c_int, c_longlong}; /// `Digest` is a high-level rust interface to the Valkey module C API /// abstracting away the raw C ffi calls. @@ -31,7 +31,7 @@ impl Digest { /// # Panics /// /// Will panic if `RedisModule_GetDbIdFromDigest` is missing in redismodule.h - pub fn get_db_id(&self) -> i32 { + pub fn get_db_id(&self) -> c_int { unsafe { raw::RedisModule_GetDbIdFromDigest .expect("RedisModule_GetDbIdFromDigest is not available.")(self.dig) @@ -59,7 +59,7 @@ impl Digest { /// # Panics /// /// Will panic if `RedisModule_DigestAddLongLong` is missing in redismodule.h - pub fn add_long_long(&mut self, ll: i64) { + pub fn add_long_long(&mut self, ll: c_longlong) { unsafe { raw::RedisModule_DigestAddLongLong .expect("RedisModule_DigestAddLongLong is not available.")(self.dig, ll) From c409d1d8a6f26f8750e093c2d93e344f85472d69 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Sun, 8 Dec 2024 01:31:38 +0900 Subject: [PATCH 12/13] tests: combine debug and debug2 tests https://github.com/valkey-io/valkeymodule-rs/pull/102#discussion_r1874064820 Signed-off-by: Andrew Hahn --- tests/integration.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/integration.rs b/tests/integration.rs index ef2470c..abe6006 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -831,11 +831,8 @@ fn test_debug() -> Result<()> { .with_context(|| "failed to run DEBUG DIGEST")?; assert_eq!(res, "0".repeat(40)); - Ok(()) -} + // Start testing add_long_long -#[test] -fn test_debug2() -> Result<()> { // DB1 let port: u16 = 6505; let _guards = vec![start_valkey_server_with_module("data_type2", port) From 96ec7834d00a996176da6ad90b694b5f139e9e7a Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Sun, 8 Dec 2024 01:42:20 +0900 Subject: [PATCH 13/13] maintenance: del outdated comments https://github.com/valkey-io/valkeymodule-rs/pull/102#discussion_r1874057086 Signed-off-by: Andrew Hahn --- examples/data_type.rs | 2 -- examples/data_type2.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/examples/data_type.rs b/examples/data_type.rs index f4d547c..e956c5d 100644 --- a/examples/data_type.rs +++ b/examples/data_type.rs @@ -19,8 +19,6 @@ static MY_VALKEY_TYPE: ValkeyType = ValkeyType::new( aof_rewrite: None, free: Some(free), digest: Some(digest), - - // Currently unused by Redis mem_usage: None, // Aux data diff --git a/examples/data_type2.rs b/examples/data_type2.rs index 0e00e28..6710ef4 100644 --- a/examples/data_type2.rs +++ b/examples/data_type2.rs @@ -23,8 +23,6 @@ static MY_VALKEY_TYPE: ValkeyType = ValkeyType::new( aof_rewrite: None, free: Some(free), digest: Some(digest), - - // Currently unused by Redis mem_usage: None, // Aux data