Skip to content

Commit 71b67d1

Browse files
authored
replace needing no work should not be an error (#1275)
Previously, if a caller requested a target replacement, and supplied the same construction request for the original and replacement arguments, a `ReplaceRequestInvalid` error would be returned. This isn't exactly an error, it's a state where no work needs to be done. Return `VcrMatches` as a `ReplaceResult` if this is the case, not an error. Note `compare_vcr_for_replacement` still returns all the same errors otherwise.
1 parent d140e4a commit 71b67d1

File tree

3 files changed

+114
-43
lines changed

3 files changed

+114
-43
lines changed

crucible-client-types/src/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub enum ReplaceResult {
103103
StartedAlready,
104104
CompletedAlready,
105105
Missing,
106+
VcrMatches,
106107
}
107108

108109
impl Debug for ReplaceResult {
@@ -120,6 +121,21 @@ impl Debug for ReplaceResult {
120121
ReplaceResult::Missing => {
121122
write!(f, "Missing")
122123
}
124+
ReplaceResult::VcrMatches { .. } => {
125+
write!(f, "VcrMatches")
126+
}
123127
}
124128
}
125129
}
130+
131+
/// Result of comparing an original volume construction request to a candidate
132+
/// replacement one.
133+
pub enum ReplacementRequestCheck {
134+
/// The replacement was validated, and this variant holds the old downstairs
135+
/// target and the new one replacing it.
136+
Valid { old: SocketAddr, new: SocketAddr },
137+
138+
/// The replacement is not necessary because the replacement matches the
139+
/// original.
140+
ReplacementMatchesOriginal,
141+
}

openapi/crucible-pantry.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,8 @@
787787
"started",
788788
"started_already",
789789
"completed_already",
790-
"missing"
790+
"missing",
791+
"vcr_matches"
791792
]
792793
},
793794
"ScrubResponse": {

upstairs/src/volume.rs

+96-42
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::ops::Range;
99
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
1010
use tokio::time::Instant;
1111

12+
use crucible_client_types::ReplacementRequestCheck;
1213
use crucible_client_types::VolumeConstructionRequest;
1314

1415
pub struct RegionExtentInfo {
@@ -779,7 +780,8 @@ impl BlockIO for Volume {
779780
match result {
780781
ReplaceResult::Started
781782
| ReplaceResult::StartedAlready
782-
| ReplaceResult::CompletedAlready => {
783+
| ReplaceResult::CompletedAlready
784+
| ReplaceResult::VcrMatches => {
783785
// found the subvolume, so stop!
784786
return Ok(result);
785787
}
@@ -1092,16 +1094,26 @@ impl Volume {
10921094
original: VolumeConstructionRequest,
10931095
replacement: VolumeConstructionRequest,
10941096
log: &Logger,
1095-
) -> Result<(SocketAddr, SocketAddr), CrucibleError> {
1097+
) -> Result<ReplacementRequestCheck, CrucibleError> {
10961098
match Self::compare_vcr_for_update(original, replacement, log)? {
1097-
Some((o, n)) => Ok((o, n)),
1098-
None => crucible_bail!(
1099-
ReplaceRequestInvalid,
1100-
"VCR targets are the same"
1101-
),
1099+
Some((o, n)) => {
1100+
Ok(ReplacementRequestCheck::Valid { old: o, new: n })
1101+
}
1102+
1103+
None => Ok(ReplacementRequestCheck::ReplacementMatchesOriginal),
11021104
}
11031105
}
11041106

1107+
/// Given two VolumeConstructionRequests, compare them, and return:
1108+
///
1109+
/// - Ok(Some(old, new)) if there's a single target difference in one of the
1110+
/// Region variants, and the replacement argument is a valid replacement
1111+
/// for the original argument
1112+
///
1113+
/// - Ok(None) if there are no differences between them
1114+
///
1115+
/// - Err(e) if there's a difference that means the replacement argument is
1116+
/// not a valid replacement for the original argument
11051117
pub fn compare_vcr_for_update(
11061118
original: VolumeConstructionRequest,
11071119
replacement: VolumeConstructionRequest,
@@ -1408,18 +1420,27 @@ impl Volume {
14081420
// Given two VolumeConstructionRequests, compare them to verify they
14091421
// only have the proper differences and if the VCRs are valid, submit
14101422
// the targets for replacement. A success here means the upstairs has
1411-
// accepted the replacement and the process has started.
1423+
// accepted the replacement and the process has started, or there's
1424+
// no work to do because the original and replacement match.
14121425
pub async fn target_replace(
14131426
&self,
14141427
original: VolumeConstructionRequest,
14151428
replacement: VolumeConstructionRequest,
14161429
) -> Result<ReplaceResult, CrucibleError> {
14171430
let (original_target, new_target) =
1418-
Self::compare_vcr_for_target_replacement(
1431+
match Self::compare_vcr_for_target_replacement(
14191432
original,
14201433
replacement,
14211434
&self.log,
1422-
)?;
1435+
)? {
1436+
ReplacementRequestCheck::Valid { old, new } => (old, new),
1437+
1438+
ReplacementRequestCheck::ReplacementMatchesOriginal => {
1439+
// The replacement VCR they sent is the same the original.
1440+
// No replacement is required
1441+
return Ok(ReplaceResult::VcrMatches);
1442+
}
1443+
};
14231444

14241445
info!(
14251446
self.log,
@@ -1464,6 +1485,15 @@ impl Volume {
14641485
Ok(ReplaceResult::CompletedAlready)
14651486
}
14661487

1488+
Ok(ReplaceResult::VcrMatches) => {
1489+
info!(
1490+
self.log,
1491+
"No work required for replace for {}", self.uuid
1492+
);
1493+
1494+
Ok(ReplaceResult::VcrMatches)
1495+
}
1496+
14671497
Err(e) => {
14681498
crucible_bail!(
14691499
ReplaceRequestInvalid,
@@ -3174,15 +3204,19 @@ mod test {
31743204

31753205
let log = csl();
31763206
info!(log, "Test replacement of CID {cid}");
3177-
let (old_t, new_t) = Volume::compare_vcr_for_target_replacement(
3178-
original,
3179-
replacement,
3180-
&log,
3181-
)?;
3207+
let ReplacementRequestCheck::Valid { old, new } =
3208+
Volume::compare_vcr_for_target_replacement(
3209+
original,
3210+
replacement,
3211+
&log,
3212+
)?
3213+
else {
3214+
panic!("wrong variant returned!");
3215+
};
31823216

3183-
info!(log, "replace {old_t} with {new_t}");
3184-
assert_eq!(original_target, old_t);
3185-
assert_eq!(new_target, new_t);
3217+
info!(log, "replace {old} with {new}");
3218+
assert_eq!(original_target, old);
3219+
assert_eq!(new_target, new);
31863220
Ok(())
31873221
}
31883222

@@ -3239,15 +3273,19 @@ mod test {
32393273
};
32403274

32413275
let log = csl();
3242-
let (old_t, new_t) = Volume::compare_vcr_for_target_replacement(
3243-
original,
3244-
replacement,
3245-
&log,
3246-
)
3247-
.unwrap();
3276+
let ReplacementRequestCheck::Valid { old, new } =
3277+
Volume::compare_vcr_for_target_replacement(
3278+
original,
3279+
replacement,
3280+
&log,
3281+
)
3282+
.unwrap()
3283+
else {
3284+
panic!("wrong variant returned!");
3285+
};
32483286

3249-
assert_eq!(original_target, old_t);
3250-
assert_eq!(new_target, new_t);
3287+
assert_eq!(original_target, old);
3288+
assert_eq!(new_target, new);
32513289
}
32523290

32533291
#[tokio::test]
@@ -3304,15 +3342,19 @@ mod test {
33043342
};
33053343

33063344
let log = csl();
3307-
let (old_t, new_t) = Volume::compare_vcr_for_target_replacement(
3308-
original,
3309-
replacement,
3310-
&log,
3311-
)
3312-
.unwrap();
3345+
let ReplacementRequestCheck::Valid { old, new } =
3346+
Volume::compare_vcr_for_target_replacement(
3347+
original,
3348+
replacement,
3349+
&log,
3350+
)
3351+
.unwrap()
3352+
else {
3353+
panic!("wrong variant returned!");
3354+
};
33133355

3314-
assert_eq!(original_target, old_t);
3315-
assert_eq!(new_target, new_t);
3356+
assert_eq!(original_target, old);
3357+
assert_eq!(new_target, new);
33163358
}
33173359

33183360
#[tokio::test]
@@ -3392,13 +3434,15 @@ mod test {
33923434

33933435
let log = csl();
33943436

3395-
// Replacement should return error.
3396-
assert!(Volume::compare_vcr_for_target_replacement(
3397-
original.clone(),
3398-
replacement.clone(),
3399-
&log,
3400-
)
3401-
.is_err());
3437+
// Replacement should return ReplacementMatchesOriginal
3438+
assert!(matches!(
3439+
Volume::compare_vcr_for_target_replacement(
3440+
original.clone(),
3441+
replacement.clone(),
3442+
&log,
3443+
),
3444+
Ok(ReplacementRequestCheck::ReplacementMatchesOriginal),
3445+
));
34023446

34033447
// Migration is valid with these VCRs
34043448
Volume::compare_vcr_for_migration(original, replacement, &log).unwrap();
@@ -3745,7 +3789,17 @@ mod test {
37453789
};
37463790

37473791
let log = csl();
3748-
Volume::compare_vcr_for_target_replacement(original, replacement, &log)
3792+
let ReplacementRequestCheck::Valid { old, new } =
3793+
Volume::compare_vcr_for_target_replacement(
3794+
original,
3795+
replacement,
3796+
&log,
3797+
)?
3798+
else {
3799+
panic!("wrong variant returned!");
3800+
};
3801+
3802+
Ok((old, new))
37493803
}
37503804

37513805
#[tokio::test]

0 commit comments

Comments
 (0)