use std::{
sync::{
Arc,
atomic::{AtomicBool, AtomicUsize, Ordering},
},
thread,
time::{Duration, Instant},
};
use super::{
cleanup::vector_set_cleanup_work_channel::VectorSetCleanupWorkChannel,
vector_manager::{
VADD_APPEND_LOG_ARG, VADD_SET_FLAGS_ARG, VREM_APPEND_LOG_ARG, VSETATTR_APPEND_LOG_ARG,
VectorManager,
},
vector_types::{VectorDistanceMetricType, VectorQuantType, VectorSetFlags, VectorValueType},
};
#[derive(Debug, Clone, PartialEq)]
pub struct VaddReplicationState {
pub key: Vec<u8>,
pub dims: u32,
pub reduce_dims: u32,
pub value_type: VectorValueType,
pub values: Vec<u8>,
pub element: Vec<u8>,
pub quantizer: VectorQuantType,
pub build_exploration_factor: u32,
pub attributes: Vec<u8>,
pub num_links: u32,
pub distance_metric: VectorDistanceMetricType,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReplicationRecord {
pub log_arg: i64,
pub namespace_bytes: Vec<u8>,
pub key: Vec<u8>,
pub value: Vec<u8>,
}
pub struct ReplicationRuntime {
replay_channel: VectorSetCleanupWorkChannel<VaddReplicationState>,
replication_log: VectorSetCleanupWorkChannel<ReplicationRecord>,
blocked: AtomicUsize,
replay_started: AtomicUsize,
active: AtomicBool,
last_arg: AtomicUsize,
log_count: AtomicUsize,
}
impl Default for ReplicationRuntime {
fn default() -> Self {
Self::new()
}
}
impl ReplicationRuntime {
pub fn new() -> Self {
Self {
replay_channel: VectorSetCleanupWorkChannel::new(),
replication_log: VectorSetCleanupWorkChannel::new(),
blocked: AtomicUsize::new(0),
replay_started: AtomicUsize::new(0),
active: AtomicBool::new(false),
last_arg: AtomicUsize::new(0),
log_count: AtomicUsize::new(0),
}
}
pub(crate) fn replicate(&self, log_arg: i64, namespace_bytes: &[u8], key: &[u8], value: &[u8]) {
self.last_arg.store(log_arg as usize, Ordering::Relaxed);
self.log_count.fetch_add(1, Ordering::Relaxed);
let _ = self.replication_log.try_publish(ReplicationRecord {
log_arg,
namespace_bytes: namespace_bytes.to_vec(),
key: key.to_vec(),
value: value.to_vec(),
});
}
pub fn replay_len(&self) -> usize {
self.log_count.load(Ordering::Relaxed)
}
pub fn last_arg(&self) -> i64 {
self.last_arg.load(Ordering::Relaxed) as i64
}
pub fn enter_operation(&self) {
self.blocked.fetch_add(1, Ordering::AcqRel);
}
pub fn exit_operation(&self) {
self.blocked.fetch_sub(1, Ordering::AcqRel);
}
pub fn is_blocked(&self) -> bool {
self.blocked.load(Ordering::Acquire) > 0
}
}
impl VectorManager {
pub fn start_replication_tasks_async(self: &Arc<Self>) {
self.replication.active.store(true, Ordering::Release);
self.start_replication_replay_tasks();
}
pub fn replicate_vector_set_add(
&self,
key: &[u8],
element: &[u8],
values: &[u8],
attributes: &[u8],
dims: u32,
quant: VectorQuantType,
) {
self
.replication
.replicate(VADD_APPEND_LOG_ARG, &[], key, element);
let _ = self
.replication
.replay_channel
.try_publish(VaddReplicationState {
key: key.to_vec(),
dims,
reduce_dims: 0,
value_type: VectorValueType::FP32,
values: values.to_vec(),
element: element.to_vec(),
quantizer: quant,
build_exploration_factor: 0,
attributes: attributes.to_vec(),
num_links: 0,
distance_metric: VectorDistanceMetricType::Cosine,
});
}
pub fn replicate_vector_set_remove(&self, key: &[u8], element: &[u8]) {
self
.replication
.replicate(VREM_APPEND_LOG_ARG, &[], key, element);
}
pub fn replicate_vector_set_set_attribute(&self, key: &[u8], element: &[u8], attribute: &[u8]) {
self
.replication
.replicate(VSETATTR_APPEND_LOG_ARG, &[], key, attribute);
let _ = element;
}
pub fn handle_vector_set_add_replication(&self, state: &VaddReplicationState) {
self.apply_vector_set_add(state);
}
pub fn start_replication_replay_tasks(self: &Arc<Self>) {
if self
.replication
.replay_started
.compare_exchange(0, 1, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
return; }
let manager = Arc::clone(self);
let _ = thread::Builder::new()
.name("vector-replay".into())
.spawn(move || {
let channel = &manager.replication.replay_channel;
while channel.wait_to_read(250) {
let Some(state) = channel.try_read() else {
continue;
};
manager.replication.enter_operation();
manager.handle_vector_set_add_replication(&state);
manager.replication.exit_operation();
}
manager.replication.active.store(false, Ordering::Release);
})
.expect("重放线程创建失败");
}
pub fn start_replica_task_async(self: &Arc<Self>) {
self.start_replication_replay_tasks();
}
pub fn apply_vector_set_add(&self, state: &VaddReplicationState) {
let Some(context) = self.resolve_replay_context(state) else {
return;
};
let prepared = match super::vector_manager_element_data::prepare_vector_data(
state.quantizer,
state.value_type,
&state.values,
) {
Ok(p) => p,
Err(_) => return,
};
let _ = self
.service
.insert(context, &state.element, &prepared.bytes, &state.attributes);
}
fn resolve_replay_context(&self, state: &VaddReplicationState) -> Option<u64> {
if let Some(bytes) = self.read_stored_index(&state.key) {
return super::vector_manager__index::Index::from_bytes(&bytes).map(|i| i.context);
}
let context = self.next_vector_set_context(0)?;
self.service.create_index(
context,
state.dims,
state.reduce_dims,
state.quantizer,
state.build_exploration_factor,
state.num_links.max(1),
state.distance_metric,
);
let record = super::vector_manager__index::Index {
context,
index_ptr: 1,
dimensions: state.dims,
reduce_dims: state.reduce_dims,
num_links: state.num_links,
build_exploration_factor: state.build_exploration_factor,
quant_type: state.quantizer,
distance_metric: state.distance_metric,
flags: VectorSetFlags::NONE,
};
self.write_stored_index(&state.key, &record.to_bytes());
Some(context)
}
pub fn reset_replay_tasks_async(&self) -> usize {
self.replication.replay_channel.drain_pending()
}
pub fn shutdown_replay_tasks(&self) {
self.replication.replay_channel.complete();
self.replication.active.store(false, Ordering::Release);
}
pub fn handle_vector_set_remove_replication(&self, key: &[u8], element: &[u8]) {
let Some(bytes) = self.read_stored_index(key) else {
return;
};
let Some(index) = super::vector_manager__index::Index::from_bytes(&bytes) else {
return;
};
let _ = self.service.remove(index.context, element);
}
pub fn handle_vector_set_set_attribute_replication(
&self,
key: &[u8],
element: &[u8],
attribute: &[u8],
) {
let Some(bytes) = self.read_stored_index(key) else {
return;
};
let Some(index) = super::vector_manager__index::Index::from_bytes(&bytes) else {
return;
};
let _ = self
.service
.set_attribute(index.context, element, attribute);
}
pub fn wait_for_vector_operations_to_complete(&self, timeout_ms: u64) -> bool {
let deadline = Instant::now() + Duration::from_millis(timeout_ms);
while !self.replication.replay_channel.is_completed()
&& (self.replication.replay_channel.has_pending() || self.replication.is_blocked())
{
if Instant::now() >= deadline {
return false;
}
thread::sleep(Duration::from_millis(1));
}
true
}
pub fn handle_vector_set_rename_copy(&self, old_key: &[u8], new_key: &[u8], value: &[u8]) {
if let Some(mut stored) = self.read_stored_index(old_key) {
stored[40] = VectorSetFlags::SUPPRESS_CLEANUP.bits();
self.write_stored_index(old_key, &stored);
}
self
.replication
.replicate(VADD_SET_FLAGS_ARG, &[], new_key, value);
}
}
#[cfg(test)]
mod tests {
use super::{
super::vector_manager::{VectorManager, VectorManagerOptions},
*,
};
#[test]
fn replication_log_injection() {
let manager = VectorManager::new(VectorManagerOptions {
is_enabled: true,
..Default::default()
});
manager.replicate_vector_set_add(
b"set",
b"e1",
&[0, 0, 0, 0],
b"{}",
1,
VectorQuantType::NoQuant,
);
manager.replicate_vector_set_remove(b"set", b"e1");
manager.replicate_vector_set_set_attribute(b"set", b"e1", b"{}");
assert!(manager.replication.replay_len() >= 3);
assert_eq!(manager.replication.last_arg(), VSETATTR_APPEND_LOG_ARG);
}
#[test]
fn replay_state_application() {
let manager = VectorManager::new(VectorManagerOptions {
is_enabled: true,
..Default::default()
});
let state = VaddReplicationState {
key: b"set".to_vec(),
dims: 2,
reduce_dims: 0,
value_type: VectorValueType::FP32,
values: [1.0f32, 2.0].iter().flat_map(|v| v.to_le_bytes()).collect(),
element: b"elem".to_vec(),
quantizer: VectorQuantType::NoQuant,
build_exploration_factor: 32,
attributes: b"{}".to_vec(),
num_links: 4,
distance_metric: VectorDistanceMetricType::L2,
};
manager.handle_vector_set_add_replication(&state);
let replayed = super::super::vector_manager__index::Index::from_bytes(
&manager.read_stored_index(b"set").unwrap(),
)
.unwrap();
assert_ne!(replayed.context, 0);
assert_eq!(manager.service.card(replayed.context), 1);
manager.handle_vector_set_remove_replication(b"set", b"elem");
assert_eq!(manager.service.card(replayed.context), 0);
}
#[test]
fn wait_and_shutdown_semantics() {
let manager = VectorManager::new(VectorManagerOptions {
is_enabled: true,
..Default::default()
});
assert!(manager.wait_for_vector_operations_to_complete(10));
let _ = manager
.replication
.replay_channel
.try_publish(VaddReplicationState {
key: b"k".into(),
dims: 1,
reduce_dims: 0,
value_type: VectorValueType::FP32,
values: vec![0; 4],
element: b"e".into(),
quantizer: VectorQuantType::NoQuant,
build_exploration_factor: 8,
attributes: vec![],
num_links: 2,
distance_metric: VectorDistanceMetricType::L2,
});
assert!(!manager.wait_for_vector_operations_to_complete(5));
assert_eq!(manager.reset_replay_tasks_async(), 1);
assert!(manager.wait_for_vector_operations_to_complete(10));
manager.shutdown_replay_tasks();
assert!(!manager.replication.active.load(Ordering::Acquire));
}
#[test]
fn blocked_event_and_rename_copy() {
let manager = VectorManager::new(VectorManagerOptions {
is_enabled: true,
..Default::default()
});
manager.replication.enter_operation();
assert!(manager.replication.is_blocked());
manager.replication.exit_operation();
assert!(!manager.replication.is_blocked());
manager.write_stored_index(b"old", &[0u8; 56]);
manager.handle_vector_set_rename_copy(b"old", b"new", b"value");
let stored = manager.read_stored_index(b"old").unwrap();
assert_eq!(stored[40], VectorSetFlags::SUPPRESS_CLEANUP.bits());
assert_eq!(manager.replication.last_arg(), VADD_SET_FLAGS_ARG);
}
}