246 lines
7.7 KiB
C#
246 lines
7.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
using FrostFS.SDK.Client;
|
|
using FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
using FrostFS.SDK.Cryptography;
|
|
|
|
namespace FrostFS.SDK;
|
|
|
|
public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList<FrostFsNodeInfo> nodeInfoCollection)
|
|
{
|
|
public ulong Epoch { get; private set; } = epoch;
|
|
|
|
public IReadOnlyList<FrostFsNodeInfo> NodeInfoCollection { get; private set; } = nodeInfoCollection;
|
|
|
|
internal static INormalizer NewReverseMinNorm(double minV)
|
|
{
|
|
return new ReverseMinNorm { min = minV };
|
|
}
|
|
|
|
// newSigmoidNorm returns a normalizer which
|
|
// normalize values in range of 0.0 to 1.0 to a scaled sigmoid.
|
|
internal static INormalizer NewSigmoidNorm(double scale)
|
|
{
|
|
return new SigmoidNorm(scale);
|
|
}
|
|
|
|
// PlacementVectors sorts container nodes returned by ContainerNodes method
|
|
// and returns placement vectors for the entity identified by the given pivot.
|
|
// For example, in order to build node list to store the object, binary-encoded
|
|
// object identifier can be used as pivot. Result is deterministic for
|
|
// the fixed NetMap and parameters.
|
|
public FrostFsNodeInfo[][] PlacementVectors(FrostFsNodeInfo[][] vectors, byte[] pivot)
|
|
{
|
|
if (vectors is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(vectors));
|
|
}
|
|
|
|
using var murmur3 = new Murmur3(0);
|
|
var hash = murmur3.GetCheckSum64(pivot);
|
|
|
|
var wf = Tools.DefaultWeightFunc(NodeInfoCollection.ToArray());
|
|
|
|
var result = new FrostFsNodeInfo[vectors.Length][];
|
|
var maxSize = vectors.Max(x => x.Length);
|
|
|
|
var spanWeigths = new double[maxSize];
|
|
|
|
for (int i = 0; i < vectors.Length; i++)
|
|
{
|
|
result[i] = new FrostFsNodeInfo[vectors[i].Length];
|
|
|
|
for (int j = 0; j < vectors[i].Length; j++)
|
|
{
|
|
result[i][j] = vectors[i][j];
|
|
}
|
|
|
|
Tools.AppendWeightsTo(result[i], wf, ref spanWeigths);
|
|
|
|
result[i] = Tools.SortHasherSliceByWeightValue(result[i].ToList<FrostFsNodeInfo>(), spanWeigths, hash).ToArray();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// SelectFilterNodes returns a two-dimensional list of nodes as a result of applying the
|
|
// given SelectFilterExpr to the NetMap.
|
|
// If the SelectFilterExpr contains only filters, the result contains a single row with the
|
|
// result of the last filter application.
|
|
// If the SelectFilterExpr contains only selectors, the result contains the selection rows
|
|
// of the last select application.
|
|
List<List<FrostFsNodeInfo>> SelectFilterNodes(SelectFilterExpr expr)
|
|
{
|
|
var policy = new FrostFsPlacementPolicy(false, expr.Cbf, [expr.Selector], expr.Filters);
|
|
|
|
var ctx = new Context(this)
|
|
{
|
|
Cbf = expr.Cbf
|
|
};
|
|
|
|
ctx.ProcessFilters(policy);
|
|
ctx.ProcessSelectors(policy);
|
|
|
|
var ret = new List<List<FrostFsNodeInfo>>();
|
|
|
|
if (expr.Selector == null)
|
|
{
|
|
var lastFilter = expr.Filters.Last();
|
|
|
|
var subCollestion = new List<FrostFsNodeInfo>();
|
|
ret.Add(subCollestion);
|
|
|
|
foreach (var nodeInfo in NodeInfoCollection)
|
|
{
|
|
if (ctx.Match(ctx.ProcessedFilters[lastFilter.Name], nodeInfo))
|
|
{
|
|
subCollestion.Add(nodeInfo);
|
|
}
|
|
}
|
|
}
|
|
else if (expr.Selector.Name != null)
|
|
{
|
|
var sel = ctx.GetSelection(ctx.ProcessedSelectors[expr.Selector.Name]);
|
|
|
|
foreach (var ns in sel)
|
|
{
|
|
var subCollestion = new List<FrostFsNodeInfo>();
|
|
ret.Add(subCollestion);
|
|
foreach (var n in ns)
|
|
{
|
|
subCollestion.Add(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
internal static Func<FrostFsNodeInfo, double> NewWeightFunc(INormalizer capNorm, INormalizer priceNorm)
|
|
{
|
|
return new Func<FrostFsNodeInfo, double>((FrostFsNodeInfo nodeInfo) =>
|
|
{
|
|
return capNorm.Normalize(nodeInfo.GetCapacity()) * priceNorm.Normalize(nodeInfo.Price);
|
|
});
|
|
}
|
|
|
|
private static FrostFsNodeInfo[] FlattenNodes(List<List<FrostFsNodeInfo>> nodes)
|
|
{
|
|
int sz = 0;
|
|
foreach (var ns in nodes)
|
|
{
|
|
sz += ns.Count;
|
|
}
|
|
|
|
var result = new FrostFsNodeInfo[sz];
|
|
|
|
int i = 0;
|
|
foreach (var ns in nodes)
|
|
{
|
|
foreach (var n in ns)
|
|
{
|
|
result[i++] = n;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ContainerNodes returns two-dimensional list of nodes as a result of applying
|
|
// given PlacementPolicy to the NetMap. Each line of the list corresponds to a
|
|
// replica descriptor. Line order corresponds to order of ReplicaDescriptor list
|
|
// in the policy. Nodes are pre-filtered according to the Filter list from
|
|
// the policy, and then selected by Selector list. Result is deterministic for
|
|
// the fixed NetMap and parameters.
|
|
//
|
|
// Result can be used in PlacementVectors.
|
|
public FrostFsNodeInfo[][] ContainerNodes(FrostFsPlacementPolicy p, byte[]? pivot)
|
|
{
|
|
var c = new Context(this)
|
|
{
|
|
Cbf = p.BackupFactor == 0 ? 3 : p.BackupFactor
|
|
};
|
|
|
|
if (pivot != null && pivot.Length > 0)
|
|
{
|
|
c.HrwSeed = pivot;
|
|
|
|
using var murmur = new Murmur3(0);
|
|
c.HrwSeedHash = murmur.GetCheckSum64(pivot);
|
|
}
|
|
|
|
c.ProcessFilters(p);
|
|
c.ProcessSelectors(p);
|
|
|
|
var unique = p.IsUnique();
|
|
|
|
var result = new List<List<FrostFsNodeInfo>>(p.Replicas.Length);
|
|
for (int i = 0; i < p.Replicas.Length; i++)
|
|
{
|
|
result.Add([]);
|
|
}
|
|
|
|
// Note that the cached selectors are not used when the policy contains the UNIQUE flag.
|
|
// This is necessary because each selection vector affects potentially the subsequent vectors
|
|
// and thus we call getSelection in such case, in order to take into account nodes previously
|
|
// marked as used by earlier replicas.
|
|
for (int i = 0; i < p.Replicas.Length; i++)
|
|
{
|
|
var sName = p.Replicas[i].Selector;
|
|
|
|
if (string.IsNullOrEmpty(sName) && !(p.Replicas.Length == 1 && p.Selectors.Count == 1))
|
|
{
|
|
var s = new FrostFsSelector(string.Empty)
|
|
{
|
|
Count = p.Replicas[i].CountNodes(),
|
|
Filter = Context.mainFilterName
|
|
};
|
|
|
|
var nodes = c.GetSelection(s);
|
|
result[i].AddRange(FlattenNodes(nodes));
|
|
|
|
if (unique)
|
|
{
|
|
foreach (var n in result[i])
|
|
{
|
|
c.UsedNodes[n.Hash()] = true;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (unique)
|
|
{
|
|
if (!c.ProcessedSelectors.TryGetValue(sName, out var s) || s == null)
|
|
{
|
|
throw new FrostFsException($"selector not found: {sName}");
|
|
}
|
|
|
|
var nodes = c.GetSelection(c.ProcessedSelectors[sName]);
|
|
|
|
result[i].AddRange(FlattenNodes(nodes));
|
|
|
|
foreach (var n in result[i])
|
|
{
|
|
c.UsedNodes[n.Hash()] = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var nodes = c.Selections[sName];
|
|
result[i].AddRange(FlattenNodes(nodes));
|
|
}
|
|
}
|
|
|
|
var collection = new FrostFsNodeInfo[result.Count][];
|
|
for (int i = 0; i < result.Count; i++)
|
|
{
|
|
collection[i] = [.. result[i]];
|
|
}
|
|
|
|
return collection;
|
|
}
|
|
}
|