-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement record based Crucible reference counting #6805
Changes from all commits
ae4282a
66c6797
410c79d
2ff34d5
221751a
00d9f38
412d665
0a45763
89744ea
bd2ef84
af6e6e5
47cc31e
3d16f26
d4de663
1b57804
eae5f31
0e0a639
58169fe
02ebcbe
b8cccb2
3849fe8
ce56ca3
5a10c77
f1e24f2
f52fa63
b020b78
ff590d6
2b511b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use super::impl_enum_type; | ||
use crate::schema::volume_resource_usage; | ||
use uuid::Uuid; | ||
|
||
impl_enum_type!( | ||
#[derive(SqlType, Debug, QueryId)] | ||
#[diesel( | ||
postgres_type(name = "volume_resource_usage_type", schema = "public") | ||
)] | ||
pub struct VolumeResourceUsageTypeEnum; | ||
|
||
#[derive(Copy, Clone, Debug, AsExpression, FromSqlRow, PartialEq, Eq, Hash)] | ||
#[diesel(sql_type = VolumeResourceUsageTypeEnum)] | ||
pub enum VolumeResourceUsageType; | ||
|
||
ReadOnlyRegion => b"read_only_region" | ||
RegionSnapshot => b"region_snapshot" | ||
); | ||
|
||
/// Crucible volumes are created by layering read-write regions over a hierarchy | ||
/// of read-only resources. Originally only a region snapshot could be used as a | ||
/// read-only resource for a volume. With the introduction of read-only regions | ||
/// (created during the region snapshot replacement process) this is no longer | ||
/// true. | ||
/// | ||
/// Read-only resources can be used by many volumes, and because of this they | ||
/// need to have a reference count so they can be deleted when they're not | ||
/// referenced anymore. The region_snapshot table used a `volume_references` | ||
/// column, which counts how many uses there are. The region table does not have | ||
/// this column, and more over a simple integer works for reference counting but | ||
/// does not tell you _what_ volume that use is from. This can be determined | ||
/// (see omdb's validate volume references command) but it's information that is | ||
/// tossed out, as Nexus knows what volumes use what resources! Instead of | ||
/// throwing away that knowledge and only incrementing and decrementing an | ||
/// integer, record what read-only resources a volume uses in this table. | ||
/// | ||
/// Note: users should not use this object directly, and instead use the | ||
/// [`VolumeResourceUsage`] enum, which is type-safe and will convert to and | ||
/// from a [`VolumeResourceUsageRecord`] when interacting with the DB. | ||
#[derive( | ||
Queryable, Insertable, Debug, Clone, Selectable, PartialEq, Eq, Hash, | ||
)] | ||
#[diesel(table_name = volume_resource_usage)] | ||
pub struct VolumeResourceUsageRecord { | ||
pub usage_id: Uuid, | ||
|
||
pub volume_id: Uuid, | ||
|
||
pub usage_type: VolumeResourceUsageType, | ||
|
||
pub region_id: Option<Uuid>, | ||
|
||
pub region_snapshot_dataset_id: Option<Uuid>, | ||
pub region_snapshot_region_id: Option<Uuid>, | ||
pub region_snapshot_snapshot_id: Option<Uuid>, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub enum VolumeResourceUsage { | ||
ReadOnlyRegion { region_id: Uuid }, | ||
|
||
RegionSnapshot { dataset_id: Uuid, region_id: Uuid, snapshot_id: Uuid }, | ||
Comment on lines
+64
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is new code - could we use omicron-uuid-kinds to make these strongly-typed? (Same in the arguments below) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm working on this but as a separate PR that I'll stage on top of this one |
||
} | ||
|
||
impl VolumeResourceUsageRecord { | ||
pub fn new(volume_id: Uuid, usage: VolumeResourceUsage) -> Self { | ||
match usage { | ||
VolumeResourceUsage::ReadOnlyRegion { region_id } => { | ||
VolumeResourceUsageRecord { | ||
usage_id: Uuid::new_v4(), | ||
volume_id, | ||
usage_type: VolumeResourceUsageType::ReadOnlyRegion, | ||
|
||
region_id: Some(region_id), | ||
|
||
region_snapshot_dataset_id: None, | ||
region_snapshot_region_id: None, | ||
region_snapshot_snapshot_id: None, | ||
} | ||
} | ||
|
||
VolumeResourceUsage::RegionSnapshot { | ||
dataset_id, | ||
region_id, | ||
snapshot_id, | ||
} => VolumeResourceUsageRecord { | ||
usage_id: Uuid::new_v4(), | ||
volume_id, | ||
usage_type: VolumeResourceUsageType::RegionSnapshot, | ||
|
||
region_id: None, | ||
|
||
region_snapshot_dataset_id: Some(dataset_id), | ||
region_snapshot_region_id: Some(region_id), | ||
region_snapshot_snapshot_id: Some(snapshot_id), | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl TryFrom<VolumeResourceUsageRecord> for VolumeResourceUsage { | ||
type Error = String; | ||
|
||
fn try_from( | ||
record: VolumeResourceUsageRecord, | ||
) -> Result<VolumeResourceUsage, String> { | ||
match record.usage_type { | ||
VolumeResourceUsageType::ReadOnlyRegion => { | ||
let Some(region_id) = record.region_id else { | ||
return Err("valid read-only region usage record".into()); | ||
}; | ||
|
||
Ok(VolumeResourceUsage::ReadOnlyRegion { region_id }) | ||
} | ||
|
||
VolumeResourceUsageType::RegionSnapshot => { | ||
let Some(dataset_id) = record.region_snapshot_dataset_id else { | ||
return Err("valid region snapshot usage record".into()); | ||
}; | ||
|
||
let Some(region_id) = record.region_snapshot_region_id else { | ||
return Err("valid region snapshot usage record".into()); | ||
}; | ||
|
||
let Some(snapshot_id) = record.region_snapshot_snapshot_id | ||
else { | ||
return Err("valid region snapshot usage record".into()); | ||
}; | ||
|
||
Ok(VolumeResourceUsage::RegionSnapshot { | ||
dataset_id, | ||
region_id, | ||
snapshot_id, | ||
}) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -264,7 +264,7 @@ impl DataStore { | |
block_size, | ||
blocks_per_extent, | ||
extent_count, | ||
read_only: false, | ||
read_only: maybe_snapshot_id.is_some(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was this a bug before this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately yes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note though that currently the only thing in Nexus that creates read-only regions is region snapshot replacement, so this wasn't a bug that was hit anywhere. |
||
}, | ||
allocation_strategy, | ||
num_regions_required, | ||
|
@@ -362,6 +362,12 @@ impl DataStore { | |
) | ||
.execute_async(&conn).await?; | ||
} | ||
|
||
// Whenever a region is hard-deleted, validate invariants | ||
// for all volumes | ||
#[cfg(any(test, feature = "testing"))] | ||
Self::validate_volume_invariants(&conn).await?; | ||
leftwo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Ok(()) | ||
} | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had no idea reading the DB schema that these were two groups of columns, and "either one or the other is non-null".
If that's the intent -- and it seems to be, based on
VolumeResourceUsage
as an enum -- maybe we could add aCHECK
on the table validating this?Something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, done in bd2ef84