Skip to content

Commit 9cd5ca0

Browse files
fix(meta): fix race condition during metadata backup (#24269) (#24335)
Co-authored-by: zwang28 <[email protected]>
1 parent 06132de commit 9cd5ca0

File tree

10 files changed

+48
-41
lines changed

10 files changed

+48
-41
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/meta/service/src/backup_service.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl BackupService for BackupServiceImpl {
7272
_request: Request<GetMetaSnapshotManifestRequest>,
7373
) -> Result<Response<GetMetaSnapshotManifestResponse>, Status> {
7474
Ok(Response::new(GetMetaSnapshotManifestResponse {
75-
manifest: Some(self.backup_manager.manifest().deref().into()),
75+
manifest: Some(self.backup_manager.manifest().await.deref().into()),
7676
}))
7777
}
7878
}

src/meta/service/src/notification_service.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ impl NotificationServiceImpl {
323323
.on_current_version(|version| version.into())
324324
.await;
325325
let hummock_write_limits = self.hummock_manager.write_limits().await;
326-
let meta_backup_manifest_id = self.backup_manager.manifest().manifest_id;
326+
let meta_backup_manifest_id = self.backup_manager.manifest().await.manifest_id;
327327
let cluster_resource = self.get_cluster_resource().await;
328328

329329
Ok(MetaSnapshot {

src/meta/src/backup_restore/backup_manager.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ impl BackupManager {
216216
.load()
217217
.0
218218
.manifest()
219+
.await
219220
.snapshot_metadata
220221
.len();
221222
if current_number > MAX_META_SNAPSHOT_NUM {
@@ -266,7 +267,7 @@ impl BackupManager {
266267
.notify_hummock_without_version(
267268
Operation::Update,
268269
Info::MetaBackupManifestId(MetaBackupManifestId {
269-
id: self.backup_store.load().0.manifest().manifest_id,
270+
id: self.backup_store.load().0.manifest().await.manifest_id,
270271
}),
271272
);
272273
self.latest_job_info.store(Arc::new((
@@ -308,26 +309,27 @@ impl BackupManager {
308309
.notify_hummock_without_version(
309310
Operation::Update,
310311
Info::MetaBackupManifestId(MetaBackupManifestId {
311-
id: self.backup_store.load().0.manifest().manifest_id,
312+
id: self.backup_store.load().0.manifest().await.manifest_id,
312313
}),
313314
);
314315
Ok(())
315316
}
316317

317318
/// List id of all objects required by backups.
318-
pub fn list_pinned_object_ids(&self) -> HashSet<HummockRawObjectId> {
319+
pub async fn list_pinned_object_ids(&self) -> HashSet<HummockRawObjectId> {
319320
self.backup_store
320321
.load()
321322
.0
322323
.manifest()
324+
.await
323325
.snapshot_metadata
324326
.iter()
325327
.flat_map(|s| s.objects.iter().copied())
326328
.collect()
327329
}
328330

329-
pub fn manifest(&self) -> Arc<MetaSnapshotManifest> {
330-
self.backup_store.load().0.manifest()
331+
pub async fn manifest(&self) -> Arc<MetaSnapshotManifest> {
332+
self.backup_store.load().0.manifest().await
331333
}
332334
}
333335

src/meta/src/backup_restore/restore.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ async fn restore_impl(
146146
Some(b) => b,
147147
};
148148
let target_id = opts.meta_snapshot_id;
149-
let snapshot_list = &backup_store.manifest().snapshot_metadata;
149+
let snapshot_list = &backup_store.manifest().await.snapshot_metadata;
150150
let snapshot = match snapshot_list.iter().find(|m| m.id == target_id) {
151151
None => {
152152
return Err(BackupError::Other(anyhow::anyhow!(

src/meta/src/backup_restore/restore_impl/v2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl LoaderV2 {
3838
#[async_trait::async_trait]
3939
impl Loader<MetadataV2> for LoaderV2 {
4040
async fn load(&self, target_id: MetaSnapshotId) -> BackupResult<MetaSnapshot<MetadataV2>> {
41-
let snapshot_list = &self.backup_store.manifest().snapshot_metadata;
41+
let snapshot_list = &self.backup_store.manifest().await.snapshot_metadata;
4242
let mut target_snapshot: MetaSnapshotV2 = self.backup_store.get(target_id).await?;
4343
tracing::debug!(
4444
"snapshot {} before rewrite:\n{}",

src/meta/src/hummock/manager/gc.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,10 @@ impl HummockManager {
318318
if object_ids.is_empty() {
319319
return Ok(0);
320320
}
321-
// It's crucial to get pinned_by_metadata_backup only after object_ids.
322-
let pinned_by_metadata_backup = backup_manager
323-
.as_ref()
324-
.map(|b| b.list_pinned_object_ids())
325-
.unwrap_or_default();
321+
let pinned_by_metadata_backup = match backup_manager.as_ref() {
322+
Some(b) => b.list_pinned_object_ids().await,
323+
None => HashSet::default(),
324+
};
326325
// It's crucial to collect_min_uncommitted_object_id (i.e. `min_object_id`) only after LIST object store (i.e. `object_ids`).
327326
// Because after getting `min_object_id`, new compute nodes may join and generate new uncommitted objects that are not covered by `min_sst_id`.
328327
// By getting `min_object_id` after `object_ids`, it's ensured `object_ids` won't include any objects from those new compute nodes.
@@ -516,7 +515,7 @@ impl HummockManager {
516515
return Ok(());
517516
};
518517
// Objects pinned by either meta backup or time travel should be filtered out.
519-
let backup_pinned: HashSet<_> = backup_manager.list_pinned_object_ids();
518+
let backup_pinned: HashSet<_> = backup_manager.list_pinned_object_ids().await;
520519
// The version_pinned is obtained after the candidate object_ids for deletion, which is new enough for filtering purpose.
521520
let version_pinned = {
522521
let versioning = self.versioning.read().await;

src/storage/backup/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ anyhow = "1"
1212
async-trait = "0.1"
1313
bytes = { version = "1", features = ["serde"] }
1414
itertools = { workspace = true }
15-
parking_lot = { workspace = true }
1615
prost = { workspace = true }
1716
risingwave_common = { workspace = true }
1817
risingwave_hummock_sdk = { workspace = true }
@@ -22,6 +21,7 @@ risingwave_pb = { workspace = true }
2221
serde = { workspace = true }
2322
serde_json = "1"
2423
thiserror = { workspace = true }
24+
tokio = { version = "0.2", package = "madsim-tokio" }
2525
twox-hash = "2"
2626

2727
[dev-dependencies]

src/storage/backup/src/storage.rs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use risingwave_object_store::object::object_metrics::ObjectStoreMetrics;
2121
use risingwave_object_store::object::{
2222
InMemObjectStore, MonitoredObjectStore, ObjectError, ObjectStoreImpl, ObjectStoreRef,
2323
};
24+
use tokio::sync::RwLock;
2425

2526
use crate::meta_snapshot::{MetaSnapshot, Metadata};
2627
use crate::{
@@ -42,7 +43,7 @@ pub trait MetaSnapshotStorage: 'static + Sync + Send {
4243
async fn get<S: Metadata>(&self, id: MetaSnapshotId) -> BackupResult<MetaSnapshot<S>>;
4344

4445
/// Gets local snapshot manifest.
45-
fn manifest(&self) -> Arc<MetaSnapshotManifest>;
46+
async fn manifest(&self) -> Arc<MetaSnapshotManifest>;
4647

4748
/// Refreshes local snapshot manifest.
4849
async fn refresh_manifest(&self) -> BackupResult<()>;
@@ -55,7 +56,7 @@ pub trait MetaSnapshotStorage: 'static + Sync + Send {
5556
pub struct ObjectStoreMetaSnapshotStorage {
5657
path: String,
5758
store: ObjectStoreRef,
58-
manifest: Arc<parking_lot::RwLock<Arc<MetaSnapshotManifest>>>,
59+
manifest: Arc<RwLock<Arc<MetaSnapshotManifest>>>,
5960
}
6061

6162
// TODO #6482: purge stale snapshots that is not in manifest.
@@ -70,13 +71,18 @@ impl ObjectStoreMetaSnapshotStorage {
7071
Ok(instance)
7172
}
7273

73-
async fn update_manifest(&self, new_manifest: MetaSnapshotManifest) -> BackupResult<()> {
74+
async fn update_manifest(
75+
&self,
76+
update: impl FnOnce(MetaSnapshotManifest) -> MetaSnapshotManifest,
77+
) -> BackupResult<()> {
78+
let mut guard = self.manifest.write().await;
79+
let new_manifest = update((**guard).clone());
7480
let bytes =
7581
serde_json::to_vec(&new_manifest).map_err(|e| BackupError::Encoding(e.into()))?;
7682
self.store
7783
.upload(&self.get_manifest_path(), bytes.into())
7884
.await?;
79-
*self.manifest.write() = Arc::new(new_manifest);
85+
*guard = Arc::new(new_manifest);
8086
Ok(())
8187
}
8288

@@ -124,19 +130,17 @@ impl MetaSnapshotStorage for ObjectStoreMetaSnapshotStorage {
124130
) -> BackupResult<()> {
125131
let path = self.get_snapshot_path(snapshot.id);
126132
self.store.upload(&path, snapshot.encode()?.into()).await?;
127-
128-
// update manifest last
129-
let mut new_manifest = (**self.manifest.read()).clone();
130-
new_manifest.manifest_id += 1;
131-
new_manifest
132-
.snapshot_metadata
133-
.push(MetaSnapshotMetadata::new(
133+
self.update_manifest(|mut manifest: MetaSnapshotManifest| {
134+
manifest.manifest_id += 1;
135+
manifest.snapshot_metadata.push(MetaSnapshotMetadata::new(
134136
snapshot.id,
135137
snapshot.metadata.hummock_version_ref(),
136138
snapshot.format_version,
137139
remarks,
138140
));
139-
self.update_manifest(new_manifest).await?;
141+
manifest
142+
})
143+
.await?;
140144
Ok(())
141145
}
142146

@@ -146,13 +150,13 @@ impl MetaSnapshotStorage for ObjectStoreMetaSnapshotStorage {
146150
MetaSnapshot::decode(&data)
147151
}
148152

149-
fn manifest(&self) -> Arc<MetaSnapshotManifest> {
150-
self.manifest.read().clone()
153+
async fn manifest(&self) -> Arc<MetaSnapshotManifest> {
154+
self.manifest.read().await.clone()
151155
}
152156

153157
async fn refresh_manifest(&self) -> BackupResult<()> {
154158
if let Some(manifest) = self.get_manifest().await? {
155-
let mut guard = self.manifest.write();
159+
let mut guard = self.manifest.write().await;
156160
if manifest.manifest_id > guard.manifest_id {
157161
*guard = Arc::new(manifest);
158162
}
@@ -161,15 +165,15 @@ impl MetaSnapshotStorage for ObjectStoreMetaSnapshotStorage {
161165
}
162166

163167
async fn delete(&self, ids: &[MetaSnapshotId]) -> BackupResult<()> {
164-
// update manifest first
165168
let to_delete: HashSet<MetaSnapshotId> = HashSet::from_iter(ids.iter().cloned());
166-
let mut new_manifest = (**self.manifest.read()).clone();
167-
new_manifest.manifest_id += 1;
168-
new_manifest
169-
.snapshot_metadata
170-
.retain(|m| !to_delete.contains(&m.id));
171-
self.update_manifest(new_manifest).await?;
172-
169+
self.update_manifest(|mut manifest: MetaSnapshotManifest| {
170+
manifest.manifest_id += 1;
171+
manifest
172+
.snapshot_metadata
173+
.retain(|m| !to_delete.contains(&m.id));
174+
manifest
175+
})
176+
.await?;
173177
let paths = ids
174178
.iter()
175179
.map(|id| self.get_snapshot_path(*id))

src/storage/src/hummock/backup_reader.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl BackupReader {
143143
let expect_manifest_id = expect_manifest_id.unwrap();
144144
// Use the same store throughout one run.
145145
let current_store = backup_reader.store.load_full();
146-
let previous_id = current_store.0.manifest().manifest_id;
146+
let previous_id = current_store.0.manifest().await.manifest_id;
147147
if expect_manifest_id <= previous_id {
148148
continue;
149149
}
@@ -161,6 +161,7 @@ impl BackupReader {
161161
let manifest: HashSet<MetaSnapshotId> = current_store
162162
.0
163163
.manifest()
164+
.await
164165
.snapshot_metadata
165166
.iter()
166167
.map(|s| s.id)
@@ -192,6 +193,7 @@ impl BackupReader {
192193
let Some(snapshot_metadata) = current_store
193194
.0
194195
.manifest()
196+
.await
195197
.snapshot_metadata
196198
.iter()
197199
.find(|v| {

0 commit comments

Comments
 (0)