refactor sync to new model

This commit is contained in:
Dustin J. Mitchell
2020-11-25 17:52:47 -05:00
parent e92fc0628b
commit a81c84d7c7
8 changed files with 193 additions and 143 deletions

View File

@@ -4,4 +4,4 @@ pub(crate) mod test;
mod signing;
mod types;
pub use types::{Blob, Server, VersionAdd};
pub use types::*;

View File

@@ -1,84 +1,78 @@
use crate::server::{Blob, Server, VersionAdd};
use crate::server::{
AddVersionResult, GetVersionResult, HistorySegment, Server, VersionId, NO_VERSION_ID,
};
use failure::Fallible;
use std::collections::HashMap;
use uuid::Uuid;
pub(crate) struct TestServer {
users: HashMap<String, User>,
struct Version {
version_id: VersionId,
parent_version_id: VersionId,
history_segment: HistorySegment,
}
struct User {
// versions, indexed at v-1
versions: Vec<Blob>,
snapshots: HashMap<u64, Blob>,
pub(crate) struct TestServer {
latest_version_id: VersionId,
// NOTE: indexed by parent_version_id!
versions: HashMap<VersionId, Version>,
}
impl TestServer {
/// A test server has no notion of clients, signatures, encryption, etc.
pub fn new() -> TestServer {
TestServer {
users: HashMap::new(),
latest_version_id: NO_VERSION_ID,
versions: HashMap::new(),
}
}
fn get_user_mut(&mut self, username: &str) -> &mut User {
self.users
.entry(username.to_string())
.or_insert_with(User::new)
}
}
impl Server for TestServer {
/// Get a vector of all versions after `since_version`
fn get_versions(&self, username: &str, since_version: u64) -> Fallible<Vec<Blob>> {
if let Some(user) = self.users.get(username) {
user.get_versions(since_version)
} else {
Ok(vec![])
}
}
/// Add a new version. If the given version number is incorrect, this responds with the
/// appropriate version and expects the caller to try again.
fn add_version(&mut self, username: &str, version: u64, blob: Blob) -> Fallible<VersionAdd> {
self.get_user_mut(username).add_version(version, blob)
fn add_version(
&mut self,
parent_version_id: VersionId,
history_segment: HistorySegment,
) -> Fallible<AddVersionResult> {
// no client lookup
// no signature validation
// check the parent_version_id for linearity
if self.latest_version_id != NO_VERSION_ID {
if parent_version_id != self.latest_version_id {
return Ok(AddVersionResult::ExpectedParentVersion(
self.latest_version_id,
));
}
}
// invent a new ID for this version
let version_id = Uuid::new_v4();
self.versions.insert(
parent_version_id,
Version {
version_id,
parent_version_id,
history_segment,
},
);
self.latest_version_id = version_id;
Ok(AddVersionResult::Ok(version_id))
}
fn add_snapshot(&mut self, username: &str, version: u64, blob: Blob) -> Fallible<()> {
self.get_user_mut(username).add_snapshot(version, blob)
}
}
impl User {
fn new() -> User {
User {
versions: vec![],
snapshots: HashMap::new(),
}
}
fn get_versions(&self, since_version: u64) -> Fallible<Vec<Blob>> {
let last_version = self.versions.len();
if last_version == since_version as usize {
return Ok(vec![]);
}
Ok(self.versions[since_version as usize..last_version]
.iter()
.map(|r| r.clone())
.collect::<Vec<Blob>>())
}
fn add_version(&mut self, version: u64, blob: Blob) -> Fallible<VersionAdd> {
// of by one here: client wants to send version 1 first
let expected_version = self.versions.len() as u64 + 1;
if version != expected_version {
return Ok(VersionAdd::ExpectedVersion(expected_version));
}
self.versions.push(blob);
Ok(VersionAdd::Ok)
}
fn add_snapshot(&mut self, version: u64, blob: Blob) -> Fallible<()> {
self.snapshots.insert(version, blob);
Ok(())
/// Get a vector of all versions after `since_version`
fn get_child_version(&self, parent_version_id: VersionId) -> Fallible<GetVersionResult> {
if let Some(version) = self.versions.get(&parent_version_id) {
Ok(GetVersionResult::Version {
version_id: version.version_id,
parent_version_id: version.parent_version_id,
history_segment: version.history_segment.clone(),
})
} else {
Ok(GetVersionResult::NoSuchVersion)
}
}
}

View File

@@ -1,26 +1,46 @@
use failure::Fallible;
use uuid::Uuid;
/// A Blob is a hunk of encoded data that is sent to the server. The server does not interpret
/// this data at all.
pub type Blob = Vec<u8>;
/// Versions are referred to with sha2 hashes.
pub type VersionId = Uuid;
/// The distinguished value for "no version"
pub const NO_VERSION_ID: VersionId = Uuid::nil();
/// A segment in the history of this task database, in the form of a sequence of operations. This
/// data is pre-encoded, and from the protocol level appears as a sequence of bytes.
pub type HistorySegment = Vec<u8>;
/// VersionAdd is the response type from [`crate:server::Server::add_version`].
pub enum VersionAdd {
/// OK, version added
Ok,
/// Rejected, must be based on the the given version
ExpectedVersion(u64),
pub enum AddVersionResult {
/// OK, version added with the given ID
Ok(VersionId),
/// Rejected; expected a version with the given parent version
ExpectedParentVersion(VersionId),
}
/// A version as downloaded from the server
pub enum GetVersionResult {
/// No such version exists
NoSuchVersion,
/// The requested version
Version {
version_id: VersionId,
parent_version_id: VersionId,
history_segment: HistorySegment,
},
}
/// A value implementing this trait can act as a server against which a replica can sync.
pub trait Server {
/// Get a vector of all versions after `since_version`
fn get_versions(&self, username: &str, since_version: u64) -> Fallible<Vec<Blob>>;
/// Add a new version.
fn add_version(
&mut self,
parent_version_id: VersionId,
history_segment: HistorySegment,
) -> Fallible<AddVersionResult>;
/// Add a new version. If the given version number is incorrect, this responds with the
/// appropriate version and expects the caller to try again.
fn add_version(&mut self, username: &str, version: u64, blob: Blob) -> Fallible<VersionAdd>;
/// TODO: undefined yet
fn add_snapshot(&mut self, username: &str, version: u64, blob: Blob) -> Fallible<()>;
/// Get the version with the given parent VersionId
fn get_child_version(&self, parent_version_id: VersionId) -> Fallible<GetVersionResult>;
}