20260418 优化去掉冗余存储repo object,mix quick最快170s
This commit is contained in:
@@ -16,6 +16,26 @@ fn raw_blob_key(sha256_hex: &str) -> String {
|
||||
format!("{RAW_BLOB_KEY_PREFIX}{sha256_hex}")
|
||||
}
|
||||
|
||||
fn validate_blob_sha256_hex(sha256_hex: &str) -> StorageResult<()> {
|
||||
if sha256_hex.len() != 64 || !sha256_hex.as_bytes().iter().all(u8::is_ascii_hexdigit) {
|
||||
return Err(StorageError::InvalidData {
|
||||
entity: "raw_blob",
|
||||
detail: format!("invalid sha256 hex: {sha256_hex}"),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_blob_bytes(bytes: &[u8]) -> StorageResult<()> {
|
||||
if bytes.is_empty() {
|
||||
return Err(StorageError::InvalidData {
|
||||
entity: "raw_blob",
|
||||
detail: "bytes must not be empty".to_string(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait RawObjectStore {
|
||||
fn get_raw_entry(&self, sha256_hex: &str) -> StorageResult<Option<RawByHashEntry>>;
|
||||
|
||||
@@ -103,6 +123,23 @@ impl ExternalRawStoreDb {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn put_blob_bytes_batch(&self, blobs: &[(String, Vec<u8>)]) -> StorageResult<()> {
|
||||
if blobs.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut batch = WriteBatch::default();
|
||||
for (sha256_hex, bytes) in blobs {
|
||||
validate_blob_sha256_hex(sha256_hex)?;
|
||||
validate_blob_bytes(bytes)?;
|
||||
let blob_key = raw_blob_key(sha256_hex);
|
||||
batch.put(blob_key.as_bytes(), bytes.as_slice());
|
||||
}
|
||||
self.db
|
||||
.write(batch)
|
||||
.map_err(|e| StorageError::RocksDb(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_raw_entry(&self, sha256_hex: &str) -> StorageResult<()> {
|
||||
let key = raw_by_hash_key(sha256_hex);
|
||||
let blob_key = raw_blob_key(sha256_hex);
|
||||
@@ -134,23 +171,11 @@ impl RawObjectStore for RocksStore {
|
||||
}
|
||||
|
||||
fn get_blob_bytes(&self, sha256_hex: &str) -> StorageResult<Option<Vec<u8>>> {
|
||||
if let Some(raw_store) = self.external_raw_store_ref() {
|
||||
return raw_store.get_blob_bytes(sha256_hex);
|
||||
}
|
||||
self.get_raw_entry(sha256_hex)
|
||||
.map(|entry| entry.map(|entry| entry.bytes))
|
||||
RocksStore::get_blob_bytes(self, sha256_hex)
|
||||
}
|
||||
|
||||
fn get_blob_bytes_batch(&self, sha256_hexes: &[String]) -> StorageResult<Vec<Option<Vec<u8>>>> {
|
||||
if let Some(raw_store) = self.external_raw_store_ref() {
|
||||
return raw_store.get_blob_bytes_batch(sha256_hexes);
|
||||
}
|
||||
self.get_raw_entries_batch(sha256_hexes).map(|entries| {
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|entry| entry.map(|entry| entry.bytes))
|
||||
.collect()
|
||||
})
|
||||
RocksStore::get_blob_bytes_batch(self, sha256_hexes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,6 +456,47 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn put_blob_bytes_batch_round_trips_without_raw_entry() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let raw_store =
|
||||
ExternalRawStoreDb::open(td.path().join("raw-store.db")).expect("open raw store");
|
||||
|
||||
let a = (sha256_hex(b"blob-a"), b"blob-a".to_vec());
|
||||
let b = (sha256_hex(b"blob-b"), b"blob-b".to_vec());
|
||||
raw_store
|
||||
.put_blob_bytes_batch(&[a.clone(), b.clone()])
|
||||
.expect("put blobs");
|
||||
|
||||
assert_eq!(
|
||||
raw_store.get_blob_bytes(&a.0).expect("get blob a"),
|
||||
Some(a.1.clone())
|
||||
);
|
||||
assert_eq!(
|
||||
raw_store.get_blob_bytes(&b.0).expect("get blob b"),
|
||||
Some(b.1.clone())
|
||||
);
|
||||
assert!(raw_store.get_raw_entry(&a.0).expect("get raw a").is_none());
|
||||
assert!(raw_store.get_raw_entry(&b.0).expect("get raw b").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn put_blob_bytes_batch_rejects_invalid_inputs() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let raw_store =
|
||||
ExternalRawStoreDb::open(td.path().join("raw-store.db")).expect("open raw store");
|
||||
|
||||
let err = raw_store
|
||||
.put_blob_bytes_batch(&[("zz".repeat(32), b"blob".to_vec())])
|
||||
.expect_err("invalid hash should fail");
|
||||
assert!(matches!(err, StorageError::InvalidData { .. }));
|
||||
|
||||
let err = raw_store
|
||||
.put_blob_bytes_batch(&[(sha256_hex(b"blob"), Vec::new())])
|
||||
.expect_err("empty bytes should fail");
|
||||
assert!(matches!(err, StorageError::InvalidData { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_raw_store_db_rejects_invalid_entry_on_put() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
|
||||
Reference in New Issue
Block a user