Skip to content

Commit 8ac6b84

Browse files
fix(topology): use topology when decreasing replicas
When removing volume replicas, take the pool topology into account and prefer to remove replicas which don't line up with the volume topology. This can happen when a volume's topology has been modified. Signed-off-by: Tiago Castro <[email protected]>
1 parent 4104acf commit 8ac6b84

File tree

4 files changed

+52
-12
lines changed

4 files changed

+52
-12
lines changed

control-plane/agents/src/bin/core/controller/scheduling/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@ impl ChildSorters {
163163
match Self::sort_by_health(a, b) {
164164
Ordering::Equal => match Self::sort_by_child(a, b) {
165165
Ordering::Equal => {
166+
// remove mismatched topology replicas first
167+
if let (Some(a), Some(b)) = (a.valid_pool_topology(), b.valid_pool_topology()) {
168+
match a.cmp(b) {
169+
Ordering::Equal => {}
170+
_else => return _else,
171+
}
172+
}
173+
166174
let childa_is_local = !a.spec().share.shared();
167175
let childb_is_local = !b.spec().share.shared();
168176
if childa_is_local == childb_is_local {

control-plane/agents/src/bin/core/controller/scheduling/resources/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ pub(crate) struct ReplicaItem {
176176
/// a Affinity Group and the already created volumes have replicas
177177
/// on this pool.
178178
ag_replicas_on_pool: Option<u64>,
179+
valid_pool_topology: Option<bool>,
179180
}
180181

181182
impl ReplicaItem {
@@ -197,8 +198,20 @@ impl ReplicaItem {
197198
child_spec,
198199
child_info,
199200
ag_replicas_on_pool,
201+
valid_pool_topology: None,
200202
}
201203
}
204+
/// Set the validity of the replica's pool topology.
205+
/// If set to false, this means the replica is not in sync with the volume topology and
206+
/// therefore should be replaced by another replica which is in sync.
207+
pub(crate) fn with_pool_topology(mut self, valid_pool_topology: Option<bool>) -> ReplicaItem {
208+
self.valid_pool_topology = valid_pool_topology;
209+
self
210+
}
211+
/// Get a reference to the topology validity flag.
212+
pub(crate) fn valid_pool_topology(&self) -> &Option<bool> {
213+
&self.valid_pool_topology
214+
}
202215
/// Get a reference to the replica spec.
203216
pub(crate) fn spec(&self) -> &ReplicaSpec {
204217
&self.replica_spec

control-plane/agents/src/bin/core/controller/scheduling/volume.rs

+8
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,14 @@ impl GetChildForRemovalContext {
343343
}),
344344
ag_rep_count,
345345
)
346+
.with_pool_topology({
347+
use crate::controller::scheduling::volume_policy::pool::PoolBaseFilters;
348+
Some(PoolBaseFilters::topology_(
349+
&self.spec,
350+
&self.registry,
351+
replica_spec.pool_name(),
352+
))
353+
})
346354
})
347355
.collect::<Vec<_>>()
348356
}

control-plane/agents/src/bin/core/controller/scheduling/volume_policy/pool.rs

+23-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
use crate::controller::scheduling::{
2-
resources::{ChildItem, PoolItem},
3-
volume::{GetSuitablePoolsContext, ReplicaResizePoolsContext},
4-
volume_policy::qualifies_label_criteria,
1+
use crate::controller::{
2+
registry::Registry,
3+
scheduling::{
4+
resources::{ChildItem, PoolItem},
5+
volume::{GetSuitablePoolsContext, ReplicaResizePoolsContext},
6+
volume_policy::qualifies_label_criteria,
7+
},
58
};
69
use std::collections::HashMap;
7-
use stor_port::types::v0::transport::{PoolStatus, PoolTopology};
10+
use stor_port::types::v0::{
11+
store::volume::VolumeSpec,
12+
transport::{PoolId, PoolStatus, PoolTopology},
13+
};
814

915
/// Filter pools used for replica creation.
1016
pub(crate) struct PoolBaseFilters {}
@@ -77,27 +83,32 @@ impl PoolBaseFilters {
7783

7884
/// Should only attempt to use pools having specific creation label if topology has it.
7985
pub(crate) fn topology(request: &GetSuitablePoolsContext, item: &PoolItem) -> bool {
86+
Self::topology_(request, request.registry(), &item.pool.id)
87+
}
88+
89+
/// Should only attempt to use pools having specific creation label if topology has it.
90+
pub(crate) fn topology_(volume: &VolumeSpec, registry: &Registry, pool_id: &PoolId) -> bool {
8091
let volume_pool_topology_inclusion_labels: HashMap<String, String>;
81-
match request.topology.clone() {
92+
match &volume.topology {
8293
None => return true,
83-
Some(topology) => match topology.pool {
94+
Some(topology) => match &topology.pool {
8495
None => return true,
85-
Some(pool_topology) => match pool_topology {
96+
Some(pool_topology) => match &pool_topology {
8697
PoolTopology::Labelled(labelled_topology) => {
8798
// The labels in Volume Pool Topology should match the pool labels if
8899
// present, otherwise selection of any pool is allowed.
89-
if !labelled_topology.inclusion.is_empty() {
90-
volume_pool_topology_inclusion_labels = labelled_topology.inclusion
91-
} else {
100+
if labelled_topology.inclusion.is_empty() {
101+
// todo: missing exclusion check?
92102
return true;
93103
}
104+
volume_pool_topology_inclusion_labels = labelled_topology.inclusion.clone()
94105
}
95106
},
96107
},
97108
};
98109

99110
// We will reach this part of code only if the volume has inclusion/exclusion labels.
100-
match request.registry().specs().pool(&item.pool.id) {
111+
match registry.specs().pool(pool_id) {
101112
Ok(spec) => match spec.labels {
102113
None => false,
103114
Some(pool_labels) => {

0 commit comments

Comments
 (0)