use crate::server::clustering::vector_clocks::VersionedValue;
use serde::{Deserialize, Serialize};
pub trait ConflictResolver<T: Clone> {
fn resolve(&self, local: &VersionedValue<T>, remote: &VersionedValue<T>) -> VersionedValue<T>;
fn name(&self) -> &str;
}
#[derive(Clone, Debug)]
pub struct LastWriteWinsResolver {
_current_server: String,
}
impl LastWriteWinsResolver {
pub fn new(current_server: String) -> Self {
LastWriteWinsResolver {
_current_server: current_server,
}
}
fn logical_time(version: &VersionedValue<Vec<u8>>) -> u64 {
version
.version
.server_ids()
.iter()
.map(|server| version.version.get(server))
.sum()
}
}
impl ConflictResolver<Vec<u8>> for LastWriteWinsResolver {
fn resolve(
&self,
local: &VersionedValue<Vec<u8>>,
remote: &VersionedValue<Vec<u8>>,
) -> VersionedValue<Vec<u8>> {
let local_time = Self::logical_time(local);
let remote_time = Self::logical_time(remote);
match local_time.cmp(&remote_time) {
std::cmp::Ordering::Greater => local.clone(),
std::cmp::Ordering::Less => remote.clone(),
std::cmp::Ordering::Equal => {
if local.last_modified_by >= remote.last_modified_by {
local.clone()
} else {
remote.clone()
}
}
}
}
fn name(&self) -> &str {
"LastWriteWins"
}
}
#[derive(Clone, Debug)]
pub struct ApplicationDefinedResolver {
name: String,
}
impl ApplicationDefinedResolver {
pub fn new(name: String) -> Self {
ApplicationDefinedResolver { name }
}
}
impl ConflictResolver<Vec<u8>> for ApplicationDefinedResolver {
fn resolve(
&self,
local: &VersionedValue<Vec<u8>>,
remote: &VersionedValue<Vec<u8>>,
) -> VersionedValue<Vec<u8>> {
local.clone()
}
fn name(&self) -> &str {
&self.name
}
}
#[derive(Clone, Debug)]
pub struct ServerPriorityResolver;
impl ConflictResolver<Vec<u8>> for ServerPriorityResolver {
fn resolve(
&self,
local: &VersionedValue<Vec<u8>>,
remote: &VersionedValue<Vec<u8>>,
) -> VersionedValue<Vec<u8>> {
if local.last_modified_by >= remote.last_modified_by {
local.clone()
} else {
remote.clone()
}
}
fn name(&self) -> &str {
"ServerPriority"
}
}
#[derive(Clone, Debug)]
pub struct MergeResolver;
impl ConflictResolver<Vec<u8>> for MergeResolver {
fn resolve(
&self,
local: &VersionedValue<Vec<u8>>,
_remote: &VersionedValue<Vec<u8>>,
) -> VersionedValue<Vec<u8>> {
let lww = LastWriteWinsResolver::new("self".to_string());
lww.resolve(local, _remote)
}
fn name(&self) -> &str {
"Merge"
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ConflictResolutionConfig {
LastWriteWins,
ServerPriority,
ApplicationDefined,
Merge,
}
impl Default for ConflictResolutionConfig {
fn default() -> Self {
ConflictResolutionConfig::LastWriteWins
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::server::clustering::vector_clocks::VectorClock;
fn create_test_value(value: u8, server: &str, time: u64) -> VersionedValue<Vec<u8>> {
let mut vc = VectorClock::new(&[server]);
for _ in 0..time {
vc.increment(server);
}
VersionedValue::new(vec![value], vc, server.to_string())
}
#[test]
fn test_last_write_wins_higher_local() {
let local = create_test_value(1, "server1", 5);
let remote = create_test_value(2, "server2", 3);
let resolver = LastWriteWinsResolver::new("server1".to_string());
let result = resolver.resolve(&local, &remote);
assert_eq!(result.value, vec![1]);
}
#[test]
fn test_last_write_wins_higher_remote() {
let local = create_test_value(1, "server1", 3);
let remote = create_test_value(2, "server2", 5);
let resolver = LastWriteWinsResolver::new("server1".to_string());
let result = resolver.resolve(&local, &remote);
assert_eq!(result.value, vec![2]);
}
#[test]
fn test_last_write_wins_equal_time_server_priority() {
let local = create_test_value(1, "server1", 2);
let remote = create_test_value(2, "server2", 2);
let resolver = LastWriteWinsResolver::new("server1".to_string());
let result = resolver.resolve(&local, &remote);
assert_eq!(result.value, vec![2]);
}
#[test]
fn test_server_priority_resolver() {
let local = create_test_value(1, "server1", 10);
let remote = create_test_value(2, "server3", 1);
let resolver = ServerPriorityResolver;
let result = resolver.resolve(&local, &remote);
assert_eq!(result.value, vec![2]);
}
#[test]
fn test_application_defined_resolver() {
let local = create_test_value(1, "server1", 2);
let remote = create_test_value(2, "server2", 5);
let resolver = ApplicationDefinedResolver::new("app_resolver".to_string());
let result = resolver.resolve(&local, &remote);
assert_eq!(result.value, vec![1]);
assert_eq!(resolver.name(), "app_resolver");
}
#[test]
fn test_resolver_names() {
let lww = LastWriteWinsResolver::new("server".to_string());
assert_eq!(lww.name(), "LastWriteWins");
let sp = ServerPriorityResolver;
assert_eq!(sp.name(), "ServerPriority");
let merge = MergeResolver;
assert_eq!(merge.name(), "Merge");
}
#[test]
fn test_conflict_resolution_config_default() {
let cfg = ConflictResolutionConfig::default();
assert!(matches!(cfg, ConflictResolutionConfig::LastWriteWins));
}
}