datastore: add new Lookup for operations tracking

We sometimes need to do some in-memory only stuff, e.g., to check if
GC is already running for a datastore, which is a try_lock on a mutex
that is in-memory.

Actually the whole thing would be nicer if we could guarantee to hold
the correct contract statically, e.g., like
https://docs.rust-embedded.org/book/static-guarantees/design-contracts.html

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2022-05-12 11:31:07 +02:00
parent d4d730e589
commit 0408f60b58
3 changed files with 21 additions and 2 deletions

View File

@ -20,8 +20,17 @@ pub const MAINTENANCE_MESSAGE_SCHEMA: Schema =
#[derive(Clone, Copy, Debug)]
/// Operation requirements, used when checking for maintenance mode.
pub enum Operation {
/// for any read operation like backup restore or RRD metric collection
Read,
/// for any write/delete operation, like backup create or GC
Write,
/// for any purely logical operation on the in-memory state of the datastore, e.g., to check if
/// some mutex could be locked (e.g., GC already running?)
///
/// NOTE: one must *not* do any IO operations when only helding this Op state
Lookup,
// GarbageCollect or Delete?
}
#[api]
@ -29,6 +38,12 @@ pub enum Operation {
#[serde(rename_all = "kebab-case")]
/// Maintenance type.
pub enum MaintenanceType {
// TODO:
// - Add "unmounting" once we got pluggable datastores
// - Add "GarbageCollection" or "DeleteOnly" as type and track GC (or all deletes) as separate
// operation, so that one can enable a mode where nothing new can be added but stuff can be
// cleaned
/// Only read operations are allowed on the datastore.
ReadOnly,
/// Neither read nor write operations are allowed on the datastore.
@ -65,7 +80,9 @@ impl MaintenanceMode {
.decode_utf8()
.unwrap_or(Cow::Borrowed(""));
if self.ty == MaintenanceType::Offline {
if let Some(Operation::Lookup) = operation {
return Ok(());
} else if self.ty == MaintenanceType::Offline {
bail!("offline maintenance mode: {}", message);
} else if self.ty == MaintenanceType::ReadOnly {
if let Some(Operation::Write) = operation {

View File

@ -133,7 +133,7 @@ impl DataStore {
if let Some(maintenance_mode) = config.get_maintenance_mode() {
if let Err(error) = maintenance_mode.check(operation) {
bail!("datastore '{}' is in {}", name, error);
bail!("datastore '{name}' is in {error}");
}
}

View File

@ -80,6 +80,7 @@ pub fn update_active_operations(name: &str, operation: Operation, count: i64) ->
match operation {
Operation::Read => task.active_operations.read += count,
Operation::Write => task.active_operations.write += count,
Operation::Lookup => (), // no IO must happen there
};
}
Some(task.clone())
@ -98,6 +99,7 @@ pub fn update_active_operations(name: &str, operation: Operation, count: i64) ->
active_operations: match operation {
Operation::Read => ActiveOperationStats { read: 1, write: 0 },
Operation::Write => ActiveOperationStats { read: 0, write: 1 },
Operation::Lookup => ActiveOperationStats { read: 0, write: 0 },
},
})
}