risingwave_meta/backup_restore/restore_impl/
v2.rs1use std::cmp;
16
17use itertools::Itertools;
18use risingwave_backup::MetaSnapshotId;
19use risingwave_backup::error::{BackupError, BackupResult};
20use risingwave_backup::meta_snapshot::MetaSnapshot;
21use risingwave_backup::meta_snapshot_v2::{
22 MetaSnapshotV2, MetadataV2, decode_hummock_sequences_from_stream,
23};
24use risingwave_backup::storage::{MetaSnapshotStorage, MetaSnapshotStorageRef};
25use sea_orm::{DbErr, EntityTrait};
26
27use crate::backup_restore::restore_impl::{Loader, Writer};
28use crate::controller::SqlMetaStore;
29
30pub struct LoaderV2 {
31 backup_store: MetaSnapshotStorageRef,
32}
33
34impl LoaderV2 {
35 pub fn new(backup_store: MetaSnapshotStorageRef) -> Self {
36 Self { backup_store }
37 }
38}
39
40#[async_trait::async_trait]
41impl Loader<MetadataV2> for LoaderV2 {
42 async fn load(&self, target_id: MetaSnapshotId) -> BackupResult<MetaSnapshot<MetadataV2>> {
43 let snapshot_list = &self.backup_store.manifest().await.snapshot_metadata;
44 let mut target_snapshot: MetaSnapshotV2 = self.backup_store.get(target_id).await?;
45 tracing::debug!(
46 "snapshot {} before rewrite:\n{}",
47 target_id,
48 target_snapshot
49 );
50 let newest_id = snapshot_list
51 .iter()
52 .map(|m| m.id)
53 .max()
54 .expect("should exist");
55 assert!(
56 newest_id >= target_id,
57 "newest_id={}, target_id={}",
58 newest_id,
59 target_id
60 );
61
62 if newest_id > target_id {
64 let newest_hummock_sequences = decode_hummock_sequences_from_stream(
65 self.backup_store.get_bytes_stream(newest_id).await?,
66 )
67 .await?;
68 for seq in &target_snapshot.metadata.hummock_sequences {
69 let newest = newest_hummock_sequences
70 .iter()
71 .find(|s| s.name == seq.name)
72 .unwrap_or_else(|| {
73 panic!(
74 "violate superset requirement. Hummock sequence name {}",
75 seq.name
76 )
77 });
78 assert!(newest.seq >= seq.seq, "violate monotonicity requirement");
79 }
80 target_snapshot.metadata.hummock_sequences = newest_hummock_sequences;
81 tracing::info!(
82 "snapshot {} is rewritten by snapshot {}:\n",
83 target_id,
84 newest_id,
85 );
86 tracing::debug!("{target_snapshot}");
87 }
88 Ok(target_snapshot)
89 }
90}
91
92pub struct WriterModelV2ToMetaStoreV2 {
93 meta_store: SqlMetaStore,
94}
95
96impl WriterModelV2ToMetaStoreV2 {
97 pub fn new(meta_store: SqlMetaStore) -> Self {
98 Self { meta_store }
99 }
100}
101
102#[async_trait::async_trait]
103impl Writer<MetadataV2> for WriterModelV2ToMetaStoreV2 {
104 async fn write(&self, target_snapshot: MetaSnapshot<MetadataV2>) -> BackupResult<()> {
105 let metadata = target_snapshot.metadata;
106 let db = &self.meta_store.conn;
107 ensure_all_meta_store_tables_are_empty(db).await?;
108 insert_models(metadata.seaql_migrations.clone(), db).await?;
109 insert_models(metadata.clusters.clone(), db).await?;
110 insert_models(metadata.version_stats.clone(), db).await?;
111 insert_models(metadata.compaction_configs.clone(), db).await?;
112 insert_models(metadata.hummock_sequences.clone(), db).await?;
113 insert_models(metadata.workers.clone(), db).await?;
114 insert_models(metadata.worker_properties.clone(), db).await?;
115 insert_models(metadata.users.clone(), db).await?;
116 use risingwave_meta_model::object::ObjectType;
118 insert_models(
119 metadata
120 .objects
121 .iter()
122 .sorted_by(|a, b| match (a.obj_type, b.obj_type) {
123 (ObjectType::Database, ObjectType::Database) => a.oid.cmp(&b.oid),
124 (ObjectType::Database, _) => cmp::Ordering::Less,
125 (_, ObjectType::Database) => cmp::Ordering::Greater,
126 (ObjectType::Schema, ObjectType::Schema) => a.oid.cmp(&b.oid),
127 (ObjectType::Schema, _) => cmp::Ordering::Less,
128 (_, ObjectType::Schema) => cmp::Ordering::Greater,
129 (_, _) => a.oid.cmp(&b.oid),
130 })
131 .cloned(),
132 db,
133 )
134 .await?;
135 insert_models(
136 metadata
137 .user_privileges
138 .iter()
139 .sorted_by_key(|u| u.id)
140 .cloned(),
141 db,
142 )
143 .await?;
144 insert_models(metadata.object_dependencies.clone(), db).await?;
145 insert_models(metadata.databases.clone(), db).await?;
146 insert_models(metadata.schemas.clone(), db).await?;
147 insert_models(metadata.streaming_jobs.clone(), db).await?;
148 insert_models(metadata.fragments.clone(), db).await?;
149 insert_models(metadata.fragment_relation.clone(), db).await?;
150 insert_models(metadata.connections.clone(), db).await?;
151 insert_models(metadata.sources.clone(), db).await?;
152 insert_models(metadata.tables.clone(), db).await?;
153 insert_models(metadata.sinks.clone(), db).await?;
154 insert_models(metadata.views.clone(), db).await?;
155 insert_models(metadata.indexes.clone(), db).await?;
156 insert_models(metadata.functions.clone(), db).await?;
157 insert_models(metadata.system_parameters.clone(), db).await?;
158 insert_models(metadata.catalog_versions.clone(), db).await?;
159 insert_models(metadata.subscriptions.clone(), db).await?;
160 insert_models(metadata.session_parameters.clone(), db).await?;
161 insert_models(metadata.secrets.clone(), db).await?;
162 insert_models(metadata.exactly_once_iceberg_sinks.clone(), db).await?;
163 insert_models(metadata.iceberg_tables.clone(), db).await?;
164 insert_models(metadata.iceberg_namespace_properties.clone(), db).await?;
165 insert_models(metadata.user_default_privilege.clone(), db).await?;
166 insert_models(metadata.fragment_splits.clone(), db).await?;
167 insert_models(metadata.pending_sink_state.clone(), db).await?;
168 insert_models(metadata.refresh_jobs.clone(), db).await?;
169 insert_models(metadata.cdc_table_snapshot_splits.clone(), db).await?;
170 insert_models(metadata.hummock_table_change_logs.clone(), db).await?;
171 update_auto_inc(&metadata, db).await?;
173 Ok(())
174 }
175
176 async fn overwrite(
177 &self,
178 new_storage_url: &str,
179 new_storage_dir: &str,
180 new_backup_url: &str,
181 new_backup_dir: &str,
182 ) -> BackupResult<()> {
183 let kvs = [
184 ("state_store", new_storage_url),
185 ("data_directory", new_storage_dir),
186 ("backup_storage_url", new_backup_url),
187 ("backup_storage_directory", new_backup_dir),
188 ];
189 for (k, v) in kvs {
190 let Some(model) = risingwave_meta_model::system_parameter::Entity::find_by_id(k)
191 .one(&self.meta_store.conn)
192 .await
193 .map_err(map_db_err)?
194 else {
195 return Err(BackupError::MetaStorage(
196 anyhow::anyhow!("{k} not found in system_parameter table").into(),
197 ));
198 };
199 let mut kv: risingwave_meta_model::system_parameter::ActiveModel = model.into();
200 kv.value = sea_orm::ActiveValue::Set(v.to_owned());
201 risingwave_meta_model::system_parameter::Entity::update(kv)
202 .exec(&self.meta_store.conn)
203 .await
204 .map_err(map_db_err)?;
205 }
206 Ok(())
207 }
208}
209
210async fn ensure_all_meta_store_tables_are_empty(
211 db: &impl sea_orm::ConnectionTrait,
212) -> BackupResult<()> {
213 macro_rules! ensure_entity_empty {
214 ($($entity_mod:ident),* $(,)?) => {
215 $(
216 if risingwave_meta_model::$entity_mod::Entity::find()
217 .one(db)
218 .await
219 .map_err(map_db_err)?
220 .is_some()
221 {
222 return Err(BackupError::NonemptyMetaStorage);
223 }
224 )*
225 };
226 }
227
228 risingwave_meta_model::for_all_meta_model_entities!(ensure_entity_empty);
229 Ok(())
230}
231
232fn map_db_err(e: DbErr) -> BackupError {
233 BackupError::MetaStorage(e.into())
234}
235
236#[macro_export]
237macro_rules! for_all_auto_increment {
238 ($metadata:ident, $db:ident, $macro:ident) => {
239 $macro! ($metadata, $db,
240 {"worker", workers, worker_id},
241 {"object", objects, oid},
242 {"user", users, user_id},
243 {"user_privilege", user_privileges, id},
244 {"fragment", fragments, fragment_id},
245 {"object_dependency", object_dependencies, id}
246 )
247 };
248}
249
250macro_rules! reset_sql_sequence {
251 ($metadata:ident, $db:ident, $( {$table:expr, $model:ident, $id_field:ident} ),*) => {
252 $(
253 match $db.get_database_backend() {
254 sea_orm::DbBackend::MySql => {
255 if let Some(v) = $metadata.$model.iter().map(|w| w.$id_field + 1).max() {
256 $db.execute(sea_orm::Statement::from_string(
257 sea_orm::DatabaseBackend::MySql,
258 format!("ALTER TABLE {} AUTO_INCREMENT = {};", $table, v),
259 ))
260 .await
261 .map_err(map_db_err)?;
262 }
263 }
264 sea_orm::DbBackend::Postgres => {
265 $db.execute(sea_orm::Statement::from_string(
266 sea_orm::DatabaseBackend::Postgres,
267 format!("SELECT setval('{}_{}_seq', (SELECT MAX({}) FROM \"{}\"));", $table, stringify!($id_field), stringify!($id_field), $table),
268 ))
269 .await
270 .map_err(map_db_err)?;
271 }
272 sea_orm::DbBackend::Sqlite => {}
273 }
274 )*
275 };
276}
277
278async fn update_auto_inc(
280 metadata: &MetadataV2,
281 db: &impl sea_orm::ConnectionTrait,
282) -> BackupResult<()> {
283 for_all_auto_increment!(metadata, db, reset_sql_sequence);
284 Ok(())
285}
286
287async fn insert_models<S, A>(
288 models: impl IntoIterator<Item = S>,
289 db: &impl sea_orm::ConnectionTrait,
290) -> BackupResult<()>
291where
292 S: sea_orm::ModelTrait + Sync + Send + Sized + sea_orm::IntoActiveModel<A>,
293 A: sea_orm::ActiveModelTrait + sea_orm::ActiveModelBehavior + Send + Sync + From<S>,
294 <<A as sea_orm::ActiveModelTrait>::Entity as sea_orm::EntityTrait>::Model:
295 sea_orm::IntoActiveModel<A>,
296{
297 use sea_orm::EntityTrait;
298 if <S as sea_orm::ModelTrait>::Entity::find()
299 .one(db)
300 .await
301 .map_err(map_db_err)?
302 .is_some()
303 {
304 return Err(BackupError::NonemptyMetaStorage);
305 }
306 for m in models {
307 m.into_active_model().insert(db).await.map_err(map_db_err)?;
308 }
309 Ok(())
310}