-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API extentions. #229
base: master
Are you sure you want to change the base?
API extentions. #229
Changes from all commits
325f4ca
d87a5f2
9e0372f
fbbdbdc
f157503
7d92069
6d81a45
b7f079f
1da738a
e04fed2
c8da3dd
4fa23e5
af22d54
db57a7d
795f01d
7786f5d
381c029
58989a6
2c899ae
1fe6e5f
7b16438
ee88a64
3cafe73
ad75618
2771300
4aa18c5
42dbce1
094c4df
244dae2
f2688c2
319b70f
a0b60a9
4d3654b
c690db2
68fac3a
f8e469b
e2074bd
5b84c4d
c70081b
99ace67
cbb036f
cd09e7d
ac5e6ca
14c5c92
70dd1dd
10572ff
3c32669
cf0473c
f873f8a
8acffbe
03b52b9
1e735bd
6a4487f
566ad27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#[macro_use] | ||
extern crate redis_module; | ||
|
||
use redis_module::{ | ||
context::keys_cursor::KeysCursor, Context, RedisResult, RedisString, RedisValue, | ||
}; | ||
|
||
fn scan_keys(ctx: &Context, _args: Vec<RedisString>) -> RedisResult { | ||
let mut keys = Vec::new(); | ||
let cursor = KeysCursor::new(); | ||
while cursor.scan(ctx, &|_ctx, key_name, _key| { | ||
keys.push(RedisValue::BulkString( | ||
key_name.try_as_str().unwrap().to_string(), | ||
)); | ||
}) {} | ||
Ok(keys.into()) | ||
} | ||
|
||
////////////////////////////////////////////////////// | ||
|
||
redis_module! { | ||
name: "scan", | ||
version: 1, | ||
data_types: [], | ||
commands: [ | ||
["SCAN_KEYS", scan_keys, "fast deny-oom readonly", 0, 0, 0], | ||
], | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#[macro_use] | ||
extern crate redis_module; | ||
|
||
use redis_module::{ | ||
context::server_events::ServerEventData, Context, RedisResult, RedisString, RedisValue, | ||
}; | ||
|
||
static mut NUM_FLUSHES: usize = 0; | ||
static mut NUM_ROLED_CHANGED: usize = 0; | ||
static mut NUM_LOADINGS: usize = 0; | ||
|
||
fn num_flushed(_ctx: &Context, _args: Vec<RedisString>) -> RedisResult { | ||
Ok(RedisValue::Integer(unsafe { NUM_FLUSHES } as i64)) | ||
} | ||
|
||
fn num_roled_changed(_ctx: &Context, _args: Vec<RedisString>) -> RedisResult { | ||
Ok(RedisValue::Integer(unsafe { NUM_ROLED_CHANGED } as i64)) | ||
} | ||
|
||
fn num_loading(_ctx: &Context, _args: Vec<RedisString>) -> RedisResult { | ||
Ok(RedisValue::Integer(unsafe { NUM_LOADINGS } as i64)) | ||
} | ||
|
||
fn on_role_changed(_ctx: &Context, _event_data: ServerEventData) { | ||
let num_roled_changed = unsafe { &mut NUM_ROLED_CHANGED }; | ||
*num_roled_changed = *num_roled_changed + 1; | ||
} | ||
|
||
fn on_loading_event(_ctx: &Context, _event_data: ServerEventData) { | ||
let num_loading = unsafe { &mut NUM_LOADINGS }; | ||
*num_loading = *num_loading + 1; | ||
} | ||
|
||
fn on_flush_event(_ctx: &Context, _event_data: ServerEventData) { | ||
let num_flushed = unsafe { &mut NUM_FLUSHES }; | ||
*num_flushed = *num_flushed + 1; | ||
} | ||
|
||
////////////////////////////////////////////////////// | ||
|
||
redis_module! { | ||
name: "server_events", | ||
version: 1, | ||
data_types: [], | ||
commands: [ | ||
MeirShpilraien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
["NUM_FLUSHED", num_flushed, "fast deny-oom readonly", 0, 0, 0], | ||
["NUM_ROLED_CHANGED", num_roled_changed, "fast deny-oom readonly", 0, 0, 0], | ||
["NUM_LOADING", num_loading, "fast deny-oom readonly", 0, 0, 0], | ||
], | ||
server_events: [ | ||
[@RuleChanged: on_role_changed], | ||
[@Loading: on_loading_event], | ||
[@Flush: on_flush_event], | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#[macro_use] | ||
extern crate redis_module; | ||
|
||
use redis_module::raw::{KeyType, RedisModuleStreamID}; | ||
use redis_module::{Context, NextArg, RedisError, RedisResult, RedisString, RedisValue}; | ||
|
||
fn stream_read_from(ctx: &Context, args: Vec<RedisString>) -> RedisResult { | ||
let mut args = args.into_iter().skip(1); | ||
|
||
let stream_key = args.next_arg()?; | ||
|
||
let stream = ctx.open_key(&stream_key); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just making sure - could also test for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, key_type should verify it. |
||
|
||
let key_type = stream.key_type(); | ||
|
||
if key_type != KeyType::Stream { | ||
return Err(RedisError::WrongType); | ||
} | ||
|
||
let mut iter = stream.get_stream_iterator()?; | ||
let element = iter.next(); | ||
let id_to_keep = iter.next().as_ref().map_or_else( | ||
|| RedisModuleStreamID { | ||
ms: u64::MAX, | ||
seq: u64::MAX, | ||
}, | ||
|e| e.id, | ||
); | ||
|
||
let stream = ctx.open_key_writable(&stream_key); | ||
stream.trim_stream_by_id(id_to_keep, false)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a parameter to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is for example usage and testing, why adding extra functionality? |
||
|
||
Ok(match element { | ||
Some(e) => RedisValue::BulkString(format!("{}-{}", e.id.ms, e.id.seq)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a parameter whether to also return fields Vec? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as above |
||
None => RedisValue::Null, | ||
}) | ||
} | ||
|
||
////////////////////////////////////////////////////// | ||
|
||
redis_module! { | ||
name: "stream", | ||
version: 1, | ||
data_types: [], | ||
commands: [ | ||
["STREAM_POP", stream_read_from, "fast deny-oom", 1, 1, 1], | ||
], | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,60 @@ | ||
use std::alloc::{GlobalAlloc, Layout}; | ||
use std::os::raw::c_void; | ||
|
||
use crate::raw; | ||
|
||
/// Panics with a message without using an allocator. | ||
/// Useful when using the allocator should be avoided or it is | ||
/// inaccessible. The default [std::panic] performs allocations and so | ||
/// will cause a double panic without a meaningful message if the | ||
/// allocator can't be used. This function makes sure we can panic with | ||
/// a reasonable message even without the allocator working. | ||
fn allocation_free_panic(message: &'static str) -> ! { | ||
use std::os::unix::io::AsRawFd; | ||
|
||
let _ = nix::unistd::write(std::io::stderr().as_raw_fd(), message.as_bytes()); | ||
|
||
std::process::abort(); | ||
} | ||
|
||
const REDIS_ALLOCATOR_NOT_AVAILABLE_MESSAGE: &str = | ||
"Critical error: the Redis Allocator isn't available. | ||
Consider enabling the \"fallback_to_system_allocator\" feature.\n"; | ||
|
||
/// Defines the Redis allocator. This allocator delegates the allocation | ||
/// and deallocation tasks to the Redis server when available, otherwise | ||
/// it fallbacks to the default Rust [std::alloc::System] allocator | ||
/// which is always available compared to the Redis allocator. | ||
#[derive(Copy, Clone)] | ||
pub struct RedisAlloc; | ||
|
||
unsafe impl GlobalAlloc for RedisAlloc { | ||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||
/* | ||
* To make sure the memory allocation by Redis is aligned to the according to the layout, | ||
* we need to align the size of the allocation to the layout. | ||
* | ||
* "Memory is conceptually broken into equal-sized chunks, | ||
* where the chunk size is a power of two that is greater than the page size. | ||
* Chunks are always aligned to multiples of the chunk size. | ||
* This alignment makes it possible to find metadata for user objects very quickly." | ||
* | ||
* From: https://linux.die.net/man/3/jemalloc | ||
*/ | ||
let size = (layout.size() + layout.align() - 1) & (!(layout.align() - 1)); | ||
|
||
raw::RedisModule_Alloc.unwrap()(size).cast::<u8>() | ||
match raw::RedisModule_Alloc { | ||
Some(alloc) => { | ||
/* | ||
* To make sure the memory allocation by Redis is aligned to the | ||
* according to the layout, we need to align the size of the | ||
* allocation to the layout. | ||
* | ||
* "Memory is conceptually broken into equal-sized chunks, | ||
* where the chunk size is a power of two that is greater than | ||
* the page size. Chunks are always aligned to multiples of the | ||
* chunk size. This alignment makes it possible to find metadata | ||
* for user objects very quickly." | ||
* | ||
* From: https://linux.die.net/man/3/jemalloc | ||
*/ | ||
let size = (layout.size() + layout.align() - 1) & (!(layout.align() - 1)); | ||
alloc(size).cast() | ||
} | ||
None => allocation_free_panic(REDIS_ALLOCATOR_NOT_AVAILABLE_MESSAGE), | ||
} | ||
} | ||
|
||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { | ||
raw::RedisModule_Free.unwrap()(ptr.cast::<c_void>()) | ||
match raw::RedisModule_Free { | ||
Some(dealloc) => dealloc(ptr.cast()), | ||
None => allocation_free_panic(REDIS_ALLOCATOR_NOT_AVAILABLE_MESSAGE), | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems equivalently unsafe as
unsafe {NUM_LOADINGS += 1};
(same with
NUM_FLUSHES
andNUM_ROLED_CHANGED
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I wanted the unsafe part to be as minimal as possible