MCPcopy Create free account
hub / github.com/cozystack/cozystack / ComputePlacement

Function ComputePlacement

internal/fluxshardoperator/placement.go:55–113  ·  view source on GitHub ↗

ComputePlacement returns the desired tenant->shard assignment. Strategy is greedy least-loaded by weight (deterministic: with uniform weights, N tenants over N shards land exactly 1 per shard), with minimal movement on rescale: - tenants whose current shard still exists keep it (scale-up never touc

(in PlacementInput)

Source from the content-addressed store, hash-verified

53// exceeds RebalanceThreshold, preferring the smallest tenant that achieves
54// the reduction (cheaper handoff).
55func ComputePlacement(in PlacementInput) map[string]string {
56 k := in.ShardCount
57 if k < 1 {
58 k = 1
59 }
60
61 assign := make(map[string]string, len(in.Tenants))
62 load := make([]int, k)
63 members := make([][]TenantInfo, k)
64
65 place := func(t TenantInfo, idx int) {
66 assign[t.Namespace] = ShardName(idx)
67 load[idx] += t.Weight
68 members[idx] = append(members[idx], t)
69 }
70
71 // Deterministic processing order.
72 tenants := make([]TenantInfo, len(in.Tenants))
73 copy(tenants, in.Tenants)
74 sort.Slice(tenants, func(i, j int) bool { return tenants[i].Namespace < tenants[j].Namespace })
75
76 var unassigned []TenantInfo
77 for _, t := range tenants {
78 if pin, ok := in.Pinned[t.Namespace]; ok && !t.Deleting {
79 if idx, ok := ParseShardIndex(pin); ok && idx < k {
80 place(t, idx)
81 continue
82 }
83 }
84 if idx, ok := ParseShardIndex(t.Current); ok {
85 if idx < k {
86 place(t, idx)
87 continue
88 }
89 if t.Deleting {
90 // Never move a deleting tenant, even off a removed shard: the
91 // provisioner keeps the shard Deployment until it drains.
92 assign[t.Namespace] = t.Current
93 continue
94 }
95 }
96 unassigned = append(unassigned, t)
97 }
98
99 // Heaviest first (LPT) gives the better balance when backfilling many
100 // tenants at once; name tie-break keeps it deterministic.
101 sort.Slice(unassigned, func(i, j int) bool {
102 if unassigned[i].Weight != unassigned[j].Weight {
103 return unassigned[i].Weight > unassigned[j].Weight
104 }
105 return unassigned[i].Namespace < unassigned[j].Namespace
106 })
107 for _, t := range unassigned {
108 place(t, argminLoad(load))
109 }
110
111 rebalance(in, k, assign, load, members)
112 return assign

Calls 4

ShardNameFunction · 0.85
ParseShardIndexFunction · 0.85
argminLoadFunction · 0.85
rebalanceFunction · 0.85