From b78821958b7a804c5d87701fdd226ac17537d022 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 11 Jul 2022 10:52:54 +0300 Subject: [PATCH 01/60] Refactoring the rust api * Split rust API to 2 projects: 1. The API 2. The tests * Allow build with Cargo. --- Cargo.toml | 6 + Makefile | 6 +- rust_api/Cargo.toml | 20 + rust_api/build.rs | 52 +++ rust_api/src/lib.rs | 5 + rust_api/src/libmr/accumulator.rs | 59 +++ rust_api/src/libmr/base_object.rs | 84 ++++ rust_api/src/libmr/execution_builder.rs | 132 ++++++ rust_api/src/libmr/execution_object.rs | 67 +++ rust_api/src/libmr/filter.rs | 43 ++ rust_api/src/libmr/mapper.rs | 47 ++ rust_api/src/libmr/mod.rs | 33 ++ rust_api/src/libmr/reader.rs | 45 ++ rust_api/src/libmr/record.rs | 143 ++++++ .../src/libmr_c_raw}/mod.rs | 2 +- tests/mr_test_module/Cargo.toml | 1 + tests/mr_test_module/Makefile | 5 +- tests/mr_test_module/build.rs | 23 - tests/mr_test_module/pytests/run_tests.sh | 4 +- tests/mr_test_module/src/include/mr.h | 181 -------- tests/mr_test_module/src/lib.rs | 414 ++++++++--------- tests/mr_test_module/src/libmr/mod.rs | 433 ------------------ 22 files changed, 930 insertions(+), 875 deletions(-) create mode 100644 Cargo.toml create mode 100644 rust_api/Cargo.toml create mode 100644 rust_api/build.rs create mode 100644 rust_api/src/lib.rs create mode 100644 rust_api/src/libmr/accumulator.rs create mode 100644 rust_api/src/libmr/base_object.rs create mode 100644 rust_api/src/libmr/execution_builder.rs create mode 100644 rust_api/src/libmr/execution_object.rs create mode 100644 rust_api/src/libmr/filter.rs create mode 100644 rust_api/src/libmr/mapper.rs create mode 100644 rust_api/src/libmr/mod.rs create mode 100644 rust_api/src/libmr/reader.rs create mode 100644 rust_api/src/libmr/record.rs rename {tests/mr_test_module/src/libmrraw => rust_api/src/libmr_c_raw}/mod.rs (78%) delete mode 100644 tests/mr_test_module/build.rs delete mode 100644 tests/mr_test_module/src/include/mr.h delete mode 100644 tests/mr_test_module/src/libmr/mod.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7d37d30 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] + +members = [ + "rust_api", + "tests/mr_test_module" +] \ No newline at end of file diff --git a/Makefile b/Makefile index 6e90abd..7dabea2 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,11 @@ clean: clean_libmr build_deps: make -C deps/ - -libmr: build_deps + +libmr_only: make -C src/ + +libmr: build_deps libmr_only run_tests: make -C ./tests/mr_test_module/ test diff --git a/rust_api/Cargo.toml b/rust_api/Cargo.toml new file mode 100644 index 0000000..eea1945 --- /dev/null +++ b/rust_api/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "lib_mr" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +redis-module = { version="0.22.0", features = ["experimental-api"]} +serde_json = "1.0" +serde = "1.0" +serde_derive = "1.0" +libc = "0.2" + +[build-dependencies] +bindgen = "0.59.2" + +[lib] +crate-type = ["rlib"] +name = "mr" diff --git a/rust_api/build.rs b/rust_api/build.rs new file mode 100644 index 0000000..ad9e4fd --- /dev/null +++ b/rust_api/build.rs @@ -0,0 +1,52 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=../src/*.c"); + println!("cargo:rerun-if-changed=../src/*.h"); + println!("cargo:rerun-if-changed=../src/utils/*.h"); + println!("cargo:rerun-if-changed=../src/utils/*.c"); + + if !Command::new("make") + .args(&["-C", "../src/"]) + .env( + "MODULE_NAME", + std::env::var("MODULE_NAME").unwrap_or_default(), + ) + .status() + .expect("failed to compile libmr") + .success() + { + panic!("failed to compile libmr"); + } + + let output_dir = env::var("OUT_DIR").expect("Can not find out directory"); + + if !Command::new("cp") + .args(&["../src/libmr.a", &output_dir]) + .status() + .expect("failed copy libmr.a to output directory") + .success() + { + panic!("failed copy libmr.a to output directory"); + } + + let build = bindgen::Builder::default(); + + let bindings = build + .header("../src/mr.h") + .size_t_is_usize(true) + .layout_tests(false) + .generate() + .expect("error generating bindings"); + + let out_path = PathBuf::from(&output_dir); + bindings + .write_to_file(out_path.join("libmr_bindings.rs")) + .expect("failed to write bindings to file"); + + println!("cargo:rustc-flags=-L{} -lmr", output_dir); +} diff --git a/rust_api/src/lib.rs b/rust_api/src/lib.rs new file mode 100644 index 0000000..99fb478 --- /dev/null +++ b/rust_api/src/lib.rs @@ -0,0 +1,5 @@ +#[macro_use] +extern crate serde_derive; + +pub mod libmr; +pub mod libmr_c_raw; diff --git a/rust_api/src/libmr/accumulator.rs b/rust_api/src/libmr/accumulator.rs new file mode 100644 index 0000000..4fc3039 --- /dev/null +++ b/rust_api/src/libmr/accumulator.rs @@ -0,0 +1,59 @@ +use crate::libmr_c_raw::bindings::{ + ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterAccumulator, Record, +}; + +use crate::libmr::base_object::{register, BaseObject}; +use crate::libmr::record; +use crate::libmr::record::MRBaseRecord; +use crate::libmr::RustMRError; + +use std::os::raw::{c_char, c_void}; + +use std::ptr; + +pub extern "C" fn rust_accumulate( + ectx: *mut ExecutionCtx, + accumulator: *mut Record, + r: *mut Record, + args: *mut c_void, +) -> *mut Record { + let s = unsafe { &*(args as *mut Step) }; + let accumulator = if accumulator.is_null() { + None + } else { + let mut accumulator = + unsafe { *Box::from_raw(accumulator as *mut MRBaseRecord) }; + Some(accumulator.record.take().unwrap()) + }; + let mut r = unsafe { Box::from_raw(r as *mut MRBaseRecord) }; + let res = match s.accumulate(accumulator, r.record.take().unwrap()) { + Ok(res) => res, + Err(e) => { + unsafe { MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len()) }; + return ptr::null_mut(); + } + }; + Box::into_raw(Box::new(MRBaseRecord::new(res))) as *mut Record +} + +pub trait AccumulateStep: BaseObject { + type InRecord: record::Record; + type Accumulator: record::Record; + + fn accumulate( + &self, + accumulator: Option, + r: Self::InRecord, + ) -> Result; + + fn register() { + let obj = register::(); + unsafe { + MR_RegisterAccumulator( + Self::get_name().as_ptr() as *mut c_char, + Some(rust_accumulate::), + obj, + ); + } + } +} diff --git a/rust_api/src/libmr/base_object.rs b/rust_api/src/libmr/base_object.rs new file mode 100644 index 0000000..346904f --- /dev/null +++ b/rust_api/src/libmr/base_object.rs @@ -0,0 +1,84 @@ +use crate::libmr_c_raw::bindings::{ + MRError, MRObjectType, MR_RegisterObject, MR_SerializationCtxReadeBuffer, + MR_SerializationCtxWriteBuffer, ReaderSerializationCtx, WriteSerializationCtx, +}; + +use std::os::raw::{c_char, c_void}; + +use serde_json::{from_str, to_string}; + +use serde::ser::Serialize; + +use serde::de::Deserialize; + +use std::slice; +use std::str; + +use std::ffi::CString; + +pub extern "C" fn rust_obj_free(ctx: *mut c_void) { + unsafe { Box::from_raw(ctx as *mut T) }; +} + +pub extern "C" fn rust_obj_dup(arg: *mut c_void) -> *mut c_void { + let obj = unsafe { &mut *(arg as *mut T) }; + let mut obj = obj.clone(); + obj.init(); + Box::into_raw(Box::new(obj)) as *mut c_void +} + +pub extern "C" fn rust_obj_serialize( + sctx: *mut WriteSerializationCtx, + arg: *mut c_void, + error: *mut *mut MRError, +) { + let obj = unsafe { &mut *(arg as *mut T) }; + let s = to_string(obj).unwrap(); + unsafe { + MR_SerializationCtxWriteBuffer(sctx, s.as_ptr() as *const c_char, s.len(), error); + } +} + +pub extern "C" fn rust_obj_deserialize( + sctx: *mut ReaderSerializationCtx, + error: *mut *mut MRError, +) -> *mut c_void { + let mut len: usize = 0; + let s = unsafe { MR_SerializationCtxReadeBuffer(sctx, &mut len as *mut usize, error) }; + if !(unsafe { *error }).is_null() { + return 0 as *mut c_void; + } + let s = str::from_utf8(unsafe { slice::from_raw_parts(s as *const u8, len) }).unwrap(); + let mut obj: T = from_str(s).unwrap(); + obj.init(); + Box::into_raw(Box::new(obj)) as *mut c_void +} + +pub extern "C" fn rust_obj_to_string(_arg: *mut c_void) -> *mut c_char { + 0 as *mut c_char +} + +pub trait BaseObject: Clone + Serialize + Deserialize<'static> { + fn get_name() -> &'static str; + fn init(&mut self) {} +} + +pub(crate) fn register() -> *mut MRObjectType { + let type_name = T::get_name(); + let type_name_cstring = CString::new(type_name).unwrap(); + unsafe { + let obj = Box::into_raw(Box::new(MRObjectType { + type_: type_name_cstring.into_raw(), + id: 0, + free: Some(rust_obj_free::), + dup: Some(rust_obj_dup::), + serialize: Some(rust_obj_serialize::), + deserialize: Some(rust_obj_deserialize::), + tostring: Some(rust_obj_to_string), + })); + + MR_RegisterObject(obj); + + obj + } +} diff --git a/rust_api/src/libmr/execution_builder.rs b/rust_api/src/libmr/execution_builder.rs new file mode 100644 index 0000000..ffb0536 --- /dev/null +++ b/rust_api/src/libmr/execution_builder.rs @@ -0,0 +1,132 @@ +use std::marker::PhantomData; + +use crate::libmr_c_raw::bindings::{ + ExecutionBuilder, MRError, MR_CreateExecution, MR_CreateExecutionBuilder, MR_ErrorGetMessage, + MR_ExecutionBuilderBuilAccumulate, MR_ExecutionBuilderCollect, MR_ExecutionBuilderFilter, + MR_ExecutionBuilderMap, MR_ExecutionBuilderReshuffle, MR_FreeExecutionBuilder, +}; + +use std::os::raw::{c_char, c_void}; + +use crate::libmr::accumulator::AccumulateStep; +use crate::libmr::execution_object::ExecutionObj; +use crate::libmr::filter::FilterStep; +use crate::libmr::mapper::MapStep; +use crate::libmr::reader::Reader; +use crate::libmr::record; +use crate::libmr::RustMRError; + +use std::slice; +use std::str; + +use libc::strlen; + +pub struct Builder { + inner_builder: Option<*mut ExecutionBuilder>, + phantom: PhantomData, +} + +pub fn create_builder(reader: Re) -> Builder { + let reader = Box::into_raw(Box::new(reader)); + let inner_builder = unsafe { + MR_CreateExecutionBuilder( + Re::get_name().as_ptr() as *const c_char, + reader as *mut c_void, + ) + }; + Builder:: { + inner_builder: Some(inner_builder), + phantom: PhantomData, + } +} + +impl Builder { + fn take(&mut self) -> *mut ExecutionBuilder { + self.inner_builder.take().unwrap() + } + + pub fn map>(mut self, step: Step) -> Builder { + let inner_builder = self.take(); + unsafe { + MR_ExecutionBuilderMap( + inner_builder, + Step::get_name().as_ptr() as *const c_char, + Box::into_raw(Box::new(step)) as *const Step as *mut c_void, + ) + } + Builder:: { + inner_builder: Some(inner_builder), + phantom: PhantomData, + } + } + + pub fn filter>(self, step: Step) -> Builder { + unsafe { + MR_ExecutionBuilderFilter( + self.inner_builder.unwrap(), + Step::get_name().as_ptr() as *const c_char, + Box::into_raw(Box::new(step)) as *const Step as *mut c_void, + ) + } + self + } + + pub fn accumulate>( + mut self, + step: Step, + ) -> Builder { + let inner_builder = self.take(); + unsafe { + MR_ExecutionBuilderBuilAccumulate( + inner_builder, + Step::get_name().as_ptr() as *const c_char, + Box::into_raw(Box::new(step)) as *const Step as *mut c_void, + ) + } + Builder:: { + inner_builder: Some(inner_builder), + phantom: PhantomData, + } + } + + pub fn collect(self) -> Self { + unsafe { + MR_ExecutionBuilderCollect(self.inner_builder.unwrap()); + } + self + } + + pub fn reshuffle(self) -> Self { + unsafe { + MR_ExecutionBuilderReshuffle(self.inner_builder.unwrap()); + } + self + } + + pub fn create_execution(&self) -> Result, RustMRError> { + let execution = unsafe { + let mut err: *mut MRError = 0 as *mut MRError; + let res = MR_CreateExecution(self.inner_builder.unwrap(), &mut err); + if !err.is_null() { + let c_msg = MR_ErrorGetMessage(err); + let r_str = + str::from_utf8(slice::from_raw_parts(c_msg.cast::(), strlen(c_msg))) + .unwrap(); + return Err(r_str.to_string()); + } + res + }; + Ok(ExecutionObj { + inner_e: execution, + phantom: PhantomData, + }) + } +} + +impl Drop for Builder { + fn drop(&mut self) { + if let Some(innder_builder) = self.inner_builder { + unsafe { MR_FreeExecutionBuilder(innder_builder) } + } + } +} diff --git a/rust_api/src/libmr/execution_object.rs b/rust_api/src/libmr/execution_object.rs new file mode 100644 index 0000000..f879c36 --- /dev/null +++ b/rust_api/src/libmr/execution_object.rs @@ -0,0 +1,67 @@ +use std::marker::PhantomData; + +use crate::libmr_c_raw::bindings::{ + Execution, ExecutionCtx, MR_ExecutionCtxGetError, MR_ExecutionCtxGetErrorsLen, + MR_ExecutionCtxGetResult, MR_ExecutionCtxGetResultsLen, MR_ExecutionSetMaxIdle, + MR_ExecutionSetOnDoneHandler, MR_FreeExecution, MR_Run, +}; + +use crate::libmr::record; + +use std::os::raw::c_void; + +use std::slice; +use std::str; + +use libc::strlen; + +pub struct ExecutionObj { + pub(crate) inner_e: *mut Execution, + pub(crate) phantom: PhantomData, +} + +pub extern "C" fn rust_on_done, Vec<&str>)>( + ectx: *mut ExecutionCtx, + pd: *mut c_void, +) { + let f = unsafe { Box::from_raw(pd as *mut F) }; + let mut res = Vec::new(); + let res_len = unsafe { MR_ExecutionCtxGetResultsLen(ectx) }; + for i in 0..res_len { + let r = + unsafe { &mut *(MR_ExecutionCtxGetResult(ectx, i) as *mut record::MRBaseRecord) }; + res.push(r.record.as_mut().unwrap()); + } + let mut errs = Vec::new(); + let errs_len = unsafe { MR_ExecutionCtxGetErrorsLen(ectx) }; + for i in 0..errs_len { + let r = unsafe { MR_ExecutionCtxGetError(ectx, i) }; + let s = + str::from_utf8(unsafe { slice::from_raw_parts(r.cast::(), strlen(r)) }).unwrap(); + errs.push(s); + } + f(res, errs); +} + +impl ExecutionObj { + pub fn set_max_idle(&self, max_idle: usize) { + unsafe { MR_ExecutionSetMaxIdle(self.inner_e, max_idle) }; + } + + pub fn set_done_hanlder, Vec<&str>)>(&self, f: F) { + let f = Box::into_raw(Box::new(f)); + unsafe { + MR_ExecutionSetOnDoneHandler(self.inner_e, Some(rust_on_done::), f as *mut c_void) + }; + } + + pub fn run(&self) { + unsafe { MR_Run(self.inner_e) }; + } +} + +impl Drop for ExecutionObj { + fn drop(&mut self) { + unsafe { MR_FreeExecution(self.inner_e) }; + } +} diff --git a/rust_api/src/libmr/filter.rs b/rust_api/src/libmr/filter.rs new file mode 100644 index 0000000..ada5d5d --- /dev/null +++ b/rust_api/src/libmr/filter.rs @@ -0,0 +1,43 @@ +use crate::libmr_c_raw::bindings::{ + ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterFilter, Record, +}; + +use crate::libmr::base_object::{register, BaseObject}; +use crate::libmr::record; +use crate::libmr::record::MRBaseRecord; +use crate::libmr::RustMRError; + +use std::os::raw::{c_char, c_int, c_void}; + +pub extern "C" fn rust_filter( + ectx: *mut ExecutionCtx, + r: *mut Record, + args: *mut c_void, +) -> c_int { + let s = unsafe { &*(args as *mut Step) }; + let r = unsafe { &*(r as *mut MRBaseRecord) }; // do not take ownership on the record + match s.filter(&r.record.as_ref().unwrap()) { + Ok(res) => res as c_int, + Err(e) => { + unsafe { MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len()) }; + 0 as c_int + } + } +} + +pub trait FilterStep: BaseObject { + type R: record::Record; + + fn filter(&self, r: &Self::R) -> Result; + + fn register() { + let obj = register::(); + unsafe { + MR_RegisterFilter( + Self::get_name().as_ptr() as *mut c_char, + Some(rust_filter::), + obj, + ); + } + } +} diff --git a/rust_api/src/libmr/mapper.rs b/rust_api/src/libmr/mapper.rs new file mode 100644 index 0000000..3328610 --- /dev/null +++ b/rust_api/src/libmr/mapper.rs @@ -0,0 +1,47 @@ +use crate::libmr_c_raw::bindings::{ + ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterMapper, Record, +}; + +use crate::libmr::base_object::{register, BaseObject}; +use crate::libmr::record; +use crate::libmr::record::MRBaseRecord; +use crate::libmr::RustMRError; + +use std::os::raw::{c_char, c_void}; + +use std::ptr; + +pub extern "C" fn rust_map( + ectx: *mut ExecutionCtx, + r: *mut Record, + args: *mut c_void, +) -> *mut Record { + let s = unsafe { &*(args as *mut Step) }; + let r = unsafe { &mut *(r as *mut MRBaseRecord) }; + let res = match s.map(r.record.take().unwrap()) { + Ok(res) => res, + Err(e) => { + unsafe { MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len()) }; + return ptr::null_mut(); + } + }; + Box::into_raw(Box::new(MRBaseRecord::new(res))) as *mut Record +} + +pub trait MapStep: BaseObject { + type InRecord: record::Record; + type OutRecord: record::Record; + + fn map(&self, r: Self::InRecord) -> Result; + + fn register() { + let obj = register::(); + unsafe { + MR_RegisterMapper( + Self::get_name().as_ptr() as *mut c_char, + Some(rust_map::), + obj, + ); + } + } +} diff --git a/rust_api/src/libmr/mod.rs b/rust_api/src/libmr/mod.rs new file mode 100644 index 0000000..4e0887d --- /dev/null +++ b/rust_api/src/libmr/mod.rs @@ -0,0 +1,33 @@ +use crate::libmr_c_raw::bindings::{MRRecordType, MR_CalculateSlot, MR_Init, RedisModuleCtx}; + +use redis_module::Context; + +use std::os::raw::c_char; + +pub mod accumulator; +pub mod base_object; +pub mod execution_builder; +pub mod execution_object; +pub mod filter; +pub mod mapper; +pub mod reader; +pub mod record; + +impl Default for crate::libmr_c_raw::bindings::Record { + fn default() -> Self { + crate::libmr_c_raw::bindings::Record { + recordType: 0 as *mut MRRecordType, + } + } +} + +pub type RustMRError = String; + +pub fn mr_init(ctx: &Context, num_threads: usize) { + unsafe { MR_Init(ctx.ctx as *mut RedisModuleCtx, num_threads) }; + record::init(); +} + +pub fn calc_slot(s: &str) -> usize { + unsafe { MR_CalculateSlot(s.as_ptr() as *const c_char, s.len()) } +} diff --git a/rust_api/src/libmr/reader.rs b/rust_api/src/libmr/reader.rs new file mode 100644 index 0000000..6711a51 --- /dev/null +++ b/rust_api/src/libmr/reader.rs @@ -0,0 +1,45 @@ +use crate::libmr_c_raw::bindings::{ + ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterReader, Record, +}; + +use crate::libmr::base_object::{register, BaseObject}; +use crate::libmr::record; +use crate::libmr::record::MRBaseRecord; +use crate::libmr::RustMRError; + +use std::os::raw::{c_char, c_void}; + +use std::ptr; + +extern "C" fn rust_reader(ectx: *mut ExecutionCtx, args: *mut c_void) -> *mut Record { + let r = unsafe { &mut *(args as *mut Step) }; + let res = match r.read() { + Ok(res) => match res { + Some(res) => res, + None => return ptr::null_mut(), + }, + Err(e) => { + unsafe { MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len()) }; + return ptr::null_mut(); + } + }; + + Box::into_raw(Box::new(MRBaseRecord::new(res))) as *mut Record +} + +pub trait Reader: BaseObject { + type R: record::Record; + + fn read(&mut self) -> Result, RustMRError>; + + fn register() { + let obj = register::(); + unsafe { + MR_RegisterReader( + Self::get_name().as_ptr() as *mut c_char, + Some(rust_reader::), + obj, + ); + } + } +} diff --git a/rust_api/src/libmr/record.rs b/rust_api/src/libmr/record.rs new file mode 100644 index 0000000..2d6467e --- /dev/null +++ b/rust_api/src/libmr/record.rs @@ -0,0 +1,143 @@ +use crate::libmr_c_raw::bindings::{ + MRError, MRObjectType, MRRecordType, MR_RegisterRecord, MR_SerializationCtxReadeBuffer, + MR_SerializationCtxWriteBuffer, ReaderSerializationCtx, RedisModuleCtx, WriteSerializationCtx, +}; + +use redis_module::RedisValue; + +use serde_json::{from_str, to_string}; + +use std::os::raw::{c_char, c_void}; + +use crate::libmr::base_object::BaseObject; + +use std::collections::HashMap; + +use std::ffi::CString; + +use std::slice; +use std::str; + +#[repr(C)] +#[derive(Clone, Serialize)] +pub(crate) struct MRBaseRecord { + #[serde(skip)] + base: crate::libmr_c_raw::bindings::Record, + pub(crate) record: Option, +} + +impl MRBaseRecord { + pub(crate) fn new(record: T) -> MRBaseRecord { + MRBaseRecord { + base: crate::libmr_c_raw::bindings::Record { + recordType: get_record_type(T::get_name()).unwrap(), + }, + record: Some(record), + } + } +} + +pub extern "C" fn rust_obj_free(ctx: *mut c_void) { + unsafe { Box::from_raw(ctx as *mut MRBaseRecord) }; +} + +pub extern "C" fn rust_obj_dup(arg: *mut c_void) -> *mut c_void { + let obj = unsafe { &mut *(arg as *mut MRBaseRecord) }; + let mut obj = obj.clone(); + obj.record.as_mut().unwrap().init(); + Box::into_raw(Box::new(obj)) as *mut c_void +} + +pub extern "C" fn rust_obj_serialize( + sctx: *mut WriteSerializationCtx, + arg: *mut c_void, + error: *mut *mut MRError, +) { + let obj = unsafe { &mut *(arg as *mut MRBaseRecord) }; + let s = to_string(obj.record.as_ref().unwrap()).unwrap(); + unsafe { + MR_SerializationCtxWriteBuffer(sctx, s.as_ptr() as *const c_char, s.len(), error); + } +} + +pub extern "C" fn rust_obj_deserialize( + sctx: *mut ReaderSerializationCtx, + error: *mut *mut MRError, +) -> *mut c_void { + let mut len: usize = 0; + let s = unsafe { MR_SerializationCtxReadeBuffer(sctx, &mut len as *mut usize, error) }; + if !(unsafe { *error }).is_null() { + return 0 as *mut c_void; + } + let s = str::from_utf8(unsafe { slice::from_raw_parts(s as *const u8, len) }).unwrap(); + let mut obj: T = from_str(s).unwrap(); + obj.init(); + Box::into_raw(Box::new(MRBaseRecord::new(obj))) as *mut c_void +} + +pub extern "C" fn rust_obj_to_string(_arg: *mut c_void) -> *mut c_char { + 0 as *mut c_char +} + +pub extern "C" fn rust_obj_send_reply( + _arg1: *mut RedisModuleCtx, + _record: *mut ::std::os::raw::c_void, +) { +} + +pub extern "C" fn rust_obj_hash_slot(record: *mut ::std::os::raw::c_void) -> usize { + let record = unsafe { &mut *(record as *mut MRBaseRecord) }; + record.record.as_ref().unwrap().hash_slot() +} + +fn register_record() -> *mut MRRecordType { + let type_name = T::get_name(); + let type_name_cstring = CString::new(type_name).unwrap(); + unsafe { + let obj = Box::into_raw(Box::new(MRRecordType { + type_: MRObjectType { + type_: type_name_cstring.into_raw(), + id: 0, + free: Some(rust_obj_free::), + dup: Some(rust_obj_dup::), + serialize: Some(rust_obj_serialize::), + deserialize: Some(rust_obj_deserialize::), + tostring: Some(rust_obj_to_string), + }, + sendReply: Some(rust_obj_send_reply), + hashTag: Some(rust_obj_hash_slot::), + })); + + MR_RegisterRecord(obj); + + obj + } +} + +static mut RECORD_TYPES: Option> = None; + +fn get_record_types_mut() -> &'static mut HashMap { + unsafe { RECORD_TYPES.as_mut().unwrap() } +} + +fn get_record_type(name: &str) -> Option<*mut MRRecordType> { + match unsafe { RECORD_TYPES.as_ref().unwrap() }.get(name) { + Some(r) => Some(*r), + None => None, + } +} + +pub(crate) fn init() { + unsafe { + RECORD_TYPES = Some(HashMap::new()); + } +} + +pub trait Record: BaseObject { + fn register() { + let record_type = register_record::(); + get_record_types_mut().insert(Self::get_name().to_string(), record_type); + } + fn to_redis_value(&mut self) -> RedisValue; + fn hash_slot(&self) -> usize; +} diff --git a/tests/mr_test_module/src/libmrraw/mod.rs b/rust_api/src/libmr_c_raw/mod.rs similarity index 78% rename from tests/mr_test_module/src/libmrraw/mod.rs rename to rust_api/src/libmr_c_raw/mod.rs index d666c92..c9e9a54 100644 --- a/tests/mr_test_module/src/libmrraw/mod.rs +++ b/rust_api/src/libmr_c_raw/mod.rs @@ -4,7 +4,7 @@ #![allow(dead_code)] pub mod bindings { - include!(concat!(env!("OUT_DIR"), "/mr.rs")); + include!(concat!(env!("OUT_DIR"), "/libmr_bindings.rs")); } // See: https://users.rust-lang.org/t/bindgen-generate-options-and-some-are-none/14027 diff --git a/tests/mr_test_module/Cargo.toml b/tests/mr_test_module/Cargo.toml index 3a8c574..9dd4d64 100644 --- a/tests/mr_test_module/Cargo.toml +++ b/tests/mr_test_module/Cargo.toml @@ -13,6 +13,7 @@ serde_json = "1.0" serde = "1.0" serde_derive = "1.0" libc = "0.2" +lib_mr = { path = "../../rust_api"} [build-dependencies] bindgen = "0.57" diff --git a/tests/mr_test_module/Makefile b/tests/mr_test_module/Makefile index 55f8e11..a3265d4 100644 --- a/tests/mr_test_module/Makefile +++ b/tests/mr_test_module/Makefile @@ -18,14 +18,15 @@ else endif RUSTFLAGS=-L$(ROOT)/src/ +MODULE_NAME=MRTESTS all: build build_libmr: - DEBUG=$(DEBUG_RUN) COVERAGE=$(COVERAGE_RUN) MODULE_NAME=MRTESTS make -C $(ROOT)/src/ + DEBUG=$(DEBUG_RUN) COVERAGE=$(COVERAGE_RUN) MODULE_NAME=$(MODULE_NAME) make -C $(ROOT)/src/ build: build_libmr - RUSTFLAGS="$(RUSTFLAGS)" cargo build $(EXTRA_ARGS) + RUSTFLAGS="$(RUSTFLAGS)" MODULE_NAME==$(MODULE_NAME) cargo build $(EXTRA_ARGS) run: build redis-server --loadmodule $(MODULE_PATH) diff --git a/tests/mr_test_module/build.rs b/tests/mr_test_module/build.rs deleted file mode 100644 index f185df0..0000000 --- a/tests/mr_test_module/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -extern crate bindgen; -extern crate cc; - -use std::env; -use std::path::PathBuf; - -#[derive(Debug)] -struct RedisModuleCallback; - -fn main() { - let build = bindgen::Builder::default(); - - let bindings = build - .header("src/include/mr.h") - .size_t_is_usize(true) - .generate() - .expect("error generating bindings"); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("mr.rs")) - .expect("failed to write bindings to file"); -} diff --git a/tests/mr_test_module/pytests/run_tests.sh b/tests/mr_test_module/pytests/run_tests.sh index 9bf04dd..99f796a 100755 --- a/tests/mr_test_module/pytests/run_tests.sh +++ b/tests/mr_test_module/pytests/run_tests.sh @@ -6,9 +6,9 @@ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" ROOT=$(cd $HERE/../../../ && pwd) if [[ $DEBUG == 1 ]]; then - MODULE_PATH=$HERE/../target/debug/libmr_test.so + MODULE_PATH=$HERE/../../../target/debug/libmr_test.so else - MODULE_PATH=$HERE/../target/release/libmr_test.so + MODULE_PATH=$HERE/../../../target/release/libmr_test.so fi diff --git a/tests/mr_test_module/src/include/mr.h b/tests/mr_test_module/src/include/mr.h deleted file mode 100644 index 3f3f983..0000000 --- a/tests/mr_test_module/src/include/mr.h +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef SRC_MR_H_ -#define SRC_MR_H_ - -#include -#include - -#define LIBMR_API __attribute__ ((visibility("default"))) - -typedef struct RedisModuleCtx RedisModuleCtx; - -typedef struct MRError MRError; - -extern RedisModuleCtx* mr_staticCtx; - -/* Opaque struct build an execution */ -typedef struct ExecutionBuilder ExecutionBuilder; - -/* Opaque struct represents an execution */ -typedef struct Execution Execution; - -/* Opaque struct represents a record that pass in the execution pipe */ -typedef struct Record Record; - -/* Opaque struct that allow to serialize and deserialize objects */ -typedef struct mr_BufferReader ReaderSerializationCtx; -typedef struct mr_BufferWriter WriteSerializationCtx; - -/* MRObjectType callbacks definition */ -typedef void (*ObjectFree)(void* arg); -typedef void* (*ObjectDuplicate)(void* arg); -typedef void (*ObjectSerialize)(WriteSerializationCtx* sctx, void* arg, MRError** error); -typedef void* (*ObjectDeserialize)(ReaderSerializationCtx* sctx, MRError** error); -typedef char* (*ObjectToString)(void* arg); - -/* represent map reduce object type */ -typedef struct MRObjectType{ - char* type; - size_t id; - ObjectFree free; - ObjectDuplicate dup; - ObjectSerialize serialize; - ObjectDeserialize deserialize; - ObjectToString tostring; -}MRObjectType; - -/* Opaque struct that is given to execution steps */ -typedef struct ExecutionCtx ExecutionCtx; -LIBMR_API Record* MR_ExecutionCtxGetResult(ExecutionCtx* ectx, size_t i); -LIBMR_API size_t MR_ExecutionCtxGetResultsLen(ExecutionCtx* ectx); -LIBMR_API const char* MR_ExecutionCtxGetError(ExecutionCtx* ectx, size_t i); -LIBMR_API size_t MR_ExecutionCtxGetErrorsLen(ExecutionCtx* ectx); -LIBMR_API void MR_ExecutionCtxSetError(ExecutionCtx* ectx, const char* err, size_t len); - -/* Execution Callback definition */ -typedef void(*ExecutionCallback)(ExecutionCtx* ectx, void* pd); - -/* step functions signiture */ -typedef Record* (*ExecutionReader)(ExecutionCtx* ectx, void* args); -typedef Record* (*ExecutionMapper)(ExecutionCtx* ectx, Record* r, void* args); -typedef int (*ExecutionFilter)(ExecutionCtx* ectx, Record* r, void* args); -typedef Record* (*ExecutionAccumulator)(ExecutionCtx* ectx, Record* accumulator, Record* r, void* args); - -/* Creatign a new execution builder */ -LIBMR_API ExecutionBuilder* MR_CreateExecutionBuilder(const char* readerName, void* args); - -/* Add map step to the given builder. - * The function takes ownership on the given - * args so the user is not allow to use it anymore. */ -LIBMR_API void MR_ExecutionBuilderMap(ExecutionBuilder* builder, const char* name, void* args); - -/* Add filter step to the given builder. - * The function takes ownership on the given - * args so the user is not allow to use it anymore. */ -LIBMR_API void MR_ExecutionBuilderFilter(ExecutionBuilder* builder, const char* name, void* args); - -/* Add accumulate step to the given builder. - * The function takes ownership on the given - * args so the user is not allow to use it anymore. */ -LIBMR_API void MR_ExecutionBuilderBuilAccumulate(ExecutionBuilder* builder, const char* name, void* args); - -/* Add a collect step to the builder. - * Will return all the records to the initiator */ -LIBMR_API void MR_ExecutionBuilderCollect(ExecutionBuilder* builder); - -/* Add a reshuffle step to the builder. */ -LIBMR_API void MR_ExecutionBuilderReshuffle(ExecutionBuilder* builder); - -/* Free the give execution builder */ -LIBMR_API void MR_FreeExecutionBuilder(ExecutionBuilder* builder); - -/* Create execution from the given builder. - * Returns Execution which need to be freed using RM_FreeExecution. - * The user can use the returned Execution to set - * different callbacks, such as on_done callback and hold/resume callbacks. - * - * After callbacks are set the user can run the execution using MR_Run - * - * The function borrow the builder, which means that once returned - * the user can still use the builder, change it, or create more executions - * from it. - * - * Return NULL on error and set the error on err out param */ -LIBMR_API Execution* MR_CreateExecution(ExecutionBuilder* builder, MRError** err); - -/* Set max idle time (in ms) for the given execution */ -LIBMR_API void MR_ExecutionSetMaxIdle(Execution* e, size_t maxIdle); - -/* Set on execution done callbac */ -LIBMR_API void MR_ExecutionSetOnDoneHandler(Execution* e, ExecutionCallback onDone, void* pd); - -/* Run the given execution, should at most once on each execution. */ -LIBMR_API void MR_Run(Execution* e); - -/* Free the given execution */ -LIBMR_API void MR_FreeExecution(Execution* e); - -/* Initialize mr library */ -LIBMR_API int MR_Init(RedisModuleCtx* ctx, size_t numThreads); - -/* Register a new object type */ -LIBMR_API int MR_RegisterObject(MRObjectType* t); - -/* Register a reader */ -LIBMR_API void MR_RegisterReader(const char* name, ExecutionReader reader, MRObjectType* argType); - -/* Register a map step */ -LIBMR_API void MR_RegisterMapper(const char* name, ExecutionMapper mapper, MRObjectType* argType); - -/* Register a filter step */ -LIBMR_API void MR_RegisterFilter(const char* name, ExecutionFilter filter, MRObjectType* argType); - -/* Register an accumulate step */ -LIBMR_API void MR_RegisterAccumulator(const char* name, ExecutionAccumulator accumulator, MRObjectType* argType); - -/* Serialization Context functions */ -LIBMR_API long long MR_SerializationCtxReadeLongLong(ReaderSerializationCtx* sctx, MRError** err); -LIBMR_API const char* MR_SerializationCtxReadeBuffer(ReaderSerializationCtx* sctx, size_t* len, MRError** err); -LIBMR_API double MR_SerializationCtxReadeDouble(ReaderSerializationCtx* sctx, MRError** err); -LIBMR_API void MR_SerializationCtxWriteLongLong(WriteSerializationCtx* sctx, long long val, MRError** err); -LIBMR_API void MR_SerializationCtxWriteBuffer(WriteSerializationCtx* sctx, const char* buff, size_t len, MRError** err); -LIBMR_API void MR_SerializationCtxWriteDouble(WriteSerializationCtx* sctx, double val, MRError** err); - -/* records functions */ -typedef void (*SendAsRedisReply)(RedisModuleCtx*, void* record); -typedef size_t (*HashTag)(void* record); - -/* represent record type */ -typedef struct MRRecordType{ - MRObjectType type; - SendAsRedisReply sendReply; - HashTag hashTag; -}MRRecordType; - -/* Base record struct, each record should have it - * as first value */ -struct Record { - MRRecordType* recordType; -}; - -/* Register a new Record type */ -LIBMR_API int MR_RegisterRecord(MRRecordType* t); - -/* Free the give Record */ -LIBMR_API void MR_RecordFree(Record* r); - -/* Calculate slot on the given buffer */ -LIBMR_API size_t MR_CalculateSlot(const char* buff, size_t len); - -/* Create a new error object */ -LIBMR_API MRError* MR_ErrorCreate(const char* msg, size_t len); - -/* Get error message from the error object */ -LIBMR_API const char* MR_ErrorGetMessage(MRError* err); - -/* Free the error object */ -LIBMR_API void MR_ErrorFree(MRError* err); - -/***************** no public API **********************/ -MRObjectType* MR_GetObjectType(size_t id); - -#endif /* SRC_MR_H_ */ diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 3ff42d3..24e9e20 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -2,57 +2,26 @@ extern crate serde_derive; use redis_module::redisraw::bindings::{ - RedisModule_ScanCursorCreate, - RedisModuleScanCursor, - RedisModule_Scan, - RedisModule_GetDetachedThreadSafeContext, - RedisModuleCtx, - RedisModuleString, - RedisModuleKey, - RedisModule_ThreadSafeContextLock, + RedisModuleCtx, RedisModuleKey, RedisModuleScanCursor, RedisModuleString, + RedisModule_GetDetachedThreadSafeContext, RedisModule_Scan, RedisModule_ScanCursorCreate, + RedisModule_ScanCursorDestroy, RedisModule_ThreadSafeContextLock, RedisModule_ThreadSafeContextUnlock, - RedisModule_ScanCursorDestroy, }; use redis_module::{ - redis_module, - redis_command, - Context, - RedisError, - RedisResult, - RedisString, - RedisValue, - Status, + redis_command, redis_module, Context, RedisError, RedisResult, RedisString, RedisValue, Status, ThreadSafeContext, }; use std::str; -mod libmrraw; -mod libmr; - -use libmr::{ - create_builder, - BaseObject, - Record, - Reader, - MapStep, - RecordType, - RustMRError, - FilterStep, - AccumulateStep, +use mr::libmr::{ + accumulator::AccumulateStep, base_object::BaseObject, calc_slot, + execution_builder::create_builder, filter::FilterStep, mapper::MapStep, mr_init, + reader::Reader, record::Record, RustMRError, }; -use libmrraw::bindings::{ - MR_Init, - MRRecordType, - MR_CalculateSlot, -}; - -use std::os::raw::{ - c_void, - c_char, -}; +use std::os::raw::c_void; use std::{thread, time}; @@ -71,9 +40,7 @@ extern "C" {} static mut DETACHED_CTX: *mut RedisModuleCtx = 0 as *mut RedisModuleCtx; fn get_redis_ctx() -> *mut RedisModuleCtx { - unsafe { - DETACHED_CTX - } + unsafe { DETACHED_CTX } } fn get_ctx() -> Context { @@ -83,44 +50,37 @@ fn get_ctx() -> Context { fn ctx_lock() { let inner = get_redis_ctx(); - unsafe{ + unsafe { RedisModule_ThreadSafeContextLock.unwrap()(inner); } } fn ctx_unlock() { let inner = get_redis_ctx(); - unsafe{ + unsafe { RedisModule_ThreadSafeContextUnlock.unwrap()(inner); } } fn strin_record_new(s: String) -> StringRecord { - let mut r = unsafe{ - HASH_RECORD_TYPE.as_ref().unwrap().create() - }; - r.s = Some(s); - r + StringRecord { s: Some(s) } } fn int_record_new(i: i64) -> IntRecord { - let mut r = unsafe{ - INT_RECORD_TYPE.as_ref().unwrap().create() - }; - r.i = i; - r + IntRecord { i: i } } fn lmr_map_error(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(KeysReader::new(None)). - map(ErrorMapper). - filter(DummyFilter). - reshuffle(). - collect(). - accumulate(CountAccumulator). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(KeysReader::new(None)) + .map(ErrorMapper) + .filter(DummyFilter) + .reshuffle() + .collect() + .accumulate(CountAccumulator) + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|res, errs|{ + execution.set_done_hanlder(|res, errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); let mut final_res = Vec::new(); final_res.push(RedisValue::Integer(res.len() as i64)); @@ -134,15 +94,16 @@ fn lmr_map_error(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_filter_error(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(KeysReader::new(None)). - filter(ErrorFilter). - map(DummyMapper). - reshuffle(). - collect(). - accumulate(CountAccumulator). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(KeysReader::new(None)) + .filter(ErrorFilter) + .map(DummyMapper) + .reshuffle() + .collect() + .accumulate(CountAccumulator) + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|res, errs|{ + execution.set_done_hanlder(|res, errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); let mut final_res = Vec::new(); final_res.push(RedisValue::Integer(res.len() as i64)); @@ -156,16 +117,17 @@ fn lmr_filter_error(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_accumulate_error(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(KeysReader::new(None)). - accumulate(ErrorAccumulator). - map(DummyMapper). - filter(DummyFilter). - reshuffle(). - collect(). - accumulate(CountAccumulator). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(KeysReader::new(None)) + .accumulate(ErrorAccumulator) + .map(DummyMapper) + .filter(DummyFilter) + .reshuffle() + .collect() + .accumulate(CountAccumulator) + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|res, errs|{ + execution.set_done_hanlder(|res, errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); let mut final_res = Vec::new(); final_res.push(RedisValue::Integer(res.len() as i64)); @@ -179,12 +141,13 @@ fn lmr_accumulate_error(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_uneven_work(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(MaxIdleReader::new(1)). - map(UnevenWorkMapper::new()). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(MaxIdleReader::new(1)) + .map(UnevenWorkMapper::new()) + .create_execution() + .map_err(|e| RedisError::String(e))?; execution.set_max_idle(2000); let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|mut res, mut errs|{ + execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); if errs.len() > 0 { let err = errs.pop().unwrap(); @@ -201,15 +164,16 @@ fn lmr_uneven_work(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_read_error(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(ErrorReader::new()). - map(DummyMapper). - filter(DummyFilter). - reshuffle(). - collect(). - accumulate(CountAccumulator). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(ErrorReader::new()) + .map(DummyMapper) + .filter(DummyFilter) + .reshuffle() + .collect() + .accumulate(CountAccumulator) + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|res, errs|{ + execution.set_done_hanlder(|res, errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); let mut final_res = Vec::new(); final_res.push(RedisValue::Integer(res.len() as i64)); @@ -223,12 +187,13 @@ fn lmr_read_error(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_count_key(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(KeysReader::new(None)). - collect(). - accumulate(CountAccumulator). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(KeysReader::new(None)) + .collect() + .accumulate(CountAccumulator) + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|mut res, mut errs|{ + execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); if errs.len() > 0 { let err = errs.pop().unwrap(); @@ -245,12 +210,13 @@ fn lmr_count_key(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_reach_max_idle(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(MaxIdleReader::new(50)). - collect(). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(MaxIdleReader::new(50)) + .collect() + .create_execution() + .map_err(|e| RedisError::String(e))?; execution.set_max_idle(20); let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|mut res, mut errs|{ + execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); if errs.len() > 0 { let err = errs.pop().unwrap(); @@ -267,12 +233,13 @@ fn lmr_reach_max_idle(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_read_keys_type(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(KeysReader::new(None)). - map(TypeMapper). - collect(). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(KeysReader::new(None)) + .map(TypeMapper) + .collect() + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|mut res, mut errs|{ + execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); if errs.len() > 0 { let err = errs.pop().unwrap(); @@ -290,17 +257,21 @@ fn lmr_read_keys_type(ctx: &Context, _args: Vec) -> RedisResult { fn replace_keys_values(ctx: &Context, args: Vec) -> RedisResult { let mut args = args.into_iter().skip(1); - let prefix = args.next().ok_or(RedisError::Str("not prefix was given"))?.try_as_str()?; - let execution = create_builder(KeysReader::new(Some(prefix.to_string()))). - filter(TypeFilter::new("string".to_string())). - map(ReadStringMapper{}). - reshuffle(). - map(WriteDummyString{}). - collect(). - create_execution().map_err(|e|RedisError::String(e))?; - + let prefix = args + .next() + .ok_or(RedisError::Str("not prefix was given"))? + .try_as_str()?; + let execution = create_builder(KeysReader::new(Some(prefix.to_string()))) + .filter(TypeFilter::new("string".to_string())) + .map(ReadStringMapper {}) + .reshuffle() + .map(WriteDummyString {}) + .collect() + .create_execution() + .map_err(|e| RedisError::String(e))?; + let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|mut res, mut errs|{ + execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); if errs.len() > 0 { let err = errs.pop().unwrap(); @@ -317,12 +288,13 @@ fn replace_keys_values(ctx: &Context, args: Vec) -> RedisResult { } fn lmr_read_string_keys(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(KeysReader::new(None)). - filter(TypeFilter::new("string".to_string())). - collect(). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(KeysReader::new(None)) + .filter(TypeFilter::new("string".to_string())) + .collect() + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|mut res, mut errs|{ + execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); if errs.len() > 0 { let err = errs.pop().unwrap(); @@ -339,11 +311,12 @@ fn lmr_read_string_keys(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_read_all_keys(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(KeysReader::new(None)). - collect(). - create_execution().map_err(|e|RedisError::String(e))?; + let execution = create_builder(KeysReader::new(None)) + .collect() + .create_execution() + .map_err(|e| RedisError::String(e))?; let blocked_client = ctx.block_client(); - execution.set_done_hanlder(|mut res, mut errs|{ + execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); if errs.len() > 0 { let err = errs.pop().unwrap(); @@ -359,82 +332,49 @@ fn lmr_read_all_keys(ctx: &Context, _args: Vec) -> RedisResult { Ok(RedisValue::NoReply) } -impl Default for crate::libmrraw::bindings::Record { - fn default() -> Self { - crate::libmrraw::bindings::Record { - recordType: 0 as *mut MRRecordType, - } - } -} - -#[repr(C)] #[derive(Clone, Serialize, Deserialize)] struct StringRecord { - #[serde(skip)] - base: crate::libmrraw::bindings::Record, pub s: Option, } impl Record for StringRecord { - fn new(t: *mut MRRecordType) -> Self { - StringRecord { - base: crate::libmrraw::bindings::Record { - recordType: t, - }, - s: None, - } - } - fn to_redis_value(&mut self) -> RedisValue { match self.s.take() { Some(s) => RedisValue::BulkString(s), None => RedisValue::Null, } - } fn hash_slot(&self) -> usize { - unsafe{MR_CalculateSlot(self.s.as_ref().unwrap().as_ptr() as *const c_char, self.s.as_ref().unwrap().len())} + calc_slot(self.s.as_ref().unwrap()) } } impl BaseObject for StringRecord { fn get_name() -> &'static str { - "StringRecord\0" + "StringRecord" } } -#[repr(C)] #[derive(Clone, Serialize, Deserialize)] struct IntRecord { - #[serde(skip)] - base: crate::libmrraw::bindings::Record, pub i: i64, } impl Record for IntRecord { - fn new(t: *mut MRRecordType) -> Self { - IntRecord { - base: crate::libmrraw::bindings::Record { - recordType: t, - }, - i: 0, - } - } - fn to_redis_value(&mut self) -> RedisValue { RedisValue::Integer(self.i) } fn hash_slot(&self) -> usize { let s = self.i.to_string(); - unsafe{MR_CalculateSlot(s.as_ptr() as *const c_char, s.len())} + calc_slot(&s) } } impl BaseObject for IntRecord { fn get_name() -> &'static str { - "IntRecord\0" + "IntRecord" } } @@ -445,19 +385,23 @@ impl AccumulateStep for CountAccumulator { type InRecord = StringRecord; type Accumulator = IntRecord; - fn accumulate(&self, accumulator: Option, _r: Self::InRecord) -> Result { + fn accumulate( + &self, + accumulator: Option, + _r: Self::InRecord, + ) -> Result { let mut accumulator = match accumulator { Some(a) => a, - None => int_record_new(0) + None => int_record_new(0), }; - accumulator.i+=1; + accumulator.i += 1; Ok(accumulator) } } impl BaseObject for CountAccumulator { fn get_name() -> &'static str { - "CountAccumulator\0" + "CountAccumulator" } } @@ -468,14 +412,18 @@ impl AccumulateStep for ErrorAccumulator { type InRecord = StringRecord; type Accumulator = StringRecord; - fn accumulate(&self, _accumulator: Option, _r: Self::InRecord) -> Result { + fn accumulate( + &self, + _accumulator: Option, + _r: Self::InRecord, + ) -> Result { Err("accumulate_error".to_string()) } } impl BaseObject for ErrorAccumulator { fn get_name() -> &'static str { - "ErrorAccumulator\0" + "ErrorAccumulator" } } @@ -493,7 +441,7 @@ impl FilterStep for DummyFilter { impl BaseObject for DummyFilter { fn get_name() -> &'static str { - "DummyFilter\0" + "DummyFilter" } } @@ -511,7 +459,7 @@ impl FilterStep for ErrorFilter { impl BaseObject for ErrorFilter { fn get_name() -> &'static str { - "ErrorFilter\0" + "ErrorFilter" } } @@ -522,10 +470,8 @@ struct TypeFilter { } impl TypeFilter { - pub fn new(t: String) -> TypeFilter{ - TypeFilter{ - t: t, - } + pub fn new(t: String) -> TypeFilter { + TypeFilter { t: t } } } @@ -535,7 +481,7 @@ impl FilterStep for TypeFilter { fn filter(&self, r: &Self::R) -> Result { let ctx = get_ctx(); ctx_lock(); - let res = ctx.call("type",&[r.s.as_ref().unwrap()]); + let res = ctx.call("type", &[r.s.as_ref().unwrap()]); ctx_unlock(); if let Ok(res) = res { if let RedisValue::SimpleString(res) = res { @@ -555,7 +501,7 @@ impl FilterStep for TypeFilter { impl BaseObject for TypeFilter { fn get_name() -> &'static str { - "TypeFilter\0" + "TypeFilter" } } @@ -570,7 +516,7 @@ impl MapStep for TypeMapper { fn map(&self, mut r: Self::InRecord) -> Result { let ctx = get_ctx(); ctx_lock(); - let res = ctx.call("type",&[r.s.as_ref().unwrap()]); + let res = ctx.call("type", &[r.s.as_ref().unwrap()]); ctx_unlock(); if let Ok(res) = res { if let RedisValue::SimpleString(res) = res { @@ -587,7 +533,7 @@ impl MapStep for TypeMapper { impl BaseObject for TypeMapper { fn get_name() -> &'static str { - "TypeMapper\0" + "TypeMapper" } } @@ -606,7 +552,7 @@ impl MapStep for ErrorMapper { impl BaseObject for ErrorMapper { fn get_name() -> &'static str { - "ErrorMapper\0" + "ErrorMapper" } } /* map key name to its type */ @@ -624,7 +570,7 @@ impl MapStep for DummyMapper { impl BaseObject for DummyMapper { fn get_name() -> &'static str { - "DummyMapper\0" + "DummyMapper" } } @@ -632,12 +578,12 @@ impl BaseObject for DummyMapper { #[derive(Clone, Serialize, Deserialize)] struct UnevenWorkMapper { #[serde(skip)] - is_initiator: bool + is_initiator: bool, } impl UnevenWorkMapper { fn new() -> UnevenWorkMapper { - UnevenWorkMapper{ is_initiator: true } + UnevenWorkMapper { is_initiator: true } } } @@ -656,11 +602,10 @@ impl MapStep for UnevenWorkMapper { impl BaseObject for UnevenWorkMapper { fn get_name() -> &'static str { - "UnevenWorkMapper\0" + "UnevenWorkMapper" } } - #[derive(Clone, Serialize, Deserialize)] struct ReadStringMapper; @@ -671,7 +616,7 @@ impl MapStep for ReadStringMapper { fn map(&self, mut r: Self::InRecord) -> Result { let ctx = get_ctx(); ctx_lock(); - let res = ctx.call("get",&[r.s.as_ref().unwrap()]); + let res = ctx.call("get", &[r.s.as_ref().unwrap()]); ctx_unlock(); if let Ok(res) = res { if let RedisValue::SimpleString(res) = res { @@ -688,7 +633,7 @@ impl MapStep for ReadStringMapper { impl BaseObject for ReadStringMapper { fn get_name() -> &'static str { - "ReadStringMapper\0" + "ReadStringMapper" } } @@ -702,7 +647,7 @@ impl MapStep for WriteDummyString { fn map(&self, mut r: Self::InRecord) -> Result { let ctx = get_ctx(); ctx_lock(); - let res = ctx.call("set",&[r.s.as_ref().unwrap(), "val"]); + let res = ctx.call("set", &[r.s.as_ref().unwrap(), "val"]); ctx_unlock(); if let Ok(res) = res { if let RedisValue::SimpleString(res) = res { @@ -719,7 +664,7 @@ impl MapStep for WriteDummyString { impl BaseObject for WriteDummyString { fn get_name() -> &'static str { - "WriteDummyString\0" + "WriteDummyString" } } @@ -733,57 +678,61 @@ struct MaxIdleReader { impl MaxIdleReader { fn new(sleep_time: usize) -> MaxIdleReader { - MaxIdleReader {is_initiator: true, sleep_time: sleep_time, is_done: false} + MaxIdleReader { + is_initiator: true, + sleep_time: sleep_time, + is_done: false, + } } } impl Reader for MaxIdleReader { type R = StringRecord; - fn read(&mut self) -> Option> { + fn read(&mut self) -> Result, RustMRError> { if self.is_done { - return None; + return Ok(None); } self.is_done = true; if !self.is_initiator { let ten_millis = time::Duration::from_millis(self.sleep_time as u64); thread::sleep(ten_millis); } - Some(Ok(strin_record_new("record".to_string()))) + Ok(Some(strin_record_new("record".to_string()))) } } impl BaseObject for MaxIdleReader { fn get_name() -> &'static str { - "MaxIdleReader\0" + "MaxIdleReader" } } #[derive(Clone, Serialize, Deserialize)] -struct ErrorReader{ +struct ErrorReader { is_done: bool, } impl ErrorReader { fn new() -> ErrorReader { - ErrorReader {is_done: false} + ErrorReader { is_done: false } } } impl Reader for ErrorReader { type R = StringRecord; - fn read(&mut self) -> Option> { + fn read(&mut self) -> Result, RustMRError> { if self.is_done { - return None; + return Ok(None); } self.is_done = true; - Some(Err("read_error".to_string())) + Err("read_error".to_string()) } } impl BaseObject for ErrorReader { fn get_name() -> &'static str { - "ErrorReader\0" + "ErrorReader" } } @@ -795,13 +744,14 @@ struct KeysReader { pending: Vec, #[serde(skip)] is_done: bool, - prefix: Option + prefix: Option, } impl KeysReader { fn new(prefix: Option) -> KeysReader { - let mut reader = KeysReader {cursor: None, - pending:Vec::new(), + let mut reader = KeysReader { + cursor: None, + pending: Vec::new(), is_done: false, prefix: prefix, }; @@ -810,12 +760,13 @@ impl KeysReader { } } -extern "C" fn cursor_callback(_ctx: *mut RedisModuleCtx, - keyname: *mut RedisModuleString, - _key: *mut RedisModuleKey, - privdata: *mut c_void) { - - let reader = unsafe{&mut *(privdata as *mut KeysReader)}; +extern "C" fn cursor_callback( + _ctx: *mut RedisModuleCtx, + keyname: *mut RedisModuleString, + _key: *mut RedisModuleKey, + privdata: *mut c_void, +) { + let reader = unsafe { &mut *(privdata as *mut KeysReader) }; let key_str = RedisString::from_ptr(keyname).unwrap(); if let Some(pre) = &reader.prefix { @@ -832,18 +783,26 @@ extern "C" fn cursor_callback(_ctx: *mut RedisModuleCtx, impl Reader for KeysReader { type R = StringRecord; - fn read(&mut self) -> Option> { - let cursor = *self.cursor.as_ref()?; + fn read(&mut self) -> Result, RustMRError> { + let cursor = *match self.cursor.as_ref() { + Some(s) => s, + None => return Ok(None), + }; loop { if let Some(element) = self.pending.pop() { - return Some(Ok(element)); + return Ok(Some(element)); } if self.is_done { - return None; + return Ok(None); } ctx_lock(); - let res = unsafe{ - let res = RedisModule_Scan.unwrap()(get_redis_ctx(), cursor, Some(cursor_callback), self as *mut KeysReader as *mut c_void); + let res = unsafe { + let res = RedisModule_Scan.unwrap()( + get_redis_ctx(), + cursor, + Some(cursor_callback), + self as *mut KeysReader as *mut c_void, + ); res }; ctx_unlock(); @@ -856,13 +815,11 @@ impl Reader for KeysReader { impl BaseObject for KeysReader { fn get_name() -> &'static str { - "KeysReader\0" + "KeysReader" } fn init(&mut self) { - self.cursor = Some(unsafe{ - RedisModule_ScanCursorCreate.unwrap()() - }); + self.cursor = Some(unsafe { RedisModule_ScanCursorCreate.unwrap()() }); self.is_done = false; } } @@ -870,26 +827,21 @@ impl BaseObject for KeysReader { impl Drop for KeysReader { fn drop(&mut self) { if let Some(c) = self.cursor { - unsafe{RedisModule_ScanCursorDestroy.unwrap()(c)}; + unsafe { RedisModule_ScanCursorDestroy.unwrap()(c) }; } } } -static mut HASH_RECORD_TYPE: Option> = None; -static mut INT_RECORD_TYPE: Option> = None; - fn init_func(ctx: &Context, _args: &Vec) -> Status { - unsafe{ + unsafe { DETACHED_CTX = RedisModule_GetDetachedThreadSafeContext.unwrap()(ctx.ctx); - MR_Init(ctx.ctx as *mut libmrraw::bindings::RedisModuleCtx, 3); + mr_init(ctx, 3); } - unsafe{ - HASH_RECORD_TYPE = Some(RecordType::new()); - INT_RECORD_TYPE = Some(RecordType::new()); - }; - KeysReader::register(); + StringRecord::register(); + IntRecord::register(); + KeysReader::register(); MaxIdleReader::register(); ErrorReader::register(); TypeMapper::register(); @@ -903,10 +855,10 @@ fn init_func(ctx: &Context, _args: &Vec) -> Status { CountAccumulator::register(); ErrorAccumulator::register(); UnevenWorkMapper::register(); - Status::Ok + Status::Ok } -redis_module!{ +redis_module! { name: "lmrtest", version: 99_99_99, data_types: [], diff --git a/tests/mr_test_module/src/libmr/mod.rs b/tests/mr_test_module/src/libmr/mod.rs deleted file mode 100644 index 7d84b37..0000000 --- a/tests/mr_test_module/src/libmr/mod.rs +++ /dev/null @@ -1,433 +0,0 @@ -use std::marker::PhantomData; -use crate::libmrraw::bindings::{ - ExecutionBuilder, - MR_CreateExecutionBuilder, - MR_FreeExecutionBuilder, - MR_ExecutionBuilderCollect, - MR_ExecutionBuilderMap, - MRObjectType, - WriteSerializationCtx, - ReaderSerializationCtx, - MR_SerializationCtxWriteBuffer, - MR_SerializationCtxReadeBuffer, - RedisModuleCtx, - MR_RegisterObject, - MR_RegisterMapper, - ExecutionCtx, - MR_RegisterReader, - MR_CreateExecution, - MR_Run, - Execution, - MR_ExecutionSetOnDoneHandler, - MR_FreeExecution, - MR_ExecutionCtxGetResultsLen, - MR_ExecutionCtxGetResult, - MR_ExecutionCtxGetErrorsLen, - MR_ExecutionCtxGetError, - MRRecordType, - MR_RegisterRecord, - MR_ExecutionCtxSetError, - MR_ExecutionBuilderFilter, - MR_RegisterFilter, - MR_ExecutionBuilderReshuffle, - MR_ExecutionSetMaxIdle, - MR_RegisterAccumulator, - MR_ExecutionBuilderBuilAccumulate, - MRError, - MR_ErrorGetMessage, -}; - -use serde::ser::{ - Serialize, -}; - -use serde::de::{ - Deserialize, -}; - -use serde_json::{ - to_string, - from_str, -}; - -use std::os::raw::{ - c_char, - c_void, - c_int, -}; - -use std::slice; -use std::str; - -use redis_module::{ - RedisValue, -}; - -use libc::{ - strlen, -}; - -pub type RustMRError = String; - -pub extern "C" fn rust_obj_free(ctx: *mut c_void) { - unsafe{Box::from_raw(ctx as *mut T)}; -} - -pub extern "C" fn rust_obj_dup(arg: *mut c_void) -> *mut c_void { - let obj = unsafe{&mut *(arg as *mut T)}; - let mut obj = obj.clone(); - obj.init(); - Box::into_raw(Box::new(obj)) as *mut c_void -} - -pub extern "C" fn rust_obj_serialize(sctx: *mut WriteSerializationCtx, arg: *mut c_void, error: *mut *mut MRError) { - let obj = unsafe{&mut *(arg as *mut T)}; - let s = to_string(obj).unwrap(); - unsafe{ - MR_SerializationCtxWriteBuffer(sctx, s.as_ptr() as *const c_char, s.len(), error); - } -} - -pub extern "C" fn rust_obj_deserialize(sctx: *mut ReaderSerializationCtx, error: *mut *mut MRError) -> *mut c_void { - let mut len: usize = 0; - let s = unsafe { - MR_SerializationCtxReadeBuffer(sctx, &mut len as *mut usize, error) - }; - if !(unsafe{*error}).is_null() { - return 0 as *mut c_void; - } - let s = str::from_utf8(unsafe { slice::from_raw_parts(s as *const u8, len) }).unwrap(); - let mut obj: T = from_str(s).unwrap(); - obj.init(); - Box::into_raw(Box::new(obj)) as *mut c_void -} - -pub extern "C" fn rust_obj_to_string(_arg: *mut c_void) -> *mut c_char { - 0 as *mut c_char -} - -pub extern "C" fn rust_obj_send_reply(_arg1: *mut RedisModuleCtx, _record: *mut ::std::os::raw::c_void) { - -} - -pub extern "C" fn rust_obj_hash_slot(record: *mut ::std::os::raw::c_void) -> usize { - let record = unsafe{&mut *(record as *mut T)}; - record.hash_slot() -} - -pub trait BaseObject: Clone + Serialize + Deserialize<'static> { - fn get_name() -> &'static str; - fn init(&mut self) {} -} - -fn register() -> *mut MRObjectType { - unsafe { - let obj = Box::into_raw(Box::new(MRObjectType { - type_: T::get_name().as_ptr() as *mut c_char, - id: 0, - free: Some(rust_obj_free::), - dup: Some(rust_obj_dup::), - serialize: Some(rust_obj_serialize::), - deserialize: Some(rust_obj_deserialize::), - tostring: Some(rust_obj_to_string), - })); - - MR_RegisterObject(obj); - - obj - } -} - -fn register_record() -> *mut MRRecordType { - unsafe { - let obj = Box::into_raw(Box::new(MRRecordType { - type_: MRObjectType{ - type_: T::get_name().as_ptr() as *mut c_char, - id: 0, - free: Some(rust_obj_free::), - dup: Some(rust_obj_dup::), - serialize: Some(rust_obj_serialize::), - deserialize: Some(rust_obj_deserialize::), - tostring: Some(rust_obj_to_string), - }, - sendReply: Some(rust_obj_send_reply), - hashTag: Some(rust_obj_hash_slot::), - })); - - MR_RegisterRecord(obj); - - obj - } -} - -pub struct RecordType { - t: *mut MRRecordType, - phantom: PhantomData, -} - -impl RecordType { - pub fn new() -> RecordType { - let obj = register_record::(); - RecordType { - t: obj, - phantom: PhantomData, - } - } - - pub fn create(&self) -> R { - R::new(self.t) - } -} - -pub trait Record: BaseObject{ - fn new(t: *mut MRRecordType) -> Self; - fn to_redis_value(&mut self) -> RedisValue; - fn hash_slot(&self) -> usize; -} - -pub extern "C" fn rust_reader(ectx: *mut ExecutionCtx, args: *mut ::std::os::raw::c_void) -> *mut crate::libmrraw::bindings::Record { - let r = unsafe{&mut *(args as *mut Step)}; - match r.read() { - Some(res) => { - match res { - Ok(res) => Box::into_raw(Box::new(res)) as *mut crate::libmrraw::bindings::Record, - Err(e) => { - unsafe{MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len())}; - 0 as *mut crate::libmrraw::bindings::Record - }, - } - }, - None => 0 as *mut crate::libmrraw::bindings::Record, - } -} - -pub trait Reader : BaseObject{ - type R: Record; - - fn read(&mut self) -> Option>; - - fn register() { - let obj = register::(); - unsafe{ - MR_RegisterReader(Self::get_name().as_ptr() as *mut c_char, Some(rust_reader::), obj); - } - } -} - -pub extern "C" fn rust_map(ectx: *mut ExecutionCtx, r: *mut crate::libmrraw::bindings::Record, args: *mut c_void) -> *mut crate::libmrraw::bindings::Record { - let s = unsafe{&*(args as *mut Step)}; - let r = unsafe{Box::from_raw(r as *mut Step::InRecord)}; - match s.map(*r) { - Ok(res) => Box::into_raw(Box::new(res)) as *mut crate::libmrraw::bindings::Record, - Err(e) => { - unsafe{MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len())}; - 0 as *mut crate::libmrraw::bindings::Record - } - } - -} - -pub trait MapStep: BaseObject{ - type InRecord: Record; - type OutRecord: Record; - - fn map(&self, r: Self::InRecord) -> Result; - - fn register() { - let obj = register::(); - unsafe{ - MR_RegisterMapper(Self::get_name().as_ptr() as *mut c_char, Some(rust_map::), obj); - } - } -} - -pub extern "C" fn rust_filter(ectx: *mut ExecutionCtx, r: *mut crate::libmrraw::bindings::Record, args: *mut c_void) -> c_int { - let s = unsafe{&*(args as *mut Step)}; - let r = unsafe{&*(r as *mut Step::R)}; // do not take ownership on the record - match s.filter(r) { - Ok(res) => res as c_int, - Err(e) => { - unsafe{MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len())}; - 0 as c_int - } - } - -} - -pub trait FilterStep: BaseObject{ - type R: Record; - - fn filter(&self, r: &Self::R) -> Result; - - fn register() { - let obj = register::(); - unsafe{ - MR_RegisterFilter(Self::get_name().as_ptr() as *mut c_char, Some(rust_filter::), obj); - } - } - -} - -pub extern "C" fn rust_accumulate(ectx: *mut ExecutionCtx, accumulator: *mut crate::libmrraw::bindings::Record, r: *mut crate::libmrraw::bindings::Record, args: *mut c_void) -> *mut crate::libmrraw::bindings::Record { - let s = unsafe{&*(args as *mut Step)}; - let accumulator = if accumulator.is_null() { - None - } else { - Some(unsafe{*Box::from_raw(accumulator as *mut Step::Accumulator)}) - }; - let r = unsafe{Box::from_raw(r as *mut Step::InRecord)}; - match s.accumulate(accumulator, *r) { - Ok(res) => Box::into_raw(Box::new(res)) as *mut crate::libmrraw::bindings::Record, - Err(e) => { - unsafe{MR_ExecutionCtxSetError(ectx, e.as_ptr() as *mut c_char, e.len())}; - 0 as *mut crate::libmrraw::bindings::Record - } - } - -} - -pub trait AccumulateStep: BaseObject{ - type InRecord: Record; - type Accumulator: Record; - - fn accumulate(&self, accumulator: Option, r: Self::InRecord) -> Result; - - fn register() { - let obj = register::(); - unsafe{ - MR_RegisterAccumulator(Self::get_name().as_ptr() as *mut c_char, Some(rust_accumulate::), obj); - } - } -} - -pub struct Builder { - inner_builder: Option<*mut ExecutionBuilder>, - phantom: PhantomData, -} - -pub fn create_builder(reader: Re) -> Builder { - let reader = Box::into_raw(Box::new(reader)); - let inner_builder = unsafe{ - MR_CreateExecutionBuilder(Re::get_name().as_ptr() as *const c_char, reader as *mut c_void) - }; - Builder:: { - inner_builder: Some(inner_builder), - phantom: PhantomData, - } -} - -impl Builder { - fn take(&mut self) -> *mut ExecutionBuilder{ - self.inner_builder.take().unwrap() - } - - pub fn map>(mut self, step: Step) -> Builder { - let inner_builder = self.take(); - unsafe { - MR_ExecutionBuilderMap(inner_builder, Step::get_name().as_ptr() as *const c_char, Box::into_raw(Box::new(step)) as *const Step as *mut c_void) - } - Builder:: { - inner_builder: Some(inner_builder), - phantom: PhantomData, - } - } - - pub fn filter>(self, step: Step) -> Builder { - unsafe { - MR_ExecutionBuilderFilter(self.inner_builder.unwrap(), Step::get_name().as_ptr() as *const c_char, Box::into_raw(Box::new(step)) as *const Step as *mut c_void) - } - self - } - - pub fn accumulate>(mut self, step: Step) -> Builder { - let inner_builder = self.take(); - unsafe { - MR_ExecutionBuilderBuilAccumulate(inner_builder, Step::get_name().as_ptr() as *const c_char, Box::into_raw(Box::new(step)) as *const Step as *mut c_void) - } - Builder:: { - inner_builder: Some(inner_builder), - phantom: PhantomData, - } - } - - pub fn collect(self) -> Self { - unsafe { - MR_ExecutionBuilderCollect(self.inner_builder.unwrap()); - } - self - } - - pub fn reshuffle(self) -> Self { - unsafe { - MR_ExecutionBuilderReshuffle(self.inner_builder.unwrap()); - } - self - } - - pub fn create_execution(&self) -> Result, RustMRError> { - let execution = unsafe { - let mut err: *mut MRError = 0 as *mut MRError; - let res = MR_CreateExecution(self.inner_builder.unwrap(), &mut err); - if !err.is_null() { - let c_msg = MR_ErrorGetMessage(err); - let r_str = str::from_utf8(slice::from_raw_parts(c_msg.cast::(), strlen(c_msg))).unwrap(); - return Err(r_str.to_string()); - } - res - }; - Ok(ExecutionObj{inner_e: execution, phantom: PhantomData,}) - } -} - -impl Drop for Builder { - fn drop(&mut self) { - if let Some(innder_builder) = self.inner_builder { - unsafe{MR_FreeExecutionBuilder(innder_builder)} - } - } -} - -pub struct ExecutionObj { - inner_e: *mut Execution, - phantom: PhantomData, -} - -pub extern "C" fn rust_on_done, Vec<&str>)>(ectx: *mut ExecutionCtx, pd: *mut c_void) { - let f = unsafe{Box::from_raw(pd as *mut F)}; - let mut res = Vec::new(); - let res_len = unsafe{MR_ExecutionCtxGetResultsLen(ectx)}; - for i in 0..res_len { - let r = unsafe{&mut *(MR_ExecutionCtxGetResult(ectx, i) as *mut R)}; - res.push(r); - } - let mut errs = Vec::new(); - let errs_len = unsafe{MR_ExecutionCtxGetErrorsLen(ectx)}; - for i in 0..errs_len { - let r = unsafe{MR_ExecutionCtxGetError(ectx, i)}; - let s = str::from_utf8(unsafe { slice::from_raw_parts(r.cast::(), strlen(r))}).unwrap(); - errs.push(s); - } - f(res, errs); -} - -impl ExecutionObj { - - pub fn set_max_idle(&self, max_idle: usize) { - unsafe{MR_ExecutionSetMaxIdle(self.inner_e, max_idle)}; - } - - pub fn set_done_hanlder, Vec<&str>)>(&self, f: F) { - let f = Box::into_raw(Box::new(f)); - unsafe{MR_ExecutionSetOnDoneHandler(self.inner_e, Some(rust_on_done::), f as *mut c_void)}; - } - - pub fn run(&self) { - unsafe{MR_Run(self.inner_e)}; - } -} - -impl Drop for ExecutionObj { - fn drop(&mut self) { - unsafe{MR_FreeExecution(self.inner_e)}; - } -} \ No newline at end of file From 66facb7379d377c93cc9e00425ad731c6cbd0634 Mon Sep 17 00:00:00 2001 From: meir Date: Wed, 13 Jul 2022 11:52:08 +0300 Subject: [PATCH 02/60] Fix valgrind failure. --- rust_api/src/libmr/mapper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust_api/src/libmr/mapper.rs b/rust_api/src/libmr/mapper.rs index 3328610..14c72d3 100644 --- a/rust_api/src/libmr/mapper.rs +++ b/rust_api/src/libmr/mapper.rs @@ -17,7 +17,7 @@ pub extern "C" fn rust_map( args: *mut c_void, ) -> *mut Record { let s = unsafe { &*(args as *mut Step) }; - let r = unsafe { &mut *(r as *mut MRBaseRecord) }; + let mut r = unsafe { Box::from_raw(r as *mut MRBaseRecord) }; let res = match s.map(r.record.take().unwrap()) { Ok(res) => res, Err(e) => { From 4cd53b312baf3bd670c6251ce6a7e60abe7ad855 Mon Sep 17 00:00:00 2001 From: meir Date: Wed, 13 Jul 2022 18:21:58 +0300 Subject: [PATCH 03/60] Added linking libs to libmr project instead of test project. --- rust_api/build.rs | 2 +- tests/mr_test_module/src/lib.rs | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/rust_api/build.rs b/rust_api/build.rs index ad9e4fd..536a001 100644 --- a/rust_api/build.rs +++ b/rust_api/build.rs @@ -48,5 +48,5 @@ fn main() { .write_to_file(out_path.join("libmr_bindings.rs")) .expect("failed to write bindings to file"); - println!("cargo:rustc-flags=-L{} -lmr", output_dir); + println!("cargo:rustc-flags=-L{} -lmr -lssl -lcrypto", output_dir); } diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 24e9e20..5e55f90 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -25,18 +25,6 @@ use std::os::raw::c_void; use std::{thread, time}; -#[allow(improper_ctypes)] -#[link(name = "mr", kind = "static")] -extern "C" {} - -#[allow(improper_ctypes)] -#[link(name = "ssl")] -extern "C" {} - -#[allow(improper_ctypes)] -#[link(name = "crypto")] -extern "C" {} - static mut DETACHED_CTX: *mut RedisModuleCtx = 0 as *mut RedisModuleCtx; fn get_redis_ctx() -> *mut RedisModuleCtx { From aade142c69f61dcdf2e29240c40463d477690cb9 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 17 Jul 2022 16:22:04 +0300 Subject: [PATCH 04/60] Added the ability to run a remote task and shard responsible for a given key. --- rust_api/src/libmr/base_object.rs | 6 +- rust_api/src/libmr/mod.rs | 1 + rust_api/src/libmr/record.rs | 6 +- rust_api/src/libmr/remote_task.rs | 116 +++++++++ src/mr.c | 276 +++++++++++++++++++++ src/mr.h | 21 ++ tests/mr_test_module/Makefile | 4 +- tests/mr_test_module/pytests/test_basic.py | 6 + tests/mr_test_module/src/lib.rs | 100 ++++++-- 9 files changed, 507 insertions(+), 29 deletions(-) create mode 100644 rust_api/src/libmr/remote_task.rs diff --git a/rust_api/src/libmr/base_object.rs b/rust_api/src/libmr/base_object.rs index 346904f..32f62a2 100644 --- a/rust_api/src/libmr/base_object.rs +++ b/rust_api/src/libmr/base_object.rs @@ -14,8 +14,6 @@ use serde::de::Deserialize; use std::slice; use std::str; -use std::ffi::CString; - pub extern "C" fn rust_obj_free(ctx: *mut c_void) { unsafe { Box::from_raw(ctx as *mut T) }; } @@ -64,11 +62,9 @@ pub trait BaseObject: Clone + Serialize + Deserialize<'static> { } pub(crate) fn register() -> *mut MRObjectType { - let type_name = T::get_name(); - let type_name_cstring = CString::new(type_name).unwrap(); unsafe { let obj = Box::into_raw(Box::new(MRObjectType { - type_: type_name_cstring.into_raw(), + type_: T::get_name().as_ptr() as *mut c_char, id: 0, free: Some(rust_obj_free::), dup: Some(rust_obj_dup::), diff --git a/rust_api/src/libmr/mod.rs b/rust_api/src/libmr/mod.rs index 4e0887d..f4e87cf 100644 --- a/rust_api/src/libmr/mod.rs +++ b/rust_api/src/libmr/mod.rs @@ -12,6 +12,7 @@ pub mod filter; pub mod mapper; pub mod reader; pub mod record; +pub mod remote_task; impl Default for crate::libmr_c_raw::bindings::Record { fn default() -> Self { diff --git a/rust_api/src/libmr/record.rs b/rust_api/src/libmr/record.rs index 2d6467e..c5d6a90 100644 --- a/rust_api/src/libmr/record.rs +++ b/rust_api/src/libmr/record.rs @@ -13,8 +13,6 @@ use crate::libmr::base_object::BaseObject; use std::collections::HashMap; -use std::ffi::CString; - use std::slice; use std::str; @@ -91,12 +89,10 @@ pub extern "C" fn rust_obj_hash_slot(record: *mut ::std::os::raw::c_v } fn register_record() -> *mut MRRecordType { - let type_name = T::get_name(); - let type_name_cstring = CString::new(type_name).unwrap(); unsafe { let obj = Box::into_raw(Box::new(MRRecordType { type_: MRObjectType { - type_: type_name_cstring.into_raw(), + type_: T::get_name().as_ptr() as *mut c_char, id: 0, free: Some(rust_obj_free::), dup: Some(rust_obj_dup::), diff --git a/rust_api/src/libmr/remote_task.rs b/rust_api/src/libmr/remote_task.rs new file mode 100644 index 0000000..6a969ee --- /dev/null +++ b/rust_api/src/libmr/remote_task.rs @@ -0,0 +1,116 @@ +use crate::libmr_c_raw::bindings::{ + MRError, MR_ErrorCreate, MR_ErrorFree, MR_ErrorGetMessage, MR_RegisterRemoteTask, MR_RunOnKey, + Record, +}; + +use crate::libmr::base_object::{register, BaseObject}; +use crate::libmr::record; +use crate::libmr::record::MRBaseRecord; +use crate::libmr::RustMRError; + +use libc::strlen; +use std::os::raw::{c_char, c_void}; + +extern "C" fn rust_remote_task( + r: *mut Record, + args: *mut ::std::os::raw::c_void, + on_done: ::std::option::Option< + unsafe extern "C" fn(PD: *mut ::std::os::raw::c_void, r: *mut Record), + >, + on_error: ::std::option::Option< + unsafe extern "C" fn(PD: *mut ::std::os::raw::c_void, r: *mut MRError), + >, + pd: *mut ::std::os::raw::c_void, +) { + let s = unsafe { Box::from_raw(args as *mut Step) }; + let mut r = unsafe { Box::from_raw(r as *mut MRBaseRecord) }; + s.task( + r.record.take().unwrap(), + Box::new(move |res| match res { + Ok(r) => { + let record = Box::new(MRBaseRecord::new(r)); + unsafe { on_done.unwrap()(pd, Box::into_raw(record) as *mut Record) } + } + Err(e) => { + let error = unsafe { MR_ErrorCreate(e.as_ptr() as *const c_char, e.len()) }; + unsafe { on_error.unwrap()(pd, error) }; + } + }), + ); +} + +pub trait RemoteTask: BaseObject { + type InRecord: record::Record; + type OutRecord: record::Record; + + fn task( + &self, + r: Self::InRecord, + on_done: Box)>, + ); + + fn register() { + let obj = register::(); + unsafe { + MR_RegisterRemoteTask( + Self::get_name().as_ptr() as *mut c_char, + Some(rust_remote_task::), + obj, + ); + } + } +} + +extern "C" fn on_done< + OutRecord: record::Record, + DoneCallback: FnOnce(Result), +>( + pd: *mut ::std::os::raw::c_void, + result: *mut Record, +) { + let callback = unsafe { Box::::from_raw(pd as *mut DoneCallback) }; + let mut r = unsafe { Box::from_raw(result as *mut MRBaseRecord) }; + callback(Ok(r.record.take().unwrap())); +} + +extern "C" fn on_error< + OutRecord: record::Record, + DoneCallback: FnOnce(Result), +>( + pd: *mut ::std::os::raw::c_void, + error: *mut MRError, +) { + let callback = unsafe { Box::::from_raw(pd as *mut DoneCallback) }; + let err_msg = unsafe { MR_ErrorGetMessage(error) }; + let r_str = std::str::from_utf8(unsafe { + std::slice::from_raw_parts(err_msg.cast::(), strlen(err_msg)) + }) + .unwrap(); + callback(Err(r_str.to_string())); + unsafe { MR_ErrorFree(error) }; +} + +pub fn run_on_key< + Remote: RemoteTask, + InRecord: record::Record, + OutRecord: record::Record, + DoneCallback: FnOnce(Result), +>( + key_name: &str, + remote_task: Remote, + r: InRecord, + done: DoneCallback, +) { + unsafe { + MR_RunOnKey( + key_name.as_ptr() as *mut c_char, + key_name.len(), + Remote::get_name().as_ptr() as *mut c_char, + Box::into_raw(Box::new(remote_task)) as *mut c_void, + Box::into_raw(Box::new(MRBaseRecord::new(r))) as *mut Record, + Some(on_done::), + Some(on_error::), + Box::into_raw(Box::new(done)) as *mut c_void, + ) + } +} diff --git a/src/mr.c b/src/mr.c index c3fe811..6c01197 100644 --- a/src/mr.c +++ b/src/mr.c @@ -26,6 +26,8 @@ functionId PASS_RECORD_FUNCTION_ID = 0; functionId NOTIFY_STEP_DONE_FUNCTION_ID = 0; functionId NOTIFY_DONE_FUNCTION_ID = 0; functionId DROP_EXECUTION_FUNCTION_ID = 0; +functionId REMOTE_TASK_FUNCTION_ID = 0; +functionId REMOTE_TASK_DONE_FUNCTION_ID = 0; typedef struct RemoteFunctionDef { functionId* funcIdPointer; @@ -50,6 +52,8 @@ static void MR_PassRecord(RedisModuleCtx *ctx, const char *sender_id, uint8_t ty static void MR_NotifyDone(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload); static void MR_NotifyStepDone(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload); static void MR_DropExecution(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload); +static void MR_RemoteTask(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload); +static void MR_RemoteTaskDone(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload); /* Remote functions array */ RemoteFunctionDef remoteFunctions[] = { @@ -81,6 +85,14 @@ RemoteFunctionDef remoteFunctions[] = { .funcIdPointer = &DROP_EXECUTION_FUNCTION_ID, .functionPointer = MR_DropExecution, }, + { + .funcIdPointer = &REMOTE_TASK_FUNCTION_ID, + .functionPointer = MR_RemoteTask, + }, + { + .funcIdPointer = &REMOTE_TASK_DONE_FUNCTION_ID, + .functionPointer = MR_RemoteTaskDone, + }, }; typedef struct MRStats { @@ -94,12 +106,16 @@ struct MRCtx { /* protected by the event loop */ mr_dict* executionsDict; + /* protected by the event loop */ + mr_dict* remoteDict; + /* should be initialized at start and then read only */ ARR(MRObjectType*) objectTypesDict; /* Steps dictionaries */ mr_dict* readerDict; mr_dict* mappersDict; + mr_dict* remoteTasksDict; mr_dict* filtersDict; mr_dict* accumulatorsDict; @@ -1347,11 +1363,13 @@ int MR_Init(RedisModuleCtx* ctx, size_t numThreads) { mrCtx.lastExecutionId = 0; mrCtx.executionsDict = mr_dictCreate(&dictTypeHeapIds, NULL); + mrCtx.remoteDict = mr_dictCreate(&dictTypeHeapIds, NULL); mrCtx.objectTypesDict = array_new(MRObjectType*, 10); mrCtx.readerDict = mr_dictCreate(&mr_dictTypeHeapStrings, NULL); mrCtx.mappersDict = mr_dictCreate(&mr_dictTypeHeapStrings, NULL); + mrCtx.remoteTasksDict = mr_dictCreate(&mr_dictTypeHeapStrings, NULL); mrCtx.filtersDict = mr_dictCreate(&mr_dictTypeHeapStrings, NULL); mrCtx.accumulatorsDict = mr_dictCreate(&mr_dictTypeHeapStrings, NULL); @@ -1434,6 +1452,17 @@ LIBMR_API void MR_RegisterAccumulator(const char* name, ExecutionAccumulator acc mr_dictAdd(mrCtx.accumulatorsDict, asd->name, asd); } +LIBMR_API void MR_RegisterRemoteTask(const char* name, RemoteTask remote, MRObjectType* argType) { + RedisModule_Assert(!mr_dictFetchValue(mrCtx.remoteTasksDict, name)); + StepDefinition* asd = MR_ALLOC(sizeof(*asd)); + *asd = (StepDefinition) { + .name = MR_STRDUP(name), + .type = argType, + .callback = remote, + }; + mr_dictAdd(mrCtx.remoteTasksDict, asd->name, asd); +} + long long MR_SerializationCtxReadeLongLong(ReaderSerializationCtx* sctx, MRError** err) { int error = 0; long res = mr_BufferReaderReadLongLong(sctx, &error); @@ -1491,3 +1520,250 @@ void MR_ErrorFree(MRError* err) { MR_FREE(err); } } + +typedef struct RunOnKeyReplyMsg { + char *sender; + char *id; +} RunOnKeyReplyMsg; + +typedef enum ReplyType { + ReplyType_OK, ReplyType_ERROR, +}ReplyType; + +typedef struct RunOnKeyMsg { + char id[ID_LEN]; + char idStr[STR_ID_LEN]; + size_t slot; + char *msg; + size_t msgLen; + void (*onDone)(void *pd, Record* result); + void (*onError)(void *pd, MRError* err); + void *pd; + union { + Record *res; + MRError *error; + }; + ReplyType replyType; +} RunOnKeyMsg; + +/* Run on thread pool */ +static void MR_RemoteTaskDoneInternal(void* pd) { + RunOnKeyMsg *msg = pd; + + if (msg->replyType == ReplyType_OK) { + msg->onDone(msg->pd, msg->res); + } else { + msg->onError(msg->pd, msg->error); + } + + MR_FREE(msg); +} + +/* Run on the event loop */ +static void MR_RemoteTaskDone(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload) { + size_t dataSize; + const char* data = RedisModule_StringPtrLen(payload, &dataSize); + mr_Buffer buff = { + .buff = (char*)data, + .size = dataSize, + .cap = dataSize, + }; + mr_BufferReader buffReader; + mr_BufferReaderInit(&buffReader, &buff); + + size_t idLen; + const char *id = mr_BufferReaderReadBuff(&buffReader, &idLen, NULL); + RedisModule_Assert(idLen == ID_LEN); + + RunOnKeyMsg *msg = mr_dictFetchValue(mrCtx.remoteDict, id); + if (!msg) { + RedisModule_Log(NULL, "warning", "Got a remote task done on none existing ID %.*s", REDISMODULE_NODE_ID_LEN, id); + return; + } + + if (mr_BufferReaderReadLongLong(&buffReader, NULL)) { + msg->res = MR_RecordDeSerialize(&buffReader); + msg->replyType = ReplyType_OK; + } else { + const char* errMsg = mr_BufferReaderReadString(&buffReader, NULL); + msg->error = MR_ErrorCreate(errMsg, strlen(errMsg)); + msg->replyType = ReplyType_ERROR; + } + + /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ + mr_dictDelete(mrCtx.remoteDict, id); + + /* Run the callback on the thread pool */ + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskDoneInternal, msg); +} + +static void MR_RemoteTaskErrorOnRemote(void *pd, MRError *error) { + RunOnKeyReplyMsg *replyMsg = pd; + + mr_Buffer buff; + mr_BufferInitialize(&buff); + mr_BufferWriter buffWriter; + mr_BufferWriterInit(&buffWriter, &buff); + + /* write id */ + mr_BufferWriterWriteBuff(&buffWriter, replyMsg->id, ID_LEN); + + mr_BufferWriterWriteLongLong(&buffWriter, 0); /* mean failure */ + + mr_BufferWriterWriteString(&buffWriter, error->msg); + + MR_ClusterSendMsg(replyMsg->sender, REMOTE_TASK_DONE_FUNCTION_ID, buff.buff, buff.size); + + MR_ErrorFree(error); + MR_FREE(replyMsg->id); + MR_FREE(replyMsg->sender); + MR_FREE(replyMsg); +} + +static void MR_RemoteTaskDoneOnRemote(void *pd, Record *res) { + RunOnKeyReplyMsg *replyMsg = pd; + + MRError* error = NULL; + mr_Buffer buff; + mr_BufferInitialize(&buff); + mr_BufferWriter buffWriter; + mr_BufferWriterInit(&buffWriter, &buff); + + /* write id */ + mr_BufferWriterWriteBuff(&buffWriter, replyMsg->id, ID_LEN); + + mr_BufferWriterWriteLongLong(&buffWriter, 1); /* mean success */ + + MR_RecordSerialize(res, &buffWriter); + /* todo: handler serialization failure */ + + MR_ClusterSendMsg(replyMsg->sender, REMOTE_TASK_DONE_FUNCTION_ID, buff.buff, buff.size); + + MR_RecordFree(res); + MR_FREE(replyMsg->id); + MR_FREE(replyMsg->sender); + MR_FREE(replyMsg); +} + +/* Runs on thread pool */ +static void MR_RemoteTaskInternal(void* pd) { + RedisModuleString* payload = pd; + size_t dataSize; + const char* data = RedisModule_StringPtrLen(payload, &dataSize); + mr_Buffer buff = { + .buff = (char*)data, + .size = dataSize, + .cap = dataSize, + }; + mr_BufferReader buffReader; + mr_BufferReaderInit(&buffReader, &buff); + + /* Read sender id */ + const char *sender = mr_BufferReaderReadString(&buffReader, NULL); + + size_t idLen; + const char *id = mr_BufferReaderReadBuff(&buffReader, &idLen, NULL); + RedisModule_Assert(idLen == ID_LEN); + + const char *remoteTaskName = mr_BufferReaderReadString(&buffReader, NULL); + StepDefinition* msd = mr_dictFetchValue(mrCtx.remoteTasksDict, remoteTaskName); + RedisModule_Assert(msd); + + MRError* error = NULL; + void *args = msd->type->deserialize(&buffReader, &error); + /* todo: handler serialization failure */ + + Record *r = MR_RecordDeSerialize(&buffReader); + /* todo: handler serialization failure */ + + RunOnKeyReplyMsg *replyMsg = MR_ALLOC(sizeof(*replyMsg)); + replyMsg->sender = MR_STRDUP(sender); + replyMsg->id = MR_ALLOC(idLen); + memcpy(replyMsg->id, id, idLen); + + ((RemoteTask)msd->callback)(r, args, MR_RemoteTaskDoneOnRemote, MR_RemoteTaskErrorOnRemote, replyMsg); + + /* We must take the Redis GIL to free the payload, + * RedisModuleString refcount are not thread safe. + * We better do it here and stuck on of the threads + * in the thread pool then do it on the event loop. + * Possible optimization would be to batch multiple + * payloads into one GIL locking */ + RedisModule_ThreadSafeContextLock(mr_staticCtx); + RedisModule_FreeString(NULL, payload); + RedisModule_ThreadSafeContextUnlock(mr_staticCtx); +} + +/* Runs on the event loop */ +static void MR_RemoteTask(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload) { + /* We can directly move the job to the thread pool for deserialization and execution */ + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskInternal, RedisModule_HoldString(NULL, payload)); +} + +/* Invoked on the event look */ +static void MR_RunOnKeyInternal(void* ctx) { + RunOnKeyMsg *msg = ctx; + + /* add the task to the remote mappers dictionary */ + mr_dictAdd(mrCtx.remoteDict, msg->id, msg); + + /* send the message to the shard */ + MR_ClusterSendMsgBySlot(msg->slot, REMOTE_TASK_FUNCTION_ID, msg->msg, msg->msgLen); + + /* ownership on the message was moved to MR_ClusterSendMsgBySlot function */ + msg->msg = NULL; + msg->msgLen = 0; +} + +LIBMR_API void MR_RunOnKey(const char* keyName, + size_t keyNameSize, + const char* remoteTaskName, + void* args, + Record* r, + void (*onDone)(void *pd, Record* result), + void (*onError)(void *pd, MRError* err), + void *pd) +{ + StepDefinition* msd = mr_dictFetchValue(mrCtx.remoteTasksDict, remoteTaskName); + RedisModule_Assert(msd); + size_t slot = MR_ClusterGetSlotdByKey(keyName, keyNameSize); + if (!MR_ClusterIsInClusterMode() || MR_ClusterIsMySlot(slot)) { + ((RemoteTask)msd->callback)(r, args, onDone, onError, pd); + return; + } + + RunOnKeyMsg *msg = MR_ALLOC(sizeof(*msg)); + msg->slot = slot; + msg->onDone = onDone; + msg->onError = onError; + msg->pd = pd; + /* Set id */ + size_t id = __atomic_add_fetch(&mrCtx.lastExecutionId, 1, __ATOMIC_RELAXED); + SetId(msg->id, msg->idStr, id); + + MRError* error = NULL; + mr_Buffer buff; + mr_BufferInitialize(&buff); + mr_BufferWriter buffWriter; + mr_BufferWriterInit(&buffWriter, &buff); + /* write sender */ + mr_BufferWriterWriteString(&buffWriter, MR_ClusterGetMyId()); + /* write id */ + mr_BufferWriterWriteBuff(&buffWriter, msg->id, ID_LEN); + /* mapped name to invoke */ + mr_BufferWriterWriteString(&buffWriter, remoteTaskName); + /* Serialize args */ + msd->type->serialize(&buffWriter, args, &error); + msd->type->free(args); + /* todo: handler serialization failure */ + /* serialize the record */ + MR_RecordSerialize(r, &buffWriter); + MR_RecordFree(r); + + msg->msg = buff.buff; + msg->msgLen = buff.size; + + + /* The remove mapper dictionary is protected by the event look so we must move to the event loop.*/ + MR_EventLoopAddTask(MR_RunOnKeyInternal, msg); +} diff --git a/src/mr.h b/src/mr.h index c833460..039c2e9 100644 --- a/src/mr.h +++ b/src/mr.h @@ -59,6 +59,24 @@ typedef Record* (*ExecutionReader)(ExecutionCtx* ectx, void* args); typedef Record* (*ExecutionMapper)(ExecutionCtx* ectx, Record* r, void* args); typedef int (*ExecutionFilter)(ExecutionCtx* ectx, Record* r, void* args); typedef Record* (*ExecutionAccumulator)(ExecutionCtx* ectx, Record* accumulator, Record* r, void* args); +typedef void (*RemoteTask)(Record* r, void* args, void (*onDone)(void* PD, Record *r), void (*onError)(void* PD, MRError *r), void *pd); + +/* Run a remote task on a shard responsible for a given key. + * There is not guarantee on which thread the task will run, if + * the current shard is responsible for the given key or if its + * a none cluster environment, then the callback will be called + * immediately (an so the onDone/onError) callbacks. + * If the key located on the remote shard, the task will + * be invoke on the thread pool of this remote shard, the onDone/onError + * callback will be invoke on the thread pool of the current shard. */ +LIBMR_API void MR_RunOnKey(const char* keyName, + size_t keyNameSize, + const char* remoteTaskName, + void* args, + Record* r, + void (*onDone)(void *pd, Record* result), + void (*onError)(void *pd, MRError* err), + void *pd); /* Creatign a new execution builder */ LIBMR_API ExecutionBuilder* MR_CreateExecutionBuilder(const char* readerName, void* args); @@ -132,6 +150,9 @@ LIBMR_API void MR_RegisterFilter(const char* name, ExecutionFilter filter, MRObj /* Register an accumulate step */ LIBMR_API void MR_RegisterAccumulator(const char* name, ExecutionAccumulator accumulator, MRObjectType* argType); +/* Register a remote task */ +LIBMR_API void MR_RegisterRemoteTask(const char* name, RemoteTask remote, MRObjectType* argType); + /* Serialization Context functions */ LIBMR_API long long MR_SerializationCtxReadeLongLong(ReaderSerializationCtx* sctx, MRError** err); LIBMR_API const char* MR_SerializationCtxReadeBuffer(ReaderSerializationCtx* sctx, size_t* len, MRError** err); diff --git a/tests/mr_test_module/Makefile b/tests/mr_test_module/Makefile index a3265d4..ea7e033 100644 --- a/tests/mr_test_module/Makefile +++ b/tests/mr_test_module/Makefile @@ -3,11 +3,11 @@ HERE=$(realpath ./) ifeq ($(DEBUG),1) EXTRA_ARGS= - MODULE_PATH=$(HERE)/target/debug/libmr_test.so + MODULE_PATH=$(ROOT)/target/debug/libmr_test.so DEBUG_RUN=1 else EXTRA_ARGS=--release - MODULE_PATH=$(HERE)/target/release/libmr_test.so + MODULE_PATH=$(ROOT)/target/release/libmr_test.so DEBUG_RUN=0 endif diff --git a/tests/mr_test_module/pytests/test_basic.py b/tests/mr_test_module/pytests/test_basic.py index 1d243be..4b6f4c3 100644 --- a/tests/mr_test_module/pytests/test_basic.py +++ b/tests/mr_test_module/pytests/test_basic.py @@ -63,3 +63,9 @@ def testUnevenWork(env, conn): except Exception as e: if str(e) != 'timeout': raise e + +@MRTestDecorator() +def testRemoteTaskOnKey(env, conn): + conn.execute_command('set', 'x', '1') + env.expect('lmrtest.get', 'x').equal('1') + env.expect('lmrtest.get', 'y').error().contains('bad result returned from') diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 5e55f90..4022602 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -18,7 +18,7 @@ use std::str; use mr::libmr::{ accumulator::AccumulateStep, base_object::BaseObject, calc_slot, execution_builder::create_builder, filter::FilterStep, mapper::MapStep, mr_init, - reader::Reader, record::Record, RustMRError, + reader::Reader, record::Record, remote_task::run_on_key, remote_task::RemoteTask, RustMRError, }; use std::os::raw::c_void; @@ -298,6 +298,33 @@ fn lmr_read_string_keys(ctx: &Context, _args: Vec) -> RedisResult { Ok(RedisValue::NoReply) } +fn lmr_get(ctx: &Context, args: Vec) -> RedisResult { + let mut args = args.into_iter().skip(1); + let ke_redis_string = args.next().ok_or(RedisError::Str("not prefix was given"))?; + let key = ke_redis_string.try_as_str()?; + let blocked_client = ctx.block_client(); + thread::spawn(move || { + let record = strin_record_new(key.to_string()); + run_on_key( + key, + RemoteTaskGet, + record, + move |res: Result| { + let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); + match res { + Ok(mut r) => { + thread_ctx.reply(Ok(r.to_redis_value())); + } + Err(e) => { + thread_ctx.reply(Err(RedisError::String(e))); + } + } + }, + ); + }); + Ok(RedisValue::NoReply) +} + fn lmr_read_all_keys(ctx: &Context, _args: Vec) -> RedisResult { let execution = create_builder(KeysReader::new(None)) .collect() @@ -320,6 +347,43 @@ fn lmr_read_all_keys(ctx: &Context, _args: Vec) -> RedisResult { Ok(RedisValue::NoReply) } +#[derive(Clone, Serialize, Deserialize)] +struct RemoteTaskGet; + +impl RemoteTask for RemoteTaskGet { + type InRecord = StringRecord; + type OutRecord = StringRecord; + + fn task( + &self, + mut r: Self::InRecord, + on_done: Box)>, + ) { + let ctx = get_ctx(); + ctx_lock(); + let res = ctx.call("get", &[r.s.as_ref().unwrap()]); + ctx_unlock(); + if let Ok(res) = res { + if let RedisValue::SimpleString(res) = res { + r.s = Some(res); + on_done(Ok(r)); + } else { + on_done(Err("bad result returned from `get` command".to_string())) + } + } else { + on_done(Err("bad result returned from `get` command".to_string())) + } + } +} + +impl BaseObject for RemoteTaskGet { + fn get_name() -> &'static str { + "RemoteTaskGet\0" + } + + fn init(&mut self) {} +} + #[derive(Clone, Serialize, Deserialize)] struct StringRecord { pub s: Option, @@ -340,7 +404,7 @@ impl Record for StringRecord { impl BaseObject for StringRecord { fn get_name() -> &'static str { - "StringRecord" + "StringRecord\0" } } @@ -362,7 +426,7 @@ impl Record for IntRecord { impl BaseObject for IntRecord { fn get_name() -> &'static str { - "IntRecord" + "IntRecord\0" } } @@ -389,7 +453,7 @@ impl AccumulateStep for CountAccumulator { impl BaseObject for CountAccumulator { fn get_name() -> &'static str { - "CountAccumulator" + "CountAccumulator\0" } } @@ -411,7 +475,7 @@ impl AccumulateStep for ErrorAccumulator { impl BaseObject for ErrorAccumulator { fn get_name() -> &'static str { - "ErrorAccumulator" + "ErrorAccumulator\0" } } @@ -429,7 +493,7 @@ impl FilterStep for DummyFilter { impl BaseObject for DummyFilter { fn get_name() -> &'static str { - "DummyFilter" + "DummyFilter\0" } } @@ -447,7 +511,7 @@ impl FilterStep for ErrorFilter { impl BaseObject for ErrorFilter { fn get_name() -> &'static str { - "ErrorFilter" + "ErrorFilter\0" } } @@ -489,7 +553,7 @@ impl FilterStep for TypeFilter { impl BaseObject for TypeFilter { fn get_name() -> &'static str { - "TypeFilter" + "TypeFilter\0" } } @@ -521,7 +585,7 @@ impl MapStep for TypeMapper { impl BaseObject for TypeMapper { fn get_name() -> &'static str { - "TypeMapper" + "TypeMapper\0" } } @@ -540,7 +604,7 @@ impl MapStep for ErrorMapper { impl BaseObject for ErrorMapper { fn get_name() -> &'static str { - "ErrorMapper" + "ErrorMapper\0" } } /* map key name to its type */ @@ -558,7 +622,7 @@ impl MapStep for DummyMapper { impl BaseObject for DummyMapper { fn get_name() -> &'static str { - "DummyMapper" + "DummyMapper\0" } } @@ -590,7 +654,7 @@ impl MapStep for UnevenWorkMapper { impl BaseObject for UnevenWorkMapper { fn get_name() -> &'static str { - "UnevenWorkMapper" + "UnevenWorkMapper\0" } } @@ -621,7 +685,7 @@ impl MapStep for ReadStringMapper { impl BaseObject for ReadStringMapper { fn get_name() -> &'static str { - "ReadStringMapper" + "ReadStringMapper\0" } } @@ -652,7 +716,7 @@ impl MapStep for WriteDummyString { impl BaseObject for WriteDummyString { fn get_name() -> &'static str { - "WriteDummyString" + "WriteDummyString\0" } } @@ -691,7 +755,7 @@ impl Reader for MaxIdleReader { impl BaseObject for MaxIdleReader { fn get_name() -> &'static str { - "MaxIdleReader" + "MaxIdleReader\0" } } @@ -720,7 +784,7 @@ impl Reader for ErrorReader { impl BaseObject for ErrorReader { fn get_name() -> &'static str { - "ErrorReader" + "ErrorReader\0" } } @@ -803,7 +867,7 @@ impl Reader for KeysReader { impl BaseObject for KeysReader { fn get_name() -> &'static str { - "KeysReader" + "KeysReader\0" } fn init(&mut self) { @@ -843,6 +907,7 @@ fn init_func(ctx: &Context, _args: &Vec) -> Status { CountAccumulator::register(); ErrorAccumulator::register(); UnevenWorkMapper::register(); + RemoteTaskGet::register(); Status::Ok } @@ -852,6 +917,7 @@ redis_module! { data_types: [], init: init_func, commands: [ + ["lmrtest.get", lmr_get, "readonly", 0,0,0], ["lmrtest.readallkeys", lmr_read_all_keys, "readonly", 0,0,0], ["lmrtest.readallkeystype", lmr_read_keys_type, "readonly", 0,0,0], ["lmrtest.readallstringkeys", lmr_read_string_keys, "readonly", 0,0,0], From 6b985a7d84a6311e7beecf677a06844a4484490e Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 17 Jul 2022 18:04:06 +0300 Subject: [PATCH 05/60] Changed the repository to be a cargo repository. This will not change the Makefile build system for someone who wants to use libmr without rust. --- Cargo.toml | 25 +++++++-- rust_api/Cargo.toml | 20 ------- rust_api/build.rs | 52 ------------------- rust_api/{src => }/lib.rs | 0 rust_api/{src => }/libmr/accumulator.rs | 0 rust_api/{src => }/libmr/base_object.rs | 0 rust_api/{src => }/libmr/execution_builder.rs | 0 rust_api/{src => }/libmr/execution_object.rs | 0 rust_api/{src => }/libmr/filter.rs | 0 rust_api/{src => }/libmr/mapper.rs | 0 rust_api/{src => }/libmr/mod.rs | 0 rust_api/{src => }/libmr/reader.rs | 0 rust_api/{src => }/libmr/record.rs | 0 rust_api/{src => }/libmr/remote_task.rs | 0 rust_api/{src => }/libmr_c_raw/mod.rs | 0 tests/mr_test_module/.cargo/config.toml | 2 + tests/mr_test_module/Cargo.toml | 2 +- tests/mr_test_module/Makefile | 4 +- tests/mr_test_module/pytests/run_tests.sh | 4 +- 19 files changed, 27 insertions(+), 82 deletions(-) delete mode 100644 rust_api/Cargo.toml delete mode 100644 rust_api/build.rs rename rust_api/{src => }/lib.rs (100%) rename rust_api/{src => }/libmr/accumulator.rs (100%) rename rust_api/{src => }/libmr/base_object.rs (100%) rename rust_api/{src => }/libmr/execution_builder.rs (100%) rename rust_api/{src => }/libmr/execution_object.rs (100%) rename rust_api/{src => }/libmr/filter.rs (100%) rename rust_api/{src => }/libmr/mapper.rs (100%) rename rust_api/{src => }/libmr/mod.rs (100%) rename rust_api/{src => }/libmr/reader.rs (100%) rename rust_api/{src => }/libmr/record.rs (100%) rename rust_api/{src => }/libmr/remote_task.rs (100%) rename rust_api/{src => }/libmr_c_raw/mod.rs (100%) create mode 100644 tests/mr_test_module/.cargo/config.toml diff --git a/Cargo.toml b/Cargo.toml index 7d37d30..e039772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,21 @@ -[workspace] +[package] +name = "lib_mr" +version = "0.1.0" +edition = "2021" -members = [ - "rust_api", - "tests/mr_test_module" -] \ No newline at end of file +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +redis-module = { version="0.22.0", features = ["experimental-api"]} +serde_json = "1.0" +serde = "1.0" +serde_derive = "1.0" +libc = "0.2" + +[build-dependencies] +bindgen = "0.59.2" + +[lib] +crate-type = ["rlib"] +name = "mr" +path = "rust_api/lib.rs" diff --git a/rust_api/Cargo.toml b/rust_api/Cargo.toml deleted file mode 100644 index eea1945..0000000 --- a/rust_api/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "lib_mr" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -redis-module = { version="0.22.0", features = ["experimental-api"]} -serde_json = "1.0" -serde = "1.0" -serde_derive = "1.0" -libc = "0.2" - -[build-dependencies] -bindgen = "0.59.2" - -[lib] -crate-type = ["rlib"] -name = "mr" diff --git a/rust_api/build.rs b/rust_api/build.rs deleted file mode 100644 index 536a001..0000000 --- a/rust_api/build.rs +++ /dev/null @@ -1,52 +0,0 @@ -extern crate bindgen; - -use std::env; -use std::path::PathBuf; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=../src/*.c"); - println!("cargo:rerun-if-changed=../src/*.h"); - println!("cargo:rerun-if-changed=../src/utils/*.h"); - println!("cargo:rerun-if-changed=../src/utils/*.c"); - - if !Command::new("make") - .args(&["-C", "../src/"]) - .env( - "MODULE_NAME", - std::env::var("MODULE_NAME").unwrap_or_default(), - ) - .status() - .expect("failed to compile libmr") - .success() - { - panic!("failed to compile libmr"); - } - - let output_dir = env::var("OUT_DIR").expect("Can not find out directory"); - - if !Command::new("cp") - .args(&["../src/libmr.a", &output_dir]) - .status() - .expect("failed copy libmr.a to output directory") - .success() - { - panic!("failed copy libmr.a to output directory"); - } - - let build = bindgen::Builder::default(); - - let bindings = build - .header("../src/mr.h") - .size_t_is_usize(true) - .layout_tests(false) - .generate() - .expect("error generating bindings"); - - let out_path = PathBuf::from(&output_dir); - bindings - .write_to_file(out_path.join("libmr_bindings.rs")) - .expect("failed to write bindings to file"); - - println!("cargo:rustc-flags=-L{} -lmr -lssl -lcrypto", output_dir); -} diff --git a/rust_api/src/lib.rs b/rust_api/lib.rs similarity index 100% rename from rust_api/src/lib.rs rename to rust_api/lib.rs diff --git a/rust_api/src/libmr/accumulator.rs b/rust_api/libmr/accumulator.rs similarity index 100% rename from rust_api/src/libmr/accumulator.rs rename to rust_api/libmr/accumulator.rs diff --git a/rust_api/src/libmr/base_object.rs b/rust_api/libmr/base_object.rs similarity index 100% rename from rust_api/src/libmr/base_object.rs rename to rust_api/libmr/base_object.rs diff --git a/rust_api/src/libmr/execution_builder.rs b/rust_api/libmr/execution_builder.rs similarity index 100% rename from rust_api/src/libmr/execution_builder.rs rename to rust_api/libmr/execution_builder.rs diff --git a/rust_api/src/libmr/execution_object.rs b/rust_api/libmr/execution_object.rs similarity index 100% rename from rust_api/src/libmr/execution_object.rs rename to rust_api/libmr/execution_object.rs diff --git a/rust_api/src/libmr/filter.rs b/rust_api/libmr/filter.rs similarity index 100% rename from rust_api/src/libmr/filter.rs rename to rust_api/libmr/filter.rs diff --git a/rust_api/src/libmr/mapper.rs b/rust_api/libmr/mapper.rs similarity index 100% rename from rust_api/src/libmr/mapper.rs rename to rust_api/libmr/mapper.rs diff --git a/rust_api/src/libmr/mod.rs b/rust_api/libmr/mod.rs similarity index 100% rename from rust_api/src/libmr/mod.rs rename to rust_api/libmr/mod.rs diff --git a/rust_api/src/libmr/reader.rs b/rust_api/libmr/reader.rs similarity index 100% rename from rust_api/src/libmr/reader.rs rename to rust_api/libmr/reader.rs diff --git a/rust_api/src/libmr/record.rs b/rust_api/libmr/record.rs similarity index 100% rename from rust_api/src/libmr/record.rs rename to rust_api/libmr/record.rs diff --git a/rust_api/src/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs similarity index 100% rename from rust_api/src/libmr/remote_task.rs rename to rust_api/libmr/remote_task.rs diff --git a/rust_api/src/libmr_c_raw/mod.rs b/rust_api/libmr_c_raw/mod.rs similarity index 100% rename from rust_api/src/libmr_c_raw/mod.rs rename to rust_api/libmr_c_raw/mod.rs diff --git a/tests/mr_test_module/.cargo/config.toml b/tests/mr_test_module/.cargo/config.toml new file mode 100644 index 0000000..94c34ce --- /dev/null +++ b/tests/mr_test_module/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +MODULE_NAME = "MRTESTS" \ No newline at end of file diff --git a/tests/mr_test_module/Cargo.toml b/tests/mr_test_module/Cargo.toml index 9dd4d64..e46ab74 100644 --- a/tests/mr_test_module/Cargo.toml +++ b/tests/mr_test_module/Cargo.toml @@ -13,7 +13,7 @@ serde_json = "1.0" serde = "1.0" serde_derive = "1.0" libc = "0.2" -lib_mr = { path = "../../rust_api"} +lib_mr = { path = "../../" } [build-dependencies] bindgen = "0.57" diff --git a/tests/mr_test_module/Makefile b/tests/mr_test_module/Makefile index ea7e033..a3265d4 100644 --- a/tests/mr_test_module/Makefile +++ b/tests/mr_test_module/Makefile @@ -3,11 +3,11 @@ HERE=$(realpath ./) ifeq ($(DEBUG),1) EXTRA_ARGS= - MODULE_PATH=$(ROOT)/target/debug/libmr_test.so + MODULE_PATH=$(HERE)/target/debug/libmr_test.so DEBUG_RUN=1 else EXTRA_ARGS=--release - MODULE_PATH=$(ROOT)/target/release/libmr_test.so + MODULE_PATH=$(HERE)/target/release/libmr_test.so DEBUG_RUN=0 endif diff --git a/tests/mr_test_module/pytests/run_tests.sh b/tests/mr_test_module/pytests/run_tests.sh index 99f796a..9bf04dd 100755 --- a/tests/mr_test_module/pytests/run_tests.sh +++ b/tests/mr_test_module/pytests/run_tests.sh @@ -6,9 +6,9 @@ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" ROOT=$(cd $HERE/../../../ && pwd) if [[ $DEBUG == 1 ]]; then - MODULE_PATH=$HERE/../../../target/debug/libmr_test.so + MODULE_PATH=$HERE/../target/debug/libmr_test.so else - MODULE_PATH=$HERE/../../../target/release/libmr_test.so + MODULE_PATH=$HERE/../target/release/libmr_test.so fi From 5cd89a826d69e43ae0057af01f28c9ec406f3432 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 17 Jul 2022 18:11:21 +0300 Subject: [PATCH 06/60] added build.rs --- build.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..fb537b1 --- /dev/null +++ b/build.rs @@ -0,0 +1,51 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=src/*.c"); + println!("cargo:rerun-if-changed=src/*.h"); + println!("cargo:rerun-if-changed=src/utils/*.h"); + println!("cargo:rerun-if-changed=src/utils/*.c"); + + if !Command::new("make") + .env( + "MODULE_NAME", + std::env::var("MODULE_NAME").unwrap_or_default(), + ) + .status() + .expect("failed to compile libmr") + .success() + { + panic!("failed to compile libmr"); + } + + let output_dir = env::var("OUT_DIR").expect("Can not find out directory"); + + if !Command::new("cp") + .args(&["src/libmr.a", &output_dir]) + .status() + .expect("failed copy libmr.a to output directory") + .success() + { + panic!("failed copy libmr.a to output directory"); + } + + let build = bindgen::Builder::default(); + + let bindings = build + .header("src/mr.h") + .size_t_is_usize(true) + .layout_tests(false) + .generate() + .expect("error generating bindings"); + + let out_path = PathBuf::from(&output_dir); + bindings + .write_to_file(out_path.join("libmr_bindings.rs")) + .expect("failed to write bindings to file"); + + println!("cargo:rustc-flags=-L{} -lmr -lssl -lcrypto", output_dir); +} From 8f3f3f06d4152c5d0dd02d52f34176bf1a468e86 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 17 Jul 2022 18:19:50 +0300 Subject: [PATCH 07/60] change redismodule-rs dep. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e039772..90dfd48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -redis-module = { version="0.22.0", features = ["experimental-api"]} +#redis-module = { version="0.22.0", features = ["experimental-api"]} +redis-module = { git = "https://github.com/RedisLabsModules/redismodule-rs", branch = "api_extentions", features = ["experimental-api"]} serde_json = "1.0" serde = "1.0" serde_derive = "1.0" From 7ca9a49b6c13b0d173c027e97a66ab5bab06fcd9 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 17 Jul 2022 18:32:10 +0300 Subject: [PATCH 08/60] Change tests redismodule-rs version --- tests/mr_test_module/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mr_test_module/Cargo.toml b/tests/mr_test_module/Cargo.toml index e46ab74..01e9996 100644 --- a/tests/mr_test_module/Cargo.toml +++ b/tests/mr_test_module/Cargo.toml @@ -7,8 +7,8 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -redis-module = { version="0.22.0", features = ["experimental-api"]} -# redis-module = { path="/home/meir/work/redismodule-rs", features = ["experimental-api"]} +#redis-module = { version="0.22.0", features = ["experimental-api"]} +redis-module = { git = "https://github.com/RedisLabsModules/redismodule-rs", branch = "api_extentions", features = ["experimental-api"]} serde_json = "1.0" serde = "1.0" serde_derive = "1.0" From 12e5bc9e0f895fc1b4d4eaa913debe5e0187de6c Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 17 Jul 2022 18:35:51 +0300 Subject: [PATCH 09/60] Added missing unblock client. --- src/cluster.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cluster.c b/src/cluster.c index 6d04ad8..15372ad 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -1093,6 +1093,7 @@ static void MR_ClusterInfo(void* pd) { RedisModuleCtx* ctx = RedisModule_GetThreadSafeContext(bc); if(!clusterCtx.CurrCluster){ RedisModule_ReplyWithStringBuffer(ctx, NO_CLUSTER_MODE_REPLY, strlen(NO_CLUSTER_MODE_REPLY)); + RedisModule_UnblockClient(bc, NULL); return; } RedisModule_ReplyWithArray(ctx, 5); From 28e2ecd23caf2a69ba99a7593fdd28f753c300d2 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 4 Aug 2022 17:26:14 +0300 Subject: [PATCH 10/60] Fix tests and build --- build.rs | 2 +- deps/Makefile | 14 +++++++++++++- rust_api/libmr/remote_task.rs | 2 +- tests/mr_test_module/src/lib.rs | 22 +++++++++++----------- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/build.rs b/build.rs index fb537b1..2856c1b 100644 --- a/build.rs +++ b/build.rs @@ -13,7 +13,7 @@ fn main() { if !Command::new("make") .env( "MODULE_NAME", - std::env::var("MODULE_NAME").unwrap_or_default(), + std::env::var("MODULE_NAME").expect("module name was not given"), ) .status() .expect("failed to compile libmr") diff --git a/deps/Makefile b/deps/Makefile index 2e6e605..2bd5571 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -1,11 +1,23 @@ all: build_hiredis build_libevent -build_hiredis: +build_hiredis: +ifneq ("$(wildcard built_hiredis)","") + echo hiredis already built +else MAKEFLAGS='' USE_SSL=1 make -C ./hiredis/ +endif + touch built_hiredis build_libevent: +ifneq ("$(wildcard built_libevent)","") + echo libevent already built +else cd libevent; autoreconf -v -i -f; CFLAGS=-fPIC ./configure; make +endif + touch built_libevent clean: make -C ./hiredis/ clean make -C ./libevent/ clean + rm built_libevent + rm built_hiredis diff --git a/rust_api/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs index 6a969ee..606de90 100644 --- a/rust_api/libmr/remote_task.rs +++ b/rust_api/libmr/remote_task.rs @@ -44,7 +44,7 @@ pub trait RemoteTask: BaseObject { type OutRecord: record::Record; fn task( - &self, + self, r: Self::InRecord, on_done: Box)>, ); diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 4022602..91f42eb 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -355,7 +355,7 @@ impl RemoteTask for RemoteTaskGet { type OutRecord = StringRecord; fn task( - &self, + self, mut r: Self::InRecord, on_done: Box)>, ) { @@ -364,8 +364,8 @@ impl RemoteTask for RemoteTaskGet { let res = ctx.call("get", &[r.s.as_ref().unwrap()]); ctx_unlock(); if let Ok(res) = res { - if let RedisValue::SimpleString(res) = res { - r.s = Some(res); + if let RedisValue::StringBuffer(res) = res { + r.s = Some(std::str::from_utf8(&res).unwrap().to_string()); on_done(Ok(r)); } else { on_done(Err("bad result returned from `get` command".to_string())) @@ -536,8 +536,8 @@ impl FilterStep for TypeFilter { let res = ctx.call("type", &[r.s.as_ref().unwrap()]); ctx_unlock(); if let Ok(res) = res { - if let RedisValue::SimpleString(res) = res { - if res == self.t { + if let RedisValue::StringBuffer(res) = res { + if std::str::from_utf8(&res).unwrap() == self.t { Ok(true) } else { Ok(false) @@ -571,8 +571,8 @@ impl MapStep for TypeMapper { let res = ctx.call("type", &[r.s.as_ref().unwrap()]); ctx_unlock(); if let Ok(res) = res { - if let RedisValue::SimpleString(res) = res { - r.s = Some(res); + if let RedisValue::StringBuffer(res) = res { + r.s = Some(std::str::from_utf8(&res).unwrap().to_string()); Ok(r) } else { Err("bad result returned from type command".to_string()) @@ -671,8 +671,8 @@ impl MapStep for ReadStringMapper { let res = ctx.call("get", &[r.s.as_ref().unwrap()]); ctx_unlock(); if let Ok(res) = res { - if let RedisValue::SimpleString(res) = res { - r.s = Some(res); + if let RedisValue::StringBuffer(res) = res { + r.s = Some(std::str::from_utf8(&res).unwrap().to_string()); Ok(r) } else { Err("bad result returned from type command".to_string()) @@ -702,8 +702,8 @@ impl MapStep for WriteDummyString { let res = ctx.call("set", &[r.s.as_ref().unwrap(), "val"]); ctx_unlock(); if let Ok(res) = res { - if let RedisValue::SimpleString(res) = res { - r.s = Some(res); + if let RedisValue::StringBuffer(res) = res { + r.s = Some(std::str::from_utf8(&res).unwrap().to_string()); Ok(r) } else { Err("bad result returned from type command".to_string()) From 008125f4500e50f96470fc8bff58294d9019014f Mon Sep 17 00:00:00 2001 From: meir Date: Tue, 27 Sep 2022 18:57:34 +0300 Subject: [PATCH 11/60] Changes to support binary keys names for run_on_key API. --- rust_api/libmr/mod.rs | 2 +- rust_api/libmr/remote_task.rs | 36 ++++++++++++++++++++++++--------- tests/mr_test_module/src/lib.rs | 8 ++++---- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/rust_api/libmr/mod.rs b/rust_api/libmr/mod.rs index f4e87cf..005bd1f 100644 --- a/rust_api/libmr/mod.rs +++ b/rust_api/libmr/mod.rs @@ -29,6 +29,6 @@ pub fn mr_init(ctx: &Context, num_threads: usize) { record::init(); } -pub fn calc_slot(s: &str) -> usize { +pub fn calc_slot(s: &[u8]) -> usize { unsafe { MR_CalculateSlot(s.as_ptr() as *const c_char, s.len()) } } diff --git a/rust_api/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs index 606de90..a246b5e 100644 --- a/rust_api/libmr/remote_task.rs +++ b/rust_api/libmr/remote_task.rs @@ -11,6 +11,18 @@ use crate::libmr::RustMRError; use libc::strlen; use std::os::raw::{c_char, c_void}; +struct VoidHolder { + pd: *mut ::std::os::raw::c_void, +} + +impl VoidHolder { + fn get(&self) -> *mut ::std::os::raw::c_void { + self.pd + } +} + +unsafe impl Send for VoidHolder {} + extern "C" fn rust_remote_task( r: *mut Record, args: *mut ::std::os::raw::c_void, @@ -22,18 +34,22 @@ extern "C" fn rust_remote_task( >, pd: *mut ::std::os::raw::c_void, ) { + let void_holder = VoidHolder{pd}; let s = unsafe { Box::from_raw(args as *mut Step) }; let mut r = unsafe { Box::from_raw(r as *mut MRBaseRecord) }; s.task( r.record.take().unwrap(), - Box::new(move |res| match res { - Ok(r) => { - let record = Box::new(MRBaseRecord::new(r)); - unsafe { on_done.unwrap()(pd, Box::into_raw(record) as *mut Record) } - } - Err(e) => { - let error = unsafe { MR_ErrorCreate(e.as_ptr() as *const c_char, e.len()) }; - unsafe { on_error.unwrap()(pd, error) }; + Box::new(move |res| { + let pd = void_holder.get(); + match res { + Ok(r) => { + let record = Box::new(MRBaseRecord::new(r)); + unsafe { on_done.unwrap()(pd, Box::into_raw(record) as *mut Record) } + } + Err(e) => { + let error = unsafe { MR_ErrorCreate(e.as_ptr() as *const c_char, e.len()) }; + unsafe { on_error.unwrap()(pd, error) }; + } } }), ); @@ -46,7 +62,7 @@ pub trait RemoteTask: BaseObject { fn task( self, r: Self::InRecord, - on_done: Box)>, + on_done: Box) + Send>, ); fn register() { @@ -96,7 +112,7 @@ pub fn run_on_key< OutRecord: record::Record, DoneCallback: FnOnce(Result), >( - key_name: &str, + key_name: &[u8], remote_task: Remote, r: InRecord, done: DoneCallback, diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 91f42eb..5504fd6 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -306,7 +306,7 @@ fn lmr_get(ctx: &Context, args: Vec) -> RedisResult { thread::spawn(move || { let record = strin_record_new(key.to_string()); run_on_key( - key, + key.as_bytes(), RemoteTaskGet, record, move |res: Result| { @@ -357,7 +357,7 @@ impl RemoteTask for RemoteTaskGet { fn task( self, mut r: Self::InRecord, - on_done: Box)>, + on_done: Box) + Send>, ) { let ctx = get_ctx(); ctx_lock(); @@ -398,7 +398,7 @@ impl Record for StringRecord { } fn hash_slot(&self) -> usize { - calc_slot(self.s.as_ref().unwrap()) + calc_slot(self.s.as_ref().unwrap().as_bytes()) } } @@ -420,7 +420,7 @@ impl Record for IntRecord { fn hash_slot(&self) -> usize { let s = self.i.to_string(); - calc_slot(&s) + calc_slot(s.as_bytes()) } } From d68fa3b8039fb01a92a8e0076f2878032dc8f9dd Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 13:18:22 +0300 Subject: [PATCH 12/60] Move to github action. --- .github/workflows/linux.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/linux.yml diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..adcf6ba --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,30 @@ +name: Linux + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: format + run: cargo fmt -- --check + - name: install rltest + run: pip install RLTest + - name: Build + run: | + cd tests/mr_test_module/ + cargo build --verbose + - name: Run tests + run: | + cd tests/mr_test_module/pytests/ + ./run_tests.sh From 4d33039ac85b9d33eef53f273edfe859a950bf50 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 13:20:14 +0300 Subject: [PATCH 13/60] Format fixes --- .github/workflows/linux.yml | 2 +- rust_api/libmr/remote_task.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index adcf6ba..74f6282 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,4 +1,4 @@ -name: Linux +name: Test ubuntu latest on: push: diff --git a/rust_api/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs index a246b5e..86bda48 100644 --- a/rust_api/libmr/remote_task.rs +++ b/rust_api/libmr/remote_task.rs @@ -16,7 +16,7 @@ struct VoidHolder { } impl VoidHolder { - fn get(&self) -> *mut ::std::os::raw::c_void { + fn get(&self) -> *mut ::std::os::raw::c_void { self.pd } } @@ -34,7 +34,7 @@ extern "C" fn rust_remote_task( >, pd: *mut ::std::os::raw::c_void, ) { - let void_holder = VoidHolder{pd}; + let void_holder = VoidHolder { pd }; let s = unsafe { Box::from_raw(args as *mut Step) }; let mut r = unsafe { Box::from_raw(r as *mut MRBaseRecord) }; s.task( From 2a65690e692ed4d2cf4abf6cce54a2e289492ba3 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 13:29:51 +0300 Subject: [PATCH 14/60] checkout submodules --- .github/workflows/linux.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 74f6282..3720e15 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -16,10 +16,14 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Checkout submodules + run: git submodule update --init --recursive - name: format run: cargo fmt -- --check - name: install rltest run: pip install RLTest + - name: install redis + run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.3; make install - name: Build run: | cd tests/mr_test_module/ From 077818594d57b270aff890e206274c101301e858 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 13:38:52 +0300 Subject: [PATCH 15/60] run tests verbose --- .github/workflows/linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 3720e15..d342d36 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,7 +23,7 @@ jobs: - name: install rltest run: pip install RLTest - name: install redis - run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.3; make install + run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install - name: Build run: | cd tests/mr_test_module/ @@ -31,4 +31,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - ./run_tests.sh + ./run_tests.sh -s From b606c2626d497d8454ff3e378486dd39d956d0f6 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 13:54:38 +0300 Subject: [PATCH 16/60] Fix tests failure --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d342d36..e03ed8e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -31,4 +31,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - ./run_tests.sh -s + DEBUG=1 ./run_tests.sh From 10ecc8a366f4d5caffa15dfb673627750f4a2b0f Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 14:05:01 +0300 Subject: [PATCH 17/60] Added mac flow --- .github/workflows/macos.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/macos.yml diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..8c966ba --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,32 @@ +name: Test Macos + +on: + push: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v3 + - name: Checkout submodules + run: git submodule update --init --recursive + - name: format + run: cargo fmt -- --check + - name: install rltest + run: pip install RLTest + - name: install redis + run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install + - name: Build + run: | + cd tests/mr_test_module/ + cargo build --verbose + - name: Run tests + run: | + cd tests/mr_test_module/pytests/ + DEBUG=1 ./run_tests.sh From 1de3b287b3c2373b07374106c25054093d5f9345 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 14:06:18 +0300 Subject: [PATCH 18/60] run mac flow on PR --- .github/workflows/macos.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8c966ba..6341a72 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -3,6 +3,8 @@ name: Test Macos on: push: branches: [ master ] + pull_request: + branches: [ master ] env: CARGO_TERM_COLOR: always From 48e6cc1fef153bdd9b454848a70742417175df80 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 14:19:36 +0300 Subject: [PATCH 19/60] Install automake on mac --- .github/workflows/macos.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 6341a72..ce2e874 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -24,6 +24,8 @@ jobs: run: pip install RLTest - name: install redis run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install + - name: install automake + run: brew install automake - name: Build run: | cd tests/mr_test_module/ From eb190a3c92529a538bff220e25322eb2e3353f58 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 14:31:43 +0300 Subject: [PATCH 20/60] Install openssl on macos. --- .github/workflows/macos.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ce2e874..2a1f436 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -26,6 +26,8 @@ jobs: run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install - name: install automake run: brew install automake + - name: install openssl + run: brew install openssl - name: Build run: | cd tests/mr_test_module/ From 421423351198ebeb5a55560f8aa05bf61ad5b19e Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 14:45:08 +0300 Subject: [PATCH 21/60] Set PKG_CONFIG_PATH env var. --- .github/workflows/macos.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 2a1f436..9423ccc 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -27,10 +27,11 @@ jobs: - name: install automake run: brew install automake - name: install openssl - run: brew install openssl + run: brew install openssl@1.1 - name: Build run: | cd tests/mr_test_module/ + export PKG_CONFIG_PATH=/usr/local/opt/openssl@1.1/lib/pkgconfig/ cargo build --verbose - name: Run tests run: | From e79e242d4e2efa104c9c808a403ce539c402b733 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 15:32:06 +0300 Subject: [PATCH 22/60] Fix makefile to add openssl include path on macos. --- src/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Makefile b/src/Makefile index eb21e13..3ab4637 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,6 +33,12 @@ ARTIFACT_NAME=libmr all: $(ARTIFACT_NAME) +uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') +ifeq ($(uname_S),Darwin) + OPENSSL_PREFIX?=/usr/local/opt/openssl@1.1 + GCC_FLAGS+=-I$(OPENSSL_PREFIX)/include/ +endif + %.o : %.c gcc -c $(GCC_FLAGS) $< -o $@ -DMODULE_NAME=$(MODULE_NAME) From 3d147f1fa0433611508e614d7b16e80c353261c4 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 15:59:38 +0300 Subject: [PATCH 23/60] Added ssl lib path to build.rs. --- build.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 2856c1b..83241f6 100644 --- a/build.rs +++ b/build.rs @@ -47,5 +47,10 @@ fn main() { .write_to_file(out_path.join("libmr_bindings.rs")) .expect("failed to write bindings to file"); - println!("cargo:rustc-flags=-L{} -lmr -lssl -lcrypto", output_dir); + let open_ssl_lib_path = match std::env::consts::OS { + "macos" => "-L/usr/local/opt/openssl@1.1/lib/", + _ => "", + }; + + println!("cargo:rustc-flags=-L{} {} -lmr -lssl -lcrypto", output_dir, open_ssl_lib_path); } From 0cb86262fe895bc0d77b159bc592bfcd41ec4999 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 16:03:52 +0300 Subject: [PATCH 24/60] Format fixes --- build.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index 83241f6..b528863 100644 --- a/build.rs +++ b/build.rs @@ -51,6 +51,9 @@ fn main() { "macos" => "-L/usr/local/opt/openssl@1.1/lib/", _ => "", }; - - println!("cargo:rustc-flags=-L{} {} -lmr -lssl -lcrypto", output_dir, open_ssl_lib_path); + + println!( + "cargo:rustc-flags=-L{} {} -lmr -lssl -lcrypto", + output_dir, open_ssl_lib_path + ); } From b5b5253e91f0d7b51a6430cc4c4d222f88f252ab Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 16:20:20 +0300 Subject: [PATCH 25/60] Install RLTest correctly on macos. --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9423ccc..8a8140d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -21,7 +21,7 @@ jobs: - name: format run: cargo fmt -- --check - name: install rltest - run: pip install RLTest + run: python3 -m pip install RLTest - name: install redis run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install - name: install automake From 88712c6d16dc0bff0d7b38a5dbeeb5923261b27e Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 16:38:12 +0300 Subject: [PATCH 26/60] Run tests with verbose output on macos. --- .github/workflows/linux.yml | 2 +- .github/workflows/macos.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e03ed8e..446cd6f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -21,7 +21,7 @@ jobs: - name: format run: cargo fmt -- --check - name: install rltest - run: pip install RLTest + run: python3 -m pip install RLTest gevent - name: install redis run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install - name: Build diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8a8140d..c26dfcd 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -21,7 +21,7 @@ jobs: - name: format run: cargo fmt -- --check - name: install rltest - run: python3 -m pip install RLTest + run: python3 -m pip install RLTest gevent - name: install redis run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install - name: install automake @@ -36,4 +36,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - DEBUG=1 ./run_tests.sh + DEBUG=1 ./run_tests.sh -s From dfc860bbd2ee8f7b3ce7421ec0eebfdd558f24eb Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 16:43:40 +0300 Subject: [PATCH 27/60] Support to run the tests on macos. --- .github/workflows/macos.yml | 2 +- tests/mr_test_module/pytests/run_tests.sh | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c26dfcd..05fe3cf 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -36,4 +36,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - DEBUG=1 ./run_tests.sh -s + DEBUG=1 ./run_tests.sh diff --git a/tests/mr_test_module/pytests/run_tests.sh b/tests/mr_test_module/pytests/run_tests.sh index 9bf04dd..0f05585 100755 --- a/tests/mr_test_module/pytests/run_tests.sh +++ b/tests/mr_test_module/pytests/run_tests.sh @@ -5,10 +5,18 @@ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" ROOT=$(cd $HERE/../../../ && pwd) +OS=$(uname -s 2>/dev/null) +echo $OS +if [[ $OS == Darwin ]]; then + LIB_EXTENTION=dylib +else + LIB_EXTENTION=so +fi + if [[ $DEBUG == 1 ]]; then - MODULE_PATH=$HERE/../target/debug/libmr_test.so + MODULE_PATH=$HERE/../target/debug/libmr_test.$LIB_EXTENTION else - MODULE_PATH=$HERE/../target/release/libmr_test.so + MODULE_PATH=$HERE/../target/release/libmr_test.$LIB_EXTENTION fi From 3778a16e4f16f65cc713d325f0cd17de02590cfd Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 17:17:44 +0300 Subject: [PATCH 28/60] Run full tests (cluster and tls) --- .github/workflows/linux.yml | 4 ++-- .github/workflows/macos.yml | 4 ++-- tests/mr_test_module/pytests/run_tests.sh | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 446cd6f..013166b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,7 +23,7 @@ jobs: - name: install rltest run: python3 -m pip install RLTest gevent - name: install redis - run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install + run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; BUILD_TLS=yes make install - name: Build run: | cd tests/mr_test_module/ @@ -31,4 +31,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - DEBUG=1 ./run_tests.sh + DEBUG=1 ./run_full_tests.sh diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 05fe3cf..9e198ff 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -23,7 +23,7 @@ jobs: - name: install rltest run: python3 -m pip install RLTest gevent - name: install redis - run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; make install + run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; BUILD_TLS=yes make install - name: install automake run: brew install automake - name: install openssl @@ -36,4 +36,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - DEBUG=1 ./run_tests.sh + DEBUG=1 ./run_full_tests.sh diff --git a/tests/mr_test_module/pytests/run_tests.sh b/tests/mr_test_module/pytests/run_tests.sh index 0f05585..95f8555 100755 --- a/tests/mr_test_module/pytests/run_tests.sh +++ b/tests/mr_test_module/pytests/run_tests.sh @@ -6,7 +6,6 @@ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" ROOT=$(cd $HERE/../../../ && pwd) OS=$(uname -s 2>/dev/null) -echo $OS if [[ $OS == Darwin ]]; then LIB_EXTENTION=dylib else From ea80516c2c1586ddb7fe0d00d4fb81eed1067f2d Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 17:30:49 +0300 Subject: [PATCH 29/60] Added missing file. --- .../mr_test_module/pytests/run_full_tests.sh | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 tests/mr_test_module/pytests/run_full_tests.sh diff --git a/tests/mr_test_module/pytests/run_full_tests.sh b/tests/mr_test_module/pytests/run_full_tests.sh new file mode 100755 index 0000000..72634f8 --- /dev/null +++ b/tests/mr_test_module/pytests/run_full_tests.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -x +set -e + +echo oss +DEBUG=$(DEBUG) ./run_tests.sh --env-reuse +echo single shard cluster +DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 1 +echo 2 shards cluster +DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 2 +echo 3 shards cluster +DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 3 + +echo ssl certificates +bash ../generate_tests_cert.sh + +echo 2 shards cluster ssl enabled +DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 2 --tls --tls-cert-file ../tests/tls/redis.crt --tls-key-file ../tests/tls/redis.key --tls-ca-cert-file ../tests/tls/ca.crt --tls-passphrase foobar + +echo 3 shards cluster ssl enabled +DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 3 --tls --tls-cert-file ../tests/tls/redis.crt --tls-key-file ../tests/tls/redis.key --tls-ca-cert-file ../tests/tls/ca.crt --tls-passphrase foobar \ No newline at end of file From 7153b83e963e1c10d5193b2fa7a0f5d3e562e061 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 13 Oct 2022 17:49:29 +0300 Subject: [PATCH 30/60] Fix full tests script. --- tests/mr_test_module/pytests/run_full_tests.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/mr_test_module/pytests/run_full_tests.sh b/tests/mr_test_module/pytests/run_full_tests.sh index 72634f8..fd25c02 100755 --- a/tests/mr_test_module/pytests/run_full_tests.sh +++ b/tests/mr_test_module/pytests/run_full_tests.sh @@ -3,19 +3,19 @@ set -x set -e echo oss -DEBUG=$(DEBUG) ./run_tests.sh --env-reuse +DEBUG=$DEBUG ./run_tests.sh --env-reuse "$@" echo single shard cluster -DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 1 +DEBUG=$DEBUG ./run_tests.sh --env-reuse --env oss-cluster --shards-count 1 "$@" echo 2 shards cluster -DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 2 +DEBUG=$DEBUG ./run_tests.sh --env-reuse --env oss-cluster --shards-count 2 "$@" echo 3 shards cluster -DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 3 +DEBUG=$DEBUG ./run_tests.sh --env-reuse --env oss-cluster --shards-count 3 "$@" echo ssl certificates bash ../generate_tests_cert.sh echo 2 shards cluster ssl enabled -DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 2 --tls --tls-cert-file ../tests/tls/redis.crt --tls-key-file ../tests/tls/redis.key --tls-ca-cert-file ../tests/tls/ca.crt --tls-passphrase foobar +DEBUG=$DEBUG ./run_tests.sh --env-reuse --env oss-cluster --shards-count 2 --tls --tls-cert-file ../tests/tls/redis.crt --tls-key-file ../tests/tls/redis.key --tls-ca-cert-file ../tests/tls/ca.crt --tls-passphrase foobar "$@" echo 3 shards cluster ssl enabled -DEBUG=$(DEBUG) ./run_tests.sh --env-reuse --env oss-cluster --shards-count 3 --tls --tls-cert-file ../tests/tls/redis.crt --tls-key-file ../tests/tls/redis.key --tls-ca-cert-file ../tests/tls/ca.crt --tls-passphrase foobar \ No newline at end of file +DEBUG=$DEBUG ./run_tests.sh --env-reuse --env oss-cluster --shards-count 3 --tls --tls-cert-file ../tests/tls/redis.crt --tls-key-file ../tests/tls/redis.key --tls-ca-cert-file ../tests/tls/ca.crt --tls-passphrase foobar "$@" \ No newline at end of file From ab5fb1419b0741bc4043b8ef40a873931e27e876 Mon Sep 17 00:00:00 2001 From: meir Date: Fri, 14 Oct 2022 20:47:42 +0300 Subject: [PATCH 31/60] Fix fluky tests. --- tests/mr_test_module/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 5504fd6..da5bd00 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -198,7 +198,7 @@ fn lmr_count_key(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_reach_max_idle(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(MaxIdleReader::new(50)) + let execution = create_builder(MaxIdleReader::new(100)) .collect() .create_execution() .map_err(|e| RedisError::String(e))?; From 7c894ff768c7fb001997e50e228c4cc1cfe5307f Mon Sep 17 00:00:00 2001 From: meir Date: Fri, 14 Oct 2022 21:06:09 +0300 Subject: [PATCH 32/60] Fix fluky tests. --- tests/mr_test_module/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index da5bd00..8089747 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -198,11 +198,11 @@ fn lmr_count_key(ctx: &Context, _args: Vec) -> RedisResult { } fn lmr_reach_max_idle(ctx: &Context, _args: Vec) -> RedisResult { - let execution = create_builder(MaxIdleReader::new(100)) + let execution = create_builder(MaxIdleReader::new(200)) .collect() .create_execution() .map_err(|e| RedisError::String(e))?; - execution.set_max_idle(20); + execution.set_max_idle(10); let blocked_client = ctx.block_client(); execution.set_done_hanlder(|mut res, mut errs| { let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); From 160f458614bf53ac79a66a2906af7fc736d2e4a0 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 09:42:14 +0300 Subject: [PATCH 33/60] Added valgrind run. --- .github/workflows/linux.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 013166b..c839a6e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -14,6 +14,10 @@ jobs: runs-on: ubuntu-latest + strategy: + matrix: + test_args: ["", "-V --vg-suppressions ../leakcheck.supp"] + steps: - uses: actions/checkout@v3 - name: Checkout submodules @@ -31,4 +35,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - DEBUG=1 ./run_full_tests.sh + DEBUG=1 ./run_full_tests.sh ${{ matrix.test_args }} From 4b4daf4ae98d1f961ccdd919ad5a131555d9f2cc Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 09:51:00 +0300 Subject: [PATCH 34/60] Install valgrind --- .github/workflows/linux.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c839a6e..098288f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -28,6 +28,8 @@ jobs: run: python3 -m pip install RLTest gevent - name: install redis run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; BUILD_TLS=yes make install + - name: install valgrind + run: apt-get install valgrind - name: Build run: | cd tests/mr_test_module/ From b7e4e28db682e6b1499ea6b96848616d5fe411aa Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 09:56:59 +0300 Subject: [PATCH 35/60] Fix valgrind installation failure --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 098288f..4303329 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -29,7 +29,7 @@ jobs: - name: install redis run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; BUILD_TLS=yes make install - name: install valgrind - run: apt-get install valgrind + run: sudo apt-get install valgrind - name: Build run: | cd tests/mr_test_module/ From 64fa1bda8a60a98fe092451f246a1dc7495946ef Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 10:19:48 +0300 Subject: [PATCH 36/60] Fix ssl_ctx leak. --- src/cluster.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cluster.c b/src/cluster.c index 15372ad..1a2aa6e 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -537,6 +537,7 @@ static void MR_OnConnectCallback(const struct redisAsyncContext* c, int status){ return; } SSL *ssl = SSL_new(ssl_context); + SSL_CTX_free(ssl_context); if (redisInitiateSSL((redisContext *)(&c->c), ssl) != REDIS_OK) { RedisModule_Log(mr_staticCtx, "warning", "SSL auth to %s:%d failed, will initiate retry.", c->c.tcp.host, c->c.tcp.port); // disconnect async, its not possible to free redisAsyncContext here From 9ab9ed7ecb130f5ddda207bf1a1b9b5793fb45b8 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 14:34:52 +0300 Subject: [PATCH 37/60] Added timeout to run_on_key API. --- rust_api/libmr/remote_task.rs | 2 ++ src/cluster.c | 14 +++++++++++++- src/mr.c | 27 +++++++++++++++++++++++++-- src/mr.h | 3 ++- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/rust_api/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs index 86bda48..b06e01b 100644 --- a/rust_api/libmr/remote_task.rs +++ b/rust_api/libmr/remote_task.rs @@ -116,6 +116,7 @@ pub fn run_on_key< remote_task: Remote, r: InRecord, done: DoneCallback, + timeout: usize, ) { unsafe { MR_RunOnKey( @@ -127,6 +128,7 @@ pub fn run_on_key< Some(on_done::), Some(on_error::), Box::into_raw(Box::new(done)) as *mut c_void, + timeout, ) } } diff --git a/src/cluster.c b/src/cluster.c index 1a2aa6e..94fef2e 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -1107,7 +1107,7 @@ static void MR_ClusterInfo(void* pd) { mr_dictEntry *entry = NULL; while((entry = mr_dictNext(iter))){ Node* n = mr_dictGetVal(entry); - RedisModule_ReplyWithArray(ctx, 16); + RedisModule_ReplyWithArray(ctx, 18); RedisModule_ReplyWithStringBuffer(ctx, "id", strlen("id")); RedisModule_ReplyWithStringBuffer(ctx, n->id, strlen(n->id)); RedisModule_ReplyWithStringBuffer(ctx, "ip", strlen("ip")); @@ -1138,6 +1138,18 @@ static void MR_ClusterInfo(void* pd) { RedisModule_ReplyWithStringBuffer(ctx, "pendingMessages", strlen("pendingMessages")); RedisModule_ReplyWithLongLong(ctx, mr_listLength(n->pendingMessages)); + RedisModule_ReplyWithStringBuffer(ctx, "status", strlen("status")); + if (n->isMe) { + RedisModule_ReplyWithStringBuffer(ctx, "connected", strlen("connected")); + } else if (n->status == NodeStatus_Connected) { + RedisModule_ReplyWithStringBuffer(ctx, "connected", strlen("connected")); + } else if (n->status == NodeStatus_Disconnected) { + RedisModule_ReplyWithStringBuffer(ctx, "disconnected", strlen("disconnected")); + } else if (n->status == NodeStatus_HelloSent) { + RedisModule_ReplyWithStringBuffer(ctx, "hello_sent", strlen("hello_sent")); + } else if (n->status == NodeStatus_Free) { + RedisModule_ReplyWithStringBuffer(ctx, "free", strlen("free")); + } } mr_dictReleaseIterator(iter); RedisModule_FreeThreadSafeContext(ctx); diff --git a/src/mr.c b/src/mr.c index 6c01197..29382f3 100644 --- a/src/mr.c +++ b/src/mr.c @@ -1544,6 +1544,8 @@ typedef struct RunOnKeyMsg { MRError *error; }; ReplyType replyType; + size_t timeout; + MR_LoopTaskCtx* timeoutTask; } RunOnKeyMsg; /* Run on thread pool */ @@ -1590,6 +1592,9 @@ static void MR_RemoteTaskDone(RedisModuleCtx *ctx, const char *sender_id, uint8_ msg->replyType = ReplyType_ERROR; } + MR_EventLoopDelayTaskCancel(msg->timeoutTask); + msg->timeoutTask = NULL; + /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ mr_dictDelete(mrCtx.remoteDict, id); @@ -1700,6 +1705,21 @@ static void MR_RemoteTask(RedisModuleCtx *ctx, const char *sender_id, uint8_t ty mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskInternal, RedisModule_HoldString(NULL, payload)); } +static void MR_RemoteTaskTimeoutOut(void* ctx) { + RunOnKeyMsg *msg = ctx; + msg->timeoutTask = NULL; + + msg->error = MR_ErrorCreate("Timeout", 7); + msg->replyType = ReplyType_ERROR; + + /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ + int res = mr_dictDelete(mrCtx.remoteDict, msg->id); + RedisModule_Assert(res == DICT_OK); + + /* Run the callback on the thread pool */ + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskDoneInternal, msg); +} + /* Invoked on the event look */ static void MR_RunOnKeyInternal(void* ctx) { RunOnKeyMsg *msg = ctx; @@ -1713,6 +1733,8 @@ static void MR_RunOnKeyInternal(void* ctx) { /* ownership on the message was moved to MR_ClusterSendMsgBySlot function */ msg->msg = NULL; msg->msgLen = 0; + + msg->timeoutTask = MR_EventLoopAddTaskWithDelay(MR_RemoteTaskTimeoutOut, msg, msg->timeout); } LIBMR_API void MR_RunOnKey(const char* keyName, @@ -1722,7 +1744,8 @@ LIBMR_API void MR_RunOnKey(const char* keyName, Record* r, void (*onDone)(void *pd, Record* result), void (*onError)(void *pd, MRError* err), - void *pd) + void *pd, + size_t timeout) { StepDefinition* msd = mr_dictFetchValue(mrCtx.remoteTasksDict, remoteTaskName); RedisModule_Assert(msd); @@ -1737,6 +1760,7 @@ LIBMR_API void MR_RunOnKey(const char* keyName, msg->onDone = onDone; msg->onError = onError; msg->pd = pd; + msg->timeout = timeout; /* Set id */ size_t id = __atomic_add_fetch(&mrCtx.lastExecutionId, 1, __ATOMIC_RELAXED); SetId(msg->id, msg->idStr, id); @@ -1763,7 +1787,6 @@ LIBMR_API void MR_RunOnKey(const char* keyName, msg->msg = buff.buff; msg->msgLen = buff.size; - /* The remove mapper dictionary is protected by the event look so we must move to the event loop.*/ MR_EventLoopAddTask(MR_RunOnKeyInternal, msg); } diff --git a/src/mr.h b/src/mr.h index 039c2e9..0a20104 100644 --- a/src/mr.h +++ b/src/mr.h @@ -76,7 +76,8 @@ LIBMR_API void MR_RunOnKey(const char* keyName, Record* r, void (*onDone)(void *pd, Record* result), void (*onError)(void *pd, MRError* err), - void *pd); + void *pd, + size_t timeout); /* Creatign a new execution builder */ LIBMR_API ExecutionBuilder* MR_CreateExecutionBuilder(const char* readerName, void* args); From 0317d9a1b59a53c115e70689c049f39026491fc4 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 14:47:43 +0300 Subject: [PATCH 38/60] Fix tests compilation. --- tests/mr_test_module/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 8089747..051c184 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -320,6 +320,7 @@ fn lmr_get(ctx: &Context, args: Vec) -> RedisResult { } } }, + usize::MAX, ); }); Ok(RedisValue::NoReply) From 69dcbafac5f5a5374c3dec5c717070b2a4e52a5d Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 15:57:55 +0300 Subject: [PATCH 39/60] Run mac tests with verbose output --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9e198ff..dcb4b7f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -36,4 +36,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - DEBUG=1 ./run_full_tests.sh + DEBUG=1 ./run_full_tests.sh -s From 38102a1dbc10a4c5eaf7e9c82956ed7f9d758231 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 16 Oct 2022 16:26:49 +0300 Subject: [PATCH 40/60] Small remote task fixes --- src/mr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mr.c b/src/mr.c index 29382f3..84870fc 100644 --- a/src/mr.c +++ b/src/mr.c @@ -1592,8 +1592,10 @@ static void MR_RemoteTaskDone(RedisModuleCtx *ctx, const char *sender_id, uint8_ msg->replyType = ReplyType_ERROR; } - MR_EventLoopDelayTaskCancel(msg->timeoutTask); - msg->timeoutTask = NULL; + if (msg->timeoutTask) { + MR_EventLoopDelayTaskCancel(msg->timeoutTask); + msg->timeoutTask = NULL; + } /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ mr_dictDelete(mrCtx.remoteDict, id); @@ -1705,6 +1707,7 @@ static void MR_RemoteTask(RedisModuleCtx *ctx, const char *sender_id, uint8_t ty mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskInternal, RedisModule_HoldString(NULL, payload)); } +/* Invoked on the event look */ static void MR_RemoteTaskTimeoutOut(void* ctx) { RunOnKeyMsg *msg = ctx; msg->timeoutTask = NULL; @@ -1761,6 +1764,7 @@ LIBMR_API void MR_RunOnKey(const char* keyName, msg->onError = onError; msg->pd = pd; msg->timeout = timeout; + msg->timeoutTask = NULL; /* Set id */ size_t id = __atomic_add_fetch(&mrCtx.lastExecutionId, 1, __ATOMIC_RELAXED); SetId(msg->id, msg->idStr, id); From d6849d8a0b2903bb733968d17fa66636a975d153 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 23 Oct 2022 16:22:32 +0300 Subject: [PATCH 41/60] Do not set timeout on SIZE_MAX. --- src/mr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mr.c b/src/mr.c index 84870fc..1c76eb4 100644 --- a/src/mr.c +++ b/src/mr.c @@ -1737,7 +1737,9 @@ static void MR_RunOnKeyInternal(void* ctx) { msg->msg = NULL; msg->msgLen = 0; - msg->timeoutTask = MR_EventLoopAddTaskWithDelay(MR_RemoteTaskTimeoutOut, msg, msg->timeout); + if (msg->timeout != SIZE_MAX) { + msg->timeoutTask = MR_EventLoopAddTaskWithDelay(MR_RemoteTaskTimeoutOut, msg, msg->timeout); + } } LIBMR_API void MR_RunOnKey(const char* keyName, From b720acd210536590d8d1d059f708e3d4eefef8a8 Mon Sep 17 00:00:00 2001 From: meir Date: Sun, 23 Oct 2022 16:44:17 +0300 Subject: [PATCH 42/60] Remove tests verbosity on mac. --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index dcb4b7f..9e198ff 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -36,4 +36,4 @@ jobs: - name: Run tests run: | cd tests/mr_test_module/pytests/ - DEBUG=1 ./run_full_tests.sh -s + DEBUG=1 ./run_full_tests.sh From 9e401e966ba1c39dfd677f4befcf25fdf1e8d392 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 24 Oct 2022 15:45:04 +0300 Subject: [PATCH 43/60] Added MR_RunOnAllShards API. --- rust_api/libmr/remote_task.rs | 57 +++- src/mr.c | 308 +++++++++++++++++---- src/mr.h | 7 + tests/mr_test_module/pytests/test_basic.py | 6 + tests/mr_test_module/src/lib.rs | 61 +++- 5 files changed, 387 insertions(+), 52 deletions(-) diff --git a/rust_api/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs index b06e01b..3706a38 100644 --- a/rust_api/libmr/remote_task.rs +++ b/rust_api/libmr/remote_task.rs @@ -1,5 +1,5 @@ use crate::libmr_c_raw::bindings::{ - MRError, MR_ErrorCreate, MR_ErrorFree, MR_ErrorGetMessage, MR_RegisterRemoteTask, MR_RunOnKey, + MRError, MR_ErrorCreate, MR_ErrorFree, MR_ErrorGetMessage, MR_RegisterRemoteTask, MR_RunOnKey, MR_RunOnAllShards, Record, }; @@ -89,6 +89,38 @@ extern "C" fn on_done< callback(Ok(r.record.take().unwrap())); } +extern "C" fn on_done_on_all_shards< + OutRecord: record::Record, + DoneCallback: FnOnce(Vec, Vec), +>( + pd: *mut ::std::os::raw::c_void, + results: *mut *mut Record, + n_results: usize, + errs: *mut *mut MRError, + n_errs: usize, +) { + let callback = unsafe { Box::::from_raw(pd as *mut DoneCallback) }; + + let results_slice = unsafe{std::slice::from_raw_parts(results, n_results)}; + let mut results_vec = Vec::new(); + for res in results_slice { + results_vec.push(unsafe { Box::from_raw(*res as *mut MRBaseRecord) }.record.take().unwrap()) + } + + let errs_slice = unsafe{std::slice::from_raw_parts(errs, n_errs)}; + let mut errs_vec = Vec::new(); + for err in errs_slice { + let err_msg = unsafe { MR_ErrorGetMessage(*err) }; + let r_str = std::str::from_utf8(unsafe { + std::slice::from_raw_parts(err_msg.cast::(), strlen(err_msg)) + }) + .unwrap(); + errs_vec.push(r_str.to_string()) + } + + callback(results_vec, errs_vec); +} + extern "C" fn on_error< OutRecord: record::Record, DoneCallback: FnOnce(Result), @@ -132,3 +164,26 @@ pub fn run_on_key< ) } } + +pub fn run_on_all_shards< + Remote: RemoteTask, + InRecord: record::Record, + OutRecord: record::Record, + DoneCallback: FnOnce(Vec, Vec), +>( + remote_task: Remote, + r: InRecord, + done: DoneCallback, + timeout: usize, +) { + unsafe { + MR_RunOnAllShards( + Remote::get_name().as_ptr() as *mut c_char, + Box::into_raw(Box::new(remote_task)) as *mut c_void, + Box::into_raw(Box::new(MRBaseRecord::new(r))) as *mut Record, + Some(on_done_on_all_shards::), + Box::into_raw(Box::new(done)) as *mut c_void, + timeout, + ) + } +} diff --git a/src/mr.c b/src/mr.c index 1c76eb4..795aac0 100644 --- a/src/mr.c +++ b/src/mr.c @@ -1530,37 +1530,122 @@ typedef enum ReplyType { ReplyType_OK, ReplyType_ERROR, }ReplyType; -typedef struct RunOnKeyMsg { - char id[ID_LEN]; +typedef enum RemoteTaksMsgType { + RemoteTaksMsgType_OnKey, RemoteTaksMsgType_OnAllShards, +} RemoteTaksMsgType; + +typedef struct RemoteTaksMsg { char idStr[STR_ID_LEN]; - size_t slot; + char id[ID_LEN]; char *msg; size_t msgLen; - void (*onDone)(void *pd, Record* result); - void (*onError)(void *pd, MRError* err); - void *pd; + size_t timeout; + MR_LoopTaskCtx* timeoutTask; + RemoteTaksMsgType remoteTaskType; +} RemoteTaksMsg; + +typedef struct RemoteTaskResult { union { Record *res; MRError *error; }; ReplyType replyType; - size_t timeout; - MR_LoopTaskCtx* timeoutTask; +} RemoteTaskResult; + +typedef struct RunOnKeyMsg { + RemoteTaksMsg remoteTaskBase; + size_t slot; + void (*onDone)(void *pd, Record* result); + void (*onError)(void *pd, MRError* err); + void *pd; + RemoteTaskResult remoteTaskRes; } RunOnKeyMsg; +typedef struct RunOnShardsMsg { + RemoteTaksMsg remoteTaskBase; + void (*onDone)(void *pd, Record** result, size_t nResults, MRError** errs, size_t nErrs); + void *pd; + ReplyType replyType; + void* args; + Record* r; + ARR(Record*) results; + ARR(MRError*) errs; + size_t expectedNResults; + size_t nResultsArrived; + StepDefinition* msd; +} RunOnShardsMsg; + /* Run on thread pool */ -static void MR_RemoteTaskDoneInternal(void* pd) { +static void MR_RemoteTaskOnShardsDoneInternal(void* pd) { + RunOnShardsMsg *msg = pd; + msg->onDone(msg->pd, msg->results, array_len(msg->results), msg->errs, array_len(msg->errs)); + + array_free(msg->results); + array_free(msg->errs); + MR_FREE(msg); +} + +/* Run on thread pool */ +static void MR_RemoteTaskOnKeyDoneInternal(void* pd) { RunOnKeyMsg *msg = pd; - if (msg->replyType == ReplyType_OK) { - msg->onDone(msg->pd, msg->res); + if (msg->remoteTaskRes.replyType == ReplyType_OK) { + msg->onDone(msg->pd, msg->remoteTaskRes.res); } else { - msg->onError(msg->pd, msg->error); + msg->onError(msg->pd, msg->remoteTaskRes.error); } MR_FREE(msg); } +/* Run on the event loop */ +static void MR_RemoteTaskDoneProcessesResult(const char *id, RemoteTaskResult remoteTaskRes) { + RemoteTaksMsg *msgBase = mr_dictFetchValue(mrCtx.remoteDict, id); + if (!msgBase) { + RedisModule_Log(NULL, "warning", "Got a remote task done on none existing ID %.*s", REDISMODULE_NODE_ID_LEN, id); + return; + } + + if (msgBase->remoteTaskType == RemoteTaksMsgType_OnKey) { + RunOnKeyMsg *msg = (RunOnKeyMsg*)msgBase; + msg->remoteTaskRes = remoteTaskRes; + + if (msg->remoteTaskBase.timeoutTask) { + MR_EventLoopDelayTaskCancel(msg->remoteTaskBase.timeoutTask); + msg->remoteTaskBase.timeoutTask = NULL; + } + + /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ + mr_dictDelete(mrCtx.remoteDict, msgBase->id); + + /* Run the callback on the thread pool */ + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskOnKeyDoneInternal, msg); + } else { + RedisModule_Assert(msgBase->remoteTaskType == RemoteTaksMsgType_OnAllShards); + RunOnShardsMsg *msg = (RunOnShardsMsg*)msgBase; + if (remoteTaskRes.replyType == ReplyType_OK) { + msg->results = array_append(msg->results, remoteTaskRes.res); + } else { + msg->errs = array_append(msg->errs, remoteTaskRes.error); + } + ++msg->nResultsArrived; + if (msg->nResultsArrived == msg->expectedNResults) { + /* Got all the results */ + + if (msg->remoteTaskBase.timeoutTask) { + MR_EventLoopDelayTaskCancel(msg->remoteTaskBase.timeoutTask); + msg->remoteTaskBase.timeoutTask = NULL; + } + + /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ + mr_dictDelete(mrCtx.remoteDict, msgBase->id); + + /* Run the callback on the thread pool */ + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskOnShardsDoneInternal, msg); + } + } +} + /* Run on the event loop */ static void MR_RemoteTaskDone(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, RedisModuleString* payload) { size_t dataSize; @@ -1577,31 +1662,18 @@ static void MR_RemoteTaskDone(RedisModuleCtx *ctx, const char *sender_id, uint8_ const char *id = mr_BufferReaderReadBuff(&buffReader, &idLen, NULL); RedisModule_Assert(idLen == ID_LEN); - RunOnKeyMsg *msg = mr_dictFetchValue(mrCtx.remoteDict, id); - if (!msg) { - RedisModule_Log(NULL, "warning", "Got a remote task done on none existing ID %.*s", REDISMODULE_NODE_ID_LEN, id); - return; - } + RemoteTaskResult remoteTaskRes; if (mr_BufferReaderReadLongLong(&buffReader, NULL)) { - msg->res = MR_RecordDeSerialize(&buffReader); - msg->replyType = ReplyType_OK; + remoteTaskRes.res = MR_RecordDeSerialize(&buffReader); + remoteTaskRes.replyType = ReplyType_OK; } else { const char* errMsg = mr_BufferReaderReadString(&buffReader, NULL); - msg->error = MR_ErrorCreate(errMsg, strlen(errMsg)); - msg->replyType = ReplyType_ERROR; + remoteTaskRes.error = MR_ErrorCreate(errMsg, strlen(errMsg)); + remoteTaskRes.replyType = ReplyType_ERROR; } - if (msg->timeoutTask) { - MR_EventLoopDelayTaskCancel(msg->timeoutTask); - msg->timeoutTask = NULL; - } - - /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ - mr_dictDelete(mrCtx.remoteDict, id); - - /* Run the callback on the thread pool */ - mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskDoneInternal, msg); + MR_RemoteTaskDoneProcessesResult(id, remoteTaskRes); } static void MR_RemoteTaskErrorOnRemote(void *pd, MRError *error) { @@ -1708,19 +1780,19 @@ static void MR_RemoteTask(RedisModuleCtx *ctx, const char *sender_id, uint8_t ty } /* Invoked on the event look */ -static void MR_RemoteTaskTimeoutOut(void* ctx) { +static void MR_RemoteTaskOnKeyTimeoutOut(void* ctx) { RunOnKeyMsg *msg = ctx; - msg->timeoutTask = NULL; + msg->remoteTaskBase.timeoutTask = NULL; - msg->error = MR_ErrorCreate("Timeout", 7); - msg->replyType = ReplyType_ERROR; + msg->remoteTaskRes.error = MR_ErrorCreate("Timeout", 7); + msg->remoteTaskRes.replyType = ReplyType_ERROR; /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ - int res = mr_dictDelete(mrCtx.remoteDict, msg->id); + int res = mr_dictDelete(mrCtx.remoteDict, msg->remoteTaskBase.id); RedisModule_Assert(res == DICT_OK); /* Run the callback on the thread pool */ - mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskDoneInternal, msg); + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskOnKeyDoneInternal, msg); } /* Invoked on the event look */ @@ -1728,17 +1800,17 @@ static void MR_RunOnKeyInternal(void* ctx) { RunOnKeyMsg *msg = ctx; /* add the task to the remote mappers dictionary */ - mr_dictAdd(mrCtx.remoteDict, msg->id, msg); + mr_dictAdd(mrCtx.remoteDict, msg->remoteTaskBase.id, msg); /* send the message to the shard */ - MR_ClusterSendMsgBySlot(msg->slot, REMOTE_TASK_FUNCTION_ID, msg->msg, msg->msgLen); + MR_ClusterSendMsgBySlot(msg->slot, REMOTE_TASK_FUNCTION_ID, msg->remoteTaskBase.msg, msg->remoteTaskBase.msgLen); /* ownership on the message was moved to MR_ClusterSendMsgBySlot function */ - msg->msg = NULL; - msg->msgLen = 0; + msg->remoteTaskBase.msg = NULL; + msg->remoteTaskBase.msgLen = 0; - if (msg->timeout != SIZE_MAX) { - msg->timeoutTask = MR_EventLoopAddTaskWithDelay(MR_RemoteTaskTimeoutOut, msg, msg->timeout); + if (msg->remoteTaskBase.timeout != SIZE_MAX) { + msg->remoteTaskBase.timeoutTask = MR_EventLoopAddTaskWithDelay(MR_RemoteTaskOnKeyTimeoutOut, msg, msg->remoteTaskBase.timeout); } } @@ -1765,11 +1837,12 @@ LIBMR_API void MR_RunOnKey(const char* keyName, msg->onDone = onDone; msg->onError = onError; msg->pd = pd; - msg->timeout = timeout; - msg->timeoutTask = NULL; + msg->remoteTaskBase.timeout = timeout; + msg->remoteTaskBase.timeoutTask = NULL; + msg->remoteTaskBase.remoteTaskType = RemoteTaksMsgType_OnKey; /* Set id */ size_t id = __atomic_add_fetch(&mrCtx.lastExecutionId, 1, __ATOMIC_RELAXED); - SetId(msg->id, msg->idStr, id); + SetId(msg->remoteTaskBase.id, msg->remoteTaskBase.idStr, id); MRError* error = NULL; mr_Buffer buff; @@ -1779,7 +1852,7 @@ LIBMR_API void MR_RunOnKey(const char* keyName, /* write sender */ mr_BufferWriterWriteString(&buffWriter, MR_ClusterGetMyId()); /* write id */ - mr_BufferWriterWriteBuff(&buffWriter, msg->id, ID_LEN); + mr_BufferWriterWriteBuff(&buffWriter, msg->remoteTaskBase.id, ID_LEN); /* mapped name to invoke */ mr_BufferWriterWriteString(&buffWriter, remoteTaskName); /* Serialize args */ @@ -1790,9 +1863,144 @@ LIBMR_API void MR_RunOnKey(const char* keyName, MR_RecordSerialize(r, &buffWriter); MR_RecordFree(r); - msg->msg = buff.buff; - msg->msgLen = buff.size; + msg->remoteTaskBase.msg = buff.buff; + msg->remoteTaskBase.msgLen = buff.size; - /* The remove mapper dictionary is protected by the event look so we must move to the event loop.*/ MR_EventLoopAddTask(MR_RunOnKeyInternal, msg); } + +typedef struct RemoteTaskLocalRun{ + char id[ID_LEN]; + StepDefinition* msd; + void* args; + Record* r; + RemoteTaskResult result; +} RemoteTaskLocalRun; + + +static void MR_RemoteTaskDoneOnLocalEVLoop(void* ctx) { + RemoteTaskLocalRun* localRun = ctx; + + MR_RemoteTaskDoneProcessesResult(localRun->id, localRun->result); + + MR_FREE(localRun); +} + +static void MR_RemoteTaskErrorOnLocal(void *pd, MRError *error) { + RemoteTaskLocalRun* localRun = pd; + localRun->result.replyType = ReplyType_ERROR; + localRun->result.error = error; + MR_EventLoopAddTask(MR_RemoteTaskDoneOnLocalEVLoop, localRun); +} + +static void MR_RemoteTaskDoneOnLocal(void *pd, Record *res) { + RemoteTaskLocalRun* localRun = pd; + localRun->result.replyType = ReplyType_OK; + localRun->result.res = res; + MR_EventLoopAddTask(MR_RemoteTaskDoneOnLocalEVLoop, localRun); +} + +static void MR_RemoteTaskRunOnLocal(void *pd) { + RemoteTaskLocalRun* localRun = pd; + void* args = localRun->args; + Record* r = localRun->r; + localRun->args = NULL; + localRun->r = NULL; + ((RemoteTask)localRun->msd->callback)(r, args, MR_RemoteTaskDoneOnLocal, MR_RemoteTaskErrorOnLocal, localRun); +} + +/* Invoked on the event look */ +static void MR_RemoteTaskOnAllShardsTimeoutOut(void* ctx) { + RunOnShardsMsg *msg = ctx; + msg->remoteTaskBase.timeoutTask = NULL; + + msg->errs = array_append(msg->errs, MR_ErrorCreate("Timeout", 7)); + + /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ + int res = mr_dictDelete(mrCtx.remoteDict, msg->remoteTaskBase.id); + RedisModule_Assert(res == DICT_OK); + + /* Run the callback on the thread pool */ + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskOnKeyDoneInternal, msg); +} + +/* Invoked on the event loop */ +static void MR_RunOnAllShardsInternal(void* ctx) { + RunOnShardsMsg *msg = ctx; + + /* add the task to the remote mappers dictionary */ + mr_dictAdd(mrCtx.remoteDict, msg->remoteTaskBase.id, msg); + + if (MR_ClusterIsInClusterMode()) { + /* send the message to the shard */ + MR_ClusterSendMsg(NULL, REMOTE_TASK_FUNCTION_ID, msg->remoteTaskBase.msg, msg->remoteTaskBase.msgLen); + } + + /* Create local run */ + RemoteTaskLocalRun* localRun = MR_ALLOC(sizeof(*localRun)); + memcpy(localRun->id, msg->remoteTaskBase.id, ID_LEN); + localRun->args = msg->args; + localRun->r = msg->r; + localRun->msd = msg->msd; + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskRunOnLocal, localRun); + msg->args = NULL; + msg->r = NULL; + + /* ownership on the message was moved to MR_ClusterSendMsgBySlot function */ + msg->remoteTaskBase.msg = NULL; + msg->remoteTaskBase.msgLen = 0; + + if (msg->remoteTaskBase.timeout != SIZE_MAX) { + msg->remoteTaskBase.timeoutTask = MR_EventLoopAddTaskWithDelay(MR_RemoteTaskOnAllShardsTimeoutOut, msg, msg->remoteTaskBase.timeout); + } +} + +LIBMR_API void MR_RunOnAllShards(const char* remoteTaskName, + void* args, + Record* r, + void (*onDone)(void *pd, Record** result, size_t nResults, MRError** errs, size_t nErrs), + void *pd, + size_t timeout) { + StepDefinition* msd = mr_dictFetchValue(mrCtx.remoteTasksDict, remoteTaskName); + RedisModule_Assert(msd); + + RunOnShardsMsg *msg = MR_ALLOC(sizeof(*msg)); + msg->onDone = onDone; + msg->pd = pd; + msg->remoteTaskBase.timeout = timeout; + msg->remoteTaskBase.timeoutTask = NULL; + msg->remoteTaskBase.remoteTaskType = RemoteTaksMsgType_OnAllShards; + msg->args = args; + msg->r = r; + msg->expectedNResults = MR_ClusterGetSize(); + msg->nResultsArrived = 0; + msg->results = array_new(Record*, 10); + msg->errs = array_new(MRError*, 10); + msg->msd = msd; + + /* Set id */ + size_t id = __atomic_add_fetch(&mrCtx.lastExecutionId, 1, __ATOMIC_RELAXED); + SetId(msg->remoteTaskBase.id, msg->remoteTaskBase.idStr, id); + + MRError* error = NULL; + mr_Buffer buff; + mr_BufferInitialize(&buff); + mr_BufferWriter buffWriter; + mr_BufferWriterInit(&buffWriter, &buff); + /* write sender */ + mr_BufferWriterWriteString(&buffWriter, MR_ClusterGetMyId()); + /* write id */ + mr_BufferWriterWriteBuff(&buffWriter, msg->remoteTaskBase.id, ID_LEN); + /* mapped name to invoke */ + mr_BufferWriterWriteString(&buffWriter, remoteTaskName); + /* Serialize args */ + msd->type->serialize(&buffWriter, args, &error); + /* todo: handler serialization failure */ + /* serialize the record */ + MR_RecordSerialize(r, &buffWriter); + + msg->remoteTaskBase.msg = buff.buff; + msg->remoteTaskBase.msgLen = buff.size; + + MR_EventLoopAddTask(MR_RunOnAllShardsInternal, msg); +} diff --git a/src/mr.h b/src/mr.h index 0a20104..c8fcba5 100644 --- a/src/mr.h +++ b/src/mr.h @@ -79,6 +79,13 @@ LIBMR_API void MR_RunOnKey(const char* keyName, void *pd, size_t timeout); +LIBMR_API void MR_RunOnAllShards(const char* remoteTaskName, + void* args, + Record* r, + void (*onDone)(void *pd, Record** result, size_t nResults, MRError** errs, size_t nErrs), + void *pd, + size_t timeout); + /* Creatign a new execution builder */ LIBMR_API ExecutionBuilder* MR_CreateExecutionBuilder(const char* readerName, void* args); diff --git a/tests/mr_test_module/pytests/test_basic.py b/tests/mr_test_module/pytests/test_basic.py index 4b6f4c3..9cedeaf 100644 --- a/tests/mr_test_module/pytests/test_basic.py +++ b/tests/mr_test_module/pytests/test_basic.py @@ -69,3 +69,9 @@ def testRemoteTaskOnKey(env, conn): conn.execute_command('set', 'x', '1') env.expect('lmrtest.get', 'x').equal('1') env.expect('lmrtest.get', 'y').error().contains('bad result returned from') + +@MRTestDecorator() +def testRemoteTaskOnAllShards(env, conn): + for i in range(1000): + conn.execute_command('set', 'doc%d' % i, '1') + env.expect('lmrtest.dbsize').equal(1000) diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index 051c184..6c063cf 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -18,7 +18,7 @@ use std::str; use mr::libmr::{ accumulator::AccumulateStep, base_object::BaseObject, calc_slot, execution_builder::create_builder, filter::FilterStep, mapper::MapStep, mr_init, - reader::Reader, record::Record, remote_task::run_on_key, remote_task::RemoteTask, RustMRError, + reader::Reader, record::Record, remote_task::run_on_key, remote_task::run_on_all_shards, remote_task::RemoteTask, RustMRError, }; use std::os::raw::c_void; @@ -298,6 +298,26 @@ fn lmr_read_string_keys(ctx: &Context, _args: Vec) -> RedisResult { Ok(RedisValue::NoReply) } +fn lmr_dbsize(ctx: &Context, _args: Vec) -> RedisResult { + let blocked_client = ctx.block_client(); + run_on_all_shards( + RemoteTaskDBSize, + int_record_new(0), + move |results: Vec, mut errs: Vec| { + let thread_ctx = ThreadSafeContext::with_blocked_client(blocked_client); + if errs.len() > 0 { + let err = errs.pop().unwrap(); + thread_ctx.reply(Err(RedisError::String(err))); + } else { + let sum: i64 = results.into_iter().map(|e| e.i).sum(); + thread_ctx.reply(Ok(RedisValue::Integer(sum))); + } + }, + usize::MAX, + ); + Ok(RedisValue::NoReply) +} + fn lmr_get(ctx: &Context, args: Vec) -> RedisResult { let mut args = args.into_iter().skip(1); let ke_redis_string = args.next().ok_or(RedisError::Str("not prefix was given"))?; @@ -385,6 +405,43 @@ impl BaseObject for RemoteTaskGet { fn init(&mut self) {} } +#[derive(Clone, Serialize, Deserialize)] +struct RemoteTaskDBSize; + +impl RemoteTask for RemoteTaskDBSize { + type InRecord = IntRecord; + type OutRecord = IntRecord; + + fn task( + self, + mut r: Self::InRecord, + on_done: Box) + Send>, + ) { + let ctx = get_ctx(); + ctx_lock(); + let res = ctx.call("dbsize", &[]); + ctx_unlock(); + if let Ok(res) = res { + if let RedisValue::Integer(res) = res { + r.i = res; + on_done(Ok(r)); + } else { + on_done(Err("bad result returned from `dbsize` command".to_string())) + } + } else { + on_done(Err("bad result returned from `dbsize` command".to_string())) + } + } +} + +impl BaseObject for RemoteTaskDBSize { + fn get_name() -> &'static str { + "RemoteTaskDBSize\0" + } + + fn init(&mut self) {} +} + #[derive(Clone, Serialize, Deserialize)] struct StringRecord { pub s: Option, @@ -909,6 +966,7 @@ fn init_func(ctx: &Context, _args: &Vec) -> Status { ErrorAccumulator::register(); UnevenWorkMapper::register(); RemoteTaskGet::register(); + RemoteTaskDBSize::register(); Status::Ok } @@ -918,6 +976,7 @@ redis_module! { data_types: [], init: init_func, commands: [ + ["lmrtest.dbsize", lmr_dbsize, "readonly", 0,0,0], ["lmrtest.get", lmr_get, "readonly", 0,0,0], ["lmrtest.readallkeys", lmr_read_all_keys, "readonly", 0,0,0], ["lmrtest.readallkeystype", lmr_read_keys_type, "readonly", 0,0,0], From d92637cce8196eb8d2a6e00c29cc557e065b7a22 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 24 Oct 2022 15:45:55 +0300 Subject: [PATCH 44/60] Format fixes --- rust_api/libmr/remote_task.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/rust_api/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs index 3706a38..ca7a4dc 100644 --- a/rust_api/libmr/remote_task.rs +++ b/rust_api/libmr/remote_task.rs @@ -1,6 +1,6 @@ use crate::libmr_c_raw::bindings::{ - MRError, MR_ErrorCreate, MR_ErrorFree, MR_ErrorGetMessage, MR_RegisterRemoteTask, MR_RunOnKey, MR_RunOnAllShards, - Record, + MRError, MR_ErrorCreate, MR_ErrorFree, MR_ErrorGetMessage, MR_RegisterRemoteTask, + MR_RunOnAllShards, MR_RunOnKey, Record, }; use crate::libmr::base_object::{register, BaseObject}; @@ -101,13 +101,18 @@ extern "C" fn on_done_on_all_shards< ) { let callback = unsafe { Box::::from_raw(pd as *mut DoneCallback) }; - let results_slice = unsafe{std::slice::from_raw_parts(results, n_results)}; + let results_slice = unsafe { std::slice::from_raw_parts(results, n_results) }; let mut results_vec = Vec::new(); for res in results_slice { - results_vec.push(unsafe { Box::from_raw(*res as *mut MRBaseRecord) }.record.take().unwrap()) + results_vec.push( + unsafe { Box::from_raw(*res as *mut MRBaseRecord) } + .record + .take() + .unwrap(), + ) } - let errs_slice = unsafe{std::slice::from_raw_parts(errs, n_errs)}; + let errs_slice = unsafe { std::slice::from_raw_parts(errs, n_errs) }; let mut errs_vec = Vec::new(); for err in errs_slice { let err_msg = unsafe { MR_ErrorGetMessage(*err) }; From a8426fc776dc5b2ee8978fc80c854b66227f1f84 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 24 Oct 2022 16:03:41 +0300 Subject: [PATCH 45/60] Compile Redis with valgrind for testing. --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4303329..7aa202e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -27,7 +27,7 @@ jobs: - name: install rltest run: python3 -m pip install RLTest gevent - name: install redis - run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; BUILD_TLS=yes make install + run: git clone https://github.com/redis/redis; cd redis; git checkout 7.0.5; BUILD_TLS=yes make valgrind install - name: install valgrind run: sudo apt-get install valgrind - name: Build From 2bc32ba7497825d6cea68cbb8e9510d337e98223 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 24 Oct 2022 16:21:55 +0300 Subject: [PATCH 46/60] Fix valgrind failure --- src/mr.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mr.c b/src/mr.c index 795aac0..ce58ea0 100644 --- a/src/mr.c +++ b/src/mr.c @@ -1934,8 +1934,14 @@ static void MR_RunOnAllShardsInternal(void* ctx) { if (MR_ClusterIsInClusterMode()) { /* send the message to the shard */ MR_ClusterSendMsg(NULL, REMOTE_TASK_FUNCTION_ID, msg->remoteTaskBase.msg, msg->remoteTaskBase.msgLen); + } else { + MR_FREE(msg->remoteTaskBase.msg); } + /* ownership on the message was moved to MR_ClusterSendMsgBySlot function */ + msg->remoteTaskBase.msg = NULL; + msg->remoteTaskBase.msgLen = 0; + /* Create local run */ RemoteTaskLocalRun* localRun = MR_ALLOC(sizeof(*localRun)); memcpy(localRun->id, msg->remoteTaskBase.id, ID_LEN); @@ -1946,10 +1952,6 @@ static void MR_RunOnAllShardsInternal(void* ctx) { msg->args = NULL; msg->r = NULL; - /* ownership on the message was moved to MR_ClusterSendMsgBySlot function */ - msg->remoteTaskBase.msg = NULL; - msg->remoteTaskBase.msgLen = 0; - if (msg->remoteTaskBase.timeout != SIZE_MAX) { msg->remoteTaskBase.timeoutTask = MR_EventLoopAddTaskWithDelay(MR_RemoteTaskOnAllShardsTimeoutOut, msg, msg->remoteTaskBase.timeout); } From 7d9e2dd98235f65a97d3ddbdcd71c595167d2a27 Mon Sep 17 00:00:00 2001 From: meir Date: Tue, 25 Oct 2022 16:27:55 +0300 Subject: [PATCH 47/60] Fix timeout handling on run_on_all_shards api. --- src/mr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mr.c b/src/mr.c index ce58ea0..a03e55c 100644 --- a/src/mr.c +++ b/src/mr.c @@ -1921,7 +1921,7 @@ static void MR_RemoteTaskOnAllShardsTimeoutOut(void* ctx) { RedisModule_Assert(res == DICT_OK); /* Run the callback on the thread pool */ - mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskOnKeyDoneInternal, msg); + mr_thpool_add_work(mrCtx.executionsThreadPool, MR_RemoteTaskOnShardsDoneInternal, msg); } /* Invoked on the event loop */ From 1f2658e24ec643fe44fc3275b7a60839558bd8cd Mon Sep 17 00:00:00 2001 From: meir Date: Tue, 25 Oct 2022 17:28:24 +0300 Subject: [PATCH 48/60] Improve run on all shards test. --- tests/mr_test_module/pytests/test_basic.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/mr_test_module/pytests/test_basic.py b/tests/mr_test_module/pytests/test_basic.py index 9cedeaf..4840d96 100644 --- a/tests/mr_test_module/pytests/test_basic.py +++ b/tests/mr_test_module/pytests/test_basic.py @@ -72,6 +72,9 @@ def testRemoteTaskOnKey(env, conn): @MRTestDecorator() def testRemoteTaskOnAllShards(env, conn): - for i in range(1000): + for i in range(100): conn.execute_command('set', 'doc%d' % i, '1') - env.expect('lmrtest.dbsize').equal(1000) + env.expect('lmrtest.dbsize').equal(100) + for i in range(100): + conn.execute_command('del', 'doc%d' % i) + env.expect('lmrtest.dbsize').equal(0) From 4dc97155aa28f969f189edf62d6fe6cd9510235c Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 1 Dec 2022 07:42:19 +0200 Subject: [PATCH 49/60] Added license to all files. --- rust_api/lib.rs | 6 ++++++ rust_api/libmr/accumulator.rs | 6 ++++++ rust_api/libmr/base_object.rs | 6 ++++++ rust_api/libmr/execution_builder.rs | 6 ++++++ rust_api/libmr/execution_object.rs | 6 ++++++ rust_api/libmr/filter.rs | 6 ++++++ rust_api/libmr/mapper.rs | 6 ++++++ rust_api/libmr/mod.rs | 6 ++++++ rust_api/libmr/reader.rs | 6 ++++++ rust_api/libmr/record.rs | 6 ++++++ rust_api/libmr/remote_task.rs | 6 ++++++ tests/mr_test_module/build.rs | 0 tests/mr_test_module/src/include/mr.h | 0 tests/mr_test_module/src/libmr/mod.rs | 0 14 files changed, 66 insertions(+) delete mode 100644 tests/mr_test_module/build.rs delete mode 100644 tests/mr_test_module/src/include/mr.h delete mode 100644 tests/mr_test_module/src/libmr/mod.rs diff --git a/rust_api/lib.rs b/rust_api/lib.rs index 99fb478..6c0b641 100644 --- a/rust_api/lib.rs +++ b/rust_api/lib.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + #[macro_use] extern crate serde_derive; diff --git a/rust_api/libmr/accumulator.rs b/rust_api/libmr/accumulator.rs index 4fc3039..78a58a8 100644 --- a/rust_api/libmr/accumulator.rs +++ b/rust_api/libmr/accumulator.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{ ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterAccumulator, Record, }; diff --git a/rust_api/libmr/base_object.rs b/rust_api/libmr/base_object.rs index 32f62a2..eb0b238 100644 --- a/rust_api/libmr/base_object.rs +++ b/rust_api/libmr/base_object.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{ MRError, MRObjectType, MR_RegisterObject, MR_SerializationCtxReadeBuffer, MR_SerializationCtxWriteBuffer, ReaderSerializationCtx, WriteSerializationCtx, diff --git a/rust_api/libmr/execution_builder.rs b/rust_api/libmr/execution_builder.rs index ffb0536..824f530 100644 --- a/rust_api/libmr/execution_builder.rs +++ b/rust_api/libmr/execution_builder.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use std::marker::PhantomData; use crate::libmr_c_raw::bindings::{ diff --git a/rust_api/libmr/execution_object.rs b/rust_api/libmr/execution_object.rs index f879c36..d2ce99a 100644 --- a/rust_api/libmr/execution_object.rs +++ b/rust_api/libmr/execution_object.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use std::marker::PhantomData; use crate::libmr_c_raw::bindings::{ diff --git a/rust_api/libmr/filter.rs b/rust_api/libmr/filter.rs index ada5d5d..58491b3 100644 --- a/rust_api/libmr/filter.rs +++ b/rust_api/libmr/filter.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{ ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterFilter, Record, }; diff --git a/rust_api/libmr/mapper.rs b/rust_api/libmr/mapper.rs index 14c72d3..f2422b2 100644 --- a/rust_api/libmr/mapper.rs +++ b/rust_api/libmr/mapper.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{ ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterMapper, Record, }; diff --git a/rust_api/libmr/mod.rs b/rust_api/libmr/mod.rs index 005bd1f..bc96c7e 100644 --- a/rust_api/libmr/mod.rs +++ b/rust_api/libmr/mod.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{MRRecordType, MR_CalculateSlot, MR_Init, RedisModuleCtx}; use redis_module::Context; diff --git a/rust_api/libmr/reader.rs b/rust_api/libmr/reader.rs index 6711a51..8140736 100644 --- a/rust_api/libmr/reader.rs +++ b/rust_api/libmr/reader.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{ ExecutionCtx, MR_ExecutionCtxSetError, MR_RegisterReader, Record, }; diff --git a/rust_api/libmr/record.rs b/rust_api/libmr/record.rs index c5d6a90..9eb584c 100644 --- a/rust_api/libmr/record.rs +++ b/rust_api/libmr/record.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{ MRError, MRObjectType, MRRecordType, MR_RegisterRecord, MR_SerializationCtxReadeBuffer, MR_SerializationCtxWriteBuffer, ReaderSerializationCtx, RedisModuleCtx, WriteSerializationCtx, diff --git a/rust_api/libmr/remote_task.rs b/rust_api/libmr/remote_task.rs index ca7a4dc..d6b1f70 100644 --- a/rust_api/libmr/remote_task.rs +++ b/rust_api/libmr/remote_task.rs @@ -1,3 +1,9 @@ +/* + * Copyright Redis Ltd. 2021 - present + * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or + * the Server Side Public License v1 (SSPLv1). + */ + use crate::libmr_c_raw::bindings::{ MRError, MR_ErrorCreate, MR_ErrorFree, MR_ErrorGetMessage, MR_RegisterRemoteTask, MR_RunOnAllShards, MR_RunOnKey, Record, diff --git a/tests/mr_test_module/build.rs b/tests/mr_test_module/build.rs deleted file mode 100644 index e69de29..0000000 diff --git a/tests/mr_test_module/src/include/mr.h b/tests/mr_test_module/src/include/mr.h deleted file mode 100644 index e69de29..0000000 diff --git a/tests/mr_test_module/src/libmr/mod.rs b/tests/mr_test_module/src/libmr/mod.rs deleted file mode 100644 index e69de29..0000000 From c05e7d6c10f83fad62b8612c6a510cbeac34c49a Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 1 Dec 2022 07:50:35 +0200 Subject: [PATCH 50/60] Set fixed python version to 3.10 on mac. --- .github/workflows/macos.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9e198ff..c657132 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -16,6 +16,9 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' - name: Checkout submodules run: git submodule update --init --recursive - name: format From d00b1e83efafbd77411161a0179fb5c432413408 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 1 Dec 2022 08:44:51 +0200 Subject: [PATCH 51/60] Added libmr derive crate. --- LibMRDerive/Cargo.toml | 16 ++++ LibMRDerive/src/lib.rs | 20 +++++ tests/mr_test_module/Cargo.toml | 1 + tests/mr_test_module/src/lib.rs | 141 +++++--------------------------- 4 files changed, 56 insertions(+), 122 deletions(-) create mode 100644 LibMRDerive/Cargo.toml create mode 100644 LibMRDerive/src/lib.rs diff --git a/LibMRDerive/Cargo.toml b/LibMRDerive/Cargo.toml new file mode 100644 index 0000000..3cc7d13 --- /dev/null +++ b/LibMRDerive/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "lib_mr_derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = "1.0" +quote = "1.0" + +[lib] +name = "mr_derive" +path = "src/lib.rs" +crate-type = ["proc-macro"] +proc-macro = true \ No newline at end of file diff --git a/LibMRDerive/src/lib.rs b/LibMRDerive/src/lib.rs new file mode 100644 index 0000000..d204e7f --- /dev/null +++ b/LibMRDerive/src/lib.rs @@ -0,0 +1,20 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use quote::quote; +use syn; + +#[proc_macro_derive(BaseObject)] +pub fn base_object_derive(item: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(item).unwrap(); + let name = &ast.ident; + + let gen = quote! { + impl BaseObject for #name { + fn get_name() -> &'static str { + concat!(stringify!(#name), "\0") + } + } + }; + + gen.into() +} \ No newline at end of file diff --git a/tests/mr_test_module/Cargo.toml b/tests/mr_test_module/Cargo.toml index 51b8116..3354dac 100644 --- a/tests/mr_test_module/Cargo.toml +++ b/tests/mr_test_module/Cargo.toml @@ -15,6 +15,7 @@ serde = "1.0" serde_derive = "1.0" libc = "0.2" lib_mr = { path = "../../" } +lib_mr_derive = { path = "../../LibMRDerive/" } [build-dependencies] bindgen = "0.57" diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index d6934db..cd00073 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -31,6 +31,8 @@ use std::os::raw::c_void; use std::{thread, time}; +use mr_derive::BaseObject; + static mut DETACHED_CTX: *mut RedisModuleCtx = 0 as *mut RedisModuleCtx; fn get_redis_ctx() -> *mut RedisModuleCtx { @@ -374,7 +376,7 @@ fn lmr_read_all_keys(ctx: &Context, _args: Vec) -> RedisResult { Ok(RedisValue::NoReply) } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct RemoteTaskGet; impl RemoteTask for RemoteTaskGet { @@ -403,15 +405,7 @@ impl RemoteTask for RemoteTaskGet { } } -impl BaseObject for RemoteTaskGet { - fn get_name() -> &'static str { - "RemoteTaskGet\0" - } - - fn init(&mut self) {} -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct RemoteTaskDBSize; impl RemoteTask for RemoteTaskDBSize { @@ -440,15 +434,7 @@ impl RemoteTask for RemoteTaskDBSize { } } -impl BaseObject for RemoteTaskDBSize { - fn get_name() -> &'static str { - "RemoteTaskDBSize\0" - } - - fn init(&mut self) {} -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct StringRecord { pub s: Option, } @@ -466,13 +452,7 @@ impl Record for StringRecord { } } -impl BaseObject for StringRecord { - fn get_name() -> &'static str { - "StringRecord\0" - } -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct IntRecord { pub i: i64, } @@ -488,13 +468,7 @@ impl Record for IntRecord { } } -impl BaseObject for IntRecord { - fn get_name() -> &'static str { - "IntRecord\0" - } -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct CountAccumulator; impl AccumulateStep for CountAccumulator { @@ -515,13 +489,7 @@ impl AccumulateStep for CountAccumulator { } } -impl BaseObject for CountAccumulator { - fn get_name() -> &'static str { - "CountAccumulator\0" - } -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct ErrorAccumulator; impl AccumulateStep for ErrorAccumulator { @@ -537,14 +505,8 @@ impl AccumulateStep for ErrorAccumulator { } } -impl BaseObject for ErrorAccumulator { - fn get_name() -> &'static str { - "ErrorAccumulator\0" - } -} - /* filter by key type */ -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct DummyFilter; impl FilterStep for DummyFilter { @@ -555,14 +517,8 @@ impl FilterStep for DummyFilter { } } -impl BaseObject for DummyFilter { - fn get_name() -> &'static str { - "DummyFilter\0" - } -} - /* filter by key type */ -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct ErrorFilter; impl FilterStep for ErrorFilter { @@ -573,14 +529,8 @@ impl FilterStep for ErrorFilter { } } -impl BaseObject for ErrorFilter { - fn get_name() -> &'static str { - "ErrorFilter\0" - } -} - /* filter by key type */ -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct TypeFilter { t: String, } @@ -615,14 +565,8 @@ impl FilterStep for TypeFilter { } } -impl BaseObject for TypeFilter { - fn get_name() -> &'static str { - "TypeFilter\0" - } -} - /* map key name to its type */ -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct TypeMapper; impl MapStep for TypeMapper { @@ -647,14 +591,8 @@ impl MapStep for TypeMapper { } } -impl BaseObject for TypeMapper { - fn get_name() -> &'static str { - "TypeMapper\0" - } -} - /* map key name to its type */ -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct ErrorMapper; impl MapStep for ErrorMapper { @@ -666,13 +604,8 @@ impl MapStep for ErrorMapper { } } -impl BaseObject for ErrorMapper { - fn get_name() -> &'static str { - "ErrorMapper\0" - } -} /* map key name to its type */ -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct DummyMapper; impl MapStep for DummyMapper { @@ -684,14 +617,8 @@ impl MapStep for DummyMapper { } } -impl BaseObject for DummyMapper { - fn get_name() -> &'static str { - "DummyMapper\0" - } -} - /* map key name to its type */ -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct UnevenWorkMapper { #[serde(skip)] is_initiator: bool, @@ -716,13 +643,7 @@ impl MapStep for UnevenWorkMapper { } } -impl BaseObject for UnevenWorkMapper { - fn get_name() -> &'static str { - "UnevenWorkMapper\0" - } -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct ReadStringMapper; impl MapStep for ReadStringMapper { @@ -747,13 +668,7 @@ impl MapStep for ReadStringMapper { } } -impl BaseObject for ReadStringMapper { - fn get_name() -> &'static str { - "ReadStringMapper\0" - } -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct WriteDummyString; impl MapStep for WriteDummyString { @@ -778,13 +693,7 @@ impl MapStep for WriteDummyString { } } -impl BaseObject for WriteDummyString { - fn get_name() -> &'static str { - "WriteDummyString\0" - } -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct MaxIdleReader { #[serde(skip)] is_initiator: bool, @@ -817,13 +726,7 @@ impl Reader for MaxIdleReader { } } -impl BaseObject for MaxIdleReader { - fn get_name() -> &'static str { - "MaxIdleReader\0" - } -} - -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, BaseObject)] struct ErrorReader { is_done: bool, } @@ -846,12 +749,6 @@ impl Reader for ErrorReader { } } -impl BaseObject for ErrorReader { - fn get_name() -> &'static str { - "ErrorReader\0" - } -} - #[derive(Clone, Serialize, Deserialize)] struct KeysReader { #[serde(skip)] From 364292260462e8ecf0692c576f9436b82cfaedc2 Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 1 Dec 2022 08:57:26 +0200 Subject: [PATCH 52/60] verbose logs on valgrind run --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 7aa202e..fdf4d56 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - test_args: ["", "-V --vg-suppressions ../leakcheck.supp"] + test_args: ["", "-V --vg-suppressions ../leakcheck.supp -s"] steps: - uses: actions/checkout@v3 From 091cfaa0d44b341baf7d06a8c0193c3b51b0969d Mon Sep 17 00:00:00 2001 From: meir Date: Thu, 1 Dec 2022 09:10:17 +0200 Subject: [PATCH 53/60] Decrease tests verbosity. --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fdf4d56..7aa202e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - test_args: ["", "-V --vg-suppressions ../leakcheck.supp -s"] + test_args: ["", "-V --vg-suppressions ../leakcheck.supp"] steps: - uses: actions/checkout@v3 From ae766a5b98d377f5be0bab72b5288365505db771 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 5 Dec 2022 13:35:04 +0200 Subject: [PATCH 54/60] Added linkme to auto creation of register objects. --- Cargo.toml | 1 + LibMRDerive/src/lib.rs | 8 ++++++++ rust_api/libmr/mod.rs | 10 +++++++++- tests/mr_test_module/Cargo.toml | 1 + tests/mr_test_module/src/lib.rs | 22 +++------------------- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90dfd48..4eb5a13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ serde_json = "1.0" serde = "1.0" serde_derive = "1.0" libc = "0.2" +linkme = "0.3" [build-dependencies] bindgen = "0.59.2" diff --git a/LibMRDerive/src/lib.rs b/LibMRDerive/src/lib.rs index d204e7f..58b6597 100644 --- a/LibMRDerive/src/lib.rs +++ b/LibMRDerive/src/lib.rs @@ -1,6 +1,7 @@ extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; +use quote::format_ident; use syn; #[proc_macro_derive(BaseObject)] @@ -8,12 +9,19 @@ pub fn base_object_derive(item: TokenStream) -> TokenStream { let ast: syn::DeriveInput = syn::parse(item).unwrap(); let name = &ast.ident; + let func_name = format_ident!("register_{}", name.to_string().to_lowercase()); + let gen = quote! { impl BaseObject for #name { fn get_name() -> &'static str { concat!(stringify!(#name), "\0") } } + + #[linkme::distributed_slice(mr::libmr::REGISTER_LIST)] + fn #func_name() { + #name::register(); + } }; gen.into() diff --git a/rust_api/libmr/mod.rs b/rust_api/libmr/mod.rs index bc96c7e..07edf55 100644 --- a/rust_api/libmr/mod.rs +++ b/rust_api/libmr/mod.rs @@ -5,11 +5,12 @@ */ use crate::libmr_c_raw::bindings::{MRRecordType, MR_CalculateSlot, MR_Init, RedisModuleCtx}; - use redis_module::Context; use std::os::raw::c_char; +use linkme::distributed_slice; + pub mod accumulator; pub mod base_object; pub mod execution_builder; @@ -20,6 +21,9 @@ pub mod reader; pub mod record; pub mod remote_task; +#[distributed_slice()] +pub static REGISTER_LIST: [fn()] = [..]; + impl Default for crate::libmr_c_raw::bindings::Record { fn default() -> Self { crate::libmr_c_raw::bindings::Record { @@ -33,6 +37,10 @@ pub type RustMRError = String; pub fn mr_init(ctx: &Context, num_threads: usize) { unsafe { MR_Init(ctx.ctx as *mut RedisModuleCtx, num_threads) }; record::init(); + + for register in REGISTER_LIST { + register(); + } } pub fn calc_slot(s: &[u8]) -> usize { diff --git a/tests/mr_test_module/Cargo.toml b/tests/mr_test_module/Cargo.toml index 3354dac..bde09d0 100644 --- a/tests/mr_test_module/Cargo.toml +++ b/tests/mr_test_module/Cargo.toml @@ -16,6 +16,7 @@ serde_derive = "1.0" libc = "0.2" lib_mr = { path = "../../" } lib_mr_derive = { path = "../../LibMRDerive/" } +linkme = "0.3" [build-dependencies] bindgen = "0.57" diff --git a/tests/mr_test_module/src/lib.rs b/tests/mr_test_module/src/lib.rs index cd00073..a0aa8db 100644 --- a/tests/mr_test_module/src/lib.rs +++ b/tests/mr_test_module/src/lib.rs @@ -848,28 +848,12 @@ impl Drop for KeysReader { fn init_func(ctx: &Context, _args: &Vec) -> Status { unsafe { DETACHED_CTX = RedisModule_GetDetachedThreadSafeContext.unwrap()(ctx.ctx); - - mr_init(ctx, 3); } - StringRecord::register(); - IntRecord::register(); + mr_init(ctx, 3); + KeysReader::register(); - MaxIdleReader::register(); - ErrorReader::register(); - TypeMapper::register(); - ErrorMapper::register(); - DummyMapper::register(); - TypeFilter::register(); - DummyFilter::register(); - ErrorFilter::register(); - WriteDummyString::register(); - ReadStringMapper::register(); - CountAccumulator::register(); - ErrorAccumulator::register(); - UnevenWorkMapper::register(); - RemoteTaskGet::register(); - RemoteTaskDBSize::register(); + Status::Ok } From 50dd24af4b96d17c518d0f4b457dc9f771e3d800 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 5 Dec 2022 14:57:04 +0200 Subject: [PATCH 55/60] Small fixes to LibMR derive. --- LibMRDerive/Cargo.toml | 1 - LibMRDerive/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/LibMRDerive/Cargo.toml b/LibMRDerive/Cargo.toml index 3cc7d13..20804e9 100644 --- a/LibMRDerive/Cargo.toml +++ b/LibMRDerive/Cargo.toml @@ -12,5 +12,4 @@ quote = "1.0" [lib] name = "mr_derive" path = "src/lib.rs" -crate-type = ["proc-macro"] proc-macro = true \ No newline at end of file diff --git a/LibMRDerive/src/lib.rs b/LibMRDerive/src/lib.rs index 58b6597..56db2a9 100644 --- a/LibMRDerive/src/lib.rs +++ b/LibMRDerive/src/lib.rs @@ -12,7 +12,7 @@ pub fn base_object_derive(item: TokenStream) -> TokenStream { let func_name = format_ident!("register_{}", name.to_string().to_lowercase()); let gen = quote! { - impl BaseObject for #name { + impl mr::libmr::base_object::BaseObject for #name { fn get_name() -> &'static str { concat!(stringify!(#name), "\0") } From ddd087c88bb1922bcbab79767529e99795c265d7 Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Wed, 22 Feb 2023 15:23:17 +0100 Subject: [PATCH 56/60] Allow specifying custom OPENSSL_PREFIX for the project. The prefix variable is used within the Makefile for LibMR and is also used by the "hiredis" dependency. --- src/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 3ab4637..60efd8a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,10 +6,13 @@ else GCC_FLAGS+=-O3 endif +OPENSSL_PREFIX?=/usr/local/opt/openssl + GCC_FLAGS+=-fvisibility=hidden -fPIC -DREDISMODULE_EXPERIMENTAL_API \ -I../deps/hiredis/ \ -I../deps/hiredis/adapters/ \ --I../deps/libevent/include/ +-I../deps/libevent/include/ \ +-I$(OPENSSL_PREFIX)/include ifeq ($(COVERAGE),1) GCC_FLAGS+=-fprofile-arcs -ftest-coverage @@ -44,7 +47,7 @@ endif $(ARTIFACT_NAME): $(SOURCES) - gcc $(SOURCES) $(HIREDIS) $(HIREDIS_SSL) $(LIBEVENT) $(LIBEVENT_PTHREADS) -r -o $(ARTIFACT_NAME).o $(LD_FLAGS) + gcc $(SOURCES) $(HIREDIS) $(HIREDIS_SSL) $(LIBEVENT) $(LIBEVENT_PTHREADS) -r -o $(ARTIFACT_NAME).o $(LD_FLAGS) ifeq ($(COMPILER),gcc) objcopy --localize-hidden $(ARTIFACT_NAME).o endif From dfb05cd41e23c06e307d3baef5184667ad02d6d9 Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Thu, 23 Feb 2023 12:36:09 +0100 Subject: [PATCH 57/60] Specify PKG_CONFIG_PATH for libevent's configure script. --- deps/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/Makefile b/deps/Makefile index 2bd5571..61ae883 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -12,7 +12,7 @@ build_libevent: ifneq ("$(wildcard built_libevent)","") echo libevent already built else - cd libevent; autoreconf -v -i -f; CFLAGS=-fPIC ./configure; make + cd libevent; autoreconf -v -i -f; CFLAGS=-fPIC ./configure PKG_CONFIG_PATH=$(PKG_CONFIG_PATH); make endif touch built_libevent From 546715baf37166d96047ab342449c3706794045a Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Fri, 3 Mar 2023 13:35:24 +0100 Subject: [PATCH 58/60] Fix the openssl path for the macOS builds. Changes the way the build script figures out the openssl library path. Now it takes into account the commonly-used OPENSSL_PREFIX environment variable and attempts to probe certain pre-defined directories to figure out the path. --- build.rs | 30 +++++++++++++++++++++++++----- deps/Makefile | 4 ++-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/build.rs b/build.rs index b528863..138e69e 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,22 @@ extern crate bindgen; use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; +fn probe_paths<'a>(paths: &'a [&'a str]) -> Option<&'a str> { + paths.iter().find(|path| Path::new(path).exists()).copied() +} + +fn find_macos_openssl_prefix_path() -> &'static str { + const PATHS: [&str; 3] = [ + "/usr/local/opt/openssl", + "/usr/local/opt/openssl@1.1", + "/opt/homebrew/opt/openssl@1.1", + ]; + probe_paths(&PATHS).unwrap_or("") +} + fn main() { println!("cargo:rerun-if-changed=src/*.c"); println!("cargo:rerun-if-changed=src/*.h"); @@ -25,7 +38,7 @@ fn main() { let output_dir = env::var("OUT_DIR").expect("Can not find out directory"); if !Command::new("cp") - .args(&["src/libmr.a", &output_dir]) + .args(["src/libmr.a", &output_dir]) .status() .expect("failed copy libmr.a to output directory") .success() @@ -47,13 +60,20 @@ fn main() { .write_to_file(out_path.join("libmr_bindings.rs")) .expect("failed to write bindings to file"); - let open_ssl_lib_path = match std::env::consts::OS { - "macos" => "-L/usr/local/opt/openssl@1.1/lib/", + let open_ssl_prefix_path = match std::option_env!("OPENSSL_PREFIX") { + Some(p) => p, + None if std::env::consts::OS == "macos" => find_macos_openssl_prefix_path(), _ => "", }; + let open_ssl_lib_path_link_argument = if open_ssl_prefix_path.is_empty() { + "".to_owned() + } else { + format!("-L{open_ssl_prefix_path}/lib/") + }; + println!( "cargo:rustc-flags=-L{} {} -lmr -lssl -lcrypto", - output_dir, open_ssl_lib_path + output_dir, open_ssl_lib_path_link_argument ); } diff --git a/deps/Makefile b/deps/Makefile index 61ae883..896c8d7 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -1,6 +1,6 @@ all: build_hiredis build_libevent -build_hiredis: +build_hiredis: ifneq ("$(wildcard built_hiredis)","") echo hiredis already built else @@ -15,7 +15,7 @@ else cd libevent; autoreconf -v -i -f; CFLAGS=-fPIC ./configure PKG_CONFIG_PATH=$(PKG_CONFIG_PATH); make endif touch built_libevent - + clean: make -C ./hiredis/ clean make -C ./libevent/ clean From fc6ff8122f65850d694f436a9ce836fb802d49d5 Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Mon, 6 Mar 2023 14:57:08 +0100 Subject: [PATCH 59/60] Change the timeout message text. --- src/mr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mr.c b/src/mr.c index fb5b362..3a04c00 100644 --- a/src/mr.c +++ b/src/mr.c @@ -1787,10 +1787,11 @@ static void MR_RemoteTask(RedisModuleCtx *ctx, const char *sender_id, uint8_t ty /* Invoked on the event look */ static void MR_RemoteTaskOnKeyTimeoutOut(void* ctx) { + static const char TIMEOUT_TEXT[] = "Remote task timeout"; RunOnKeyMsg *msg = ctx; msg->remoteTaskBase.timeoutTask = NULL; - msg->remoteTaskRes.error = MR_ErrorCreate("Timeout", 7); + msg->remoteTaskRes.error = MR_ErrorCreate(TIMEOUT_TEXT, sizeof(TIMEOUT_TEXT) - 1); msg->remoteTaskRes.replyType = ReplyType_ERROR; /* Remove msg from remoteDict, we will be done with it once we fire the done callback */ From 86fa62c8e4ceb53bd7d441d9c6cda4b1b47fb48b Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Mon, 6 Mar 2023 15:27:28 +0100 Subject: [PATCH 60/60] Force the CI to work with the refactoring_rust_api branch. --- .github/workflows/linux.yml | 4 ++-- .github/workflows/macos.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 7aa202e..62245fc 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -2,9 +2,9 @@ name: Test ubuntu latest on: push: - branches: [ master ] + branches: [ master, refactoring_rust_api ] pull_request: - branches: [ master ] + branches: [ master, refactoring_rust_api ] env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c657132..97f540d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -2,9 +2,9 @@ name: Test Macos on: push: - branches: [ master ] + branches: [ master, refactoring_rust_api ] pull_request: - branches: [ master ] + branches: [ master, refactoring_rust_api ] env: CARGO_TERM_COLOR: always