mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-21 03:46:37 +00:00
fix(cache): harden object data cache coordination (#5004)
* fix(cache): enforce projected entry capacity Refs: rustfs/backlog#1335 Co-Authored-By: heihutu <heihutu@gmail.com> * fix(cache): fence identity budget eviction by generation Refs rustfs/backlog#1334. Co-Authored-By: heihutu <heihutu@gmail.com> * fix(cache): fence clear against concurrent fills Refs rustfs/backlog#1333 Co-Authored-By: heihutu <heihutu@gmail.com> * fix(cache): linearize memory reservation claims Co-Authored-By: heihutu <heihutu@gmail.com> * fix(cache): retain allocation memory claims Co-Authored-By: heihutu <heihutu@gmail.com> * fix(cache): publish memory snapshots by epoch Co-Authored-By: heihutu <heihutu@gmail.com> * fix(cache): coordinate cold object fills Co-Authored-By: heihutu <heihutu@gmail.com> * fix(ecstore): fence metadata cache transition races Co-Authored-By: heihutu <heihutu@gmail.com> --------- Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
@@ -12,7 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::index::{ObjectDataCacheIndexInsertResult, ObjectDataCacheKeySet, ObjectDataCacheKeyToken};
|
||||
use crate::index::{
|
||||
ObjectDataCacheGeneration, ObjectDataCacheGenerationalInsertResult, ObjectDataCacheIndexInsertResult, ObjectDataCacheKeySet,
|
||||
ObjectDataCacheKeyToken,
|
||||
};
|
||||
use crate::key::{ObjectDataCacheIdentity, ObjectDataCacheKey};
|
||||
use starshard::{AsyncShardedHashMap, DEFAULT_SHARDS, SnapshotMode};
|
||||
use std::collections::hash_map::RandomState;
|
||||
@@ -56,6 +59,21 @@ impl StarshardIdentityIndex {
|
||||
key: ObjectDataCacheKey,
|
||||
token: ObjectDataCacheKeyToken,
|
||||
) -> ObjectDataCacheIndexInsertResult {
|
||||
let generation = u64::try_from(token).unwrap_or(u64::MAX);
|
||||
match self.insert_generation(identity, key, generation).await {
|
||||
ObjectDataCacheGenerationalInsertResult::Inserted { evicted } => ObjectDataCacheIndexInsertResult::Inserted {
|
||||
evicted_keys: evicted.into_iter().map(|entry| entry.key).collect(),
|
||||
},
|
||||
ObjectDataCacheGenerationalInsertResult::Duplicate => ObjectDataCacheIndexInsertResult::Duplicate,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn insert_generation(
|
||||
&self,
|
||||
identity: ObjectDataCacheIdentity,
|
||||
key: ObjectDataCacheKey,
|
||||
generation: ObjectDataCacheGeneration,
|
||||
) -> ObjectDataCacheGenerationalInsertResult {
|
||||
let max_keys = self.max_keys_per_identity;
|
||||
loop {
|
||||
let mut outcome = None;
|
||||
@@ -65,7 +83,7 @@ impl StarshardIdentityIndex {
|
||||
let _ = self
|
||||
.by_object
|
||||
.compute_if_present(&identity, move |mut key_set| {
|
||||
let result = key_set.insert(key, token, max_keys);
|
||||
let result = key_set.insert_generation(key, generation, max_keys);
|
||||
// Insert never empties the set (it either dedups or
|
||||
// bounded-evicts and adds the new key), but keep the
|
||||
// guard so an already-empty entry is not republished.
|
||||
@@ -80,7 +98,7 @@ impl StarshardIdentityIndex {
|
||||
|
||||
// Identity not tracked yet: publish a fresh single-key set.
|
||||
let mut fresh = ObjectDataCacheKeySet::default();
|
||||
let result = fresh.insert(key.clone(), token, max_keys);
|
||||
let result = fresh.insert_generation(key.clone(), generation, max_keys);
|
||||
let final_set = self.by_object.compute_if_absent(identity.clone(), move || fresh).await;
|
||||
if final_set.contains(&key) {
|
||||
return result;
|
||||
@@ -146,6 +164,16 @@ impl StarshardIdentityIndex {
|
||||
identity: &ObjectDataCacheIdentity,
|
||||
key: &ObjectDataCacheKey,
|
||||
token: ObjectDataCacheKeyToken,
|
||||
) -> bool {
|
||||
self.remove_generation(identity, key, u64::try_from(token).unwrap_or(u64::MAX))
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn remove_generation(
|
||||
&self,
|
||||
identity: &ObjectDataCacheIdentity,
|
||||
key: &ObjectDataCacheKey,
|
||||
generation: ObjectDataCacheGeneration,
|
||||
) -> bool {
|
||||
let mut removed = false;
|
||||
{
|
||||
@@ -153,7 +181,7 @@ impl StarshardIdentityIndex {
|
||||
let _ = self
|
||||
.by_object
|
||||
.compute_if_present(identity, move |mut key_set| {
|
||||
*removed = key_set.remove_evicted_key(key, token);
|
||||
*removed = key_set.remove_generation(key, generation);
|
||||
(!key_set.is_empty()).then_some(key_set)
|
||||
})
|
||||
.await;
|
||||
@@ -169,6 +197,18 @@ impl StarshardIdentityIndex {
|
||||
.is_some_and(|key_set| key_set.contains(key))
|
||||
}
|
||||
|
||||
pub(crate) async fn contains_generation(
|
||||
&self,
|
||||
identity: &ObjectDataCacheIdentity,
|
||||
key: &ObjectDataCacheKey,
|
||||
generation: ObjectDataCacheGeneration,
|
||||
) -> bool {
|
||||
self.by_object
|
||||
.get(identity)
|
||||
.await
|
||||
.is_some_and(|key_set| key_set.contains_generation(key, generation))
|
||||
}
|
||||
|
||||
/// Returns the number of tracked identities.
|
||||
#[cfg(test)]
|
||||
pub(crate) async fn identity_count(&self) -> usize {
|
||||
|
||||
Reference in New Issue
Block a user