Skip to content
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

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
325f4ca
API extentions.
MeirShpilraien May 17, 2022
d87a5f2
fix build of freebsd
MeirShpilraien May 26, 2022
9e0372f
fix tests failures
MeirShpilraien May 26, 2022
fbbdbdc
format fixes
MeirShpilraien May 26, 2022
f157503
Added key missed notification.
MeirShpilraien Jun 19, 2022
7d92069
Apply suggestions from code review
MeirShpilraien Jun 19, 2022
6d81a45
review fixes
MeirShpilraien Jun 19, 2022
b7f079f
review fixes
MeirShpilraien Jun 19, 2022
1da738a
review fixes
MeirShpilraien Jun 19, 2022
e04fed2
Merge branch 'master' into api_extentions
MeirShpilraien Jun 19, 2022
c8da3dd
allow RM_Call with different flags.
MeirShpilraien Jun 20, 2022
4fa23e5
Update redismodule.h, added support for ctx acl authentication.
MeirShpilraien Jun 21, 2022
af22d54
rename: read_only -> no_writes, safe -> script_mode.
MeirShpilraien Jun 21, 2022
db57a7d
format fixes
MeirShpilraien Jun 21, 2022
795f01d
make CallOptions cloneable.
MeirShpilraien Jun 21, 2022
7786f5d
added is_oom, idicating with we reached OOM.
MeirShpilraien Jun 21, 2022
381c029
Added acl_check_key_permission API
MeirShpilraien Jun 26, 2022
58989a6
Configuration API base on new Redis 7 module configuration.
MeirShpilraien Jun 27, 2022
2c899ae
Added configuration file
MeirShpilraien Jun 27, 2022
1fe6e5f
Fix invalid memory access on string configuration.
MeirShpilraien Jun 28, 2022
7b16438
added enum configuration value
MeirShpilraien Jun 28, 2022
ee88a64
added the ability to see which flag a configuration value has.
MeirShpilraien Jul 6, 2022
3cafe73
fix leak on user object.
MeirShpilraien Jul 21, 2022
ad75618
Merge branch 'master' into api_extentions
MeirShpilraien Jul 25, 2022
2771300
format fixes
MeirShpilraien Jul 25, 2022
4aa18c5
Support for resp3 on RM_Call.
MeirShpilraien Jul 26, 2022
42dbce1
format fixes
MeirShpilraien Jul 26, 2022
094c4df
added api to check if blocking is allow.
MeirShpilraien Jul 26, 2022
244dae2
avoid crash on binary result on call reply.
MeirShpilraien Jul 26, 2022
f2688c2
pass RedisString to key space notifications to support binary key nam…
MeirShpilraien Jul 26, 2022
319b70f
Change call_ext to except u8 slice to support calling commands with b…
MeirShpilraien Aug 2, 2022
a0b60a9
added api to reply with null.
MeirShpilraien Aug 3, 2022
4d3654b
fix tests
MeirShpilraien Aug 3, 2022
c690db2
format fixes
MeirShpilraien Aug 3, 2022
68fac3a
fix get_redis_version_internal api.
MeirShpilraien Aug 3, 2022
f8e469b
format fixes
MeirShpilraien Aug 3, 2022
e2074bd
Support for replicating slice
MeirShpilraien Aug 3, 2022
5b84c4d
Change call reply map and set to be Vec<v8>
MeirShpilraien Aug 14, 2022
c70081b
Added support for module change event.
MeirShpilraien Nov 10, 2022
99ace67
format code
gkorland Jan 15, 2023
cbb036f
Merge branch 'master' into api_extentions
gkorland Jan 15, 2023
cd09e7d
Fix arm compilation failure.
MeirShpilraien Jan 16, 2023
ac5e6ca
Fix invalid use of the crate keyword in macro_rules. (#283)
iddm Feb 21, 2023
14c5c92
Fix minor code issues. (#284)
iddm Feb 21, 2023
70dd1dd
Allow for easier version compatibility checking.
iddm Feb 28, 2023
10572ff
Merge pull request #286 from vityafx/allow-for-easier-version-compati…
iddm Feb 28, 2023
3c32669
Allow the crate to operate when the Redis API isn't available.
iddm Mar 28, 2023
cf0473c
Address clippy lints
iddm Mar 28, 2023
f873f8a
Fix the comment typos and width.
iddm Mar 28, 2023
8acffbe
Enable the unit and doc tests to work.
iddm Mar 28, 2023
03b52b9
Revert to panicking when the redis allocator isn't available.
iddm Mar 29, 2023
1e735bd
Merge pull request #300 from RedisLabsModules/fallback-to-the-system-…
iddm Mar 29, 2023
6a4487f
Fix the backwards compatibility with old Rust.
iddm Mar 31, 2023
566ad27
Merge pull request #301 from RedisLabsModules/use-unix-asrawfd
iddm Mar 31, 2023
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: 24 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,21 @@ name = "info"
crate-type = ["cdylib"]
required-features = []

[[example]]
name = "stream"
crate-type = ["cdylib"]
required-features = ["experimental-api"]

[[example]]
name = "server_events"
crate-type = ["cdylib"]
required-features = ["experimental-api"]

[[example]]
name = "scan"
crate-type = ["cdylib"]
required-features = ["experimental-api"]

[dependencies]
bitflags = "1.2"
libc = "0.2"
Expand All @@ -70,6 +85,7 @@ regex = "1"
strum_macros = "0.24"
#failure = "0.1"
backtrace = "0.3"
nix = "0.26"

[dev-dependencies]
anyhow = "1.0.38"
Expand All @@ -82,8 +98,11 @@ cc = "1.0"
[features]
default = []
experimental-api = []

# Workaround to allow cfg(feature = "test") in dependencies:
# https://github.com/rust-lang/rust/issues/59168#issuecomment-472653680
# This requires running the tests with `--features test`
test = []
# Allows to fallback to the default Rust allocator when the Redis
# allocator is not available. Particularly useful for tests without
# having a running Redis instance and running as a Redis module.
fallback_to_system_allocator = []
# TODO remove this "feature" as its name is misleading and using of
# which should be avoided in favour of well-named and self-explanatory
# features doing just one job following the SRP and KISS principles.
test = ["fallback_to_system_allocator"]
7 changes: 6 additions & 1 deletion examples/data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,16 @@ static MY_REDIS_TYPE: RedisType = RedisType::new(
unlink: None,
copy: None,
defrag: None,

free_effort2: None,
unlink2: None,
copy2: None,
mem_usage2: None,
},
);

unsafe extern "C" fn free(value: *mut c_void) {
Box::from_raw(value.cast::<MyType>());
drop(Box::from_raw(value.cast::<MyType>()));
}

fn alloc_set(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
Expand Down
8 changes: 5 additions & 3 deletions examples/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ extern crate redis_module;

use redis_module::{Context, NotifyEvent, RedisError, RedisResult, RedisString, Status};

fn on_event(ctx: &Context, event_type: NotifyEvent, event: &str, key: &str) {
fn on_event(ctx: &Context, event_type: NotifyEvent, event: &str, key: &[u8]) {
let msg = format!(
"Received event: {:?} on key: {} via event: {}",
event_type, key, event
event_type,
std::str::from_utf8(key).unwrap(),
event
);
ctx.log_debug(msg.as_str());
}

fn on_stream(ctx: &Context, _event_type: NotifyEvent, _event: &str, _key: &str) {
fn on_stream(ctx: &Context, _event_type: NotifyEvent, _event: &str, _key: &[u8]) {
ctx.log_debug("Stream event received!");
}

Expand Down
28 changes: 28 additions & 0 deletions examples/scan.rs
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],
],
}
55 changes: 55 additions & 0 deletions examples/server_events.rs
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;
Comment on lines +30 to +31
Copy link
Contributor

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 and NUM_ROLED_CHANGED)

Copy link
Collaborator Author

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

}

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],
]
}
48 changes: 48 additions & 0 deletions examples/stream.rs
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making sure - could also test for null here but can also skip this test since it is also handled in key_type() (since the common usage would not be a missing key, it would be better to skip a null test here and let key_type() handle nulls)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a parameter to stream_read_from for trimming options,
e.g., an enum with NoTrim, TrimApprox, TrimExact

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a parameter whether to also return fields Vec?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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],
],
}
2 changes: 0 additions & 2 deletions examples/test_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ fn test_helper_version(ctx: &Context, _args: Vec<RedisString>) -> RedisResult {
Ok(response.into())
}

#[cfg(feature = "test")]
fn test_helper_version_rm_call(ctx: &Context, _args: Vec<RedisString>) -> RedisResult {
let ver = ctx.get_redis_version_rm_call()?;
let response: Vec<i64> = vec![ver.major.into(), ver.minor.into(), ver.patch.into()];
Expand Down Expand Up @@ -43,7 +42,6 @@ fn add_info(ctx: &InfoContext, _for_crash_report: bool) {

//////////////////////////////////////////////////////

#[cfg(feature = "test")]
redis_module! {
name: "test_helper",
version: 1,
Expand Down
63 changes: 47 additions & 16 deletions src/alloc.rs
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),
}
}
}
Loading