forked from projectmoon/tenebrous-dicebot
Migrate existing delineators to delimiter 0xfe.
This commit is contained in:
parent
3ccd60c173
commit
7e15379c58
|
@ -6,7 +6,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
/// Shortcut to defining db migration versions. Will probably
|
/// Shortcut to defining db migration versions. Will probably
|
||||||
/// eventually be moved to a config file.
|
/// eventually be moved to a config file.
|
||||||
const MIGRATION_VERSION: u32 = 3;
|
const MIGRATION_VERSION: u32 = 4;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
|
|
|
@ -6,9 +6,10 @@ use phf::phf_map;
|
||||||
pub(super) type DataMigration = (fn(&Database) -> Result<(), DataError>, &'static str);
|
pub(super) type DataMigration = (fn(&Database) -> Result<(), DataError>, &'static str);
|
||||||
|
|
||||||
static MIGRATIONS: phf::Map<u32, DataMigration> = phf_map! {
|
static MIGRATIONS: phf::Map<u32, DataMigration> = phf_map! {
|
||||||
1u32 => (add_room_user_variable_count, "add_room_user_variable_count"),
|
1u32 => (add_room_user_variable_count::migrate, "add_room_user_variable_count"),
|
||||||
2u32 => (delete_v0_schema, "delete_v0_schema"),
|
2u32 => (delete_v0_schema, "delete_v0_schema"),
|
||||||
3u32 => (delete_variable_count, "delete_variable_count"),
|
3u32 => (delete_variable_count, "delete_variable_count"),
|
||||||
|
4u32 => (change_delineator_delimiter::migrate, "change_delineator_delimiter")
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get_migrations(versions: &[u32]) -> Result<Vec<DataMigration>, MigrationError> {
|
pub fn get_migrations(versions: &[u32]) -> Result<Vec<DataMigration>, MigrationError> {
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl<'a> From<RoomAndUser<'a>> for Vec<u8> {
|
||||||
fn from(value: RoomAndUser<'a>) -> Vec<u8> {
|
fn from(value: RoomAndUser<'a>) -> Vec<u8> {
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
bytes.extend_from_slice(value.0.as_bytes());
|
bytes.extend_from_slice(value.0.as_bytes());
|
||||||
bytes.push(0xff);
|
bytes.push(0xfe);
|
||||||
bytes.extend_from_slice(value.1.as_bytes());
|
bytes.extend_from_slice(value.1.as_bytes());
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use sled::{Batch, IVec};
|
||||||
use zerocopy::byteorder::U32;
|
use zerocopy::byteorder::U32;
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
|
pub(in crate::db) mod add_room_user_variable_count {
|
||||||
|
use super::*;
|
||||||
//Not to be confused with the super::RoomAndUser delineator.
|
//Not to be confused with the super::RoomAndUser delineator.
|
||||||
#[derive(PartialEq, Eq, std::hash::Hash)]
|
#[derive(PartialEq, Eq, std::hash::Hash)]
|
||||||
struct RoomAndUser {
|
struct RoomAndUser {
|
||||||
|
@ -50,7 +52,7 @@ fn map_value_to_room_and_user(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::db) fn add_room_user_variable_count(db: &Database) -> Result<(), DataError> {
|
pub(in crate::db) fn migrate(db: &Database) -> Result<(), DataError> {
|
||||||
let tree = &db.variables.0;
|
let tree = &db.variables.0;
|
||||||
let prefix = variables_space_prefix("");
|
let prefix = variables_space_prefix("");
|
||||||
|
|
||||||
|
@ -70,7 +72,8 @@ pub(in crate::db) fn add_room_user_variable_count(db: &Database) -> Result<(), D
|
||||||
});
|
});
|
||||||
|
|
||||||
//Start a transaction on the variables tree.
|
//Start a transaction on the variables tree.
|
||||||
let tx_result: Result<_, TransactionError<DataError>> = db.variables.0.transaction(|tx_vars| {
|
let tx_result: Result<_, TransactionError<DataError>> =
|
||||||
|
db.variables.0.transaction(|tx_vars| {
|
||||||
let batch = counts.iter().fold(Batch::default(), |mut batch, entry| {
|
let batch = counts.iter().fold(Batch::default(), |mut batch, entry| {
|
||||||
let (info, count) = entry;
|
let (info, count) = entry;
|
||||||
|
|
||||||
|
@ -93,6 +96,7 @@ pub(in crate::db) fn add_room_user_variable_count(db: &Database) -> Result<(), D
|
||||||
tx_result?; //For some reason, it cannot infer the type
|
tx_result?; //For some reason, it cannot infer the type
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(in crate::db) fn delete_v0_schema(db: &Database) -> Result<(), DataError> {
|
pub(in crate::db) fn delete_v0_schema(db: &Database) -> Result<(), DataError> {
|
||||||
let mut vars = db.variables.0.scan_prefix("");
|
let mut vars = db.variables.0.scan_prefix("");
|
||||||
|
@ -132,3 +136,92 @@ pub(in crate::db) fn delete_variable_count(db: &Database) -> Result<(), DataErro
|
||||||
db.variables.0.apply_batch(batch)?;
|
db.variables.0.apply_batch(batch)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::db) mod change_delineator_delimiter {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// An entry in the room user variables keyspace.
|
||||||
|
struct UserVariableEntry {
|
||||||
|
room_id: String,
|
||||||
|
username: String,
|
||||||
|
variable_name: String,
|
||||||
|
value: IVec,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract keys and values from the variables keyspace according
|
||||||
|
/// to the v1 schema.
|
||||||
|
fn extract_v1_entries(
|
||||||
|
entry: sled::Result<(IVec, IVec)>,
|
||||||
|
) -> Result<UserVariableEntry, MigrationError> {
|
||||||
|
if let Ok((key, value)) = entry {
|
||||||
|
let keys: Vec<Result<&str, _>> = key
|
||||||
|
.split(|&b| b == 0xff)
|
||||||
|
.map(|b| str::from_utf8(b))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let &[_, Ok(room_id), Ok(username), Ok(variable)] = keys.as_slice() {
|
||||||
|
Ok(UserVariableEntry {
|
||||||
|
room_id: room_id.to_owned(),
|
||||||
|
username: username.to_owned(),
|
||||||
|
variable_name: variable.to_owned(),
|
||||||
|
value: value,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(MigrationError::MigrationFailed(
|
||||||
|
"a key violates utf8 schema".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(MigrationError::MigrationFailed(
|
||||||
|
"encountered unexpected key".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an old key, where delineator is separated by 0xff.
|
||||||
|
fn create_old_key(prefix: &[u8], insert: &UserVariableEntry) -> Vec<u8> {
|
||||||
|
let mut key = vec![];
|
||||||
|
key.extend_from_slice(&prefix); //prefix already has 0xff.
|
||||||
|
key.extend_from_slice(&insert.room_id.as_bytes());
|
||||||
|
key.push(0xff);
|
||||||
|
key.extend_from_slice(&insert.username.as_bytes());
|
||||||
|
key.push(0xff);
|
||||||
|
key.extend_from_slice(&insert.variable_name.as_bytes());
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an old key, where delineator is separated by 0xfe.
|
||||||
|
fn create_new_key(prefix: &[u8], insert: &UserVariableEntry) -> Vec<u8> {
|
||||||
|
let mut key = vec![];
|
||||||
|
key.extend_from_slice(&prefix); //prefix already has 0xff.
|
||||||
|
key.extend_from_slice(&insert.room_id.as_bytes());
|
||||||
|
key.push(0xfe);
|
||||||
|
key.extend_from_slice(&insert.username.as_bytes());
|
||||||
|
key.push(0xff);
|
||||||
|
key.extend_from_slice(&insert.variable_name.as_bytes());
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn migrate(db: &Database) -> Result<(), DataError> {
|
||||||
|
let tree = &db.variables.0;
|
||||||
|
let prefix = variables_space_prefix("");
|
||||||
|
|
||||||
|
let results: Vec<UserVariableEntry> = tree
|
||||||
|
.scan_prefix(&prefix)
|
||||||
|
.map(extract_v1_entries)
|
||||||
|
.collect::<Result<Vec<_>, MigrationError>>()?;
|
||||||
|
|
||||||
|
let mut batch = Batch::default();
|
||||||
|
|
||||||
|
for insert in results {
|
||||||
|
let old = create_old_key(&prefix, &insert);
|
||||||
|
let new = create_new_key(&prefix, &insert);
|
||||||
|
|
||||||
|
batch.remove(old);
|
||||||
|
batch.insert(new, insert.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.apply_batch(batch)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue