mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
add server-side config --snapshot-{days,versions}
This commit is contained in:
parent
74aee49107
commit
4d19ca7bdb
14 changed files with 305 additions and 91 deletions
|
@ -6,3 +6,6 @@
|
|||
Run `taskchampion-sync-server` to start the sync server.
|
||||
Use `--port` to specify the port it should listen on, and `--data-dir` to specify the directory which it should store its data.
|
||||
It only serves HTTP; the expectation is that a frontend proxy will be used for HTTPS support.
|
||||
|
||||
The server has optional parameters `--snapshot-days` and `--snapshot-version`, giving the target number of days and versions, respectively, between snapshots of the client state.
|
||||
The default values for these parameters are generally adequate.
|
||||
|
|
|
@ -5,7 +5,7 @@ use taskchampion_sync_server::{storage::InMemoryStorage, Server};
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn cross_sync() -> anyhow::Result<()> {
|
||||
let server = Server::new(Box::new(InMemoryStorage::new()));
|
||||
let server = Server::new(Default::default(), Box::new(InMemoryStorage::new()));
|
||||
let httpserver =
|
||||
HttpServer::new(move || App::new().configure(|sc| server.config(sc))).bind("0.0.0.0:0")?;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::api::{client_key_header, failure_to_ise, ServerState, SNAPSHOT_CONTEN
|
|||
use crate::server::{add_snapshot, VersionId, NIL_VERSION_ID};
|
||||
use actix_web::{error, post, web, HttpMessage, HttpRequest, HttpResponse, Result};
|
||||
use futures::StreamExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Max snapshot size: 100MB
|
||||
const MAX_SIZE: usize = 100 * 1024 * 1024;
|
||||
|
@ -17,7 +18,7 @@ const MAX_SIZE: usize = 100 * 1024 * 1024;
|
|||
#[post("/v1/client/add-snapshot/{version_id}")]
|
||||
pub(crate) async fn service(
|
||||
req: HttpRequest,
|
||||
server_state: web::Data<ServerState>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
web::Path((version_id,)): web::Path<(VersionId,)>,
|
||||
mut payload: web::Payload,
|
||||
) -> Result<HttpResponse> {
|
||||
|
@ -46,7 +47,7 @@ pub(crate) async fn service(
|
|||
// note that we do not open the transaction until the body has been read
|
||||
// completely, to avoid blocking other storage access while that data is
|
||||
// in transit.
|
||||
let mut txn = server_state.txn().map_err(failure_to_ise)?;
|
||||
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?;
|
||||
|
||||
// get, or create, the client
|
||||
let client = match txn.get_client(client_key).map_err(failure_to_ise)? {
|
||||
|
@ -58,7 +59,15 @@ pub(crate) async fn service(
|
|||
}
|
||||
};
|
||||
|
||||
add_snapshot(txn, client_key, client, version_id, body.to_vec()).map_err(failure_to_ise)?;
|
||||
add_snapshot(
|
||||
txn,
|
||||
&server_state.config,
|
||||
client_key,
|
||||
client,
|
||||
version_id,
|
||||
body.to_vec(),
|
||||
)
|
||||
.map_err(failure_to_ise)?;
|
||||
Ok(HttpResponse::Ok().body(""))
|
||||
}
|
||||
|
||||
|
@ -84,7 +93,7 @@ mod test {
|
|||
txn.add_version(client_key, version_id, NIL_VERSION_ID, vec![])?;
|
||||
}
|
||||
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -126,7 +135,7 @@ mod test {
|
|||
txn.new_client(client_key, NIL_VERSION_ID).unwrap();
|
||||
}
|
||||
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -156,7 +165,7 @@ mod test {
|
|||
let client_key = Uuid::new_v4();
|
||||
let version_id = Uuid::new_v4();
|
||||
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new());
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -176,7 +185,7 @@ mod test {
|
|||
let client_key = Uuid::new_v4();
|
||||
let version_id = Uuid::new_v4();
|
||||
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new());
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::api::{
|
|||
use crate::server::{add_version, AddVersionResult, SnapshotUrgency, VersionId, NIL_VERSION_ID};
|
||||
use actix_web::{error, post, web, HttpMessage, HttpRequest, HttpResponse, Result};
|
||||
use futures::StreamExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Max history segment size: 100MB
|
||||
const MAX_SIZE: usize = 100 * 1024 * 1024;
|
||||
|
@ -25,7 +26,7 @@ const MAX_SIZE: usize = 100 * 1024 * 1024;
|
|||
#[post("/v1/client/add-version/{parent_version_id}")]
|
||||
pub(crate) async fn service(
|
||||
req: HttpRequest,
|
||||
server_state: web::Data<ServerState>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
web::Path((parent_version_id,)): web::Path<(VersionId,)>,
|
||||
mut payload: web::Payload,
|
||||
) -> Result<HttpResponse> {
|
||||
|
@ -54,7 +55,7 @@ pub(crate) async fn service(
|
|||
// note that we do not open the transaction until the body has been read
|
||||
// completely, to avoid blocking other storage access while that data is
|
||||
// in transit.
|
||||
let mut txn = server_state.txn().map_err(failure_to_ise)?;
|
||||
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?;
|
||||
|
||||
// get, or create, the client
|
||||
let client = match txn.get_client(client_key).map_err(failure_to_ise)? {
|
||||
|
@ -66,8 +67,14 @@ pub(crate) async fn service(
|
|||
}
|
||||
};
|
||||
|
||||
let (result, snap_urgency) =
|
||||
add_version(txn, client_key, client, parent_version_id, body.to_vec())
|
||||
let (result, snap_urgency) = add_version(
|
||||
txn,
|
||||
&server_state.config,
|
||||
client_key,
|
||||
client,
|
||||
parent_version_id,
|
||||
body.to_vec(),
|
||||
)
|
||||
.map_err(failure_to_ise)?;
|
||||
|
||||
Ok(match result {
|
||||
|
@ -114,7 +121,7 @@ mod test {
|
|||
txn.new_client(client_key, Uuid::nil()).unwrap();
|
||||
}
|
||||
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -156,7 +163,7 @@ mod test {
|
|||
txn.new_client(client_key, version_id).unwrap();
|
||||
}
|
||||
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -184,7 +191,7 @@ mod test {
|
|||
let client_key = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new());
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -204,7 +211,7 @@ mod test {
|
|||
let client_key = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new());
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::api::{
|
|||
};
|
||||
use crate::server::{get_child_version, GetVersionResult, VersionId};
|
||||
use actix_web::{error, get, web, HttpRequest, HttpResponse, Result};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Get a child version.
|
||||
///
|
||||
|
@ -16,10 +17,10 @@ use actix_web::{error, get, web, HttpRequest, HttpResponse, Result};
|
|||
#[get("/v1/client/get-child-version/{parent_version_id}")]
|
||||
pub(crate) async fn service(
|
||||
req: HttpRequest,
|
||||
server_state: web::Data<ServerState>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
web::Path((parent_version_id,)): web::Path<(VersionId,)>,
|
||||
) -> Result<HttpResponse> {
|
||||
let mut txn = server_state.txn().map_err(failure_to_ise)?;
|
||||
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?;
|
||||
|
||||
let client_key = client_key_header(&req)?;
|
||||
|
||||
|
@ -28,7 +29,13 @@ pub(crate) async fn service(
|
|||
.map_err(failure_to_ise)?
|
||||
.ok_or_else(|| error::ErrorNotFound("no such client"))?;
|
||||
|
||||
return match get_child_version(txn, client_key, client, parent_version_id)
|
||||
return match get_child_version(
|
||||
txn,
|
||||
&server_state.config,
|
||||
client_key,
|
||||
client,
|
||||
parent_version_id,
|
||||
)
|
||||
.map_err(failure_to_ise)?
|
||||
{
|
||||
GetVersionResult::Success {
|
||||
|
@ -69,7 +76,7 @@ mod test {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -103,7 +110,7 @@ mod test {
|
|||
let client_key = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new());
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -129,7 +136,7 @@ mod test {
|
|||
let mut txn = storage.txn().unwrap();
|
||||
txn.new_client(client_key, Uuid::new_v4()).unwrap();
|
||||
}
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::api::{
|
|||
};
|
||||
use crate::server::get_snapshot;
|
||||
use actix_web::{error, get, web, HttpRequest, HttpResponse, Result};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Get a snapshot.
|
||||
///
|
||||
|
@ -15,9 +16,9 @@ use actix_web::{error, get, web, HttpRequest, HttpResponse, Result};
|
|||
#[get("/v1/client/snapshot")]
|
||||
pub(crate) async fn service(
|
||||
req: HttpRequest,
|
||||
server_state: web::Data<ServerState>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse> {
|
||||
let mut txn = server_state.txn().map_err(failure_to_ise)?;
|
||||
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?;
|
||||
|
||||
let client_key = client_key_header(&req)?;
|
||||
|
||||
|
@ -27,7 +28,7 @@ pub(crate) async fn service(
|
|||
.ok_or_else(|| error::ErrorNotFound("no such client"))?;
|
||||
|
||||
if let Some((version_id, data)) =
|
||||
get_snapshot(txn, client_key, client).map_err(failure_to_ise)?
|
||||
get_snapshot(txn, &server_state.config, client_key, client).map_err(failure_to_ise)?
|
||||
{
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type(SNAPSHOT_CONTENT_TYPE)
|
||||
|
@ -58,7 +59,7 @@ mod test {
|
|||
txn.new_client(client_key, Uuid::new_v4()).unwrap();
|
||||
}
|
||||
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
@ -94,7 +95,7 @@ mod test {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
let server = Server::new(storage);
|
||||
let server = Server::new(Default::default(), storage);
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::server::ClientKey;
|
||||
use crate::storage::Storage;
|
||||
use crate::ServerConfig;
|
||||
use actix_web::{error, http::StatusCode, web, HttpRequest, Result, Scope};
|
||||
use std::sync::Arc;
|
||||
|
||||
mod add_snapshot;
|
||||
mod add_version;
|
||||
|
@ -27,8 +27,11 @@ pub(crate) const PARENT_VERSION_ID_HEADER: &str = "X-Parent-Version-Id";
|
|||
/// The header name for parent version ID
|
||||
pub(crate) const SNAPSHOT_REQUEST_HEADER: &str = "X-Snapshot-Request";
|
||||
|
||||
/// The type containing a reference to the Storage object in the Actix state.
|
||||
pub(crate) type ServerState = Arc<dyn Storage>;
|
||||
/// The type containing a reference to the persistent state for the server
|
||||
pub(crate) struct ServerState {
|
||||
pub(crate) storage: Box<dyn Storage>,
|
||||
pub(crate) config: ServerConfig,
|
||||
}
|
||||
|
||||
pub(crate) fn api_scope() -> Scope {
|
||||
web::scope("")
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use actix_web::{middleware::Logger, App, HttpServer};
|
||||
use clap::Arg;
|
||||
use taskchampion_sync_server::storage::SqliteStorage;
|
||||
use taskchampion_sync_server::Server;
|
||||
use taskchampion_sync_server::{Server, ServerConfig};
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
|
@ -31,12 +31,33 @@ async fn main() -> anyhow::Result<()> {
|
|||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("snapshot-versions")
|
||||
.long("snapshot-versions")
|
||||
.value_name("NUM")
|
||||
.help("Target number of versions between snapshots")
|
||||
.default_value("100")
|
||||
.takes_value(true)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("snapshot-days")
|
||||
.long("snapshot-days")
|
||||
.value_name("NUM")
|
||||
.help("Target number of days between snapshots")
|
||||
.default_value("14")
|
||||
.takes_value(true)
|
||||
.required(false),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let data_dir = matches.value_of("data-dir").unwrap();
|
||||
let port = matches.value_of("port").unwrap();
|
||||
let snapshot_versions = matches.value_of("snapshot-versions").unwrap();
|
||||
let snapshot_days = matches.value_of("snapshot-versions").unwrap();
|
||||
|
||||
let server = Server::new(Box::new(SqliteStorage::new(data_dir)?));
|
||||
let config = ServerConfig::from_args(snapshot_days, snapshot_versions)?;
|
||||
let server = Server::new(config, Box::new(SqliteStorage::new(data_dir)?));
|
||||
|
||||
log::warn!("Serving on port {}", port);
|
||||
HttpServer::new(move || {
|
||||
|
@ -58,7 +79,7 @@ mod test {
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn test_index_get() {
|
||||
let server = Server::new(Box::new(InMemoryStorage::new()));
|
||||
let server = Server::new(Default::default(), Box::new(InMemoryStorage::new()));
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
|
|
@ -6,7 +6,9 @@ pub mod storage;
|
|||
|
||||
use crate::storage::Storage;
|
||||
use actix_web::{get, middleware, web, Responder};
|
||||
use anyhow::Context;
|
||||
use api::{api_scope, ServerState};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[get("/")]
|
||||
async fn index() -> impl Responder {
|
||||
|
@ -16,14 +18,44 @@ async fn index() -> impl Responder {
|
|||
/// A Server represents a sync server.
|
||||
#[derive(Clone)]
|
||||
pub struct Server {
|
||||
storage: ServerState,
|
||||
server_state: Arc<ServerState>,
|
||||
}
|
||||
|
||||
/// ServerConfig contains configuration parameters for the server.
|
||||
pub struct ServerConfig {
|
||||
/// Target number of days between snapshots.
|
||||
pub snapshot_days: i64,
|
||||
|
||||
/// Target number of versions between snapshots.
|
||||
pub snapshot_versions: u32,
|
||||
}
|
||||
|
||||
impl Default for ServerConfig {
|
||||
fn default() -> Self {
|
||||
ServerConfig {
|
||||
snapshot_days: 14,
|
||||
snapshot_versions: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ServerConfig {
|
||||
pub fn from_args(snapshot_days: &str, snapshot_versions: &str) -> anyhow::Result<ServerConfig> {
|
||||
Ok(ServerConfig {
|
||||
snapshot_days: snapshot_days
|
||||
.parse()
|
||||
.context("--snapshot-days must be a number")?,
|
||||
snapshot_versions: snapshot_versions
|
||||
.parse()
|
||||
.context("--snapshot-days must be a number")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Create a new sync server with the given storage implementation.
|
||||
pub fn new(storage: Box<dyn Storage>) -> Self {
|
||||
pub fn new(config: ServerConfig, storage: Box<dyn Storage>) -> Self {
|
||||
Self {
|
||||
storage: storage.into(),
|
||||
server_state: Arc::new(ServerState { config, storage }),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +63,7 @@ impl Server {
|
|||
pub fn config(&self, cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("")
|
||||
.data(self.storage.clone())
|
||||
.data(self.server_state.clone())
|
||||
.wrap(
|
||||
middleware::DefaultHeaders::new()
|
||||
.header("Cache-Control", "no-store, max-age=0"),
|
||||
|
@ -55,7 +87,7 @@ mod test {
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn test_cache_control() {
|
||||
let server = Server::new(Box::new(InMemoryStorage::new()));
|
||||
let server = Server::new(Default::default(), Box::new(InMemoryStorage::new()));
|
||||
let app = App::new().configure(|sc| server.config(sc));
|
||||
let mut app = test::init_service(app).await;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//! invariants, and so on. This does not implement the HTTP-specific portions; those
|
||||
//! are in [`crate::api`]. See the protocol documentation for details.
|
||||
use crate::storage::{Client, Snapshot, StorageTxn};
|
||||
use crate::ServerConfig; // TODO: move here
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -13,12 +14,6 @@ pub const NIL_VERSION_ID: VersionId = Uuid::nil();
|
|||
/// than this will be rejected.
|
||||
const SNAPSHOT_SEARCH_LEN: i32 = 5;
|
||||
|
||||
/// Maximum number of days between snapshots
|
||||
const SNAPSHOT_DAYS: i64 = 14;
|
||||
|
||||
/// Maximum number of versions between snapshots
|
||||
const SNAPSHOT_VERSIONS: u32 = 30;
|
||||
|
||||
pub(crate) type HistorySegment = Vec<u8>;
|
||||
pub(crate) type ClientKey = Uuid;
|
||||
pub(crate) type VersionId = Uuid;
|
||||
|
@ -38,6 +33,7 @@ pub(crate) enum GetVersionResult {
|
|||
/// Implementation of the GetChildVersion protocol transaction
|
||||
pub(crate) fn get_child_version<'a>(
|
||||
mut txn: Box<dyn StorageTxn + 'a>,
|
||||
_config: &ServerConfig,
|
||||
client_key: ClientKey,
|
||||
client: Client,
|
||||
parent_version_id: VersionId,
|
||||
|
@ -96,10 +92,10 @@ pub(crate) enum SnapshotUrgency {
|
|||
|
||||
impl SnapshotUrgency {
|
||||
/// Calculate the urgency for a snapshot based on its age in days
|
||||
fn for_days(days: i64) -> Self {
|
||||
if days >= SNAPSHOT_DAYS * 3 / 2 {
|
||||
fn for_days(config: &ServerConfig, days: i64) -> Self {
|
||||
if days >= config.snapshot_days * 3 / 2 {
|
||||
SnapshotUrgency::High
|
||||
} else if days >= SNAPSHOT_DAYS {
|
||||
} else if days >= config.snapshot_days {
|
||||
SnapshotUrgency::Low
|
||||
} else {
|
||||
SnapshotUrgency::None
|
||||
|
@ -107,10 +103,10 @@ impl SnapshotUrgency {
|
|||
}
|
||||
|
||||
/// Calculate the urgency for a snapshot based on its age in versions
|
||||
fn for_versions_since(versions_since: u32) -> Self {
|
||||
if versions_since >= SNAPSHOT_VERSIONS * 3 / 2 {
|
||||
fn for_versions_since(config: &ServerConfig, versions_since: u32) -> Self {
|
||||
if versions_since >= config.snapshot_versions * 3 / 2 {
|
||||
SnapshotUrgency::High
|
||||
} else if versions_since >= SNAPSHOT_VERSIONS {
|
||||
} else if versions_since >= config.snapshot_versions {
|
||||
SnapshotUrgency::Low
|
||||
} else {
|
||||
SnapshotUrgency::None
|
||||
|
@ -121,6 +117,7 @@ impl SnapshotUrgency {
|
|||
/// Implementation of the AddVersion protocol transaction
|
||||
pub(crate) fn add_version<'a>(
|
||||
mut txn: Box<dyn StorageTxn + 'a>,
|
||||
config: &ServerConfig,
|
||||
client_key: ClientKey,
|
||||
client: Client,
|
||||
parent_version_id: VersionId,
|
||||
|
@ -156,7 +153,7 @@ pub(crate) fn add_version<'a>(
|
|||
let time_urgency = match client.snapshot {
|
||||
None => SnapshotUrgency::High,
|
||||
Some(Snapshot { timestamp, .. }) => {
|
||||
SnapshotUrgency::for_days((Utc::now() - timestamp).num_days())
|
||||
SnapshotUrgency::for_days(config, (Utc::now() - timestamp).num_days())
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -164,7 +161,7 @@ pub(crate) fn add_version<'a>(
|
|||
let version_urgency = match client.snapshot {
|
||||
None => SnapshotUrgency::High,
|
||||
Some(Snapshot { versions_since, .. }) => {
|
||||
SnapshotUrgency::for_versions_since(versions_since)
|
||||
SnapshotUrgency::for_versions_since(config, versions_since)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -177,6 +174,7 @@ pub(crate) fn add_version<'a>(
|
|||
/// Implementation of the AddSnapshot protocol transaction
|
||||
pub(crate) fn add_snapshot<'a>(
|
||||
mut txn: Box<dyn StorageTxn + 'a>,
|
||||
_config: &ServerConfig,
|
||||
client_key: ClientKey,
|
||||
client: Client,
|
||||
version_id: VersionId,
|
||||
|
@ -261,6 +259,7 @@ pub(crate) fn add_snapshot<'a>(
|
|||
/// Implementation of the GetSnapshot protocol transaction
|
||||
pub(crate) fn get_snapshot<'a>(
|
||||
mut txn: Box<dyn StorageTxn + 'a>,
|
||||
_config: &ServerConfig,
|
||||
client_key: ClientKey,
|
||||
client: Client,
|
||||
) -> anyhow::Result<Option<(Uuid, Vec<u8>)>> {
|
||||
|
@ -297,18 +296,29 @@ mod test {
|
|||
#[test]
|
||||
fn snapshot_urgency_for_days() {
|
||||
use SnapshotUrgency::*;
|
||||
assert_eq!(SnapshotUrgency::for_days(0), None);
|
||||
assert_eq!(SnapshotUrgency::for_days(SNAPSHOT_DAYS), Low);
|
||||
assert_eq!(SnapshotUrgency::for_days(SNAPSHOT_DAYS * 2), High);
|
||||
let config = ServerConfig::default();
|
||||
assert_eq!(SnapshotUrgency::for_days(&config, 0), None);
|
||||
assert_eq!(
|
||||
SnapshotUrgency::for_days(&config, config.snapshot_days),
|
||||
Low
|
||||
);
|
||||
assert_eq!(
|
||||
SnapshotUrgency::for_days(&config, config.snapshot_days * 2),
|
||||
High
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_urgency_for_versions_since() {
|
||||
use SnapshotUrgency::*;
|
||||
assert_eq!(SnapshotUrgency::for_versions_since(0), None);
|
||||
assert_eq!(SnapshotUrgency::for_versions_since(SNAPSHOT_VERSIONS), Low);
|
||||
let config = ServerConfig::default();
|
||||
assert_eq!(SnapshotUrgency::for_versions_since(&config, 0), None);
|
||||
assert_eq!(
|
||||
SnapshotUrgency::for_versions_since(SNAPSHOT_VERSIONS * 2),
|
||||
SnapshotUrgency::for_versions_since(&config, config.snapshot_versions),
|
||||
Low
|
||||
);
|
||||
assert_eq!(
|
||||
SnapshotUrgency::for_versions_since(&config, config.snapshot_versions * 2),
|
||||
High
|
||||
);
|
||||
}
|
||||
|
@ -325,7 +335,13 @@ mod test {
|
|||
// when no snapshot exists, the first version is NotFound
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
assert_eq!(
|
||||
get_child_version(txn, client_key, client, NIL_VERSION_ID)?,
|
||||
get_child_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
NIL_VERSION_ID
|
||||
)?,
|
||||
GetVersionResult::NotFound
|
||||
);
|
||||
Ok(())
|
||||
|
@ -353,7 +369,13 @@ mod test {
|
|||
// when a snapshot exists, the first version is GONE
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
assert_eq!(
|
||||
get_child_version(txn, client_key, client, NIL_VERSION_ID)?,
|
||||
get_child_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
NIL_VERSION_ID
|
||||
)?,
|
||||
GetVersionResult::Gone
|
||||
);
|
||||
Ok(())
|
||||
|
@ -374,7 +396,13 @@ mod test {
|
|||
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
assert_eq!(
|
||||
get_child_version(txn, client_key, client, parent_version_id)?,
|
||||
get_child_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
parent_version_id
|
||||
)?,
|
||||
GetVersionResult::NotFound
|
||||
);
|
||||
Ok(())
|
||||
|
@ -395,7 +423,13 @@ mod test {
|
|||
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
assert_eq!(
|
||||
get_child_version(txn, client_key, client, parent_version_id)?,
|
||||
get_child_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
parent_version_id
|
||||
)?,
|
||||
GetVersionResult::Gone
|
||||
);
|
||||
Ok(())
|
||||
|
@ -422,7 +456,13 @@ mod test {
|
|||
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
assert_eq!(
|
||||
get_child_version(txn, client_key, client, parent_version_id)?,
|
||||
get_child_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
parent_version_id
|
||||
)?,
|
||||
GetVersionResult::Success {
|
||||
version_id,
|
||||
parent_version_id,
|
||||
|
@ -516,7 +556,15 @@ mod test {
|
|||
|
||||
// try to add a child of a version other than the latest
|
||||
assert_eq!(
|
||||
add_version(txn, client_key, client, versions[1], vec![3, 6, 9])?.0,
|
||||
add_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
versions[1],
|
||||
vec![3, 6, 9]
|
||||
)?
|
||||
.0,
|
||||
AddVersionResult::ExpectedParentVersion(versions[2])
|
||||
);
|
||||
|
||||
|
@ -539,7 +587,14 @@ mod test {
|
|||
let mut txn = storage.txn()?;
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
|
||||
let result = add_version(txn, client_key, client, versions[0], vec![3, 6, 9])?;
|
||||
let result = add_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
versions[0],
|
||||
vec![3, 6, 9],
|
||||
)?;
|
||||
|
||||
av_success_check(
|
||||
&storage,
|
||||
|
@ -563,7 +618,14 @@ mod test {
|
|||
let client = txn.get_client(client_key)?.unwrap();
|
||||
|
||||
let parent_version_id = Uuid::nil();
|
||||
let result = add_version(txn, client_key, client, parent_version_id, vec![3, 6, 9])?;
|
||||
let result = add_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
parent_version_id,
|
||||
vec![3, 6, 9],
|
||||
)?;
|
||||
|
||||
av_success_check(
|
||||
&storage,
|
||||
|
@ -586,7 +648,14 @@ mod test {
|
|||
let mut txn = storage.txn()?;
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
|
||||
let result = add_version(txn, client_key, client, versions[0], vec![1, 2, 3])?;
|
||||
let result = add_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
versions[0],
|
||||
vec![1, 2, 3],
|
||||
)?;
|
||||
|
||||
av_success_check(
|
||||
&storage,
|
||||
|
@ -610,7 +679,14 @@ mod test {
|
|||
let mut txn = storage.txn()?;
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
|
||||
let result = add_version(txn, client_key, client, versions[0], vec![1, 2, 3])?;
|
||||
let result = add_version(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
versions[0],
|
||||
vec![1, 2, 3],
|
||||
)?;
|
||||
|
||||
av_success_check(
|
||||
&storage,
|
||||
|
@ -634,7 +710,17 @@ mod test {
|
|||
let mut txn = storage.txn()?;
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
|
||||
let result = add_version(txn, client_key, client, versions[49], vec![1, 2, 3])?;
|
||||
let result = add_version(
|
||||
txn,
|
||||
&ServerConfig {
|
||||
snapshot_versions: 30,
|
||||
..ServerConfig::default()
|
||||
},
|
||||
client_key,
|
||||
client,
|
||||
versions[49],
|
||||
vec![1, 2, 3],
|
||||
)?;
|
||||
|
||||
av_success_check(
|
||||
&storage,
|
||||
|
@ -664,7 +750,14 @@ mod test {
|
|||
|
||||
// add a snapshot for that version
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
add_snapshot(txn, client_key, client, version_id, vec![1, 2, 3])?;
|
||||
add_snapshot(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
version_id,
|
||||
vec![1, 2, 3],
|
||||
)?;
|
||||
|
||||
// verify the snapshot
|
||||
let mut txn = storage.txn()?;
|
||||
|
@ -697,7 +790,14 @@ mod test {
|
|||
|
||||
// add a snapshot for version 1
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
add_snapshot(txn, client_key, client, version_id_1, vec![1, 2, 3])?;
|
||||
add_snapshot(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
version_id_1,
|
||||
vec![1, 2, 3],
|
||||
)?;
|
||||
|
||||
// verify the snapshot
|
||||
let mut txn = storage.txn()?;
|
||||
|
@ -731,7 +831,14 @@ mod test {
|
|||
// add a snapshot for unknown version
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
let version_id_unk = Uuid::new_v4();
|
||||
add_snapshot(txn, client_key, client, version_id_unk, vec![1, 2, 3])?;
|
||||
add_snapshot(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
version_id_unk,
|
||||
vec![1, 2, 3],
|
||||
)?;
|
||||
|
||||
// verify the snapshot does not exist
|
||||
let mut txn = storage.txn()?;
|
||||
|
@ -763,7 +870,14 @@ mod test {
|
|||
|
||||
// add a snapshot for the earliest of those
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
add_snapshot(txn, client_key, client, version_ids[0], vec![1, 2, 3])?;
|
||||
add_snapshot(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
version_ids[0],
|
||||
vec![1, 2, 3],
|
||||
)?;
|
||||
|
||||
// verify the snapshot does not exist
|
||||
let mut txn = storage.txn()?;
|
||||
|
@ -805,7 +919,14 @@ mod test {
|
|||
|
||||
// add a snapshot for the earliest of those
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
add_snapshot(txn, client_key, client, version_ids[0], vec![9, 9, 9])?;
|
||||
add_snapshot(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
version_ids[0],
|
||||
vec![9, 9, 9],
|
||||
)?;
|
||||
|
||||
// verify the snapshot was not replaced
|
||||
let mut txn = storage.txn()?;
|
||||
|
@ -834,7 +955,14 @@ mod test {
|
|||
|
||||
// add a snapshot for the nil version
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
add_snapshot(txn, client_key, client, NIL_VERSION_ID, vec![9, 9, 9])?;
|
||||
add_snapshot(
|
||||
txn,
|
||||
&ServerConfig::default(),
|
||||
client_key,
|
||||
client,
|
||||
NIL_VERSION_ID,
|
||||
vec![9, 9, 9],
|
||||
)?;
|
||||
|
||||
// verify the snapshot does not exist
|
||||
let mut txn = storage.txn()?;
|
||||
|
@ -867,7 +995,7 @@ mod test {
|
|||
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
assert_eq!(
|
||||
get_snapshot(txn, client_key, client)?,
|
||||
get_snapshot(txn, &ServerConfig::default(), client_key, client)?,
|
||||
Some((snapshot_version_id, data.clone()))
|
||||
);
|
||||
|
||||
|
@ -885,7 +1013,10 @@ mod test {
|
|||
txn.new_client(client_key, NIL_VERSION_ID)?;
|
||||
let client = txn.get_client(client_key)?.unwrap();
|
||||
|
||||
assert_eq!(get_snapshot(txn, client_key, client)?, None);
|
||||
assert_eq!(
|
||||
get_snapshot(txn, &ServerConfig::default(), client_key, client)?,
|
||||
None
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::server::{
|
||||
AddVersionResult, GetVersionResult, HistorySegment, Server, VersionId, NO_VERSION_ID,
|
||||
AddVersionResult, GetVersionResult, HistorySegment, Server, VersionId, NIL_VERSION_ID,
|
||||
};
|
||||
use crate::storage::sqlite::StoredUuid;
|
||||
use anyhow::Context;
|
||||
|
@ -53,7 +53,7 @@ impl LocalServer {
|
|||
|r| r.get(0),
|
||||
)
|
||||
.optional()?;
|
||||
Ok(result.map(|x| x.0).unwrap_or(NO_VERSION_ID))
|
||||
Ok(result.map(|x| x.0).unwrap_or(NIL_VERSION_ID))
|
||||
}
|
||||
|
||||
fn set_latest_version_id(&mut self, version_id: VersionId) -> anyhow::Result<()> {
|
||||
|
@ -122,7 +122,7 @@ impl Server for LocalServer {
|
|||
|
||||
// check the parent_version_id for linearity
|
||||
let latest_version_id = self.get_latest_version_id()?;
|
||||
if latest_version_id != NO_VERSION_ID && parent_version_id != latest_version_id {
|
||||
if latest_version_id != NIL_VERSION_ID && parent_version_id != latest_version_id {
|
||||
return Ok(AddVersionResult::ExpectedParentVersion(latest_version_id));
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ mod test {
|
|||
fn test_empty() -> anyhow::Result<()> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
let mut server = LocalServer::new(&tmp_dir.path())?;
|
||||
let child_version = server.get_child_version(NO_VERSION_ID)?;
|
||||
let child_version = server.get_child_version(NIL_VERSION_ID)?;
|
||||
assert_eq!(child_version, GetVersionResult::NoSuchVersion);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -176,17 +176,17 @@ mod test {
|
|||
let tmp_dir = TempDir::new()?;
|
||||
let mut server = LocalServer::new(&tmp_dir.path())?;
|
||||
let history = b"1234".to_vec();
|
||||
match server.add_version(NO_VERSION_ID, history.clone())? {
|
||||
match server.add_version(NIL_VERSION_ID, history.clone())? {
|
||||
AddVersionResult::ExpectedParentVersion(_) => {
|
||||
panic!("should have accepted the version")
|
||||
}
|
||||
AddVersionResult::Ok(version_id) => {
|
||||
let new_version = server.get_child_version(NO_VERSION_ID)?;
|
||||
let new_version = server.get_child_version(NIL_VERSION_ID)?;
|
||||
assert_eq!(
|
||||
new_version,
|
||||
GetVersionResult::Version {
|
||||
version_id,
|
||||
parent_version_id: NO_VERSION_ID,
|
||||
parent_version_id: NIL_VERSION_ID,
|
||||
history_segment: history,
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::server::{
|
||||
AddVersionResult, GetVersionResult, HistorySegment, Server, VersionId, NO_VERSION_ID,
|
||||
AddVersionResult, GetVersionResult, HistorySegment, Server, VersionId, NIL_VERSION_ID,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
@ -20,7 +20,7 @@ impl TestServer {
|
|||
/// A test server has no notion of clients, signatures, encryption, etc.
|
||||
pub fn new() -> TestServer {
|
||||
TestServer {
|
||||
latest_version_id: NO_VERSION_ID,
|
||||
latest_version_id: NIL_VERSION_ID,
|
||||
versions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ impl Server for TestServer {
|
|||
// no signature validation
|
||||
|
||||
// check the parent_version_id for linearity
|
||||
if self.latest_version_id != NO_VERSION_ID {
|
||||
if self.latest_version_id != NIL_VERSION_ID {
|
||||
if parent_version_id != self.latest_version_id {
|
||||
return Ok(AddVersionResult::ExpectedParentVersion(
|
||||
self.latest_version_id,
|
||||
|
|
|
@ -4,7 +4,7 @@ use uuid::Uuid;
|
|||
pub type VersionId = Uuid;
|
||||
|
||||
/// The distinguished value for "no version"
|
||||
pub const NO_VERSION_ID: VersionId = Uuid::nil();
|
||||
pub const NIL_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.
|
||||
|
|
|
@ -36,7 +36,7 @@ fn taskmap_with(mut properties: Vec<(String, String)>) -> TaskMap {
|
|||
pub use crate::server::VersionId;
|
||||
|
||||
/// The default for base_version.
|
||||
pub(crate) const DEFAULT_BASE_VERSION: Uuid = crate::server::NO_VERSION_ID;
|
||||
pub(crate) const DEFAULT_BASE_VERSION: Uuid = crate::server::NIL_VERSION_ID;
|
||||
|
||||
/// A Storage transaction, in which storage operations are performed.
|
||||
///
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue