Skip to content

Commit 4e285fa

Browse files
committed
fixup! Add edge crate
Implement segment config compatibility check
1 parent 0320488 commit 4e285fa

2 files changed

Lines changed: 88 additions & 3 deletions

File tree

lib/edge/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ impl Shard {
102102
};
103103

104104
if let Some(config) = &config {
105-
// TODO: Is simple equality check sufficient? 🤔
106-
if config != segment.config() {
105+
if !config.is_compatible(segment.config()) {
107106
return Err(OperationError::service_error(format!(
108-
"segment {} does not match expected segment config: expected {:?}, but received {:?}",
107+
"segment {} is incompatible with provided config or previously loaded segments: \
108+
expected {:?}, but received {:?}",
109109
segment_path.display(),
110110
config,
111111
segment.config(),

lib/segment/src/types.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,56 @@ impl SegmentConfig {
14051405
)
14061406
.all(|v| v)
14071407
}
1408+
1409+
pub fn is_compatible(&self, other: &Self) -> bool {
1410+
// Vector data have to be compatible between two segments.
1411+
// Sparse vector data can be different, but a placeholder check is implemented to catch
1412+
// and enforce compatibility check for future changes.
1413+
// Payload storage type can be different.
1414+
1415+
// Assert segment config fields
1416+
let Self {
1417+
vector_data: _,
1418+
sparse_vector_data: _,
1419+
payload_storage_type: _,
1420+
} = self;
1421+
1422+
let is_vector_config_compatible = is_compatible(
1423+
&self.vector_data,
1424+
&other.vector_data,
1425+
VectorDataConfig::is_compatible,
1426+
);
1427+
1428+
let is_sparse_vector_config_compatible = is_compatible(
1429+
&self.sparse_vector_data,
1430+
&other.sparse_vector_data,
1431+
SparseVectorDataConfig::is_compatible,
1432+
);
1433+
1434+
is_vector_config_compatible && is_sparse_vector_config_compatible
1435+
}
1436+
}
1437+
1438+
fn is_compatible<V, C, F>(this: &HashMap<V, C>, other: &HashMap<V, C>, check: F) -> bool
1439+
where
1440+
V: Eq + Hash,
1441+
F: Fn(&C, &C) -> bool,
1442+
{
1443+
if this.len() != other.len() {
1444+
return false;
1445+
}
1446+
1447+
for (vector_name, config) in this {
1448+
let Some(other_config) = other.get(vector_name) else {
1449+
return false;
1450+
};
1451+
1452+
if !check(config, other_config) {
1453+
return false;
1454+
}
1455+
}
1456+
1457+
true
14081458
}
14091459

14101460
/// Storage types for vectors
@@ -1526,6 +1576,29 @@ impl VectorDataConfig {
15261576
};
15271577
is_index_appendable && is_storage_appendable
15281578
}
1579+
1580+
pub fn is_compatible(&self, other: &Self) -> bool {
1581+
// Size and distance have to be the same for both segments.
1582+
// Storage type, index and quantization config can be different.
1583+
//
1584+
// TODO: Can multivector config and datatype be different?
1585+
1586+
// Assert vector data config fields
1587+
let Self {
1588+
size: _,
1589+
distance: _,
1590+
storage_type: _,
1591+
index: _,
1592+
quantization_config: _,
1593+
multivector_config: _,
1594+
datatype: _,
1595+
} = self;
1596+
1597+
self.size == other.size
1598+
&& self.distance == other.distance
1599+
&& self.multivector_config == other.multivector_config // TODO: 🤔
1600+
&& self.datatype == other.datatype // TODO: 🤔
1601+
}
15291602
}
15301603

15311604
#[derive(
@@ -1582,6 +1655,18 @@ impl SparseVectorDataConfig {
15821655
pub fn is_indexed(&self) -> bool {
15831656
true
15841657
}
1658+
1659+
pub fn is_compatible(&self, _other: &Self) -> bool {
1660+
// Both index and storage type can be different for two segments to be compatible
1661+
1662+
// Assert sparse vector config fields
1663+
let Self {
1664+
index: _,
1665+
storage_type: _,
1666+
} = self;
1667+
1668+
true
1669+
}
15851670
}
15861671

15871672
/// Default value based on experiments and observations

0 commit comments

Comments
 (0)