mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-16 04:06:44 +00:00
placement: let callers reject candidates placement cannot judge (#10581)
Replica placement rules and "this node already holds the volume" are constraints the picker has no way to model. The predicate receives the candidate's rack and data center, because the constraints needing them are exactly the ones a bare node cannot express. Claude-Session: https://claude.ai/code/session_01Ks16jnt4S7gdDk8cheQ3xu
This commit is contained in:
@@ -20,6 +20,12 @@ type PlacementPreference struct {
|
||||
// Exclude names nodes already spoken for by the same plan, so a caller
|
||||
// placing several copies does not stack them on one node.
|
||||
Exclude map[string]bool
|
||||
// Accept rejects candidates the caller cannot use for reasons placement does
|
||||
// not model -- replica placement rules, a node already holding the volume.
|
||||
// It receives the candidate's rack and data center because the constraints
|
||||
// that need them are exactly the ones a bare node cannot express. Nil
|
||||
// accepts everything.
|
||||
Accept func(node *master_pb.DataNodeInfo, dataCenter, rack string) bool
|
||||
// VolumeBytes is what this move will actually consume on the destination.
|
||||
// Zero falls back to the tier's average volume size, which is all a caller
|
||||
// planning a not-yet-created volume can know.
|
||||
@@ -73,6 +79,9 @@ func PickTarget(topo *master_pb.TopologyInfo, pref PlacementPreference) *master_
|
||||
if d == nil || d.VolumeCount >= d.MaxVolumeCount {
|
||||
continue
|
||||
}
|
||||
if pref.Accept != nil && !pref.Accept(n.info, n.dc, n.rack) {
|
||||
continue
|
||||
}
|
||||
c := candidate{node: n}
|
||||
c.bytes, c.hasBytes = d.DiskFreeBytes, d.DiskTotalBytes != 0
|
||||
c.slots = d.MaxVolumeCount - d.VolumeCount
|
||||
|
||||
@@ -224,3 +224,35 @@ func TestPickTargetReservesTheVolumeSize(t *testing.T) {
|
||||
t.Fatalf("second pick %q, want a2 after 800 bytes were spent on a1", second.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickTargetHonoursTheCallerPredicate(t *testing.T) {
|
||||
// Constraints placement does not model -- replica placement, a node already
|
||||
// holding the volume -- stay with the caller, which keeps them out of the
|
||||
// ranking rather than duplicating them here.
|
||||
topo := placementTopo(
|
||||
placementNode("a1", 10, 1, 1000, 900),
|
||||
placementNode("a2", 10, 1, 1000, 500),
|
||||
)
|
||||
got := PickTarget(topo, PlacementPreference{
|
||||
Source: "z9", DiskType: types.SsdType,
|
||||
Accept: func(n *master_pb.DataNodeInfo, dc, rack string) bool { return n.Id != "a1" },
|
||||
})
|
||||
if got.GetId() != "a2" {
|
||||
t.Fatalf("got %q, want a2 -- a1 was rejected despite being emptier", got.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickTargetPredicateSeesRackAndDataCenter(t *testing.T) {
|
||||
topo := placementTopo(placementNode("a1", 10, 1, 1000, 900))
|
||||
var sawDc, sawRack string
|
||||
PickTarget(topo, PlacementPreference{
|
||||
Source: "z9", DiskType: types.SsdType,
|
||||
Accept: func(n *master_pb.DataNodeInfo, dc, rack string) bool {
|
||||
sawDc, sawRack = dc, rack
|
||||
return true
|
||||
},
|
||||
})
|
||||
if sawDc != "dc1" || sawRack != "a" {
|
||||
t.Fatalf("predicate saw dc=%q rack=%q, want dc1/a", sawDc, sawRack)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user