-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement defrag support for IValue
.
#1
Conversation
The PR adds support for defrag on `IValue`. The defrag uses the provided `DefragAllocator` to reallocate all the memory of the `IValue`. ### Shared String Support In general, it is considered easy to reallocate all the pointer of the `IValue` the only exception is the shared strings which we can not simply reallocate and free the old pointer because there might be more references that points to it. To solve this issue we provide a way to clean the shared strings dictionary. This will cause new strings to be allocated from scratch and old shared strings will be freed when there will have no more references. Notice that this means that now a shared string might not actually be shared between all jsons. Two json might have the same strings but with a different instance. This means that we can no longer count on pointer value for strings hash function, we must perform the hash function on the string value itself.
The PR adds support for active defrag on RedisJSON. A pre condition for this PR is that the following PR's will be megred: * [Redis defrad module API extentions](redis/redis#13509) * [redismodule-rs support for active defrag API](RedisLabsModules/redismodule-rs#387) * [IJSON support for defrag](RedisJSON/ijson#1) The PR register defrag function on the json datatype and uses the new capability of ISON to defrag the key. **Notice**: * that increamental defrag of the json key is **not** support. In order to support it we need to implement the free_effort callback. This is not trivial and even if it was it would have cause the json object to potentially be freed when the GIL is not hold, which violate the assumption of our shared string implementation (it is not thread safe). We leave it for future improvment. * If we run on a Redis version that do not support the defrag start callback, we can still partially support defrag. In that case the IJSON object will be defraged but the shared strings dictionaty will not be reinitialize. This basically means that shared strings will not be defraged. Tests were added to cover the new functionality.
src/number.rs
Outdated
impl<A: DefragAllocator> Defrag<A> for INumber { | ||
fn defrag(mut self, defrag_allocator: &mut A) -> Self { | ||
let hd = self.header(); | ||
if hd.type_ == NumberType::Static { | ||
return self; | ||
} | ||
unsafe { | ||
let ptr = self.0.ptr().cast::<Header>(); | ||
self.0.set_ptr( | ||
defrag_allocator.realloc_ptr(ptr.cast(), Self::layout((*ptr).type_).unwrap()), | ||
) | ||
}; | ||
self | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Three comments:
Use expect
instead of unwrap
.
Would it be possible to get the ptr
from the Header
itself instead of going through the IValue::ptr
?
And would it be possible to reuse the already extracted type
instead of accessing it again through ptr
?
eg something like:
impl<A: DefragAllocator> Defrag<A> for INumber { | |
fn defrag(mut self, defrag_allocator: &mut A) -> Self { | |
let hd = self.header(); | |
if hd.type_ == NumberType::Static { | |
return self; | |
} | |
unsafe { | |
let ptr = self.0.ptr().cast::<Header>(); | |
self.0.set_ptr( | |
defrag_allocator.realloc_ptr(ptr.cast(), Self::layout((*ptr).type_).unwrap()), | |
) | |
}; | |
self | |
} | |
} | |
impl<A: DefragAllocator> Defrag<A> for INumber { | |
fn defrag(mut self, defrag_allocator: &mut A) -> Self { | |
let hd = self.header(); | |
match hd.type_ { | |
NumberType::Static => self, | |
t => { | |
let ptr = hd.ptr(); | |
let layout = Self::layout(t).expect(...); | |
unsafe { | |
self.0.set_ptr(defrag_allocator.realloc_ptr(ptr.cast(), layout)); | |
} | |
self | |
} | |
} | |
} | |
} |
src/object.rs
Outdated
return self; | ||
} | ||
let header = unsafe { self.header_mut() }; | ||
let slit_header = header.split_mut(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
split_header
src/unsafe_string.rs
Outdated
@@ -160,10 +168,10 @@ impl IString { | |||
.pad_to_align()) | |||
} | |||
|
|||
fn alloc(s: &str) -> *mut Header { | |||
fn alloc<A: FnMut(Layout) -> *mut u8>(s: &str, mut allocator: A) -> *mut Header { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the Allocator not be FnOnce
?
src/unsafe_string.rs
Outdated
@@ -175,29 +183,31 @@ impl IString { | |||
} | |||
} | |||
|
|||
fn dealloc(ptr: *mut Header) { | |||
fn dealloc<D: FnMut(*mut u8, Layout)>(ptr: *mut Header, mut deallocator: D) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FnOnce
src/unsafe_string.rs
Outdated
/// Converts a `&str` to an `IString` by interning it in the global string cache. | ||
#[must_use] | ||
pub fn intern(s: &str) -> Self { | ||
fn intern_with_allocator<A: FnMut(Layout) -> *mut u8>(s: &str, allocator: A) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FnOnce
src/unsafe_string.rs
Outdated
@@ -242,26 +252,36 @@ impl IString { | |||
unsafe { self.0.raw_copy() } | |||
} | |||
} | |||
pub(crate) fn drop_impl(&mut self) { | |||
|
|||
fn drop_impl_with_deallocator<D: FnMut(*mut u8, Layout)>(&mut self, deallocator: D) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FnOnce
src/unsafe_string.rs
Outdated
assert!((s.len() as u64) < (1 << 48)); | ||
unsafe { | ||
let ptr = alloc(Self::layout(s.len()).unwrap()).cast::<Header>(); | ||
let ptr = allocator(Self::layout(s.len()).unwrap()).cast::<Header>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect
src/array.rs
Outdated
unsafe { | ||
let new_ptr = defrag_allocator.realloc_ptr( | ||
self.0.ptr(), | ||
Self::layout((*self.0.ptr().cast::<Header>()).cap).unwrap(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect
src/value.rs
Outdated
@@ -208,6 +210,35 @@ pub enum ValueType { | |||
unsafe impl Send for IValue {} | |||
unsafe impl Sync for IValue {} | |||
|
|||
impl<A: DefragAllocator> Defrag<A> for IValue { | |||
fn defrag(self, defrag_allocator: &mut A) -> Self { | |||
match self.type_() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
match self.destructure()
src/value.rs
Outdated
// Safety: Must be an array | ||
unsafe fn to_array_unchecked(self) -> IArray { | ||
IArray(self) | ||
} | ||
|
||
// Safety: Must be an array | ||
unsafe fn to_object_unchecked(self) -> IObject { | ||
IObject(self) | ||
} | ||
|
||
unsafe fn to_string_unchecked(self) -> IString { | ||
IString(self) | ||
} | ||
|
||
unsafe fn to_number_unchecked(self) -> INumber { | ||
INumber(self) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these necessary anymore?
The PR adds support for active defrag on RedisJSON. A pre condition for this PR is that the following PR's will be megred: * [Redis defrad module API extentions](redis/redis#13509) * [redismodule-rs support for active defrag API](RedisLabsModules/redismodule-rs#387) * [IJSON support for defrag](RedisJSON/ijson#1) The PR register defrag function on the json datatype and uses the new capability of ISON to defrag the key. **Notice**: * Increamental defrag of the json key is **not** support. In order to support it we need to implement the free_effort callback. This is not trivial and even if it was it would have cause the json object to potentially be freed when the GIL is not hold, which violate the assumption of our shared string implementation (it is not thread safe). We leave it for future improvment. * If we run on a Redis version that do not support the defrag start callback, we can still partially support defrag. In that case the IJSON object will be defraged but the shared strings dictionatrywill not be reinitialize. This basically means that shared strings will not be defraged. Tests were added to cover the new functionality.
The PR adds support for active defrag on RedisJSON. A pre condition for this PR is that the following PR's will be megred: * [Redis defrad module API extentions](redis/redis#13509) * [redismodule-rs support for active defrag API](RedisLabsModules/redismodule-rs#387) * [IJSON support for defrag](RedisJSON/ijson#1) The PR register defrag function on the json datatype and uses the new capability of ISON to defrag the key. **Notice**: * Increamental defrag of the json key is **not** support. In order to support it we need to implement the free_effort callback. This is not trivial and even if it was it would have cause the json object to potentially be freed when the GIL is not hold, which violate the assumption of our shared string implementation (it is not thread safe). We leave it for future improvment. * If we run on a Redis version that do not support the defrag start callback, we can still partially support defrag. In that case the IJSON object will be defraged but the shared strings dictionatrywill not be reinitialize. This basically means that shared strings will not be defraged. Tests were added to cover the new functionality. (cherry picked from commit 6539e1e)
The PR adds support for active defrag on RedisJSON. A pre condition for this PR is that the following PR's will be megred: * [Redis defrad module API extentions](redis/redis#13509) * [redismodule-rs support for active defrag API](RedisLabsModules/redismodule-rs#387) * [IJSON support for defrag](RedisJSON/ijson#1) The PR register defrag function on the json datatype and uses the new capability of ISON to defrag the key. **Notice**: * Increamental defrag of the json key is **not** support. In order to support it we need to implement the free_effort callback. This is not trivial and even if it was it would have cause the json object to potentially be freed when the GIL is not hold, which violate the assumption of our shared string implementation (it is not thread safe). We leave it for future improvment. * If we run on a Redis version that do not support the defrag start callback, we can still partially support defrag. In that case the IJSON object will be defraged but the shared strings dictionatrywill not be reinitialize. This basically means that shared strings will not be defraged. Tests were added to cover the new functionality. (cherry picked from commit 6539e1e)
The PR adds support for active defrag on RedisJSON. A pre condition for this PR is that the following PR's will be megred: * [Redis defrad module API extentions](redis/redis#13509) * [redismodule-rs support for active defrag API](RedisLabsModules/redismodule-rs#387) * [IJSON support for defrag](RedisJSON/ijson#1) The PR register defrag function on the json datatype and uses the new capability of ISON to defrag the key. **Notice**: * Increamental defrag of the json key is **not** support. In order to support it we need to implement the free_effort callback. This is not trivial and even if it was it would have cause the json object to potentially be freed when the GIL is not hold, which violate the assumption of our shared string implementation (it is not thread safe). We leave it for future improvment. * If we run on a Redis version that do not support the defrag start callback, we can still partially support defrag. In that case the IJSON object will be defraged but the shared strings dictionatrywill not be reinitialize. This basically means that shared strings will not be defraged. Tests were added to cover the new functionality. (cherry picked from commit 6539e1e)
The PR adds support for defrag on
IValue
. The defrag uses the providedDefragAllocator
to reallocate all the memory of theIValue
.Shared String Support
In general, it is considered easy to reallocate all the pointer of the
IValue
the only exception is the shared strings which we can not simply reallocate and free the old pointer because there might be more references that points to it.To solve this issue we provide a way to clean the shared strings dictionary. This will cause new strings to be allocated from scratch and old shared strings will be freed when there will have no more references.
Notice that this means that now a shared string might not actually be shared between all jsons. Two json might have the same strings but with a different instance. This means that we can no longer count on pointer value for strings hash function, we must perform the hash function on the string value itself.