forked from TraceMachina/nativelink
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add existence cache (TraceMachina#383)
- Loading branch information
1 parent
8a1cb6b
commit e8e6701
Showing
9 changed files
with
299 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright 2022 The Turbo Cache Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use hashbrown::HashSet; | ||
use std::pin::Pin; | ||
use std::sync::{Arc, Mutex}; | ||
|
||
use async_trait::async_trait; | ||
|
||
use buf_channel::{DropCloserReadHalf, DropCloserWriteHalf}; | ||
use common::DigestInfo; | ||
use error::Error; | ||
use traits::{StoreTrait, UploadSizeInfo}; | ||
|
||
pub struct ExistenceStore { | ||
inner_store: Arc<dyn StoreTrait>, | ||
pub existence_cache: Mutex<HashSet<DigestInfo>>, | ||
} | ||
|
||
impl ExistenceStore { | ||
pub fn new(inner_store: Arc<dyn StoreTrait>) -> Self { | ||
Self { | ||
inner_store, | ||
// TODO (BlakeHatch): | ||
// Consider using RwLock in a future commit. | ||
// Since HashSet implements Send and Sync this should | ||
// be a drop-in replacement in theory. | ||
// Make sure benchmark is done to justify. | ||
existence_cache: Mutex::new(HashSet::new()), | ||
} | ||
} | ||
|
||
fn pin_inner(&self) -> Pin<&dyn StoreTrait> { | ||
Pin::new(self.inner_store.as_ref()) | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl StoreTrait for ExistenceStore { | ||
async fn has_with_results( | ||
self: Pin<&Self>, | ||
digests: &[DigestInfo], | ||
results: &mut [Option<usize>], | ||
) -> Result<(), Error> { | ||
let mut pruned_digests = Vec::new(); | ||
|
||
for (i, digest) in digests.iter().enumerate() { | ||
if self.existence_cache.lock().unwrap().contains(digest) { | ||
results[i] = Some(1); | ||
} else { | ||
pruned_digests.push(*digest); | ||
} | ||
} | ||
|
||
if !pruned_digests.is_empty() { | ||
let mut inner_results = vec![None; pruned_digests.len()]; | ||
self.pin_inner() | ||
.has_with_results(&pruned_digests, &mut inner_results) | ||
.await?; | ||
|
||
for (i, result) in inner_results.iter().enumerate() { | ||
if result.is_some() { | ||
self.existence_cache.lock().unwrap().insert(pruned_digests[i]); | ||
results[i] = Some(1); | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn update( | ||
self: Pin<&Self>, | ||
digest: DigestInfo, | ||
reader: DropCloserReadHalf, | ||
size_info: UploadSizeInfo, | ||
) -> Result<(), Error> { | ||
self.pin_inner().update(digest, reader, size_info).await | ||
} | ||
|
||
async fn get_part_ref( | ||
self: Pin<&Self>, | ||
digest: DigestInfo, | ||
writer: &mut DropCloserWriteHalf, | ||
offset: usize, | ||
length: Option<usize>, | ||
) -> Result<(), Error> { | ||
self.pin_inner().get_part_ref(digest, writer, offset, length).await | ||
} | ||
|
||
fn as_any(self: Arc<Self>) -> Box<dyn std::any::Any + Send> { | ||
Box::new(self) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2022 The Turbo Cache Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use std::pin::Pin; | ||
use std::sync::Arc; | ||
|
||
#[cfg(test)] | ||
mod verify_store_tests { | ||
use super::*; | ||
|
||
use common::DigestInfo; | ||
use error::Error; | ||
use existence_store::ExistenceStore; | ||
use memory_store::MemoryStore; | ||
|
||
use traits::StoreTrait; | ||
|
||
const VALID_HASH1: &str = "0123456789abcdef000000000000000000010000000000000123456789abcdef"; | ||
|
||
async fn has_in_cache(store: Pin<&ExistenceStore>, digest: &DigestInfo) -> bool { | ||
match store.existence_cache.lock() { | ||
Ok(cache) => cache.contains(digest), | ||
Err(_) => false, | ||
} | ||
} | ||
|
||
#[tokio::test] | ||
async fn verify_existence_caching() -> Result<(), Error> { | ||
const VALUE: &str = "123"; | ||
let inner_store = Arc::new(MemoryStore::new(&config::stores::MemoryStore::default())); | ||
let store_owned = ExistenceStore::new(inner_store.clone()); | ||
let store = Pin::new(&store_owned); | ||
|
||
let digest = DigestInfo::try_new(VALID_HASH1, 3).unwrap(); | ||
let _result = store.update_oneshot(digest, VALUE.into()).await; | ||
|
||
assert!( | ||
!has_in_cache(store, &digest).await, | ||
"Expected digest to not exist in cache before has call" | ||
); | ||
|
||
let _result = store.has(digest).await; | ||
|
||
assert!( | ||
has_in_cache(store, &digest).await, | ||
"Expected digest to exist in cache after has call" | ||
); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# This file is automatically generated from `tools/build_cargo_manifest.py`. | ||
# If you want to add a dependency add it to `tools/cargo_shared.bzl` | ||
# then run `python tools/build_cargo_manifest.py`. | ||
# Do not edit this file directly. | ||
|
||
[package] | ||
name = "existence_store" | ||
version = "0.0.0" | ||
edition = "2021" | ||
autobins = false | ||
autoexamples = false | ||
autotests = false | ||
autobenches = false | ||
|
||
[lib] | ||
name = "existence_store" | ||
path = "../../cas/store/existence_store.rs" | ||
# TODO(allada) We should support doctests. | ||
doctest = false | ||
|
||
[dependencies] | ||
async-trait = { workspace = true } | ||
hashbrown = { workspace = true } | ||
hex = { workspace = true } | ||
sha2 = { workspace = true } | ||
tokio = { workspace = true } | ||
|
||
# Local libraries. | ||
ac_utils = { workspace = true } | ||
traits = { workspace = true } | ||
config = { workspace = true } | ||
proto = { workspace = true } | ||
buf_channel = { workspace = true } | ||
common = { workspace = true } | ||
error = { workspace = true } | ||
metrics_utils = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# This file is automatically generated from `tools/build_cargo_manifest.py`. | ||
# If you want to add a dependency add it to `tools/cargo_shared.bzl` | ||
# then run `python tools/build_cargo_manifest.py`. | ||
# Do not edit this file directly. | ||
|
||
[package] | ||
name = "existence_store_test" | ||
version = "0.0.0" | ||
edition = "2021" | ||
autobins = false | ||
autoexamples = false | ||
autotests = false | ||
autobenches = false | ||
|
||
[[test]] | ||
name = "existence_store_test" | ||
path = "../../cas/store/tests/existence_store_test.rs" | ||
# TODO(allada) We should support doctests. | ||
doctest = false | ||
|
||
[dependencies] | ||
futures = { workspace = true } | ||
pretty_assertions = { workspace = true } | ||
tokio = { workspace = true } | ||
|
||
# Local libraries. | ||
existence_store = { workspace = true } | ||
memory_store = { workspace = true } | ||
traits = { workspace = true } | ||
config = { workspace = true } | ||
buf_channel = { workspace = true } | ||
common = { workspace = true } | ||
error = { workspace = true } |