-
Notifications
You must be signed in to change notification settings - Fork 5
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
feat: momento leaderboard client #438
base: main
Are you sure you want to change the base?
Conversation
Adds client functionality for the Momento Leaderboard API.
The control client isn't necessary/used in the leaderboard client.
The leaderboard client, unlike the cache client, uses a reosurce oriented pattern. The user calls `leaderboard` on the leaderboard client to get a leaderboard object for a specific cache- and leaderboard-name. They then invoke specific operations on that leaderboard without needing to pass in the cache- and leaderboard-name.
Refactors the leaderboard to use the method, request, and response names as per the JS client.
Moves the `Leaderboard` struct and impl into a dedicated mod.
Adds comprehensive integration tests for the leaderboard client. Applies fixes and refactoring to get tests green.
Ensure the score range doesn't have a nonsensical range, eg (+inf, -inf).
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.
I ran out of time today about halfway through. I'll give this feedback before continuing to review tomorrow.
/// Client to work with Momento Leaderboards. | ||
#[derive(Clone, Debug)] | ||
pub struct LeaderboardClient { | ||
data_clients: Vec<SLbClient<InterceptedService<Channel, HeaderInterceptor>>>, |
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.
Momento typically does not use import aliases, and tends to avoid obfuscating abbreviation. Is there a compelling reason to write SLbClient
rather than matching the de-facto code style and using the struct's name LeaderboardClient
? Alternatively, what does S mean? Can you write a comment explaining this name, or switch to a clearer name?
If you're struggling with the protos squatting on the name, consider aliasing to something clearer like as ProtoLeaderboardClient
, or use momento_protos::leaderboard::leaderboard_client as leaderboard_proto;
and refer to leaderboard_proto::LeaderboardClient
.
(here and everywhere "SLbClient" is defined)
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.
I've changed this to qualify from the leaderboard proto mod instead as per your suggestion in 5803c70
end_exclusive: u32, | ||
} | ||
|
||
impl From<Range<u32>> for RankRange { |
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 this be implemented for RangeInclusive as well?
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.
Done in d749551. I'm unclear how to convey the unlikely but troubling edge case if a user provides u32::MAX
as the end index inclusive and we convert to exclusive. I've just clamped to u32::MAX
in my implementation. Any suggestions?
/// Returns the ranked elements in the response. | ||
pub fn elements(&self) -> &[RankedElement] { | ||
&self.elements | ||
} |
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.
there should be an into_elements(self)
as well here to avoid forcing users to clone if they want owned elements.
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.
response | ||
.elements | ||
.iter() | ||
.map(|v| RankedElement { |
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.
getrank, fetchbyscore, fetchbyrank - is v the same type in all 3? Just impl From<Whatever V Is> for RankedElement
and replace this line and the other 2 with .map(|v| v.into())
instead of repeating the same marshaling stanza
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.
Nice. Fixed in 305d247.
} | ||
|
||
/// Validates the score range. | ||
pub fn validate(&self) -> MomentoResult<()> { |
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.
why is this public? Why is it possible to create an invalid ScoreRange? Shouldn't new()
return a MomentoResult<Self>
?
Should new
just .clamp
min and max to f64::MIN
and f64::MAX
instead of making it an error to write 1.0..
?
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.
1.0..
not working was an oversight my on part. I've added From<RangeFrom<f64>>
and From<RangeTo<f64>>
to handle those cases in 0576f7a
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.
The error cases are when the user passes in min=fp64::POS_INFINITY
or max=fp64::NEG_INFINITY
. We translate those to the proto Unbounded
message. That's why the check val.is_finite()
isn't enough before marshalling to Unbounded
: if min
or max
is the wrong infinity, then it's an error.
I can have new
return MomentoResult<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.
Well maybe not. The top level methods take as input impl Into<ScoreRange>
. If we let new
return MomentoResult<ScoreRange>
then the callers will have to invoke the function with something like client.fetch_by_score((1.0..5.0).into()?)
, right? Because the From<Range<f64>>
implementation is for ScoreRange
, not MomentoResult<ScoreRange>
. See j1c7c02d
impl From<Option<ScoreRange>> for ScoreRange { | ||
fn from(val: Option<ScoreRange>) -> Self { | ||
val.unwrap_or_else(ScoreRange::unbounded) | ||
} | ||
} |
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.
This appears to be unused, and it is unclear to me that unbounded means the same thing as None for a whole score range. If you want to keep it, what's the utility here, and how should this be communicated so it doesn't surprise people that None
means Everything
?
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.
Good catch -- this was a leftover while I was iterating on the UI. I've removed this in e930128
Previously we used an alias to the leaderboard proto client. We instead qualify from the proto mod for clarity.
In the configuration we communicated the client timeout as a "deadline". We refactor to use the name "client_timeout" throughout the leaderboard code. More broadly (cache/topics) we will have to rename later.
Since we model the leaderboard resource as an object, we align with rust naming conventions to use `len` to measure its size.
Adds documentation about the default ordering for fetch by score, fetch by rank, and get rank.
Renames `MomentoRequest` to `LeaderboardRequest` for clarity. This way we don't have `MomentoRequest` defined in various places (here and in the cache client).
Removes helper function and inlines since it's only used in one place.
Implements `From<>` on the proto `RankedElement` for the SDK `RankedElement` to simplify marshalling code.
In order to make the various `From` implementations continue to work, we defer validation until the RPC call.
Prefer `impl IntoIterator<Item = impl Into<Element>>` for eleemnts.
Adds client functionality for the Momento Leaderboard API.