refactor(tvix/store): remove ChunkService

Whether chunking is involved or not, is an implementation detail of each
Blobstore. Consumers of a whole blob shouldn't need to worry about that.
It currently is not visible in the gRPC interface either. It
shouldn't bleed into everything.

Let the BlobService trait provide `open_read` and `open_write` methods,
which return handles providing io::Read or io::Write, and leave the
details up to the implementation.

This means, our custom BlobReader module can go away, and all the
chunking bits in there, too.

In the future, we might still want to add more chunking-aware syncing,
but as a syncing strategy some stores can expose, not as a fundamental
protocol component.

This currently needs "SyncReadIntoAsyncRead", taken and vendored in from
https://github.com/tokio-rs/tokio/pull/5669.
It provides a AsyncRead for a sync Read, which is necessary to connect
our (sync) BlobReader interface to a GRPC server implementation.

As an alternative, we could also make the BlobReader itself async, and
let consumers of the trait (EvalIO) deal with the async-ness, but this
is less of a change for now.

In terms of vendoring, I initially tried to move our tokio crate to
these commits, but ended up in version incompatibilities, so let's
vendor it in for now.

Change-Id: I5969ebbc4c0e1ceece47981be3b9e7cfb3f59ad0
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8551
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
Florian Klink 2023-05-11 15:49:01 +03:00 committed by flokli
parent b22b685f0b
commit 616fa4476f
26 changed files with 560 additions and 1297 deletions

View file

@ -2,7 +2,6 @@ use count_write::CountWrite;
use sha2::{Digest, Sha256};
use crate::blobservice::BlobService;
use crate::chunkservice::ChunkService;
use crate::directoryservice::DirectoryService;
use crate::proto;
@ -12,26 +11,20 @@ use super::{NARCalculationService, RenderError};
/// A NAR calculation service which simply renders the whole NAR whenever
/// we ask for the calculation.
#[derive(Clone)]
pub struct NonCachingNARCalculationService<
BS: BlobService,
CS: ChunkService + Clone,
DS: DirectoryService,
> {
nar_renderer: NARRenderer<BS, CS, DS>,
pub struct NonCachingNARCalculationService<BS: BlobService, DS: DirectoryService> {
nar_renderer: NARRenderer<BS, DS>,
}
impl<BS: BlobService, CS: ChunkService + Clone, DS: DirectoryService>
NonCachingNARCalculationService<BS, CS, DS>
{
pub fn new(blob_service: BS, chunk_service: CS, directory_service: DS) -> Self {
impl<BS: BlobService, DS: DirectoryService> NonCachingNARCalculationService<BS, DS> {
pub fn new(blob_service: BS, directory_service: DS) -> Self {
Self {
nar_renderer: NARRenderer::new(blob_service, chunk_service, directory_service),
nar_renderer: NARRenderer::new(blob_service, directory_service),
}
}
}
impl<BS: BlobService, CS: ChunkService + Clone, DS: DirectoryService> NARCalculationService
for NonCachingNARCalculationService<BS, CS, DS>
impl<BS: BlobService, DS: DirectoryService> NARCalculationService
for NonCachingNARCalculationService<BS, DS>
{
fn calculate_nar(
&self,

View file

@ -1,10 +1,11 @@
use std::io::{self, BufReader};
use crate::{
blobservice::BlobService,
chunkservice::ChunkService,
directoryservice::DirectoryService,
proto::{self, NamedNode},
BlobReader,
};
use data_encoding::BASE64;
use nix_compat::nar;
use super::RenderError;
@ -12,17 +13,15 @@ use super::RenderError;
/// A NAR renderer, using a blob_service, chunk_service and directory_service
/// to render a NAR to a writer.
#[derive(Clone)]
pub struct NARRenderer<BS: BlobService, CS: ChunkService + Clone, DS: DirectoryService> {
pub struct NARRenderer<BS: BlobService, DS: DirectoryService> {
blob_service: BS,
chunk_service: CS,
directory_service: DS,
}
impl<BS: BlobService, CS: ChunkService + Clone, DS: DirectoryService> NARRenderer<BS, CS, DS> {
pub fn new(blob_service: BS, chunk_service: CS, directory_service: DS) -> Self {
impl<BS: BlobService, DS: DirectoryService> NARRenderer<BS, DS> {
pub fn new(blob_service: BS, directory_service: DS) -> Self {
Self {
blob_service,
chunk_service,
directory_service,
}
}
@ -65,49 +64,22 @@ impl<BS: BlobService, CS: ChunkService + Clone, DS: DirectoryService> NARRendere
))
})?;
// query blob_service for blob_meta
let resp = self
.blob_service
.stat(&proto::StatBlobRequest {
digest: digest.to_vec(),
include_chunks: true,
..Default::default()
})
.map_err(RenderError::StoreError)?;
// TODO: handle error
let mut blob_reader = match self.blob_service.open_read(&digest).unwrap() {
Some(blob_reader) => Ok(BufReader::new(blob_reader)),
None => Err(RenderError::NARWriterError(io::Error::new(
io::ErrorKind::NotFound,
format!("blob with digest {} not found", BASE64.encode(&digest)),
))),
}?;
match resp {
// if it's None, that's an error!
None => {
return Err(RenderError::BlobNotFound(
digest.to_vec(),
proto_file_node.name.to_owned(),
));
}
Some(blob_meta) => {
// make sure the blob_meta size matches what we expect from proto_file_node
let blob_meta_size = blob_meta.chunks.iter().fold(0, |acc, e| acc + e.size);
if blob_meta_size != proto_file_node.size {
return Err(RenderError::UnexpectedBlobMeta(
digest.to_vec(),
proto_file_node.name.to_owned(),
proto_file_node.size,
blob_meta_size,
));
}
let mut blob_reader = std::io::BufReader::new(BlobReader::open(
&self.chunk_service,
blob_meta,
));
nar_node
.file(
proto_file_node.executable,
proto_file_node.size.into(),
&mut blob_reader,
)
.map_err(RenderError::NARWriterError)?;
}
}
nar_node
.file(
proto_file_node.executable,
proto_file_node.size.into(),
&mut blob_reader,
)
.map_err(RenderError::NARWriterError)?;
}
proto::node::Node::Directory(proto_directory_node) => {
let digest: [u8; 32] =