Skip to content

Commit 6f8db12

Browse files
authored
[ENH] A tool to purge the cache. (#5085)
## Description of changes In anticipation of turning on the disk cache for wal3, we have a tool that allows us to purge a single entry from the cache. ## Test plan It seems to work from tilt. ## Documentation Changes N/A
1 parent a6829a0 commit 6f8db12

4 files changed

Lines changed: 149 additions & 10 deletions

File tree

go/pkg/log/server/server.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ func (s *logServer) GarbageCollectPhase2(ctx context.Context, req *logservicepb.
187187
return
188188
}
189189

190+
func (s *logServer) PurgeFromCache(ctx context.Context, req *logservicepb.PurgeFromCacheRequest) (res *logservicepb.PurgeFromCacheResponse, err error) {
191+
return
192+
}
193+
190194
func NewLogServer(lr *repository.LogRepository) logservicepb.LogServiceServer {
191195
return &logServer{
192196
lr: lr,

idl/chromadb/proto/logservice.proto

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ message GarbageCollectPhase2Request {
154154
message GarbageCollectPhase2Response {
155155
}
156156

157+
message PurgeFromCacheRequest {
158+
oneof entry_to_evict {
159+
string cursor_for_collection_id = 1;
160+
string manifest_for_collection_id = 2;
161+
FragmentToEvict fragment = 3;
162+
};
163+
}
164+
165+
message FragmentToEvict {
166+
string collection_id = 1;
167+
string fragment_path = 2;
168+
};
169+
170+
message PurgeFromCacheResponse {
171+
}
172+
157173
service LogService {
158174
rpc PushLogs(PushLogsRequest) returns (PushLogsResponse) {}
159175
rpc ScoutLogs(ScoutLogsRequest) returns (ScoutLogsResponse) {}
@@ -162,17 +178,19 @@ service LogService {
162178
rpc GetAllCollectionInfoToCompact(GetAllCollectionInfoToCompactRequest) returns (GetAllCollectionInfoToCompactResponse) {}
163179
rpc UpdateCollectionLogOffset(UpdateCollectionLogOffsetRequest) returns (UpdateCollectionLogOffsetResponse) {}
164180
rpc PurgeDirtyForCollection(PurgeDirtyForCollectionRequest) returns (PurgeDirtyForCollectionResponse) {}
165-
// RPC endpoints to expose for operator debuggability.
166181
// This endpoint must route to the rust log service.
167182
rpc InspectDirtyLog(InspectDirtyLogRequest) returns (InspectDirtyLogResponse) {}
168183
// This endpoint must route to the go log service.
169184
rpc SealLog(SealLogRequest) returns (SealLogResponse) {}
170185
// This endpoint must route to the rust log service.
171186
rpc MigrateLog(MigrateLogRequest) returns (MigrateLogResponse) {}
187+
// RPC endpoints to expose for operator debuggability.
172188
// This endpoint can be supported by any log service.
173189
rpc InspectLogState(InspectLogStateRequest) returns (InspectLogStateResponse) {}
174190
// This endpoint should route to the rust log service.
175191
rpc ScrubLog(ScrubLogRequest) returns (ScrubLogResponse) {}
176192
// This endpoint should route to the rust log service.
177193
rpc GarbageCollectPhase2(GarbageCollectPhase2Request) returns (GarbageCollectPhase2Response) {}
194+
// This endpoint will purge from cache the specified items.
195+
rpc PurgeFromCache(PurgeFromCacheRequest) returns (PurgeFromCacheResponse) {}
178196
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use tonic::transport::Channel;
2+
3+
use chroma_types::chroma_proto::log_service_client::LogServiceClient;
4+
use chroma_types::chroma_proto::{
5+
purge_from_cache_request::EntryToEvict, FragmentToEvict, PurgeFromCacheRequest,
6+
};
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let args = std::env::args().skip(1).collect::<Vec<_>>();
11+
if args.len() != 3 && args.len() != 4 {
12+
eprintln!(
13+
"USAGE: chroma-log-service-purge-cache-entry HOST TYPE COLLECTION_UUID [FRAGMENT_PATH]"
14+
);
15+
std::process::exit(13);
16+
}
17+
let logservice = Channel::from_shared(args[0].clone())
18+
.expect("could not create channel")
19+
.connect()
20+
.await
21+
.expect("could not connect to log service");
22+
let req = match args[1].as_str() {
23+
"cursor" => {
24+
if args.len() != 3 {
25+
eprintln!("purge cache entry takes no fragment path");
26+
std::process::exit(13);
27+
}
28+
PurgeFromCacheRequest {
29+
entry_to_evict: Some(EntryToEvict::CursorForCollectionId(args[2].clone())),
30+
}
31+
}
32+
"manifest" => {
33+
if args.len() != 3 {
34+
eprintln!("purge cache entry takes no fragment path");
35+
std::process::exit(13);
36+
}
37+
PurgeFromCacheRequest {
38+
entry_to_evict: Some(EntryToEvict::ManifestForCollectionId(args[2].clone())),
39+
}
40+
}
41+
"fragment" => {
42+
if args.len() != 4 {
43+
eprintln!("purge cache entry takes a fragment path");
44+
std::process::exit(13);
45+
}
46+
PurgeFromCacheRequest {
47+
entry_to_evict: Some(EntryToEvict::Fragment(FragmentToEvict {
48+
collection_id: args[2].clone(),
49+
fragment_path: args[3].clone(),
50+
})),
51+
}
52+
}
53+
_ => {
54+
eprintln!("unknown type: {}", args[1]);
55+
std::process::exit(13);
56+
}
57+
};
58+
let mut client = LogServiceClient::new(logservice);
59+
let _state = client
60+
.purge_from_cache(req)
61+
.await
62+
.expect("could not purge from cache");
63+
}

rust/log-service/src/lib.rs

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ use chroma_storage::Storage;
1818
use chroma_tracing::util::wrap_span_with_parent_context;
1919
use chroma_types::chroma_proto::{
2020
garbage_collect_phase2_request::LogToCollect, log_service_client::LogServiceClient,
21-
log_service_server::LogService, scrub_log_request::LogToScrub, CollectionInfo,
22-
GarbageCollectPhase2Request, GarbageCollectPhase2Response,
23-
GetAllCollectionInfoToCompactRequest, GetAllCollectionInfoToCompactResponse,
24-
InspectDirtyLogRequest, InspectDirtyLogResponse, InspectLogStateRequest,
25-
InspectLogStateResponse, LogRecord, MigrateLogRequest, MigrateLogResponse, OperationRecord,
26-
PullLogsRequest, PullLogsResponse, PurgeDirtyForCollectionRequest,
27-
PurgeDirtyForCollectionResponse, PushLogsRequest, PushLogsResponse, ScoutLogsRequest,
28-
ScoutLogsResponse, ScrubLogRequest, ScrubLogResponse, SealLogRequest, SealLogResponse,
21+
log_service_server::LogService, purge_from_cache_request::EntryToEvict,
22+
scrub_log_request::LogToScrub, CollectionInfo, GarbageCollectPhase2Request,
23+
GarbageCollectPhase2Response, GetAllCollectionInfoToCompactRequest,
24+
GetAllCollectionInfoToCompactResponse, InspectDirtyLogRequest, InspectDirtyLogResponse,
25+
InspectLogStateRequest, InspectLogStateResponse, LogRecord, MigrateLogRequest,
26+
MigrateLogResponse, OperationRecord, PullLogsRequest, PullLogsResponse,
27+
PurgeDirtyForCollectionRequest, PurgeDirtyForCollectionResponse, PurgeFromCacheRequest,
28+
PurgeFromCacheResponse, PushLogsRequest, PushLogsResponse, ScoutLogsRequest, ScoutLogsResponse,
29+
ScrubLogRequest, ScrubLogResponse, SealLogRequest, SealLogResponse,
2930
UpdateCollectionLogOffsetRequest, UpdateCollectionLogOffsetResponse,
3031
};
3132
use chroma_types::chroma_proto::{ForkLogsRequest, ForkLogsResponse};
@@ -293,6 +294,10 @@ fn cache_key_for_cursor(collection_id: CollectionUuid, name: &CursorName) -> Str
293294
format!("{collection_id}::cursor::{}", name.path())
294295
}
295296

297+
fn cache_key_for_fragment(collection_id: CollectionUuid, fragment_path: &str) -> String {
298+
format!("{collection_id}::{}", fragment_path)
299+
}
300+
296301
////////////////////////////////////////// CachedFragment //////////////////////////////////////////
297302

298303
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
@@ -1473,7 +1478,7 @@ impl LogServer {
14731478
.map(|fragment| async {
14741479
let prefix = collection_id.storage_prefix_for_log();
14751480
if let Some(cache) = self.cache.as_ref() {
1476-
let cache_key = format!("{collection_id}::{}", fragment.path);
1481+
let cache_key = cache_key_for_fragment(collection_id, &fragment.path);
14771482
if let Ok(Some(answer)) = cache.get(&cache_key).await {
14781483
return Ok(Arc::new(answer.bytes));
14791484
}
@@ -2007,6 +2012,48 @@ impl LogServer {
20072012
.instrument(span)
20082013
.await
20092014
}
2015+
2016+
async fn purge_from_cache(
2017+
&self,
2018+
request: Request<PurgeFromCacheRequest>,
2019+
) -> Result<Response<PurgeFromCacheResponse>, Status> {
2020+
let span = wrap_span_with_parent_context(
2021+
tracing::trace_span!("PurgeFromCache",),
2022+
request.metadata(),
2023+
);
2024+
let purge = request.into_inner();
2025+
async move {
2026+
let key = match purge.entry_to_evict {
2027+
Some(EntryToEvict::CursorForCollectionId(x)) => {
2028+
let collection_id = Uuid::parse_str(&x)
2029+
.map(CollectionUuid)
2030+
.map_err(|_| Status::invalid_argument("Failed to parse collection id"))?;
2031+
Some(cache_key_for_cursor(collection_id, &COMPACTION))
2032+
}
2033+
Some(EntryToEvict::ManifestForCollectionId(x)) => {
2034+
let collection_id = Uuid::parse_str(&x)
2035+
.map(CollectionUuid)
2036+
.map_err(|_| Status::invalid_argument("Failed to parse collection id"))?;
2037+
Some(cache_key_for_manifest(collection_id))
2038+
}
2039+
Some(EntryToEvict::Fragment(f)) => {
2040+
let collection_id = Uuid::parse_str(&f.collection_id)
2041+
.map(CollectionUuid)
2042+
.map_err(|_| Status::invalid_argument("Failed to parse collection id"))?;
2043+
Some(cache_key_for_fragment(collection_id, &f.fragment_path))
2044+
}
2045+
None => None,
2046+
};
2047+
if let Some(key) = key {
2048+
if let Some(cache) = self.cache.as_ref() {
2049+
cache.remove(&key).await;
2050+
}
2051+
}
2052+
Ok(Response::new(PurgeFromCacheResponse {}))
2053+
}
2054+
.instrument(span)
2055+
.await
2056+
}
20102057
}
20112058

20122059
struct LogServerWrapper {
@@ -2107,6 +2154,13 @@ impl LogService for LogServerWrapper {
21072154
) -> Result<Response<GarbageCollectPhase2Response>, Status> {
21082155
self.log_server.garbage_collect_phase2(request).await
21092156
}
2157+
2158+
async fn purge_from_cache(
2159+
&self,
2160+
request: Request<PurgeFromCacheRequest>,
2161+
) -> Result<Response<PurgeFromCacheResponse>, Status> {
2162+
self.log_server.purge_from_cache(request).await
2163+
}
21102164
}
21112165

21122166
fn parquet_to_records(parquet: Arc<Vec<u8>>) -> Result<Vec<(LogPosition, Vec<u8>)>, Status> {

0 commit comments

Comments
 (0)