5 releases (breaking)
new 0.5.0 | Mar 5, 2025 |
---|---|
0.4.0 | Mar 3, 2025 |
0.3.0 | Feb 28, 2025 |
0.2.0 | Feb 24, 2025 |
0.1.0 | Feb 22, 2025 |
#93 in Caching
578 downloads per month
14KB
165 lines
generic_cache
A generic cached object which provide user two possible usage options.
- Use
Object::get()
until it returnTimeoutError
then manually callObject::refresh()
function. - Use
Object::get_or_refresh()
which will automatically refresh the value when it is expired.
Rationale
For performance critical application, most of the time, major cost came from IO. To reduce cost, the easiest way is to cache the value. In some case, it is possible to delegate this work to network layer, e.g. Proxy. In some other case, it is not possible due to security reason. An example of such case is the bearer token which is used to communicate between API server. It is normally obtained via HTTP POST which proxy won't cache. In such case, some vendor provide a library which handle token caching but it is not always the case. This is where this library fit in.
Breaking change
Version 0.3.0
- Change
ttl
argument type fromu128
tostd::time::Duration
type.
lib.rs
:
A generic cached object which provide user two possible usage options.
- Use [Object::get()] until it return [TimeoutError] then manually call [Object::refresh()] function.
- Use [Object::get_or_refresh()] which will automatically refresh the value when it is expired.
The different between the two is that the [Object::get()] is more flexible because it only borrow
the cache value while the [Object::get_or_refresh()] will required a borrow mut of [Object] itself because it
might need to change the cached value. However, the auto refresh is convenient because user doesn't
need to handle [TimeoutError] when cache is expired.
Both usage options still need to handle refresh_fn
error if any.
Example
- Verify two cached call to get value back to back to check if it is actually the same value.
use generic_cache::Object;
let cached = Object::new(std::time::Duration::from_secs(1), 100, async || {Ok::<u16, ()>(200)}); // Explicitly define type for Error. Otherwise, compile will fail.
let first = cached.get().unwrap();
let second = cached.get().unwrap();
assert_eq!(*first, 100, "Expect {} to equals {}", *first, 0);
assert_eq!(first, second, "Expect {} to equals {}", first, second);
- Check for expired then refresh the cache
use core::time;
use std::thread::sleep;
use generic_cache::Object;
let mut cached = Object::new(std::time::Duration::from_millis(100), 100, async || {Ok::<u16, ()>(200)}); // Explicitly define type for Error. Otherwise, compile will fail.
let first = *cached.get().unwrap();
sleep(time::Duration::from_millis(200));
if let Ok(_) = cached.get() {
panic!("Cache should be expired but it is not.")
} else {
cached.refresh().await.unwrap();
}
let second = *cached.get().unwrap();
assert_ne!(first, second, "Expect {} to equals {}", first, second);
- Auto refresh expired cache value
use core::time;
use std::thread::sleep;
use generic_cache::Object;
let mut cached = Object::new(std::time::Duration::from_secs(0), 100, async || {Ok::<u16, ()>(200)}); // Explicitly define type for Error. Otherwise, compile will fail.
let first = *cached.get_or_refresh().await.unwrap();
sleep(time::Duration::from_millis(1));
let second = *cached.get_or_refresh().await.unwrap();
assert_eq!(first, second, "Expect {} to equals {}", first, second);
- No default value when create a cache and auto refresh expired cache value
use core::time;
use std::thread::sleep;
use generic_cache::Object;
let mut cached = Object::new_and_refresh(std::time::Duration::from_secs(1), async || {Ok::<u16, ()>(200)}).await.unwrap(); // Explicitly define type for Error. Otherwise, compile will fail.
let first = *cached.get_or_refresh().await.unwrap();
let second = *cached.get_or_refresh().await.unwrap();
assert_eq!(first, second, "Expect {} to equals {}", first, second);