risingwave_meta/backup_restore/
meta_snapshot_builder.rsuse std::future::Future;
use itertools::Itertools;
use risingwave_backup::error::{BackupError, BackupResult};
use risingwave_backup::meta_snapshot_v2::{MetaSnapshotV2, MetadataV2};
use risingwave_backup::MetaSnapshotId;
use risingwave_hummock_sdk::version::{HummockVersion, HummockVersionDelta};
use risingwave_meta_model as model;
use risingwave_pb::hummock::PbHummockVersionDelta;
use sea_orm::{DbErr, EntityTrait, QueryOrder, TransactionTrait};
use crate::controller::SqlMetaStore;
const VERSION: u32 = 2;
fn map_db_err(e: DbErr) -> BackupError {
BackupError::MetaStorage(e.into())
}
macro_rules! define_set_metadata {
($( {$name:ident, $mod_path:ident::$mod_name:ident} ),*) => {
async fn set_metadata(
metadata: &mut MetadataV2,
txn: &sea_orm::DatabaseTransaction,
) -> BackupResult<()> {
$(
metadata.$name = $mod_path::$mod_name::Entity::find()
.all(txn)
.await
.map_err(map_db_err)?;
)*
Ok(())
}
};
}
risingwave_backup::for_all_metadata_models_v2!(define_set_metadata);
pub struct MetaSnapshotV2Builder {
snapshot: MetaSnapshotV2,
meta_store: SqlMetaStore,
}
impl MetaSnapshotV2Builder {
pub fn new(meta_store: SqlMetaStore) -> Self {
Self {
snapshot: MetaSnapshotV2::default(),
meta_store,
}
}
pub async fn build(
&mut self,
id: MetaSnapshotId,
hummock_version_builder: impl Future<Output = HummockVersion>,
) -> BackupResult<()> {
self.snapshot.format_version = VERSION;
self.snapshot.id = id;
let hummock_version = hummock_version_builder.await;
let txn = self
.meta_store
.conn
.begin_with_config(
Some(sea_orm::IsolationLevel::Serializable),
Some(sea_orm::AccessMode::ReadOnly),
)
.await
.map_err(map_db_err)?;
let version_deltas = model::prelude::HummockVersionDelta::find()
.order_by_asc(model::hummock_version_delta::Column::Id)
.all(&txn)
.await
.map_err(map_db_err)?
.into_iter()
.map_into::<PbHummockVersionDelta>()
.map(|pb_delta| HummockVersionDelta::from_persisted_protobuf(&pb_delta));
let hummock_version = {
let mut redo_state = hummock_version;
let mut max_log_id = None;
for version_delta in version_deltas {
if version_delta.prev_id == redo_state.id {
redo_state.apply_version_delta(&version_delta);
}
max_log_id = Some(version_delta.id);
}
if let Some(max_log_id) = max_log_id {
if max_log_id != redo_state.id {
return Err(BackupError::Other(anyhow::anyhow!(format!(
"inconsistent hummock version: expected {}, actual {}",
max_log_id, redo_state.id
))));
}
}
redo_state
};
let mut metadata = MetadataV2 {
hummock_version,
..Default::default()
};
set_metadata(&mut metadata, &txn).await?;
txn.commit().await.map_err(map_db_err)?;
self.snapshot.metadata = metadata;
Ok(())
}
pub fn finish(self) -> BackupResult<MetaSnapshotV2> {
Ok(self.snapshot)
}
}