Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve mdb #96

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

Improve mdb #96

wants to merge 3 commits into from

Conversation

kjughx
Copy link
Contributor

@kjughx kjughx commented Oct 7, 2024

  1. It doesn't make much sense to bundle OwnedOrBorrowedError when they serve different use cases, better split them into separate types.
  2. Message borrows *const mdb_sys::mdb_message_t so should contain a marker with a lifetime bound.
  3. The callbacks used in mdb are hidden behind pointers to pointers to DSTs when they could just be generic pointers. This also simplifies the UX since a consumer doesn't have to wrap callbacks in Box::new().

crates/mdb/src/lib.rs Show resolved Hide resolved
crates/mdb/src/lib.rs Show resolved Hide resolved
apps/consume_analytics_metadata/src/main.rs Show resolved Hide resolved

pub struct BorrowedError<'a> {
ptr: *const mdb_sys::mdb_error_t,
_marker: PhantomData<&'a mdb_sys::mdb_error_t>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Well spotted.

Copy link
Contributor

Choose a reason for hiding this comment

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

As I think more about this, I start doubting that BorrowedError should have an associated lifetime; maybe it is better/correct that the API only gives out &'a BorrowedError instead, where 'a is the lifetime of the reference. This looks at least superficially similar to &'a CStr.

I want to do some research on this as well; If you are impatient about getting the new callbacks merged, I would suggest scaling back the scope of the PR to only those.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See #116, there the API only exposes Error and &Error. The owned Error as a return value and &Error in the callback.

crates/mdb/src/error.rs Outdated Show resolved Hide resolved
value
}

pub trait MdbError<Ptr> {
Copy link
Contributor

Choose a reason for hiding this comment

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

What are some benefits of defining a trait for this?

In particular, do we really want new and as_ref to be available to users of the library?

write!(f, "Code: {}; Message: {:?};", self.code(), self.message())
}
}
pub trait Callback<T>: FnMut(T) + Send + 'static {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, I have not seen this pattern for handling callbacks before. What are some benefits of using our own trait instead of relying on only the Fn* traits?

Have you seen any other crates or authors using it (i.e. is there anywhere I can learn more about it)?

Copy link
Contributor

Choose a reason for hiding this comment

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

The benefit of this is not having to repeat for<'a> FnMut(BorrowedError<'a>) + Send + 'static et. al. and risk making mistakes. I am still weary of introducing a trait for this purpose, so I will experiment a bit to learn more and maybe see if there is another way to achieve this DRYness.

apps/consume_analytics_metadata/src/main.rs Show resolved Hide resolved
}
(false, true) => {
let on_message = config.on_message;
config.on_message = std::ptr::null_mut();
Copy link
Contributor

Choose a reason for hiding this comment

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

If we don't store the on_message callback on the Subscriber, will it not be dropped as soon as this try_new returns?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Could Subscriber just own SubscriberConfig which then is dropped when Subscriber is dropped?

Copy link
Contributor

Choose a reason for hiding this comment

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

It could. I actually wrote it like that at first to minimize the complexity, but then I changed my mind thinking that it is unnecessary to tie up the mdb_subscriber_config_t for the lifetime of the connection. Are there reasons other than complexity for going back? Is there anything in particular that you think contributes to complexity with the current solution (assuming complexity is the reason you want to change this)?

crates/mdb/src/lib.rs Show resolved Hide resolved
Philip Johansson added 2 commits October 9, 2024 09:35
The variants of this enum are always treated as separate types so they
should just be separate types.
Message borrows *const mdb_sys::mdb_message_t and should have a marker
to indicate that.
It's very clunky to have to pass a Box<> callback. The functions that
accept callbacks should be generic over the callback. The ownership of
the callbacks was also unclear. Also no reason to allocate pointers to
pointers on the heap. pointers are pointers.
@apljungquist
Copy link
Contributor

Consider splitting the PR, the parts that are purely a refactoring are an especially good candidate. That way we can get something merged faster by focusing our attention on a smaller change. It may also help us write a good commit message; I'm trying to get into the habit of following conventional commits, but since I'm inconsistent at best this is not a requirement.

write!(f, "Code: {}; Message: {:?};", self.code(), self.message())
}
}
pub trait Callback<T>: FnMut(T) + Send + 'static {}
Copy link
Contributor

Choose a reason for hiding this comment

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

The benefit of this is not having to repeat for<'a> FnMut(BorrowedError<'a>) + Send + 'static et. al. and risk making mistakes. I am still weary of introducing a trait for this purpose, so I will experiment a bit to learn more and maybe see if there is another way to achieve this DRYness.

crates/mdb/src/lib.rs Show resolved Hide resolved
crates/mdb/src/lib.rs Show resolved Hide resolved
}
(false, true) => {
let on_message = config.on_message;
config.on_message = std::ptr::null_mut();
Copy link
Contributor

Choose a reason for hiding this comment

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

It could. I actually wrote it like that at first to minimize the complexity, but then I changed my mind thinking that it is unnecessary to tie up the mdb_subscriber_config_t for the lifetime of the connection. Are there reasons other than complexity for going back? Is there anything in particular that you think contributes to complexity with the current solution (assuming complexity is the reason you want to change this)?


pub struct BorrowedError<'a> {
ptr: *const mdb_sys::mdb_error_t,
_marker: PhantomData<&'a mdb_sys::mdb_error_t>,
Copy link
Contributor

Choose a reason for hiding this comment

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

As I think more about this, I start doubting that BorrowedError should have an associated lifetime; maybe it is better/correct that the API only gives out &'a BorrowedError instead, where 'a is the lifetime of the reference. This looks at least superficially similar to &'a CStr.

I want to do some research on this as well; If you are impatient about getting the new callbacks merged, I would suggest scaling back the scope of the PR to only those.

}

impl Connection {
// TODO: Consider adopting a builder-like pattern.
pub fn try_new(on_error: Option<Box<OnError>>) -> Result<Self, Error> {
/// Passing `None` as the `@on_error` callback requires qualifying the generic like so:
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I know it is convention in Rust to reference parameters without an @ prefix, see for instance Box::new.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants