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  }