Skip to main content

shardline_server/local_backend/
xorbs.rs

1use axum::body::Bytes;
2use shardline_protocol::{ByteRange, RepositoryScope};
3use shardline_storage::ObjectStore;
4
5use super::LocalBackend;
6use crate::{
7    ServerError,
8    download_stream::{ServerByteStream, object_byte_range_stream, object_byte_stream},
9    upload_ingest::RequestBodyReader,
10    xet_adapter::{
11        XorbUploadResponse, resolve_dedupe_shard_object, store_uploaded_xorb_bytes, xorb_object_key,
12    },
13};
14
15impl LocalBackend {
16    /// Stores a raw xorb body under its content hash.
17    ///
18    /// # Errors
19    ///
20    /// Returns [`ServerError`] when the supplied hash is invalid, the body hash does not
21    /// match, or persistence fails.
22    pub async fn upload_xorb(
23        &self,
24        expected_hash: &str,
25        body: Bytes,
26    ) -> Result<XorbUploadResponse, ServerError> {
27        self.upload_xorb_stream(expected_hash, RequestBodyReader::from_bytes(body))
28            .await
29    }
30
31    /// Stores a bounded raw xorb body under its content hash.
32    ///
33    /// # Errors
34    ///
35    /// Returns [`ServerError`] when request streaming, hash validation, or persistence
36    /// fails.
37    pub(crate) async fn upload_xorb_stream(
38        &self,
39        expected_hash: &str,
40        mut body: RequestBodyReader,
41    ) -> Result<XorbUploadResponse, ServerError> {
42        let uploaded_body = crate::upload_ingest::read_body_to_bytes(&mut body).await?;
43        let object_store = self.object_store();
44        store_uploaded_xorb_bytes(&object_store, expected_hash, &uploaded_body)
45            .map_err(ServerError::from)
46    }
47
48    pub(crate) async fn read_dedupe_shard_stream(
49        &self,
50        hash_hex: &str,
51    ) -> Result<(ServerByteStream, u64), ServerError> {
52        let object_store = self.object_store();
53        let (object_key, total_length) =
54            resolve_dedupe_shard_object(&self.index_store, &object_store, hash_hex).await?;
55        let byte_stream = object_byte_stream(object_store, object_key, total_length).await?;
56
57        Ok((byte_stream, total_length))
58    }
59
60    pub(crate) async fn dedupe_shard_length(&self, hash_hex: &str) -> Result<u64, ServerError> {
61        let object_store = self.object_store();
62        let (_object_key, total_length) =
63            resolve_dedupe_shard_object(&self.index_store, &object_store, hash_hex).await?;
64
65        Ok(total_length)
66    }
67
68    /// Streams a stored xorb byte range by hash.
69    ///
70    /// # Errors
71    ///
72    /// Returns [`ServerError`] when the hash is invalid, the xorb is missing, or the
73    /// requested byte range cannot be served.
74    pub(crate) async fn read_xorb_range_stream(
75        &self,
76        hash_hex: &str,
77        total_length: u64,
78        range: ByteRange,
79    ) -> Result<ServerByteStream, ServerError> {
80        let object_store = self.object_store();
81        let object_key = xorb_object_key(hash_hex)?;
82
83        object_byte_range_stream(object_store, object_key, total_length, range).await
84    }
85
86    /// Loads the stored byte length for a serialized xorb object.
87    ///
88    /// # Errors
89    ///
90    /// Returns [`ServerError`] when the hash is invalid or the xorb is missing.
91    pub async fn xorb_length(&self, hash_hex: &str) -> Result<u64, ServerError> {
92        let object_store = self.object_store();
93        let object_key = xorb_object_key(hash_hex)?;
94        let metadata = object_store.metadata(&object_key)?;
95        let Some(metadata) = metadata else {
96            return Err(ServerError::NotFound);
97        };
98
99        Ok(metadata.length())
100    }
101
102    pub(crate) async fn repository_references_xorb(
103        &self,
104        hash_hex: &str,
105        repository_scope: &RepositoryScope,
106    ) -> Result<bool, ServerError> {
107        super::records::repository_references_xorb(&self.record_store, hash_hex, repository_scope)
108            .await
109    }
110}