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