Skip to content

Commit 44c43b9

Browse files
committed
WIP: restore constraints earlier
1 parent 4aba4f4 commit 44c43b9

File tree

4 files changed

+155
-12
lines changed

4 files changed

+155
-12
lines changed

crates/cli/src/commands/syn2mas.rs

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

242251
let clock = SystemClock::default();
243252
// 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: 110 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(name = "syn2mas.mas_writer.new", 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,55 @@ 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(name = "syn2mas.mas_writer.restore_task", 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+
tracing::info!("Restoring task done");
555+
Ok(())
556+
}
557+
.instrument(tracing::info_span!("syn2mas.mas_writer.restore_loop")),
558+
);
559+
560+
(constraint_restore_tx, index_restore_tx, restorer_task)
561+
}
562+
512563
#[tracing::instrument(skip_all)]
513564
async fn pause_indices(
514565
conn: &mut PgConnection,
@@ -571,6 +622,39 @@ impl<'conn> MasWriter<'conn> {
571622
Ok(())
572623
}
573624

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

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

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

594695
query("BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;")
595696
.execute(self.conn.as_mut())
596697
.await
597698
.into_database("begin MAS transaction")?;
598699

599-
Self::restore_indices(
600-
&mut self.conn,
601-
&self.indices_to_restore,
602-
&self.constraints_to_restore,
603-
)
604-
.await?;
605-
606700
self.conn
607701
.as_mut()
608702
.execute_many(include_str!("syn2mas_revert_temporary_tables.sql"))
@@ -1250,11 +1344,16 @@ mod test {
12501344
.detach(),
12511345
);
12521346
}
1347+
let index_restore_conn = pool
1348+
.acquire()
1349+
.await
1350+
.expect("failed to acquire index restore connection")
1351+
.detach();
12531352
let locked_main_conn = LockedMasDatabase::try_new(main_conn)
12541353
.await
12551354
.expect("failed to lock MAS database")
12561355
.expect_left("MAS database is already locked");
1257-
MasWriter::new(locked_main_conn, writer_conns)
1356+
MasWriter::new(locked_main_conn, index_restore_conn, writer_conns)
12581357
.await
12591358
.expect("failed to construct MasWriter")
12601359
}

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)