Comments (10)
Thanks. No need to rush. Please take your time.
If we decided to use
entry_by_ref
, I would keep it consistent and useget_with_by_ref
/try_get_with_by_ref
even though it is a bit long.
I started to play with the entry API and I am almost certain to use entry_by_ref
there. So using get_with_by_ref
/ try_get_with_by_ref
would make sense.
I have got the following code to compile.
https://gitlab.com/moka-labs/moka-entry-api/moka-entry-api-mock/-/blob/main/src/main.rs#L12-27
let cache: Cache<String, String> = Cache::new(100);
let key = "key".to_string();
let entry = cache
.entry_by_ref(key.as_str())
.or_insert_with(|| "value".to_string());
dbg!(entry.is_fresh()); // bool
// dbg!(entry.key()); // &K
dbg!(entry.value()); // &V
let _value = entry.into_value(); // V
// dbg!(entry.value()); // Error: Borrow of moved value.
let _value = cache
.entry(key)
.or_insert_with(|| "value".to_string())
.into_value(); // V
Note: I am not directly modifying Moka's source code yet, but created a thin wrapper called moka_entry_api_mock
to see if all those trait and lifetime bounds would work.
use moka_entry_api_mock as moka_mock;
use moka_mock::sync::Cache;
Also, I realized the ToOwned
bound in the previous example was wrong. The correct bound should be the following:
pub fn get_with_by_ref(&self, key: &Q, init: impl FnOnce() -> V) -> V
where
K: Borrow<Q>,
Q: ToOwned<Owned = K> + Hash + Eq + ?Sized,
Owned = K
was needed to create K
from &Q
in my thin wrapper:
https://gitlab.com/moka-labs/moka-entry-api/moka-entry-api-mock/-/blob/main/src/sync.rs#L85
impl<'a, K, Q, V, S> RefKeyEntrySelector<'a, K, Q, V, S>
where
K: Borrow<Q> + Hash + Eq + Send + Sync + 'static,
Q: ToOwned<Owned = K> + Hash + Eq + ?Sized,
V: Clone + Send + Sync + 'static,
S: BuildHasher + Clone + Send + Sync + 'static,
{
pub fn or_insert_with(self, init: impl FnOnce() -> V) -> Entry<K, V> {
let owned_key: K = self.ref_key.to_owned();
from moka.
Push a PR, feel free to take a look when you have time @tatsuya6502 :)
from moka.
Thank you for the suggestion. We can achieve this by cloning the key (at line 1054) in an internal method get_or_insert_with_hash_and_fun
.
I would add them as new APIs rather than changing the current ones. The current ones will still have advantages in some use cases:
- New ones (to pass key by reference) will be better when:
- You own the key, and you still need it after calling the API. (and the key type implements
Clone
) - Or, you do not own the key, but have an reference to it, and the key type implements
Clone
.
- You own the key, and you still need it after calling the API. (and the key type implements
- Current ones (to pass key by value) will be better when:
- You own the key, and you do not need it after calling the API.
- If so, cloning the key in the API is redundant.
- Or, your key type does not implement
Clone
.
- You own the key, and you do not need it after calling the API.
use moka::sync::Cache;
let cache = Cache::new(100);
let key = "key".to_string();
// New API
let _ = cache.rget_with(&key, || 0);
// Current API
// We can move the key into the API because we do not use it after calling the API.
let _ = cache.get_with(key, || 0);
The reason I made the current APIs to pass key by value is that the entry
API in std::collections::HashMap
does so:
https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html#method.entry
pub fn entry(&mut self, key: K) -> Entry<'_, K, V>
But I understand there are some use cases that the current APIs are not suitable for.
I am not sure for the naming of the new APIs. In the above example, I used rget_*
where r
stands for "reference" (key by reference). It is a silly name, but I thought we would prefer a short name? I am open to any suggestions.
The method signature will be like this:
pub fn rget_with(&self, key: &Q, init: impl FnOnce() -> V) -> V
where
K: Borrow<Q>,
Q: ToOwned + Hash + Eq + ?Sized,
We will use ToOwned
rather than Clone
, because it supports creating String
from &str
, in addition to from &String
. Note that all Clone
types automatically implement ToOwned
.
from moka.
FYI, I am planning to move some advanced or unpopular APIs to under a new API that mimics the entry
API in the std HashMap
. I have not finalized the design yet, but it will be something like this:
// Pass key by reference
let entry = cache.entry_by_ref(&key).or_insert_with(|| 0);
// `is_fresh` is a new API requested in https://github.com/moka-rs/moka/issues/136.
// => `true`: the entry is just inserted.
// `false`: the entry is already in the cache.
log::info!(entry.is_fresh());
// Consume the entry to get the owned value.
let value = entry.into_value();
// Pass key by value
let value = cache.entry(key.clone()).or_insert_with(|| 0).into_value();
// Advanced or unpopular APIs
// `compute` is a new API requested in https://github.com/moka-rs/moka/issues/179.
cache.entry_by_ref(&key)
.compute(
// If the entry is not in the cache, insert a new value.
|| 0,
// If the entry is in the cache, update the value by returning `Some()`,
// or invalidate the entry by returning `None`.
|old_value| Some(old_value += 1));
// `get_with_if` is an existing API and we will move it to the `entry` API.
cache.entry_by_ref(&key)
.get_or_insert_with_if(
// If the entry is not in the cache, insert a new value.
|| 0,
// Invalidate the entry if the closure returns `true`.
|value| value >= 10;
So we will have entry_by_ref
and entry
to pass key by reference and by value, respectively.
And we will keep/add the following convenient APIs as shortcuts of above:
// Pass key by reference
cache.rget_with(&key, || 0);
let _ = cache.try_rget_with(&key, || Ok(0));
let _ = cache.optionally_rget_with(&key, || Some(0));
// Pass key by value
cache.get_with(key.clone(), || 0);
let _ = cache.try_get_with(key.clone(), || Ok(0));
let _ = cache.optionally_get_with(key.clone(), || Some(0));
from moka.
Hi @tatsuya6502 . I think rget_with/try_rget_with
is fine. A potential alternative is get_with_ref/try_get_with_ref
or even get_with_by_ref/try_get_with_by_ref
to matching entry_by_ref
style. I will spend sometime on this once the other PR is finalized.
from moka.
Thanks for the method name ideas. I have no preference/it is hard to choose to me. All names are fine. If you have any preference, please pick one.
from moka.
I don't have a strong preference. If we decided to use entry_by_ref
, I would keep it consistent and use get_with_by_ref/try_get_with_by_ref
even though it is a bit long.
I will look into this issue a bit later this week. :)
from moka.
Thanks @tatsuya6502 . The new entry api looks pretty cool. I like it. Also, I will try out the trait bound when I implement the by_ref api. I think it should work. :)
One question I have about the entry api is, if the entry returns the reference to V, since this is a concurrent cache, if it is called from another thread/tokio task to write the same entry, will there be any potential contentions? I haven't dive into the code to see how this will work. But just curious about how to solve this problem.
from moka.
One question I have about the entry api is, if the entry returns the reference to V, since this is a concurrent cache, if it is called from another thread/tokio task to write the same entry, will there be any potential contentions?
No, there is no contention. or_insert_with()
method returns an Entry
instance, and that entry owns a clone of value V
. The &V
returned from value()
is referencing the value in the entry, instead of the one in the cache. So, there is no lock to guard the value in the cache and other threads are free to replace it.
Yeah, it might be a bit confusing.
from moka.
I got it. Thanks for the explaination.
from moka.
Related Issues (20)
- Question about usage with channels to create a Stream like interface HOT 6
- can you please add an example of concurrent modification in Moka? HOT 2
- Add an example to show how to run pending tasks in an interval HOT 3
- Add a way to pass the exact eviction time to the eviction listener? HOT 1
- Add support for something like an event handler, which can be used to refresh expiring entry?
- entries are leaked when sync::Cache is dropped HOT 5
- Try `seize` as a replacement of `crossbeam-epoch`? HOT 4
- Refactoring: Cache policy implementations HOT 2
- Do not swallow the panic when the eviction listener is panicked
- Crash in iOS HOT 3
- Unable to use moka when miri testing HOT 3
- Inefficient size aware eviction when inserting new entries with larger size HOT 3
- Consider switching to TinyUFO HOT 2
- Pending `run_pending_tasks` may cause busy loop in `schedule_write_op` HOT 14
- Miri errors on the `timer_wheel` module. (Miri v0.1.0 8b2459c1 2024-04-09) HOT 1
- CI: Kani verifier v0.49.0 uses Rust `nightly-2024-03-29`, which cannot compile `[email protected]` HOT 2
- Should Moka work in WASM? HOT 2
- Size Aware Eviction: First-In-First-Out, or evicting largest items first? HOT 2
- Latest triomphe update breaks MSRV
- The `once_cell` dependency should be optional
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from moka.