@@ -3,7 +3,6 @@ use std::{
33 collections:: { BTreeMap , HashMap } ,
44 fmt:: Debug ,
55 hash:: Hash ,
6- ops:: DerefMut ,
76 sync:: Arc ,
87 time:: SystemTime ,
98} ;
@@ -49,10 +48,120 @@ impl<K, V> Clone for Cache<K, V> {
4948 }
5049}
5150
51+ /// A hashmap that also maintains a BTreeMap of keys ordered by a given value
52+ /// This is useful for structures that need fast O(1) lookups, but also need to evict the oldest or least recently used entries
53+ struct OrderedHashMap < K , O , V > ( ( HashMap < K , ( O , V ) > , BTreeMap < O , Vec < K > > ) ) ;
54+
55+ impl < K , O , V > OrderedHashMap < K , O , V > {
56+ fn new ( ) -> Self {
57+ Self ( ( HashMap :: new ( ) , BTreeMap :: new ( ) ) )
58+ }
59+ }
60+
61+ impl < K : Hash + Eq + Clone , O : Ord + Copy , V > OrderedHashMap < K , O , V > {
62+ fn len ( & self ) -> usize {
63+ let ( lookup, _) = & self . 0 ;
64+ lookup. len ( )
65+ }
66+ fn get ( & self , key : & K ) -> Option < & ( O , V ) > {
67+ let ( lookup, _) = & self . 0 ;
68+ lookup. get ( key)
69+ }
70+ fn get_key_value (
71+ & self ,
72+ selector : Box < dyn Fn ( & BTreeMap < O , Vec < K > > ) -> Option < ( & O , & Vec < K > ) > > ,
73+ ) -> Option < ( & K , & O , & V ) > {
74+ let ( lookup, ordered_lookup) = & self . 0 ;
75+ selector ( ordered_lookup) . and_then ( |( _, keys) | {
76+ keys. first ( )
77+ . and_then ( |key| lookup. get ( key) . and_then ( |( o, v) | Some ( ( key, o, v) ) ) )
78+ } )
79+ }
80+ /// gets the entry with the lowest order value
81+ fn get_first_key_value ( & self ) -> Option < ( & K , & O , & V ) > {
82+ self . get_key_value ( Box :: new ( |ordered_lookup| ordered_lookup. first_key_value ( ) ) )
83+ }
84+ /// gets the entry with the highest order value
85+ fn get_last_key_value ( & self ) -> Option < ( & K , & O , & V ) > {
86+ self . get_key_value ( Box :: new ( |ordered_lookup| ordered_lookup. last_key_value ( ) ) )
87+ }
88+ /// re-orders the entry with the given key
89+ fn re_order ( & mut self , key : & K , new_order : O ) {
90+ let ( lookup, order_lookup) = & mut self . 0 ;
91+ if let Some ( ( old_order, _) ) = lookup. get ( key) {
92+ // remove entry in btree
93+ match order_lookup. get_mut ( old_order) {
94+ Some ( keys) => {
95+ keys. retain ( |k| k != key) ;
96+ if keys. len ( ) == 0 {
97+ order_lookup. remove ( old_order) ;
98+ }
99+ }
100+ None => { }
101+ }
102+ }
103+ order_lookup
104+ . entry ( new_order)
105+ . or_insert ( vec ! [ ] )
106+ . push ( key. clone ( ) ) ;
107+ lookup. get_mut ( key) . map ( |( o, _) | * o = new_order) ;
108+ }
109+ /// inserts a new entry with the given key and value and order
110+ fn insert ( & mut self , key : K , value : V , order : O ) -> Option < V > {
111+ let ( lookup, order_lookup) = & mut self . 0 ;
112+
113+ if let Some ( ( old_order, _) ) = lookup. get ( & key) {
114+ // remove entry in btree
115+ match order_lookup. get_mut ( old_order) {
116+ Some ( keys) => {
117+ keys. retain ( |k| k != & key) ;
118+ if keys. len ( ) == 0 {
119+ order_lookup. remove ( old_order) ;
120+ }
121+ }
122+ None => { }
123+ }
124+ }
125+ order_lookup
126+ . entry ( order)
127+ . or_insert ( vec ! [ ] )
128+ . push ( key. clone ( ) ) ;
129+ lookup
130+ . insert ( key, ( order, value) )
131+ . and_then ( |( _, v) | Some ( v) )
132+ }
133+ /// removes the entry with the given key
134+ fn remove ( & mut self , key : & K ) -> Option < ( O , V ) > {
135+ let ( lookup, order_lookup) = & mut self . 0 ;
136+ lookup. remove ( key) . and_then ( |( order, v) | {
137+ match order_lookup. get_mut ( & order) {
138+ Some ( keys) => {
139+ keys. retain ( |k| k != key) ;
140+ if keys. len ( ) == 0 {
141+ order_lookup. remove ( & order) ;
142+ }
143+ }
144+ None => { }
145+ }
146+ Some ( ( order, v) )
147+ } )
148+ }
149+ /// removes the entry with the lowest order value
150+ fn remove_first ( & mut self ) -> Option < ( K , O , V ) > {
151+ let first_key = self . get_first_key_value ( ) . map ( |( k, _, _) | k. clone ( ) ) ;
152+ if let Some ( first_key) = first_key {
153+ self . remove ( & first_key)
154+ . map ( |( order, v) | ( first_key, order, v) )
155+ } else {
156+ None
157+ }
158+ }
159+ }
160+
52161/// A simple in-memory cache that uses timestamps to expire entries. Once the cache fills up, the oldest entry is evicted.
53162/// Uses a hashmap for lookups and a BTreeMap for ordering by age
54163pub struct MemCacheStorageTTL < K , V > {
55- store : ( HashMap < K , ( V , u128 ) > , BTreeMap < u128 , Vec < K > > ) ,
164+ store : OrderedHashMap < K , u128 , V > ,
56165 capacity : usize ,
57166 startup_time : SystemTime ,
58167 expire_after : u128 ,
@@ -62,28 +171,21 @@ impl<K, V> MemCacheStorageTTL<K, V> {
62171 /// Create a new cache with the given capacity and expiration time in milliseconds
63172 pub fn new ( capacity : usize , expire_after : u128 ) -> Self {
64173 Self {
65- store : ( HashMap :: new ( ) , BTreeMap :: new ( ) ) ,
174+ store : OrderedHashMap :: new ( ) ,
66175 capacity,
67176 startup_time : SystemTime :: now ( ) ,
68177 expire_after,
69178 }
70179 }
71- fn get_oldest_ts ( cache_order : & mut BTreeMap < u128 , Vec < K > > ) -> u128 {
72- match cache_order. first_key_value ( ) {
73- Some ( ( oldest_ts_ref, _) ) => * oldest_ts_ref,
74- None => 0 ,
75- }
76- }
77180}
78181
79182#[ async_trait]
80183impl < K : Hash + Eq + Send + Sync + ' static + Clone + Debug , V : Clone + Send + Sync + ' static >
81184 CacheStorage < K , V > for MemCacheStorageTTL < K , V >
82185{
83186 async fn get ( & self , key : & K ) -> Option < V > {
84- let ( cache_lookup, _) = & self . store ;
85- match cache_lookup. get ( key) {
86- Some ( ( v, ts) ) => {
187+ match self . store . get ( key) {
188+ Some ( ( ts, v) ) => {
87189 let current_time = SystemTime :: now ( )
88190 . duration_since ( self . startup_time )
89191 . unwrap ( )
@@ -98,96 +200,51 @@ impl<K: Hash + Eq + Send + Sync + 'static + Clone + Debug, V: Clone + Send + Syn
98200 }
99201 }
100202 async fn remove ( & mut self , key : & K ) -> Option < V > {
101- let ( cache_lookup, cache_order) = & mut self . store ;
102- let ttl_val = cache_lookup. remove ( key) ;
103- match ttl_val {
104- Some ( ( v, ts) ) => {
105- let val = cache_order. get_mut ( & ts) . unwrap ( ) ;
106- if val. len ( ) <= 1 {
107- cache_order. remove ( & ts) ;
108- } else {
109- val. retain ( |k| k != key) ;
110- }
111- Some ( v)
112- }
113- None => None ,
114- }
203+ self . store . remove ( key) . map ( |( _, v) | v)
115204 }
116205 async fn insert ( & mut self , key : K , value : V ) -> Option < V > {
117- let ( cache_lookup, cache_order) = & mut self . store ;
118- let ts = SystemTime :: now ( )
206+ let current_ts = SystemTime :: now ( )
119207 . duration_since ( self . startup_time )
120208 . unwrap ( )
121209 . as_millis ( ) ;
122210
123211 // remove expired entries
124- while cache_order. len ( ) > 0 {
125- let oldest_ts = Self :: get_oldest_ts ( cache_order) ;
126- if ts > oldest_ts + self . expire_after {
127- let expired_keys = cache_order. get ( & oldest_ts) . unwrap ( ) ;
128- for key in expired_keys. iter ( ) {
129- println ! (
130- "removing expired key: {:?}, exp_time {:?}, oldest entry date {:?}" ,
131- key,
132- ts + self . expire_after,
133- oldest_ts
134- ) ;
135- cache_lookup. remove ( key) ;
136- }
137- cache_order. remove ( & oldest_ts) ;
138- } else {
139- break ;
140- }
212+ let exp_offset = self . expire_after ;
213+ while self . store . len ( ) > 0
214+ && self
215+ . store
216+ . get_first_key_value ( )
217+ . map ( |( _, ts, _) | ts + exp_offset < current_ts)
218+ . unwrap_or ( false )
219+ {
220+ self . store . remove_first ( ) ;
141221 }
142222
143223 // remove the oldest item if the cache is still full
144- if cache_lookup . len ( ) >= self . capacity && cache_lookup . get ( & key) . is_none ( ) {
224+ if self . store . len ( ) >= self . capacity && self . store . get ( & key) . is_none ( ) {
145225 // remove the oldest item
146- let oldest_ts = Self :: get_oldest_ts ( cache_order) ;
147- let oldest_keys = cache_order. get_mut ( & oldest_ts) . unwrap ( ) ;
148- let removal_key = oldest_keys. first ( ) . and_then ( |k| Some ( k. clone ( ) ) ) ;
149- if oldest_keys. len ( ) <= 1 {
150- // remove the whole array since it's the last entry
151- cache_order. remove ( & oldest_ts) ;
152- } else {
153- oldest_keys. swap_remove ( 0 ) ;
154- }
155- cache_lookup. remove ( & key) ;
226+ let removal_key = self . store . get_first_key_value ( ) . map ( |( k, _, _) | k. clone ( ) ) ;
156227 if let Some ( removal_key) = removal_key {
157- cache_lookup . remove ( & removal_key) ;
228+ self . store . remove ( & removal_key) ;
158229 }
159230 } ;
160231
161- // if value is overwritten when inserting a new key, we need to remove the old key from the order index
162- cache_order. entry ( ts) . or_insert ( vec ! [ ] ) . push ( key. clone ( ) ) ;
163- match cache_lookup. insert ( key. clone ( ) , ( value. clone ( ) , ts) ) {
164- Some ( ( v, ts) ) => {
165- if let Some ( ord_keys) = cache_order. get_mut ( & ts) {
166- if ord_keys. len ( ) <= 1 {
167- cache_order. remove ( & ts) ;
168- } else {
169- ord_keys. retain ( |k| k != & key) ;
170- }
171- }
172- Some ( v)
173- }
174- None => None ,
175- }
232+ self . store . insert ( key, value, current_ts)
176233 }
177234}
178235
179236/// A simple in-memory LRU cache. Once the cache fills up, the least recently used entry is evicted.
180237/// Uses a hashmap for lookups and a BTreeMap for ordering by least recently used
181238pub struct MemCacheStorageLRU < K , V > {
182239 // The store is wrapped in an arc and a mutex so that get() can be immutable
183- store : Arc < Mutex < ( HashMap < K , ( V , u64 ) > , BTreeMap < u64 , K > ) > > ,
240+ store : Arc < Mutex < OrderedHashMap < K , u64 , V > > > ,
184241 capacity : usize ,
185242}
186243
187244impl < K , V > MemCacheStorageLRU < K , V > {
188245 pub fn new ( capacity : usize ) -> Self {
189246 Self {
190- store : Arc :: new ( Mutex :: new ( ( HashMap :: new ( ) , BTreeMap :: new ( ) ) ) ) ,
247+ store : Arc :: new ( Mutex :: new ( OrderedHashMap :: new ( ) ) ) ,
191248 capacity,
192249 }
193250 }
@@ -198,62 +255,37 @@ impl<K: Hash + Eq + Send + Sync + 'static + Clone, V: Clone + Send + Sync + 'sta
198255{
199256 async fn get ( & self , key : & K ) -> Option < V > {
200257 // move the key to the end of the LRU index
201- // this is O(log(n)) in the worst case, but in the average case it's close to O(1)
258+ // this is O(log(n))
202259 let mut store_lock = self . store . lock ( ) . await ;
203- let ( cache_lookup, cache_order) = store_lock. deref_mut ( ) ;
204- let highest_lru = cache_order
205- . last_key_value ( )
206- . map ( |( hts, _) | hts + 1 )
260+ let highest_lru = store_lock
261+ . get_last_key_value ( )
262+ . map ( |( _, ts, _) | ts + 1 )
207263 . unwrap_or ( 0 ) ;
208- match cache_lookup. get_mut ( key) {
209- Some ( ( v, ts) ) => {
210- cache_order. remove ( ts) . unwrap ( ) ;
211- cache_order. entry ( highest_lru) . or_insert ( key. clone ( ) ) ;
212- * ts = highest_lru;
213- Some ( v. clone ( ) )
214- }
215- None => None ,
216- }
264+ store_lock. re_order ( key, highest_lru) ;
265+ store_lock. get ( key) . map ( |( _, v) | v. clone ( ) )
217266 }
218267 async fn remove ( & mut self , key : & K ) -> Option < V > {
219268 let mut store_lock = self . store . lock ( ) . await ;
220- let ( cache_lookup, cache_order) = store_lock. deref_mut ( ) ;
221- let lru_val = cache_lookup. remove ( key) ;
222- match lru_val {
223- Some ( ( v, ts) ) => {
224- cache_order. remove ( & ts) ;
225- Some ( v)
226- }
227- None => None ,
228- }
269+ store_lock. remove ( key) . map ( |( _, v) | v)
229270 }
230271 async fn insert ( & mut self , key : K , value : V ) -> Option < V > {
231272 // this will be O(log(n)) in all cases when cache is at capacity since we need to fetch the first and last element from the btree
232273 let mut store_lock = self . store . lock ( ) . await ;
233- let ( cache_lookup, cache_order) = store_lock. deref_mut ( ) ;
234- let highest_lru = cache_order
235- . last_key_value ( )
236- . map ( |( ts, _) | ts + 1 )
274+ let highest_lru = store_lock
275+ . get_last_key_value ( )
276+ . map ( |( _, ts, _) | ts + 1 )
237277 . unwrap_or ( 0 ) ;
238- if cache_lookup. len ( ) >= self . capacity && cache_lookup. get ( & key) . is_none ( ) {
278+
279+ if store_lock. len ( ) >= self . capacity && store_lock. get ( & key) . is_none ( ) {
239280 // remove the LRU item
240- let ( lru_ts, lru_key) = match cache_order. first_key_value ( ) {
241- Some ( ( ts, key) ) => ( * ts, key. clone ( ) ) ,
242- None => return None ,
243- } ;
244- cache_lookup. remove ( & lru_key) ;
245- cache_order. remove ( & lru_ts) ;
281+ let lru_key = store_lock
282+ . get_first_key_value ( )
283+ . map ( |( k, _, _) | k. clone ( ) )
284+ . unwrap ( ) ;
285+ store_lock. remove ( & lru_key) ;
246286 } ;
247287
248- // if value is overwritten when inserting a new key, we need to remove the old key from the order index
249- cache_order. insert ( highest_lru, key. clone ( ) ) ;
250- match cache_lookup. insert ( key. clone ( ) , ( value. clone ( ) , highest_lru) ) {
251- Some ( ( v, ts) ) => {
252- cache_order. remove ( & ts) ;
253- Some ( v)
254- }
255- None => None ,
256- }
288+ store_lock. insert ( key, value, highest_lru)
257289 }
258290}
259291
0 commit comments