frostfs-sdk-csharp/src/FrostFS.SDK.Client/Models/Netmap/FrostFsNetmapSnapshot.cs
Pavel Gross 6ae96c1d77 [#41] Client: Remove ranges
Signed-off-by: Pavel Gross <p.gross@yadro.com>
2025-03-12 00:11:50 +03:00

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;
}
}