syn2mas/mas_writer/
checks.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.

//! # MAS Database Checks
//!
//! This module provides safety checks to run against a MAS database before
//! running the Synapse-to-MAS migration.

use thiserror::Error;
use thiserror_ext::ContextInto;
use tracing::Instrument as _;

use super::{MAS_TABLES_AFFECTED_BY_MIGRATION, is_syn2mas_in_progress, locking::LockedMasDatabase};

#[derive(Debug, Error, ContextInto)]
pub enum Error {
    #[error("the MAS database is not empty: rows found in at least `{table}`")]
    MasDatabaseNotEmpty { table: &'static str },

    #[error("query against {table} failed — is this actually a MAS database?")]
    MaybeNotMas {
        #[source]
        source: sqlx::Error,
        table: &'static str,
    },

    #[error(transparent)]
    Sqlx(#[from] sqlx::Error),

    #[error("unable to check if syn2mas is already in progress")]
    UnableToCheckInProgress(#[source] super::Error),
}

/// Check that a MAS database is ready for being migrated to.
///
/// Concretely, this checks that the database is empty.
///
/// If syn2mas is already in progress on this database, the checks are skipped.
///
/// # Errors
///
/// Errors are returned under the following circumstances:
///
/// - If any database access error occurs.
/// - If any MAS tables involved in the migration are not empty.
/// - If we can't check whether syn2mas is already in progress on this database
///   or not.
#[tracing::instrument(name = "syn2mas.mas_pre_migration_checks", skip_all)]
pub async fn mas_pre_migration_checks(mas_connection: &mut LockedMasDatabase) -> Result<(), Error> {
    if is_syn2mas_in_progress(mas_connection.as_mut())
        .await
        .map_err(Error::UnableToCheckInProgress)?
    {
        // syn2mas already in progress, so we already performed the checks
        return Ok(());
    }

    // Check that the database looks like a MAS database and that it is also an
    // empty database.

    for &table in MAS_TABLES_AFFECTED_BY_MIGRATION {
        let query = format!("SELECT 1 AS dummy FROM {table} LIMIT 1");
        let span = tracing::info_span!("db.query", db.query.text = query);
        let row_present = sqlx::query(&query)
            .fetch_optional(mas_connection.as_mut())
            .instrument(span)
            .await
            .into_maybe_not_mas(table)?
            .is_some();

        if row_present {
            return Err(Error::MasDatabaseNotEmpty { table });
        }
    }

    Ok(())
}