-
-
Notifications
You must be signed in to change notification settings - Fork 76
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
Entry and_compute
method for performing an insert, update, or remove operation with single closure
#227
Comments
I will probably have time in next few weeks for implementing and testing this feature. If so, it will be included in v0.10.1 release. But for now, I will put it under the backlog. |
Another example: #229 (comment). |
I would suggest to make the As a workaround, I’m adding a per-item TTL manually in getsentry/symbolicator#1028 right now by making my cache value a |
Although it would not quite be the same, as your suggested |
Thank you for the feedback!
Yeah, I agree. I did so too in my example above. I had to call I think I will rename the original New
|
Sounds good. I tried your idea and found a way to optionally add per-item TTL to an inserting value. We can do it without breaking the current API including The following is a draft of the new API. It only demonstrates use std::time::Duration;
use moka::sync::FakeCache;
// New trait to convert a value `V` into `CacheValue<V>`. You will need
// this `use` when you use `expires_after` method.
use moka::IntoCacheValue;
let mut cache = FakeCache::default(); // FakeCache<i32, &'static str>
// Insert a value "value1" that never expires.
cache.insert(1, "value1");
// Insert another value that expires after 5 minutes.
cache.insert(2, "value2".expires_after(Duration::from_secs(5 * 60))); Here are the definitions of pub mod moka {
use std::time::Duration;
/// `CacheValue<V>` stores a value `V` and optional expiration time in
/// `std::time::Duration`.
///
/// `Cache`'s write methods such as `get_with` and `insert` indirectly take
/// `CacheValue<V>` as an argument via a conversion trait `IntoCacheValue<V>`.
pub struct CacheValue<V> {
value: V,
#[allow(dead_code)]
expires_after: Option<Duration>,
}
/// `IntoCacheValue<V>` is a conversion trait that converts a value into
/// `CacheValue<V>`.
pub trait IntoCacheValue<V> {
/// Converts a value `Self` into a `CacheValue<V>`.
fn into_val(self) -> CacheValue<V>;
/// Converts a value `Self` into `CacheValue<V>` and set the optional
/// expiration time.
fn expires_after(self, duration: Duration) -> CacheValue<V>;
}
// Implement `IntoCacheValue<V>` for arbitrarily `V` type and `CacheValue<V>`.
impl<V> IntoCacheValue<V> for V {
fn into_val(self) -> CacheValue<V> {
CacheValue {
value: self,
expires_after: None,
}
}
fn expires_after(self, duration: Duration) -> CacheValue<V> {
CacheValue {
value: self,
expires_after: Some(duration),
}
}
}
impl<V> IntoCacheValue<V> for CacheValue<V> {
fn into_val(self) -> CacheValue<V> {
self
}
fn expires_after(self, duration: Duration) -> CacheValue<V> {
Self {
expires_after: Some(duration),
..self
}
}
}
pub mod sync {
use super::IntoCacheValue;
use std::{collections::HashMap, hash::Hash};
#[derive(Default)]
pub struct FakeCache<K, V> {
map: HashMap<K, V>,
}
impl<K: Eq + Hash, V> FakeCache<K, V> {
pub fn insert(&mut self, key: K, value: impl IntoCacheValue<V>) {
self.map.insert(key, value.into_val().value);
}
}
}
} |
The compute methods are incredibly useful and you should definitely provide them, if possible. The make writing concurrent code dramatically easier. So this is great! fwiw, Caffeine's preferred compute methods are in the I am more inclined towards callback evaluators for the entry's metadata (e.g. weigher, expiry) over having the call site supply this directly. The The flaw is that it needs all of the context on the entry to extract from, e.g. the user might have to bloat their value with a field to calculate the cache's internal field from, which adds a little memory overhead. For that specialized compute, I was torn on the more simplistic direct approach as chosen, or some complex return type like in your example. The JCache's EntryProcessor api does that and it is a bit convoluted. I decided that since the policy api is already messy then instead of over engineering I could add it if a user had a justified need, reimplement the current methods onto that common one, and otherwise try to steer users towards the callback style. But since API design is hard, I can't claim it was the best choice and merely the was pragmatic one. |
Is there a MVP version to use now? |
Out of necessity, I recently hacked together something I called Looking back, I should probably fold that logic into the compute function, at which point it would be quite similar to your original proposal (initially I implemented this with two functions I can definitely attest to its usefulness in more complex, concurrent scenarios! |
I am reviewing #370 now and will merge it today. I will publish moka |
The entry API was added to Moka v0.10.0. It has some methods such as
entry_by_ref(&key).or_insert_with(|| ...)
to atomically insert a value when key does not exist. This issue will add another methodand_compute
to the entry API for more complex tasks. It is similar to thecompute
method in Java Caffeine cache. Moka user can useand_compute
to insert, update, or remove a value for a key with single closure or function.A new entry API:
and_compute
methodmoka::ops::Op
andmoka::ops::PerformedOp
will be defined as:Example
Here is an example of a future cache holding counters. (from a question in #179)
Concurrency
The
and_compute
should have the following attributes about concurrency:and_compute
calls on the same key, only one call will proceed at a time. The other calls will wait until the first call completes.and_compute
andor_insert_with
on the same key. Only one call will proceed at a time, and the other calls will wait until the first call completes.or_insert_with
, there is no call coercing between simultaneousand_compute
calls on the same key.and_compute
will not do idempotent operation. (e.g. counting up a value)The text was updated successfully, but these errors were encountered: