From 122361795b3b1376c6ba03ed6b160e9b89da93d7 Mon Sep 17 00:00:00 2001 From: Vika Date: Sun, 1 May 2022 04:35:16 +0300 Subject: FileStorage: lockless reads and atomic writes - Reads don't lock anymore. At all. - Writes create a temporary file and use `rename(2)` to atomically replace it - since OpenOptions::create_new(true) is used, tempfile creation is atomic (and since tempfile names are per-post, a post can only be edited by one request at a time) - Since written files get atomically replaced, readers can't read a corrupted file Potential pitfalls: 1. This approach is not covered by unit tests (yet) 2. Stale tempfiles can prevent editing posts (can be solved by throwing out tempfiles that are older than, say, a day) 3. Crashed edits can leave stale tempfiles (honestly that sounds better than corrupting the whole database, doesn't sound like a bug to me at all!) --- src/database/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/database/mod.rs') diff --git a/src/database/mod.rs b/src/database/mod.rs index 57223f8..0d98dd4 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -35,12 +35,14 @@ pub enum ErrorKind { /// The user's query or request to the database was malformed. Used whenever the database processes /// the user's query directly, such as when editing posts inside of the database (e.g. Redis backend) BadRequest, + /// the user's query collided with an in-flight request and needs to be retried + Conflict, /// - ErrorKind::Other - when something so weird happens that it becomes undescribable. Other, } /// Enum representing settings that might be stored in the site's database. -#[derive(Serialize, Debug, Clone, Copy)] +#[derive(Deserialize, Serialize, Debug, Clone, Copy)] #[serde(rename_all = "snake_case")] pub enum Settings { /// The name of the website -- displayed in the header and the browser titlebar. @@ -87,6 +89,7 @@ impl std::fmt::Display for StorageError { ErrorKind::PermissionDenied => write!(f, "permission denied: "), ErrorKind::NotFound => write!(f, "not found: "), ErrorKind::BadRequest => write!(f, "bad request: "), + ErrorKind::Conflict => write!(f, "conflict with an in-flight request or existing data: "), ErrorKind::Other => write!(f, "generic storage layer error: "), } { Ok(_) => write!(f, "{}", self.msg), -- cgit 1.4.1