Skip to content
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

Merged
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ae4282a
Implement record based Crucible reference counting
jmpesp Oct 4, 2024
66c6797
Merge branch 'main' into read_only_regions_need_ref_counting_too
jmpesp Oct 9, 2024
410c79d
full table scan required during clean slate check
jmpesp Oct 9, 2024
2ff34d5
Explicitly separate region deletion code
jmpesp Oct 16, 2024
221751a
be explicit in comment!
jmpesp Oct 16, 2024
00d9f38
reduce denting
jmpesp Oct 16, 2024
412d665
TODO be smart enough
jmpesp Oct 16, 2024
0a45763
use TryFrom instead of From, as then a database edit cannot panic Nexus
jmpesp Oct 16, 2024
89744ea
fmt
jmpesp Oct 16, 2024
bd2ef84
add CONSTRAINT to volume_resource_usage table to separate two enum uses
jmpesp Oct 16, 2024
af6e6e5
constraint typo
jmpesp Oct 17, 2024
47cc31e
assert read-only regions never have snapshots
jmpesp Oct 17, 2024
3d16f26
DO NOT UNWRAP FOR THE LOVE OF ALL THAT IS HOLY
jmpesp Oct 24, 2024
d4de663
Implement a deleting flag for read-only regions
jmpesp Oct 24, 2024
1b57804
use REGION_REDUNDANCY_THRESHOLD instead of 3
jmpesp Oct 24, 2024
eae5f31
use an enum instead of bool
jmpesp Oct 24, 2024
0e0a639
use SocketAddrV6 instead of passing around strings
jmpesp Oct 24, 2024
58169fe
fix bad wording
jmpesp Oct 24, 2024
02ebcbe
correct 2 bit variant for v4 uuid
jmpesp Oct 24, 2024
b8cccb2
actually correct ipv6
jmpesp Oct 24, 2024
3849fe8
we do not need to panic
jmpesp Oct 24, 2024
ce56ca3
no lines longer than 80!
jmpesp Oct 24, 2024
5a10c77
Merge branch 'main' into read_only_regions_need_ref_counting_too
jmpesp Oct 24, 2024
f1e24f2
Merge branch 'main' into read_only_regions_need_ref_counting_too
jmpesp Oct 31, 2024
f52fa63
expand on different old_region_in_vcr + new_region_in_vcr cases
jmpesp Nov 5, 2024
b020b78
Merge branch 'main' into read_only_regions_need_ref_counting_too
jmpesp Nov 5, 2024
ff590d6
retain previous find_deleted_volume_regions behaviour: only operate o…
jmpesp Nov 7, 2024
2b511b1
non-jank idempotent query to populate volume_resource_usage
jmpesp Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions dev-tools/omdb/src/bin/omdb/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2564,26 +2564,22 @@ async fn cmd_db_region_find_deleted(
struct Row {
dataset_id: Uuid,
region_id: Uuid,
region_snapshot_id: String,
volume_id: Uuid,
volume_id: String,
}

let rows: Vec<Row> = datasets_regions_volumes
.into_iter()
.map(|row| {
let (dataset, region, region_snapshot, volume) = row;
let (dataset, region, volume) = row;

Row {
dataset_id: dataset.id(),
region_id: region.id(),
region_snapshot_id: if let Some(region_snapshot) =
region_snapshot
{
region_snapshot.snapshot_id.to_string()
volume_id: if let Some(volume) = volume {
volume.id().to_string()
} else {
String::from("")
},
volume_id: volume.id(),
}
})
.collect();
Expand Down
8 changes: 8 additions & 0 deletions nexus/db-model/src/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ pub struct Region {

// A region may be read-only
read_only: bool,

// Shared read-only regions require a "deleting" flag to avoid a
// use-after-free scenario
deleting: bool,
}

impl Region {
Expand All @@ -67,6 +71,7 @@ impl Region {
extent_count: extent_count as i64,
port: Some(port.into()),
read_only,
deleting: false,
}
}

Expand Down Expand Up @@ -99,4 +104,7 @@ impl Region {
pub fn read_only(&self) -> bool {
self.read_only
}
pub fn deleting(&self) -> bool {
self.deleting
}
}
2 changes: 2 additions & 0 deletions nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,8 @@ table! {
port -> Nullable<Int4>,

read_only -> Bool,

deleting -> Bool,
}
}

Expand Down
8 changes: 6 additions & 2 deletions nexus/db-model/src/schema_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::collections::BTreeMap;
///
/// This must be updated when you change the database schema. Refer to
/// schema/crdb/README.adoc in the root of this repository for details.
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(109, 0, 0);
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(113, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -29,7 +29,11 @@ static KNOWN_VERSIONS: Lazy<Vec<KnownVersion>> = Lazy::new(|| {
// | leaving the first copy as an example for the next person.
// v
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
KnownVersion::new(109, "crucible-ref-count-records"),
KnownVersion::new(113, "crucible-ref-count-records"),
KnownVersion::new(112, "blueprint-dataset"),
KnownVersion::new(111, "drop-omicron-zone-underlay-address"),
KnownVersion::new(110, "clickhouse-policy"),
KnownVersion::new(109, "inv-clickhouse-keeper-membership"),
KnownVersion::new(108, "internet-gateway"),
KnownVersion::new(107, "add-instance-boot-disk"),
KnownVersion::new(106, "dataset-kinds-update"),
Expand Down
56 changes: 33 additions & 23 deletions nexus/db-model/src/volume_resource_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ impl_enum_type!(
///
/// 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 uses a `volume_references`
/// 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, record
/// what read-only resources a volume uses here.
/// 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
Expand Down Expand Up @@ -101,31 +102,40 @@ impl VolumeResourceUsageRecord {
}
}

impl From<VolumeResourceUsageRecord> for VolumeResourceUsage {
fn from(record: VolumeResourceUsageRecord) -> VolumeResourceUsage {
impl TryFrom<VolumeResourceUsageRecord> for VolumeResourceUsage {
type Error = String;

fn try_from(
record: VolumeResourceUsageRecord,
) -> Result<VolumeResourceUsage, String> {
match record.usage_type {
VolumeResourceUsageType::ReadOnlyRegion => {
VolumeResourceUsage::ReadOnlyRegion {
region_id: record
.region_id
.expect("valid read-only region usage record"),
}
let Some(region_id) = record.region_id else {
return Err("valid read-only region usage record".into());
};

Ok(VolumeResourceUsage::ReadOnlyRegion { region_id })
}

VolumeResourceUsageType::RegionSnapshot => {
VolumeResourceUsage::RegionSnapshot {
dataset_id: record
.region_snapshot_dataset_id
.expect("valid region snapshot usage record"),

region_id: record
.region_snapshot_region_id
.expect("valid region snapshot usage record"),

snapshot_id: record
.region_snapshot_snapshot_id
.expect("valid region snapshot usage record"),
}
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,
})
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions nexus/db-queries/src/db/datastore/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
})
Expand Down
17 changes: 14 additions & 3 deletions nexus/db-queries/src/db/datastore/region_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,25 @@ impl DataStore {
) -> DeleteResult {
use db::schema::region_snapshot::dsl;

diesel::delete(dsl::region_snapshot)
let conn = self.pool_connection_unauthorized().await?;

let result = diesel::delete(dsl::region_snapshot)
.filter(dsl::dataset_id.eq(dataset_id))
.filter(dsl::region_id.eq(region_id))
.filter(dsl::snapshot_id.eq(snapshot_id))
.execute_async(&*self.pool_connection_unauthorized().await?)
.execute_async(&*conn)
.await
.map(|_rows_deleted| ())
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server));

// Whenever a region snapshot is hard-deleted, validate invariants for
// all volumes
#[cfg(any(test, feature = "testing"))]
Self::validate_volume_invariants(&conn)
.await
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))?;

result
}

/// Find region snapshots on expunged disks
Expand Down
19 changes: 19 additions & 0 deletions nexus/db-queries/src/db/datastore/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,4 +322,23 @@ impl DataStore {
.optional()
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
}

/// Get a snapshot, returning None if it does not exist (instead of a
/// NotFound error).
pub async fn snapshot_get(
&self,
opctx: &OpContext,
snapshot_id: Uuid,
) -> LookupResult<Option<Snapshot>> {
let conn = self.pool_connection_authorized(opctx).await?;

use db::schema::snapshot::dsl;
dsl::snapshot
.filter(dsl::id.eq(snapshot_id))
.select(Snapshot::as_select())
.first_async(&*conn)
.await
.optional()
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
}
}
Loading
Loading