git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/netmap/netmap.go (about) 1 package netmap 2 3 import ( 4 "fmt" 5 6 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" 7 "git.frostfs.info/TrueCloudLab/hrw" 8 ) 9 10 // NetMap represents FrostFS network map. It includes information about all 11 // storage nodes registered in FrostFS the network. 12 // 13 // NetMap is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap.NetMap 14 // message. See ReadFromV2 / WriteToV2 methods. 15 // 16 // Instances can be created using built-in var declaration. 17 type NetMap struct { 18 epoch uint64 19 20 nodes []NodeInfo 21 } 22 23 // ReadFromV2 reads NetMap from the netmap.NetMap message. Checks if the 24 // message conforms to FrostFS API V2 protocol. 25 // 26 // See also WriteToV2. 27 func (m *NetMap) ReadFromV2(msg netmap.NetMap) error { 28 var err error 29 nodes := msg.Nodes() 30 31 if nodes == nil { 32 m.nodes = nil 33 } else { 34 m.nodes = make([]NodeInfo, len(nodes)) 35 36 for i := range nodes { 37 err = m.nodes[i].ReadFromV2(nodes[i]) 38 if err != nil { 39 return fmt.Errorf("invalid node info: %w", err) 40 } 41 } 42 } 43 44 m.epoch = msg.Epoch() 45 46 return nil 47 } 48 49 // WriteToV2 writes NetMap to the netmap.NetMap message. The message 50 // MUST NOT be nil. 51 // 52 // See also ReadFromV2. 53 func (m NetMap) WriteToV2(msg *netmap.NetMap) { 54 var nodes []netmap.NodeInfo 55 56 if m.nodes != nil { 57 nodes = make([]netmap.NodeInfo, len(m.nodes)) 58 59 for i := range m.nodes { 60 m.nodes[i].WriteToV2(&nodes[i]) 61 } 62 63 msg.SetNodes(nodes) 64 } 65 66 msg.SetEpoch(m.epoch) 67 } 68 69 // SetNodes sets information list about all storage nodes from the FrostFS network. 70 // 71 // Argument MUST NOT be mutated, make a copy first. 72 // 73 // See also Nodes. 74 func (m *NetMap) SetNodes(nodes []NodeInfo) { 75 m.nodes = nodes 76 } 77 78 // Nodes returns nodes set using SetNodes. 79 // 80 // Return value MUST not be mutated, make a copy first. 81 func (m NetMap) Nodes() []NodeInfo { 82 return m.nodes 83 } 84 85 // SetEpoch specifies revision number of the NetMap. 86 // 87 // See also Epoch. 88 func (m *NetMap) SetEpoch(epoch uint64) { 89 m.epoch = epoch 90 } 91 92 // Epoch returns epoch set using SetEpoch. 93 // 94 // Zero NetMap has zero revision. 95 func (m NetMap) Epoch() uint64 { 96 return m.epoch 97 } 98 99 // nodes is a slice of NodeInfo instances needed for HRW sorting. 100 type nodes []NodeInfo 101 102 // assert nodes type provides hrw.Hasher required for HRW sorting. 103 var _ hrw.Hasher = nodes{} 104 105 // Hash is a function from hrw.Hasher interface. It is implemented 106 // to support weighted hrw sorting of buckets. Each bucket is already sorted by hrw, 107 // thus giving us needed "randomness". 108 func (n nodes) Hash() uint64 { 109 if len(n) > 0 { 110 return n[0].Hash() 111 } 112 113 return 0 114 } 115 116 func (n nodes) appendWeightsTo(wf weightFunc, w []float64) []float64 { 117 if cap(w) < len(n) { 118 w = make([]float64, 0, len(n)) 119 } 120 for i := range n { 121 w = append(w, wf(n[i])) 122 } 123 124 return w 125 } 126 127 func flattenNodes(ns []nodes) nodes { 128 var sz, i int 129 130 for i = range ns { 131 sz += len(ns[i]) 132 } 133 134 result := make(nodes, 0, sz) 135 136 for i := range ns { 137 result = append(result, ns[i]...) 138 } 139 140 return result 141 } 142 143 // PlacementVectors sorts container nodes returned by ContainerNodes method 144 // and returns placement vectors for the entity identified by the given pivot. 145 // For example, in order to build node list to store the object, binary-encoded 146 // object identifier can be used as pivot. Result is deterministic for 147 // the fixed NetMap and parameters. 148 func (m NetMap) PlacementVectors(vectors [][]NodeInfo, pivot []byte) ([][]NodeInfo, error) { 149 h := hrw.Hash(pivot) 150 wf := defaultWeightFunc(m.nodes) 151 result := make([][]NodeInfo, len(vectors)) 152 153 var ws []float64 154 155 for i := range vectors { 156 result[i] = make([]NodeInfo, len(vectors[i])) 157 copy(result[i], vectors[i]) 158 ws = nodes(result[i]).appendWeightsTo(wf, ws[:0]) 159 hrw.SortHasherSliceByWeightValue(result[i], ws, h) 160 } 161 162 return result, nil 163 } 164 165 // SelectFilterNodes returns a two-dimensional list of nodes as a result of applying the 166 // given SelectFilterExpr to the NetMap. 167 // If the SelectFilterExpr contains only filters, the result contains a single row with the 168 // result of the last filter application. 169 // If the SelectFilterExpr contains only selectors, the result contains the selection rows 170 // of the last select application. 171 func (m NetMap) SelectFilterNodes(expr *SelectFilterExpr) ([][]NodeInfo, error) { 172 p := PlacementPolicy{ 173 filters: expr.filters, 174 } 175 176 if expr.selector != nil { 177 p.selectors = append(p.selectors, *expr.selector) 178 } 179 180 c := newContext(m) 181 c.setCBF(expr.cbf) 182 183 if err := c.processFilters(p); err != nil { 184 return nil, err 185 } 186 if err := c.processSelectors(p); err != nil { 187 return nil, err 188 } 189 190 if expr.selector == nil { 191 var ret []NodeInfo 192 lastFilter := expr.filters[len(expr.filters)-1] 193 for _, ni := range m.nodes { 194 if c.match(c.processedFilters[lastFilter.GetName()], ni) { 195 ret = append(ret, ni) 196 } 197 } 198 return [][]NodeInfo{ret}, nil 199 } 200 201 sel, err := c.getSelection(*c.processedSelectors[expr.selector.GetName()]) 202 if err != nil { 203 return nil, err 204 } 205 206 var ret [][]NodeInfo 207 for i, ns := range sel { 208 ret = append(ret, []NodeInfo{}) 209 for _, n := range ns { 210 ret[i] = append(ret[i], n) 211 } 212 } 213 return ret, nil 214 } 215 216 func countNodes(r netmap.Replica) uint32 { 217 if r.GetCount() != 0 { 218 return r.GetCount() 219 } 220 return r.GetECDataCount() + r.GetECParityCount() 221 } 222 223 func (p PlacementPolicy) isUnique() bool { 224 if p.unique { 225 return true 226 } 227 for _, r := range p.replicas { 228 if r.GetECDataCount() != 0 || r.GetECParityCount() != 0 { 229 return true 230 } 231 } 232 return false 233 } 234 235 // ContainerNodes returns two-dimensional list of nodes as a result of applying 236 // given PlacementPolicy to the NetMap. Each line of the list corresponds to a 237 // replica descriptor. Line order corresponds to order of ReplicaDescriptor list 238 // in the policy. Nodes are pre-filtered according to the Filter list from 239 // the policy, and then selected by Selector list. Result is deterministic for 240 // the fixed NetMap and parameters. 241 // 242 // Result can be used in PlacementVectors. 243 func (m NetMap) ContainerNodes(p PlacementPolicy, pivot []byte) ([][]NodeInfo, error) { 244 c := newContext(m) 245 c.setPivot(pivot) 246 c.setCBF(p.backupFactor) 247 248 if err := c.processFilters(p); err != nil { 249 return nil, err 250 } 251 252 if err := c.processSelectors(p); err != nil { 253 return nil, err 254 } 255 256 unique := p.isUnique() 257 result := make([][]NodeInfo, len(p.replicas)) 258 259 // Note that the cached selectors are not used when the policy contains the UNIQUE flag. 260 // This is necessary because each selection vector affects potentially the subsequent vectors 261 // and thus we call getSelection in such case, in order to take into account nodes previously 262 // marked as used by earlier replicas. 263 for i := range p.replicas { 264 sName := p.replicas[i].GetSelector() 265 if sName == "" && !(len(p.replicas) == 1 && len(p.selectors) == 1) { 266 var s netmap.Selector 267 s.SetCount(countNodes(p.replicas[i])) 268 s.SetFilter(mainFilterName) 269 270 nodes, err := c.getSelection(s) 271 if err != nil { 272 return nil, err 273 } 274 275 result[i] = append(result[i], flattenNodes(nodes)...) 276 277 if unique { 278 c.addUsedNodes(result[i]...) 279 } 280 281 continue 282 } 283 284 if unique { 285 if c.processedSelectors[sName] == nil { 286 return nil, fmt.Errorf("selector not found: '%s'", sName) 287 } 288 nodes, err := c.getSelection(*c.processedSelectors[sName]) 289 if err != nil { 290 return nil, err 291 } 292 result[i] = append(result[i], flattenNodes(nodes)...) 293 c.addUsedNodes(result[i]...) 294 } else { 295 nodes, ok := c.selections[sName] 296 if !ok { 297 return nil, fmt.Errorf("selector not found: REPLICA '%s'", sName) 298 } 299 result[i] = append(result[i], flattenNodes(nodes)...) 300 } 301 } 302 303 return result, nil 304 }