Skip to content

Commit 95fd7f1

Browse files
committed
WIP: restore constraints earlier
1 parent ff0a27c commit 95fd7f1

File tree

4 files changed

+154
-12
lines changed

4 files changed

+154
-12
lines changed

crates/cli/src/commands/syn2mas.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,16 @@ impl Options {
236236
.await?,
237237
);
238238
}
239-
let writer = MasWriter::new(mas_connection, writer_mas_connections).await?;
239+
let index_restore_conn = database_connection_from_config_with_options(
240+
&config,
241+
&DatabaseConnectOptions {
242+
log_slow_statements: false,
243+
},
244+
)
245+
.await?;
246+
let writer =
247+
MasWriter::new(mas_connection, index_restore_conn, writer_mas_connections)
248+
.await?;
240249

241250
let clock = SystemClock::default();
242251
// TODO is this rng ok?

crates/syn2mas/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
// SPDX-License-Identifier: AGPL-3.0-only
44
// Please see LICENSE in the repository root for full details.
55

6+
#![expect(
7+
clippy::overly_complex_bool_expr,
8+
reason = "TODO: remove when we've removed the hacks"
9+
)]
10+
611
mod mas_writer;
712
mod synapse_reader;
813

crates/syn2mas/src/mas_writer/mod.rs

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ use futures_util::{future::BoxFuture, FutureExt, TryStreamExt};
2121
use sqlx::{query, query_as, Executor, PgConnection};
2222
use thiserror::Error;
2323
use thiserror_ext::{Construct, ContextInto};
24-
use tokio::sync::mpsc::{self, Receiver, Sender};
24+
use tokio::{
25+
sync::mpsc::{self, Receiver, Sender},
26+
task::JoinHandle,
27+
};
2528
use tracing::{error, info, warn, Instrument, Level, Span};
2629
use tracing_indicatif::span_ext::IndicatifSpanExt;
2730
use uuid::Uuid;
@@ -45,6 +48,9 @@ pub enum Error {
4548
context: String,
4649
},
4750

51+
#[error("failed to restore index or constraint, channel closed")]
52+
CantRestoreIndexOrConstraint,
53+
4854
#[error("writer connection pool shut down due to error")]
4955
#[allow(clippy::enum_variant_names)]
5056
WriterConnectionPoolError,
@@ -244,6 +250,10 @@ pub struct MasWriter<'c> {
244250
indices_to_restore: Vec<IndexDescription>,
245251
constraints_to_restore: Vec<ConstraintDescription>,
246252

253+
constraint_restore_tx: mpsc::Sender<ConstraintDescription>,
254+
index_restore_tx: mpsc::Sender<IndexDescription>,
255+
restorer_task: JoinHandle<Result<(), Error>>,
256+
247257
write_buffer_finish_checker: FinishChecker,
248258
}
249259

@@ -390,6 +400,7 @@ impl<'conn> MasWriter<'conn> {
390400
#[tracing::instrument(skip_all)]
391401
pub async fn new(
392402
mut conn: LockedMasDatabase<'conn>,
403+
index_restore_conn: PgConnection,
393404
mut writer_connections: Vec<PgConnection>,
394405
) -> Result<Self, Error> {
395406
// Given that we don't have any concurrent transactions here,
@@ -500,15 +511,54 @@ impl<'conn> MasWriter<'conn> {
500511
.into_database("begin MAS writer transaction")?;
501512
}
502513

514+
let (constraint_restore_tx, index_restore_tx, restorer_task) =
515+
Self::restore_task(index_restore_conn);
516+
503517
Ok(Self {
504518
conn,
505519
writer_pool: WriterConnectionPool::new(writer_connections),
506520
indices_to_restore,
507521
constraints_to_restore,
522+
constraint_restore_tx,
523+
index_restore_tx,
524+
restorer_task,
508525
write_buffer_finish_checker: FinishChecker::default(),
509526
})
510527
}
511528

529+
#[tracing::instrument(skip_all)]
530+
fn restore_task(
531+
mut conn: PgConnection,
532+
) -> (
533+
mpsc::Sender<ConstraintDescription>,
534+
mpsc::Sender<IndexDescription>,
535+
JoinHandle<Result<(), Error>>,
536+
) {
537+
let (constraint_restore_tx, mut constraint_restore_rx) = mpsc::channel(10);
538+
let (index_restore_tx, mut index_restore_rx) = mpsc::channel(10);
539+
let restorer_task = tokio::spawn(
540+
async move {
541+
loop {
542+
tokio::select! {
543+
constraint = constraint_restore_rx.recv() => {
544+
let Some(constraint) = constraint else { break; };
545+
constraint_pausing::restore_constraint(conn.as_mut(), &constraint).await?;
546+
}
547+
index = index_restore_rx.recv() => {
548+
let Some(index) = index else { break; };
549+
constraint_pausing::restore_index(conn.as_mut(), &index).await?;
550+
}
551+
}
552+
}
553+
554+
Ok(())
555+
}
556+
.instrument(tracing::info_span!("restore")),
557+
);
558+
559+
(constraint_restore_tx, index_restore_tx, restorer_task)
560+
}
561+
512562
#[tracing::instrument(skip_all)]
513563
async fn pause_indices(
514564
conn: &mut PgConnection,
@@ -571,6 +621,39 @@ impl<'conn> MasWriter<'conn> {
571621
Ok(())
572622
}
573623

624+
/// Liberate a table, so that the indexes can start to be restored
625+
///
626+
/// # Errors
627+
///
628+
/// Returns an error if the task to restore indexes and constraints is
629+
/// borked
630+
pub async fn liberate_table(&mut self, table: &str) -> Result<(), Error> {
631+
// Extract all the constraints and indexes from the table
632+
let constraints_to_restore = std::mem::take(&mut self.constraints_to_restore);
633+
for constraint in constraints_to_restore {
634+
if constraint.table_name == table && !constraint.is_fk {
635+
self.constraint_restore_tx
636+
.send(constraint)
637+
.await
638+
.map_err(|_| Error::CantRestoreIndexOrConstraint)?;
639+
} else {
640+
self.constraints_to_restore.push(constraint);
641+
}
642+
}
643+
let indices_to_restore = std::mem::take(&mut self.indices_to_restore);
644+
for index in indices_to_restore {
645+
if index.table_name == table {
646+
self.index_restore_tx
647+
.send(index)
648+
.await
649+
.map_err(|_| Error::CantRestoreIndexOrConstraint)?;
650+
} else {
651+
self.indices_to_restore.push(index);
652+
}
653+
}
654+
Ok(())
655+
}
656+
574657
/// Finish writing to the MAS database, flushing and committing all changes.
575658
///
576659
/// # Errors
@@ -582,27 +665,37 @@ impl<'conn> MasWriter<'conn> {
582665
pub async fn finish(mut self) -> Result<(), Error> {
583666
self.write_buffer_finish_checker.check_all_finished()?;
584667

668+
// Send all the remaining constraints and indices to the restorer task
669+
for constraint in self.constraints_to_restore {
670+
self.constraint_restore_tx
671+
.send(constraint)
672+
.await
673+
.map_err(|_| Error::CantRestoreIndexOrConstraint)?;
674+
}
675+
for index in self.indices_to_restore {
676+
self.index_restore_tx
677+
.send(index)
678+
.await
679+
.map_err(|_| Error::CantRestoreIndexOrConstraint)?;
680+
}
681+
585682
// Commit all writer transactions to the database.
586683
self.writer_pool
587684
.finish()
588685
.await
589686
.map_err(|errors| Error::Multiple(MultipleErrors::from(errors)))?;
590687

591-
// Now all the data has been migrated, finish off by restoring indices and
592-
// constraints!
688+
// Wait for the restorer task to finish
689+
self.restorer_task
690+
.await
691+
.expect("restorer task panicked")
692+
.expect("restorer task failed");
593693

594694
query("BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;")
595695
.execute(self.conn.as_mut())
596696
.await
597697
.into_database("begin MAS transaction")?;
598698

599-
Self::restore_indices(
600-
&mut self.conn,
601-
&self.indices_to_restore,
602-
&self.constraints_to_restore,
603-
)
604-
.await?;
605-
606699
self.conn
607700
.as_mut()
608701
.execute_many(include_str!("syn2mas_revert_temporary_tables.sql"))
@@ -1250,11 +1343,16 @@ mod test {
12501343
.detach(),
12511344
);
12521345
}
1346+
let index_restore_conn = pool
1347+
.acquire()
1348+
.await
1349+
.expect("failed to acquire index restore connection")
1350+
.detach();
12531351
let locked_main_conn = LockedMasDatabase::try_new(main_conn)
12541352
.await
12551353
.expect("failed to lock MAS database")
12561354
.expect_left("MAS database is already locked");
1257-
MasWriter::new(locked_main_conn, writer_conns)
1355+
MasWriter::new(locked_main_conn, index_restore_conn, writer_conns)
12581356
.await
12591357
.expect("failed to construct MasWriter")
12601358
}

crates/syn2mas/src/migration.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ pub async fn migrate(
117117
)
118118
.await?;
119119

120+
mas.liberate_table("syn2mas__users")
121+
.await
122+
.into_mas("liberating users table")?;
123+
mas.liberate_table("syn2mas__user_passwords")
124+
.await
125+
.into_mas("liberating user_passwords table")?;
126+
120127
span.pb_set_message("migrating threepids");
121128
span.pb_inc(1);
122129
migrate_threepids(
@@ -132,6 +139,13 @@ pub async fn migrate(
132139
)
133140
.await?;
134141

142+
mas.liberate_table("syn2mas__user_emails")
143+
.await
144+
.into_mas("liberating user_emails table")?;
145+
mas.liberate_table("syn2mas__user_unsupported_third_party_ids")
146+
.await
147+
.into_mas("liberating user_unsupported_third_party_ids table")?;
148+
135149
span.pb_set_message("migrating user external IDs");
136150
span.pb_inc(1);
137151
migrate_external_ids(
@@ -148,6 +162,10 @@ pub async fn migrate(
148162
)
149163
.await?;
150164

165+
mas.liberate_table("syn2mas__upstream_oauth_links")
166+
.await
167+
.into_mas("liberating upstream_oauth_links table")?;
168+
151169
// `(MAS user_id, device_id)` mapped to `compat_session` ULID
152170
let mut devices_to_compat_sessions: HashMap<(Uuid, CompactString), Uuid> =
153171
HashMap::with_capacity_and_hasher(
@@ -193,6 +211,13 @@ pub async fn migrate(
193211
)
194212
.await?;
195213

214+
mas.liberate_table("syn2mas__compat_access_tokens")
215+
.await
216+
.into_mas("liberating compat_access_tokens table")?;
217+
mas.liberate_table("syn2mas__compat_refresh_tokens")
218+
.await
219+
.into_mas("liberating compat_refresh_tokens table")?;
220+
196221
span.pb_set_message("migrating devices");
197222
span.pb_inc(1);
198223
migrate_devices(
@@ -210,6 +235,10 @@ pub async fn migrate(
210235
)
211236
.await?;
212237

238+
mas.liberate_table("syn2mas__compat_sessions")
239+
.await
240+
.into_mas("liberating compat_sessions table")?;
241+
213242
span.pb_set_message("closing Synapse database");
214243
span.pb_inc(1);
215244
synapse
@@ -277,6 +306,7 @@ async fn migrate_users(
277306
}
278307

279308
user_buffer.finish(mas).await.into_mas("writing users")?;
309+
280310
password_buffer
281311
.finish(mas)
282312
.await

0 commit comments

Comments
 (0)