indexeddb: Move v0 to c5 migration into own module

Signed-off-by: Andy Balaam <andy.balaam@matrix.org>
This commit is contained in:
Andy Balaam
2024-02-05 14:40:05 +00:00
parent f0c97d94b2
commit d64a5e4dcc
2 changed files with 139 additions and 124 deletions

View File

@@ -17,11 +17,12 @@ use tracing::info;
use wasm_bindgen::JsValue;
use crate::{
crypto_store::{indexeddb_serializer::IndexeddbSerializer, keys, Result},
crypto_store::{indexeddb_serializer::IndexeddbSerializer, Result},
IndexeddbCryptoStoreError,
};
mod old_keys;
mod v0_to_v5;
mod v5_to_v7;
mod v7;
mod v7_to_v8;
@@ -44,7 +45,7 @@ pub async fn open_and_upgrade_db(
// Perform the schema-only migrations
if old_version < 5 {
migrate_schema_up_to_v5(name).await?;
v0_to_v5::migrate_schema_up_to_v5(name).await?;
}
// If we have yet to complete the migration to V7, migrate the schema to V6
@@ -85,115 +86,6 @@ pub async fn open_and_upgrade_db(
Ok(IdbDatabase::open_u32(name, 10)?.await?)
}
async fn migrate_schema_up_to_v5(name: &str) -> Result<(), DomException> {
do_schema_upgrade(name, 5, |db, old_version| {
// An old_version of 1 could either mean actually the first version of the
// schema, or a completely empty schema that has been created with a
// call to `IdbDatabase::open` with no explicit "version". So, to determine
// if we need to create the V1 stores, we actually check if the schema is empty.
if db.object_store_names().next().is_none() {
migrate_stores_to_v1(db)?;
}
if old_version < 2 {
migrate_stores_to_v2(db)?;
}
if old_version < 3 {
migrate_stores_to_v3(db)?;
}
if old_version < 4 {
migrate_stores_to_v4(db)?;
}
if old_version < 5 {
migrate_stores_to_v5(db)?;
}
Ok(())
})
.await
}
fn migrate_stores_to_v1(db: &IdbDatabase) -> Result<(), DomException> {
db.create_object_store(keys::CORE)?;
db.create_object_store(keys::SESSION)?;
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
db.create_object_store(keys::TRACKED_USERS)?;
db.create_object_store(keys::OLM_HASHES)?;
db.create_object_store(keys::DEVICES)?;
db.create_object_store(keys::IDENTITIES)?;
db.create_object_store(keys::BACKUP_KEYS)?;
Ok(())
}
fn migrate_stores_to_v2(db: &IdbDatabase) -> Result<(), DomException> {
// We changed how we store inbound group sessions, the key used to
// be a tuple of `(room_id, sender_key, session_id)` now it's a
// tuple of `(room_id, session_id)`
//
// Let's just drop the whole object store.
db.delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
db.create_object_store(keys::ROOM_SETTINGS)?;
Ok(())
}
fn migrate_stores_to_v3(db: &IdbDatabase) -> Result<(), DomException> {
// We changed the way we store outbound session.
// ShareInfo changed from a struct to an enum with struct variant.
// Let's just discard the existing outbounds
db.delete_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
// Support for MSC2399 withheld codes
db.create_object_store(keys::DIRECT_WITHHELD_INFO)?;
Ok(())
}
fn migrate_stores_to_v4(db: &IdbDatabase) -> Result<(), DomException> {
db.create_object_store(keys::SECRETS_INBOX)?;
Ok(())
}
fn migrate_stores_to_v5(db: &IdbDatabase) -> Result<(), DomException> {
// Create a new store for outgoing secret requests
let object_store = db.create_object_store(keys::GOSSIP_REQUESTS)?;
let mut params = IdbIndexParameters::new();
params.unique(false);
object_store.create_index_with_params(
keys::GOSSIP_REQUESTS_UNSENT_INDEX,
&IdbKeyPath::str("unsent"),
&params,
)?;
let mut params = IdbIndexParameters::new();
params.unique(true);
object_store.create_index_with_params(
keys::GOSSIP_REQUESTS_BY_INFO_INDEX,
&IdbKeyPath::str("info"),
&params,
)?;
if db.object_store_names().any(|n| n == "outgoing_secret_requests") {
// Delete the old store names. We just delete any existing requests.
db.delete_object_store("outgoing_secret_requests")?;
db.delete_object_store("unsent_secret_requests")?;
db.delete_object_store("secret_requests_by_info")?;
}
Ok(())
}
async fn do_schema_upgrade<F>(name: &str, version: u32, f: F) -> Result<(), DomException>
where
F: Fn(&IdbDatabase, u32) -> Result<(), JsValue> + 'static,
@@ -235,10 +127,11 @@ mod tests {
use tracing_subscriber::util::SubscriberInitExt;
use web_sys::console;
use super::v7::InboundGroupSessionIndexedDbObject2;
use super::{v0_to_v5, v7::InboundGroupSessionIndexedDbObject2};
use crate::{
crypto_store::{
indexeddb_serializer::MaybeEncrypted, migrations::*, InboundGroupSessionIndexedDbObject,
indexeddb_serializer::MaybeEncrypted, keys, migrations::*,
InboundGroupSessionIndexedDbObject,
},
IndexeddbCryptoStore,
};
@@ -626,16 +519,7 @@ mod tests {
}
async fn create_v5_db(name: &str) -> std::result::Result<IdbDatabase, DomException> {
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(name, 5)?;
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
let db = evt.db();
migrate_stores_to_v1(db)?;
migrate_stores_to_v2(db)?;
migrate_stores_to_v3(db)?;
migrate_stores_to_v4(db)?;
migrate_stores_to_v5(db)?;
Ok(())
}));
db_req.await
v0_to_v5::migrate_schema_up_to_v5(name).await?;
IdbDatabase::open_u32(name, 5)?.await
}
}

View File

@@ -0,0 +1,131 @@
// Copyright 2024 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use indexed_db_futures::{IdbDatabase, IdbKeyPath};
use web_sys::{DomException, IdbIndexParameters};
use crate::crypto_store::{
keys,
migrations::{do_schema_upgrade, old_keys},
Result,
};
pub(crate) async fn migrate_schema_up_to_v5(name: &str) -> Result<(), DomException> {
do_schema_upgrade(name, 5, |db, old_version| {
// An old_version of 1 could either mean actually the first version of the
// schema, or a completely empty schema that has been created with a
// call to `IdbDatabase::open` with no explicit "version". So, to determine
// if we need to create the V1 stores, we actually check if the schema is empty.
if db.object_store_names().next().is_none() {
migrate_stores_to_v1(db)?;
}
if old_version < 2 {
migrate_stores_to_v2(db)?;
}
if old_version < 3 {
migrate_stores_to_v3(db)?;
}
if old_version < 4 {
migrate_stores_to_v4(db)?;
}
if old_version < 5 {
migrate_stores_to_v5(db)?;
}
Ok(())
})
.await
}
fn migrate_stores_to_v1(db: &IdbDatabase) -> Result<(), DomException> {
db.create_object_store(keys::CORE)?;
db.create_object_store(keys::SESSION)?;
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
db.create_object_store(keys::TRACKED_USERS)?;
db.create_object_store(keys::OLM_HASHES)?;
db.create_object_store(keys::DEVICES)?;
db.create_object_store(keys::IDENTITIES)?;
db.create_object_store(keys::BACKUP_KEYS)?;
Ok(())
}
fn migrate_stores_to_v2(db: &IdbDatabase) -> Result<(), DomException> {
// We changed how we store inbound group sessions, the key used to
// be a tuple of `(room_id, sender_key, session_id)` now it's a
// tuple of `(room_id, session_id)`
//
// Let's just drop the whole object store.
db.delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
db.create_object_store(keys::ROOM_SETTINGS)?;
Ok(())
}
fn migrate_stores_to_v3(db: &IdbDatabase) -> Result<(), DomException> {
// We changed the way we store outbound session.
// ShareInfo changed from a struct to an enum with struct variant.
// Let's just discard the existing outbounds
db.delete_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
// Support for MSC2399 withheld codes
db.create_object_store(keys::DIRECT_WITHHELD_INFO)?;
Ok(())
}
fn migrate_stores_to_v4(db: &IdbDatabase) -> Result<(), DomException> {
db.create_object_store(keys::SECRETS_INBOX)?;
Ok(())
}
fn migrate_stores_to_v5(db: &IdbDatabase) -> Result<(), DomException> {
// Create a new store for outgoing secret requests
let object_store = db.create_object_store(keys::GOSSIP_REQUESTS)?;
let mut params = IdbIndexParameters::new();
params.unique(false);
object_store.create_index_with_params(
keys::GOSSIP_REQUESTS_UNSENT_INDEX,
&IdbKeyPath::str("unsent"),
&params,
)?;
let mut params = IdbIndexParameters::new();
params.unique(true);
object_store.create_index_with_params(
keys::GOSSIP_REQUESTS_BY_INFO_INDEX,
&IdbKeyPath::str("info"),
&params,
)?;
if db.object_store_names().any(|n| n == "outgoing_secret_requests") {
// Delete the old store names. We just delete any existing requests.
db.delete_object_store("outgoing_secret_requests")?;
db.delete_object_store("unsent_secret_requests")?;
db.delete_object_store("secret_requests_by_info")?;
}
Ok(())
}