Skip to content

Commit 41ccda7

Browse files
author
Meir Shpilraien (Spielrein)
authored
Added support for defrag API. (#387)
* Added support for defrag API. The PR adds support for defrag API. The PR introduce a new struct, `DefragContext`, which provide a safe API over `*mut raw::RedisModuleDefragCtx`. **Notice** that we do expose an unsafe API to create `DefragContext` from `*mut raw::RedisModuleDefragCtx`. This is because we do not have a safe API for datatype registeration. User must register an unsafe function as the defrag callback of the datatype and create the `DefragContext` from `*mut raw::RedisModuleDefragCtx`. We should consider adding a safe API for datatype creation. In addition, the PR introduce 3 new proc macros: * defrag_function - allows to register a function to defrag global data * defrag_start_function - allows to register a function to defrag global data when defrag cycle starts. * defrag_end_function - allows to register a function to defrag global data when defrag cycle ends. Example and test for the new API were added. * Review fixes * Fix compilation issue * Review fixes
1 parent 713c930 commit 41ccda7

File tree

10 files changed

+468
-7
lines changed

10 files changed

+468
-7
lines changed

examples/data_type.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
1+
use lazy_static::lazy_static;
2+
use libc::c_int;
3+
use redis_module::defrag::DefragContext;
14
use redis_module::native_types::RedisType;
2-
use redis_module::{raw, redis_module, Context, NextArg, RedisResult, RedisString};
5+
use redis_module::redisvalue::RedisValueKey;
6+
use redis_module::{
7+
raw, redis_module, Context, NextArg, RedisGILGuard, RedisResult, RedisString, RedisValue,
8+
};
9+
use redis_module_macros::{defrag_end_function, defrag_function, defrag_start_function};
310
use std::os::raw::c_void;
411

512
#[derive(Debug)]
613
struct MyType {
714
data: String,
815
}
916

17+
lazy_static! {
18+
static ref NUM_KEYS_DEFRAG: RedisGILGuard<usize> = RedisGILGuard::default();
19+
static ref NUM_DEFRAG_START: RedisGILGuard<usize> = RedisGILGuard::default();
20+
static ref NUM_DEFRAG_END: RedisGILGuard<usize> = RedisGILGuard::default();
21+
static ref NUM_DEFRAG_GLOBALS: RedisGILGuard<usize> = RedisGILGuard::default();
22+
}
23+
1024
static MY_REDIS_TYPE: RedisType = RedisType::new(
1125
"mytype123",
1226
0,
@@ -30,7 +44,7 @@ static MY_REDIS_TYPE: RedisType = RedisType::new(
3044
free_effort: None,
3145
unlink: None,
3246
copy: None,
33-
defrag: None,
47+
defrag: Some(defrag),
3448

3549
copy2: None,
3650
free_effort2: None,
@@ -43,6 +57,35 @@ unsafe extern "C" fn free(value: *mut c_void) {
4357
drop(Box::from_raw(value.cast::<MyType>()));
4458
}
4559

60+
unsafe extern "C" fn defrag(
61+
ctx: *mut raw::RedisModuleDefragCtx,
62+
_key: *mut raw::RedisModuleString,
63+
_value: *mut *mut c_void,
64+
) -> c_int {
65+
let defrag_ctx = DefragContext::new(ctx);
66+
let mut num_keys_defrag = NUM_KEYS_DEFRAG.lock(&defrag_ctx);
67+
*num_keys_defrag += 1;
68+
0
69+
}
70+
71+
#[defrag_start_function]
72+
fn defrag_end(defrag_ctx: &DefragContext) {
73+
let mut num_defrag_end = NUM_DEFRAG_END.lock(defrag_ctx);
74+
*num_defrag_end += 1;
75+
}
76+
77+
#[defrag_end_function]
78+
fn defrag_start(defrag_ctx: &DefragContext) {
79+
let mut num_defrag_start = NUM_DEFRAG_START.lock(defrag_ctx);
80+
*num_defrag_start += 1;
81+
}
82+
83+
#[defrag_function]
84+
fn defrag_globals(defrag_ctx: &DefragContext) {
85+
let mut num_defrag_globals = NUM_DEFRAG_GLOBALS.lock(defrag_ctx);
86+
*num_defrag_globals += 1;
87+
}
88+
4689
fn alloc_set(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
4790
let mut args = args.into_iter().skip(1);
4891
let key = args.next_arg()?;
@@ -78,6 +121,35 @@ fn alloc_get(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
78121
Ok(value)
79122
}
80123

124+
fn alloc_defragstats(ctx: &Context, _args: Vec<RedisString>) -> RedisResult {
125+
let num_keys_defrag = NUM_KEYS_DEFRAG.lock(ctx);
126+
let num_defrag_globals = NUM_DEFRAG_GLOBALS.lock(ctx);
127+
let num_defrag_start = NUM_DEFRAG_START.lock(ctx);
128+
let num_defrag_end = NUM_DEFRAG_END.lock(ctx);
129+
Ok(RedisValue::OrderedMap(
130+
[
131+
(
132+
RedisValueKey::String("num_keys_defrag".to_owned()),
133+
RedisValue::Integer(*num_keys_defrag as i64),
134+
),
135+
(
136+
RedisValueKey::String("num_defrag_globals".to_owned()),
137+
RedisValue::Integer(*num_defrag_globals as i64),
138+
),
139+
(
140+
RedisValueKey::String("num_defrag_start".to_owned()),
141+
RedisValue::Integer(*num_defrag_start as i64),
142+
),
143+
(
144+
RedisValueKey::String("num_defrag_end".to_owned()),
145+
RedisValue::Integer(*num_defrag_end as i64),
146+
),
147+
]
148+
.into_iter()
149+
.collect(),
150+
))
151+
}
152+
81153
//////////////////////////////////////////////////////
82154

83155
redis_module! {
@@ -90,5 +162,6 @@ redis_module! {
90162
commands: [
91163
["alloc.set", alloc_set, "write", 1, 1, 1],
92164
["alloc.get", alloc_get, "readonly", 1, 1, 1],
165+
["alloc.defragstats", alloc_defragstats, "readonly", 0, 0, 0]
93166
],
94167
}

examples/open_key_with_flags.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use redis_module::{
2-
key::KeyFlags, raw, redis_module, Context, NextArg, RedisError, RedisResult, RedisString,
3-
RedisValue,
2+
key::KeyFlags, redis_module, Context, NextArg, RedisError, RedisResult, RedisString, RedisValue,
43
};
54
use redis_module_macros::command;
65

redismodule-rs-macros/src/lib.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,72 @@ pub fn info_command_handler(_attr: TokenStream, item: TokenStream) -> TokenStrea
451451
pub fn info_section(item: TokenStream) -> TokenStream {
452452
info_section::info_section(item)
453453
}
454+
455+
/// Proc macro which is set on a function that need to be called whenever server performs defrag.
456+
/// The function must accept a [&DefragContext]. If defrag is not supported by the Redis version
457+
/// the function will never be called.
458+
///
459+
/// Example:
460+
///
461+
/// ```rust,no_run,ignore
462+
/// #[defrag_function]
463+
/// fn defrag(ctx: &DefragContext) { ... }
464+
/// ```
465+
#[proc_macro_attribute]
466+
pub fn defrag_function(_attr: TokenStream, item: TokenStream) -> TokenStream {
467+
let ast: ItemFn = match syn::parse(item) {
468+
Ok(res) => res,
469+
Err(e) => return e.to_compile_error().into(),
470+
};
471+
let gen = quote! {
472+
#[linkme::distributed_slice(redis_module::defrag::DEFRAG_FUNCTIONS_LIST)]
473+
#ast
474+
};
475+
gen.into()
476+
}
477+
478+
/// Proc macro which is set on a function that need to be called whenever server start performs defrag.
479+
/// The function must accept a [&DefragContext]. If defrag start event is not supported by the Redis version
480+
/// the function will never be called.
481+
///
482+
/// Example:
483+
///
484+
/// ```rust,no_run,ignore
485+
/// #[defrag_start_function]
486+
/// fn defrag_start(ctx: &DefragContext) { ... }
487+
/// ```
488+
#[proc_macro_attribute]
489+
pub fn defrag_start_function(_attr: TokenStream, item: TokenStream) -> TokenStream {
490+
let ast: ItemFn = match syn::parse(item) {
491+
Ok(res) => res,
492+
Err(e) => return e.to_compile_error().into(),
493+
};
494+
let gen = quote! {
495+
#[linkme::distributed_slice(redis_module::defrag::DEFRAG_START_FUNCTIONS_LIST)]
496+
#ast
497+
};
498+
gen.into()
499+
}
500+
501+
/// Proc macro which is set on a function that need to be called whenever server end performs defrag.
502+
/// The function must accept a [&DefragContext]. If defrag end event is not supported by the Redis version
503+
/// the function will never be called.
504+
///
505+
/// Example:
506+
///
507+
/// ```rust,no_run,ignore
508+
/// #[defrag_end_function]
509+
/// fn defrag_end(ctx: &DefragContext) { ... }
510+
/// ```
511+
#[proc_macro_attribute]
512+
pub fn defrag_end_function(_attr: TokenStream, item: TokenStream) -> TokenStream {
513+
let ast: ItemFn = match syn::parse(item) {
514+
Ok(res) => res,
515+
Err(e) => return e.to_compile_error().into(),
516+
};
517+
let gen = quote! {
518+
#[linkme::distributed_slice(redis_module::defrag::DEFRAG_END_FUNCTIONS_LIST)]
519+
#ast
520+
};
521+
gen.into()
522+
}

src/context/commands.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,12 +454,12 @@ api! {[
454454
// the only CString pointers which are not freed are those of the key_specs, lets free them here.
455455
key_specs.into_iter().for_each(|v|{
456456
if !v.notes.is_null() {
457-
unsafe{CString::from_raw(v.notes as *mut c_char)};
457+
drop(unsafe{CString::from_raw(v.notes as *mut c_char)});
458458
}
459459
if v.begin_search_type == raw::RedisModuleKeySpecBeginSearchType_REDISMODULE_KSPEC_BS_KEYWORD {
460460
let keyword = unsafe{v.bs.keyword.keyword};
461461
if !keyword.is_null() {
462-
unsafe{CString::from_raw(v.bs.keyword.keyword as *mut c_char)};
462+
drop(unsafe{CString::from_raw(v.bs.keyword.keyword as *mut c_char)});
463463
}
464464
}
465465
});

0 commit comments

Comments
 (0)