uqa_storage/key_value/vector_index/
guards.rs1use super::{KeyValueBatch, KeyValueVectorIndex, StorageBackendResult};
10use uqa_core::memory::{BudgetedVec, MemoryError};
11
12pub(crate) const ROOT: &[u8] = b"\0uqa-vector-field-guards-v1\0";
13
14pub(crate) fn reference_key(
15 vectors: &[u8],
16 control: &crate::read_control::StorageReadControl,
17) -> StorageBackendResult<BudgetedVec<u8>> {
18 control.check()?;
19 let mut key = BudgetedVec::new(control.memory());
20 key.extend_from_slice(ROOT)?;
21 key.push(1)?;
22 key.extend_from_slice(vectors)?;
23 Ok(key)
24}
25
26pub struct KeyValueVectorFieldGuards;
27
28impl crate::mvcc::VectorFieldGuardLayout for KeyValueVectorFieldGuards {
29 fn prefix(
30 &self,
31 control: &crate::read_control::StorageReadControl,
32 ) -> crate::mvcc::VersionResult<BudgetedVec<u8>> {
33 let mut prefix = BudgetedVec::new(control.memory());
34 prefix.extend_from_slice(ROOT)?;
35 Ok(prefix)
36 }
37
38 fn reference(
39 &self,
40 key: &[u8],
41 control: &crate::read_control::StorageReadControl,
42 ) -> crate::mvcc::VersionResult<Option<crate::mvcc::VectorFieldGuard>> {
43 use crate::key_value::codec::read_segment;
44 use crate::mvcc::{VectorFieldGuard, VersionError};
45
46 control.check()?;
47 let suffix = key
48 .strip_prefix(ROOT)
49 .ok_or(VersionError::InvalidEncoding("foreign vector field guard"))?;
50 let (&kind, vectors) = suffix.split_first().ok_or(VersionError::InvalidEncoding(
51 "truncated vector field guard",
52 ))?;
53 if kind > 1 || vectors.first() != Some(&crate::key_value::TAG_VECTOR) {
54 return Err(VersionError::InvalidEncoding("invalid vector field guard"));
55 }
56 let mut offset = 1;
57 for _ in 0..2 {
58 std::str::from_utf8(read_segment(vectors, &mut offset)?).map_err(|_| {
59 VersionError::InvalidEncoding("vector field guard name is not UTF-8")
60 })?;
61 }
62 if offset != vectors.len() {
63 return Err(VersionError::InvalidEncoding(
64 "vector field guard has trailing bytes",
65 ));
66 }
67 if kind == 0 {
68 return Ok(None);
69 }
70 let copy = |bytes: &[u8]| -> crate::mvcc::VersionResult<BudgetedVec<u8>> {
71 let mut output = BudgetedVec::new(control.memory());
72 output.extend_from_slice(bytes)?;
73 Ok(output)
74 };
75 let mut lifetime = copy(key)?;
76 lifetime[ROOT.len()] = 0;
77 Ok(Some(VectorFieldGuard {
78 lifetime,
79 references: copy(key)?,
80 reference_value: copy(&[1])?,
81 vectors: copy(vectors)?,
82 }))
83 }
84}
85
86impl KeyValueVectorIndex {
87 pub(super) fn coordinate_field(
88 &self,
89 batch: &mut dyn KeyValueBatch,
90 structural: bool,
91 ) -> StorageBackendResult<()> {
92 if !self.store.transaction_model().is_versioned() {
93 return Ok(());
94 }
95 let control = self.store.retention_control().ok_or_else(|| {
96 super::other_error("versioned vector owner has no retention allowance")
97 })?;
98 control.check()?;
99 let bytes = self
100 .table
101 .len()
102 .checked_add(self.field.len())
103 .and_then(|size| size.checked_add(32))
104 .and_then(|size| size.checked_mul(2))
105 .ok_or(MemoryError::SizeOverflow)?;
106 let _workspace = control.memory().reserve(bytes)?;
107 let prefix = super::vector_field_prefix(&self.table, &self.field)?;
108 let mut lifetime = BudgetedVec::new(control.memory());
109 lifetime.extend_from_slice(ROOT)?;
110 lifetime.push(0)?;
111 lifetime.extend_from_slice(&prefix)?;
112 let references = reference_key(&prefix, &control)?;
113 if structural {
114 batch.fence_record(&lifetime)?;
115 batch.fence_record(&references)
116 } else {
117 batch.require_unchanged(&lifetime)?;
118 batch.touch_marker(&references, &[1])
119 }
120 }
121}