Skip to content

Commit

Permalink
fifo
Browse files Browse the repository at this point in the history
  • Loading branch information
just-in-chang committed Mar 8, 2024
1 parent 59afbd3 commit a6adf78
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ members = [
"crates/aptos-genesis",
"crates/aptos-github-client",
"crates/aptos-id-generator",
"crates/aptos-in-memory-cache",
"crates/aptos-infallible",
"crates/aptos-inspection-service",
"crates/aptos-jwk-consensus",
Expand Down Expand Up @@ -339,6 +340,7 @@ aptos-genesis = { path = "crates/aptos-genesis" }
aptos-github-client = { path = "crates/aptos-github-client" }
aptos-global-constants = { path = "config/global-constants" }
aptos-id-generator = { path = "crates/aptos-id-generator" }
aptos-in-memory-cache = { path = "crates/aptos-in-memory-cache" }
aptos-indexer = { path = "crates/indexer" }
aptos-indexer-grpc-cache-worker = { path = "ecosystem/indexer-grpc/indexer-grpc-cache-worker" }
aptos-indexer-grpc-data-service = { path = "ecosystem/indexer-grpc/indexer-grpc-data-service" }
Expand Down Expand Up @@ -624,6 +626,7 @@ proptest-derive = "0.4.0"
prost = { version = "0.12.3", features = ["no-recursion-limit"] }
prost-types = "0.12.3"
quanta = "0.10.1"
quick_cache = "0.4.1"
quote = "1.0.18"
rand = "0.7.3"
rand_core = "0.5.1"
Expand Down
19 changes: 19 additions & 0 deletions crates/aptos-in-memory-cache/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "aptos-in-memory-cache"
description = "In-Memory cache"
version = "0.1.0"

# Workspace inherited keys
authors = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
publish = { workspace = true }
repository = { workspace = true }
rust-version = { workspace = true }

[dependencies]
dashmap = { workspace = true }
quick_cache = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true }
116 changes: 116 additions & 0 deletions crates/aptos-in-memory-cache/src/caches/fifo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use crate::{Cache, Incrementable, Ordered, Weighted};
use dashmap::DashMap;
use std::hash::Hash;

#[derive(Debug, Clone, Copy)]
struct CacheMetadata<K>
where
K: Hash + Eq + PartialEq + Incrementable + Send + Sync + Clone,
{
max_size_in_bytes: u64,
total_size_in_bytes: u64,
last_key: Option<K>,
first_key: Option<K>,
}

/// FIFO is a simple in-memory cache with a deterministic FIFO eviction policy.
pub struct FIFOCache<K, V>
where
K: Hash + Eq + PartialEq + Incrementable + Send + Sync + Clone,
V: Weighted + Send + Sync + Clone,
{
/// Cache maps the cache key to the deserialized Transaction.
items: DashMap<K, V>,
cache_metadata: CacheMetadata<K>,
}

impl<K, V> FIFOCache<K, V>
where
K: Hash + Eq + PartialEq + Incrementable + Send + Sync + Clone,
V: Weighted + Send + Sync + Clone,
{
pub fn new(max_size_in_bytes: u64) -> Self {
FIFOCache {
items: DashMap::new(),
cache_metadata: CacheMetadata {
max_size_in_bytes,
total_size_in_bytes: 0,
last_key: None,
first_key: None,
},
}
}

fn pop(&mut self) -> Option<u64> {
if let Some(first_key) = self.cache_metadata.first_key.clone() {
let next_key = first_key.next();
return self.items.remove(&first_key).map(|(_, v)| {
let weight = v.weight();
self.cache_metadata.first_key = Some(next_key);
self.cache_metadata.total_size_in_bytes -= weight;
weight
});
}
None
}

fn evict(&mut self, new_value_weight: u64) -> (u64, u64) {
let mut garbage_collection_count = 0;
let mut garbage_collection_size = 0;
while self.cache_metadata.total_size_in_bytes + new_value_weight
> self.cache_metadata.max_size_in_bytes
{
if let Some(weight) = self.pop() {
garbage_collection_count += 1;
garbage_collection_size += weight;
}
}
(garbage_collection_count, garbage_collection_size)
}

fn insert_impl(&mut self, key: K, value: V) {
self.cache_metadata.last_key = Some(key.clone());
self.cache_metadata.total_size_in_bytes += value.weight();
self.items.insert(key, value);
}
}

impl<K, V> Cache<K, V> for FIFOCache<K, V>
where
K: Hash + Eq + PartialEq + Incrementable + Send + Sync + Clone,
V: Weighted + Send + Sync + Clone,
{
fn get(&self, key: &K) -> Option<V> {
self.items.get(key).map(|v| v.value().clone())
}

fn insert(&mut self, key: K, value: V) -> (u64, u64) {
// If cache is empty, set the first to the new key.
if self.items.is_empty() {
self.cache_metadata.first_key = Some(key.clone());
}

// Evict until enough space is available for next value.
let (garbage_collection_count, garbage_collection_size) = self.evict(value.weight());
self.insert_impl(key, value);

return (garbage_collection_count, garbage_collection_size);
}
}

impl<K, V> Ordered<K, V> for FIFOCache<K, V>
where
K: Hash + Eq + PartialEq + Incrementable + Send + Sync + Clone,
V: Weighted + Send + Sync + Clone,
{
fn first_key(&self) -> Option<K> {
self.cache_metadata.first_key.clone()
}

fn last_key(&self) -> Option<K> {
self.cache_metadata.last_key.clone()
}
}
5 changes: 5 additions & 0 deletions crates/aptos-in-memory-cache/src/caches/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

pub mod fifo;
pub mod s3fifo;
59 changes: 59 additions & 0 deletions crates/aptos-in-memory-cache/src/caches/s3fifo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use crate::{Cache, Ordered};
use quick_cache::{sync::Cache as S3FIFOCache, Lifecycle, Weighter};
use std::hash::{BuildHasher, Hash};

impl<K, V, We, B, L> Cache<K, V> for S3FIFOCache<K, V, We, B, L>
where
K: Eq + Hash + Clone + Send + Sync,
V: Clone + Send + Sync,
We: Weighter<K, V> + Clone + Send + Sync,
B: BuildHasher + Clone + Send + Sync,
L: Lifecycle<K, V> + Clone + Send + Sync,
{
fn get(&self, key: &K) -> Option<V> {
S3FIFOCache::get(self, key)
}

fn insert(&mut self, key: K, value: V) -> (u64, u64) {
S3FIFOCache::insert(self, key, value);
(0, 0)
}
}

impl<K, V, We, B, L> Ordered<K, V> for S3FIFOCache<K, V, We, B, L>
where
K: Eq + Hash + Clone + Send + Sync,
V: Clone + Send + Sync,
We: Weighter<K, V> + Clone + Send + Sync,
B: BuildHasher + Clone + Send + Sync,
L: Lifecycle<K, V> + Clone + Send + Sync,
{
fn first_key(&self) -> Option<K> {
unimplemented!();
}

fn last_key(&self) -> Option<K> {
unimplemented!();
}
}

#[cfg(test)]
mod tests {
use super::*;
use quick_cache::sync::Cache as S3FIFOCache;

fn get_s3fifo_cache() -> S3FIFOCache<i32, i32> {
S3FIFOCache::<i32, i32>::new(10)
}

#[test]
fn test_s3fifo_cache() {
let mut cache: Box<dyn Cache<i32, i32>> = Box::new(get_s3fifo_cache());
cache.insert(1, 1);
assert_eq!(cache.get(&1), Some(1));
assert_eq!(cache.get(&2), None);
}
}
37 changes: 37 additions & 0 deletions crates/aptos-in-memory-cache/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use std::hash::Hash;

pub mod caches;

/// A trait for a cache that can be used to store key-value pairs.
pub trait Cache<K, V>: Send + Sync
where
K: Eq + Hash + Clone + Send + Sync,
V: Clone + Send + Sync,
{
/// Get the value for a given key. Return [`None`] if the key is not in the cache.
fn get(&self, key: &K) -> Option<V>;

/// Inserts a given key-value pair in cache. Panics if the insert fails.
fn insert(&mut self, key: K, value: V) -> (u64, u64);
}

pub trait Ordered<K, V>: Send + Sync {
/// Returns the first key in the cache. Returns [`None`] if the cache is empty.
fn first_key(&self) -> Option<K>;

/// Returns the last key in the cache. Returns [`None`] if the cache is empty.
fn last_key(&self) -> Option<K>;
}

pub trait Weighted {
/// Returns the size of the value in bytes.
fn weight(&self) -> u64;
}

pub trait Incrementable {
/// Increments the value.
fn next(&self) -> Self;
}

0 comments on commit a6adf78

Please sign in to comment.