Switch to CBOR for metadata serialization and simplify key management

- Replace `bincode` serialization with `CBOR` for obsoleted package metadata to improve interoperability and error messaging.
- Simplify key handling by directly using FMRI strings as keys, removing the need for intermediate serialization steps.
- Update deserialization, error handling, and logging to align with `CBOR` serialization.
- Add `serde_cbor` as a dependency, updating `Cargo.toml` and `Cargo.lock` accordingly.
- Refactor `ObsoletedPackageKey` to include conversion between FMRI strings and data components.
This commit is contained in:
Till Wegmueller 2025-07-29 23:14:47 +02:00
parent a5f1b7d815
commit 845ffec13b
No known key found for this signature in database
3 changed files with 87 additions and 30 deletions

21
Cargo.lock generated
View file

@ -615,6 +615,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "half"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@ -977,6 +983,7 @@ dependencies = [
"regex", "regex",
"semver", "semver",
"serde", "serde",
"serde_cbor",
"serde_json", "serde_json",
"sha2 0.9.9", "sha2 0.9.9",
"sha3", "sha3",
@ -1453,7 +1460,7 @@ dependencies = [
"reqwest", "reqwest",
"shellexpand", "shellexpand",
"specfile", "specfile",
"thiserror 1.0.69", "thiserror 2.0.12",
"url", "url",
"which", "which",
] ]
@ -1781,6 +1788,16 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.219" version = "1.0.219"
@ -1915,7 +1932,7 @@ dependencies = [
"anyhow", "anyhow",
"pest", "pest",
"pest_derive", "pest_derive",
"thiserror 1.0.69", "thiserror 2.0.12",
] ]
[[package]] [[package]]

View file

@ -29,6 +29,7 @@ pest_derive = "2.1.0"
strum = { version = "0.24.1", features = ["derive"] } strum = { version = "0.24.1", features = ["derive"] }
serde = { version = "1.0.207", features = ["derive"] } serde = { version = "1.0.207", features = ["derive"] }
serde_json = "1.0.124" serde_json = "1.0.124"
serde_cbor = "0.11.2"
flate2 = "1.0.28" flate2 = "1.0.28"
lz4 = "1.24.0" lz4 = "1.24.0"
semver = { version = "1.0.20", features = ["serde"] } semver = { version = "1.0.20", features = ["serde"] }

View file

@ -1,11 +1,12 @@
use crate::fmri::Fmri; use crate::fmri::Fmri;
use crate::repository::{Result, RepositoryError}; use crate::repository::{Result, RepositoryError};
use bincode::{deserialize, serialize};
use chrono::{DateTime, Duration as ChronoDuration, Utc}; use chrono::{DateTime, Duration as ChronoDuration, Utc};
use miette::Diagnostic; use miette::Diagnostic;
use regex::Regex; use regex::Regex;
use redb::{Database, ReadableTable, TableDefinition}; use redb::{Database, ReadableTable, TableDefinition};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json;
use serde_cbor;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::RwLock; use std::sync::RwLock;
@ -273,6 +274,39 @@ impl ObsoletedPackageKey {
version: version.to_string(), version: version.to_string(),
} }
} }
/// Get the FMRI string for this key
fn to_fmri_string(&self) -> String {
format!("pkg://{}/{}@{}", self.publisher, self.stem, self.version)
}
/// Parse an FMRI string and create a key from it
fn from_fmri_string(fmri: &str) -> Result<Self> {
// Parse the FMRI string to extract publisher, stem, and version
// Format: pkg://publisher/stem@version
// Remove the pkg:// prefix if present
let fmri = fmri.trim_start_matches("pkg://");
// Split by / to get publisher and the rest
let parts: Vec<&str> = fmri.splitn(2, '/').collect();
if parts.len() != 2 {
return Err(ObsoletedPackageError::FmriParseError(format!("Invalid FMRI format: {}", fmri)).into());
}
let publisher = parts[0];
// Split the rest by @ to get stem and version
let parts: Vec<&str> = parts[1].splitn(2, '@').collect();
if parts.len() != 2 {
return Err(ObsoletedPackageError::FmriParseError(format!("Invalid FMRI format: {}", fmri)).into());
}
let stem = parts[0];
let version = parts[1];
Ok(Self::from_components(publisher, stem, version))
}
} }
@ -406,20 +440,14 @@ impl RedbObsoletedPackageIndex {
metadata.content_hash.clone() metadata.content_hash.clone()
}; };
// Serialize the key and metadata // Use the FMRI string directly as the key
let key_bytes = match serialize(key) { let key_bytes = metadata.fmri.as_bytes();
Ok(bytes) => bytes,
Err(e) => {
error!("Failed to serialize key: {}", e);
return Err(e.into());
}
};
let metadata_bytes = match serialize(metadata) { let metadata_bytes = match serde_cbor::to_vec(metadata) {
Ok(bytes) => bytes, Ok(bytes) => bytes,
Err(e) => { Err(e) => {
error!("Failed to serialize metadata: {}", e); error!("Failed to serialize metadata with CBOR: {}", e);
return Err(e.into()); return Err(ObsoletedPackageError::SerializationError(format!("Failed to serialize metadata with CBOR: {}", e)).into());
} }
}; };
@ -452,7 +480,7 @@ impl RedbObsoletedPackageIndex {
// Insert the metadata directly with FMRI as the key // Insert the metadata directly with FMRI as the key
// This is the new approach that eliminates the intermediate hash lookup // This is the new approach that eliminates the intermediate hash lookup
if let Err(e) = fmri_to_metadata.insert(key_bytes.as_slice(), metadata_bytes.as_slice()) { if let Err(e) = fmri_to_metadata.insert(key_bytes, metadata_bytes.as_slice()) {
error!("Failed to insert into FMRI_TO_METADATA_TABLE: {}", e); error!("Failed to insert into FMRI_TO_METADATA_TABLE: {}", e);
return Err(e.into()); return Err(e.into());
} }
@ -478,15 +506,16 @@ impl RedbObsoletedPackageIndex {
/// Remove an entry from the index /// Remove an entry from the index
fn remove_entry(&self, key: &ObsoletedPackageKey) -> Result<bool> { fn remove_entry(&self, key: &ObsoletedPackageKey) -> Result<bool> {
// Serialize the key // Use the FMRI string directly as the key
let key_bytes = serialize(key)?; let fmri = key.to_fmri_string();
let key_bytes = fmri.as_bytes();
// First, check if the key exists in the new table // First, check if the key exists in the new table
let exists_in_new_table = { let exists_in_new_table = {
let read_txn = self.db.begin_read()?; let read_txn = self.db.begin_read()?;
let fmri_to_metadata = read_txn.open_table(FMRI_TO_METADATA_TABLE)?; let fmri_to_metadata = read_txn.open_table(FMRI_TO_METADATA_TABLE)?;
fmri_to_metadata.get(key_bytes.as_slice())?.is_some() fmri_to_metadata.get(key_bytes)?.is_some()
}; };
// If the key doesn't exist in either table, return early // If the key doesn't exist in either table, return early
@ -500,7 +529,7 @@ impl RedbObsoletedPackageIndex {
// Remove the entry from the new table // Remove the entry from the new table
if exists_in_new_table { if exists_in_new_table {
let mut fmri_to_metadata = write_txn.open_table(FMRI_TO_METADATA_TABLE)?; let mut fmri_to_metadata = write_txn.open_table(FMRI_TO_METADATA_TABLE)?;
fmri_to_metadata.remove(key_bytes.as_slice())?; fmri_to_metadata.remove(key_bytes)?;
} }
} }
@ -511,8 +540,9 @@ impl RedbObsoletedPackageIndex {
/// Get an entry from the index /// Get an entry from the index
fn get_entry(&self, key: &ObsoletedPackageKey) -> Result<Option<(ObsoletedPackageMetadata, String)>> { fn get_entry(&self, key: &ObsoletedPackageKey) -> Result<Option<(ObsoletedPackageMetadata, String)>> {
// Serialize the key // Use the FMRI string directly as the key
let key_bytes = serialize(key)?; let fmri = key.to_fmri_string();
let key_bytes = fmri.as_bytes();
// First, try to get the metadata directly from the new table // First, try to get the metadata directly from the new table
let metadata_result = { let metadata_result = {
@ -520,15 +550,15 @@ impl RedbObsoletedPackageIndex {
let fmri_to_metadata = read_txn.open_table(FMRI_TO_METADATA_TABLE)?; let fmri_to_metadata = read_txn.open_table(FMRI_TO_METADATA_TABLE)?;
// Get the metadata bytes // Get the metadata bytes
match fmri_to_metadata.get(key_bytes.as_slice())? { match fmri_to_metadata.get(key_bytes)? {
Some(bytes) => { Some(bytes) => {
// Convert to owned bytes before the transaction is dropped // Convert to owned bytes before the transaction is dropped
let metadata_bytes = bytes.value().to_vec(); let metadata_bytes = bytes.value().to_vec();
// Try to deserialize the metadata // Try to deserialize the metadata
match deserialize::<ObsoletedPackageMetadata>(&metadata_bytes) { match serde_cbor::from_slice::<ObsoletedPackageMetadata>(&metadata_bytes) {
Ok(metadata) => Some(metadata), Ok(metadata) => Some(metadata),
Err(e) => { Err(e) => {
warn!("Failed to deserialize metadata from FMRI_TO_METADATA_TABLE: {}", e); warn!("Failed to deserialize metadata from FMRI_TO_METADATA_TABLE with CBOR: {}", e);
None None
} }
} }
@ -617,19 +647,28 @@ impl RedbObsoletedPackageIndex {
let key_data = key_bytes.value().to_vec(); let key_data = key_bytes.value().to_vec();
let metadata_data = metadata_bytes.value().to_vec(); let metadata_data = metadata_bytes.value().to_vec();
// Deserialize the key and metadata // Convert key bytes to string and parse as FMRI
let key: ObsoletedPackageKey = match deserialize(&key_data) { let fmri_str = match std::str::from_utf8(&key_data) {
Ok(key) => key, Ok(s) => s,
Err(e) => { Err(e) => {
warn!("Failed to deserialize key from FMRI_TO_METADATA_TABLE: {}", e); warn!("Failed to convert key bytes to string: {}", e);
continue; continue;
} }
}; };
let metadata: ObsoletedPackageMetadata = match deserialize(&metadata_data) { // Parse the FMRI string to create an ObsoletedPackageKey
let key = match ObsoletedPackageKey::from_fmri_string(fmri_str) {
Ok(key) => key,
Err(e) => {
warn!("Failed to parse FMRI string: {}", e);
continue;
}
};
let metadata: ObsoletedPackageMetadata = match serde_cbor::from_slice(&metadata_data) {
Ok(metadata) => metadata, Ok(metadata) => metadata,
Err(e) => { Err(e) => {
warn!("Failed to deserialize metadata from FMRI_TO_METADATA_TABLE: {}", e); warn!("Failed to deserialize metadata from FMRI_TO_METADATA_TABLE with CBOR: {}", e);
continue; continue;
} }
}; };