[#29] Client: Add PlacementVector unit tests
Signed-off-by: Pavel Gross <p.gross@yadro.com>
This commit is contained in:
parent
568bdc67e8
commit
43e300c773
33 changed files with 3054 additions and 234 deletions
|
@ -16,6 +16,9 @@ public static class PlacementPolicyMapper
|
|||
|
||||
return new FrostFsPlacementPolicy(
|
||||
placementPolicy.Unique,
|
||||
placementPolicy.ContainerBackupFactor,
|
||||
new System.Collections.ObjectModel.Collection<FrostFsSelector>(placementPolicy.Selectors.Select(selector => selector.ToModel()).ToList()),
|
||||
new System.Collections.ObjectModel.Collection<FrostFsFilter>(placementPolicy.Filters.Select(filter => filter.ToModel()).ToList()),
|
||||
placementPolicy.Replicas.Select(replica => replica.ToModel()).ToArray()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using FrostFS.Netmap;
|
||||
|
||||
namespace FrostFS.SDK.Client;
|
||||
|
||||
public static class ReplicaMapper
|
||||
public static class PolicyMapper
|
||||
{
|
||||
public static Replica ToMessage(this FrostFsReplica replica)
|
||||
{
|
||||
|
@ -24,4 +25,67 @@ public static class ReplicaMapper
|
|||
|
||||
return new FrostFsReplica((int)replica.Count, replica.Selector);
|
||||
}
|
||||
|
||||
public static Selector ToMessage(this FrostFsSelector selector)
|
||||
{
|
||||
if (selector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(selector));
|
||||
}
|
||||
|
||||
return new Selector
|
||||
{
|
||||
Name = selector.Name,
|
||||
Count = selector.Count,
|
||||
Clause = (Clause)selector.Clause,
|
||||
Attribute = selector.Attribute,
|
||||
Filter = selector.Filter
|
||||
};
|
||||
}
|
||||
|
||||
public static FrostFsSelector ToModel(this Selector selector)
|
||||
{
|
||||
if (selector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(selector));
|
||||
}
|
||||
|
||||
return new FrostFsSelector(selector.Name)
|
||||
{
|
||||
Count = selector.Count,
|
||||
Clause = (int)selector.Clause,
|
||||
Attribute = selector.Attribute,
|
||||
Filter = selector.Filter
|
||||
};
|
||||
}
|
||||
|
||||
public static Filter ToMessage(this FrostFsFilter filter)
|
||||
{
|
||||
if (filter is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filter));
|
||||
}
|
||||
|
||||
var message = new Filter
|
||||
{
|
||||
Name = filter.Name,
|
||||
Key = filter.Key,
|
||||
Op = (Operation)filter.Operation,
|
||||
Value = filter.Value,
|
||||
};
|
||||
|
||||
message.Filters.AddRange(filter.Filters.Select(f => f.ToMessage()));
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static FrostFsFilter ToModel(this Filter filter)
|
||||
{
|
||||
if (filter is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filter));
|
||||
}
|
||||
|
||||
return new FrostFsFilter(filter.Name, filter.Key, (int)filter.Op, filter.Value, filter.Filters.Select(f => f.ToModel()).ToArray());
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
using FrostFS.SDK.Client;
|
||||
|
@ -58,9 +57,9 @@ public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList<FrostFsNodeInfo> n
|
|||
result[i][j] = vectors[i][j];
|
||||
}
|
||||
|
||||
Tools.AppendWeightsTo(result[i], wf, spanWeigths);
|
||||
Tools.AppendWeightsTo(result[i], wf, ref spanWeigths);
|
||||
|
||||
Tools.SortHasherSliceByWeightValue(result[i].ToList<FrostFsNodeInfo>(), spanWeigths, hash);
|
||||
result[i] = Tools.SortHasherSliceByWeightValue(result[i].ToList<FrostFsNodeInfo>(), spanWeigths, hash).ToArray();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -74,12 +73,7 @@ public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList<FrostFsNodeInfo> n
|
|||
// of the last select application.
|
||||
List<List<FrostFsNodeInfo>> SelectFilterNodes(SelectFilterExpr expr)
|
||||
{
|
||||
var policy = new FrostFsPlacementPolicy(false);
|
||||
|
||||
foreach (var f in expr.Filters)
|
||||
policy.Filters.Add(f);
|
||||
|
||||
policy.Selectors.Add(expr.Selector);
|
||||
var policy = new FrostFsPlacementPolicy(false, expr.Cbf, [expr.Selector], expr.Filters);
|
||||
|
||||
var ctx = new Context(this)
|
||||
{
|
||||
|
@ -166,7 +160,7 @@ public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList<FrostFsNodeInfo> n
|
|||
{
|
||||
var c = new Context(this)
|
||||
{
|
||||
Cbf = p.BackupFactor
|
||||
Cbf = p.BackupFactor == 0 ? 3 : p.BackupFactor
|
||||
};
|
||||
|
||||
if (pivot != null && pivot.Length > 0)
|
||||
|
@ -198,20 +192,14 @@ public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList<FrostFsNodeInfo> n
|
|||
|
||||
if (string.IsNullOrEmpty(sName) && !(p.Replicas.Length == 1 && p.Selectors.Count == 1))
|
||||
{
|
||||
var s = new FrostFsSelector(Context.mainFilterName)
|
||||
var s = new FrostFsSelector(string.Empty)
|
||||
{
|
||||
Count = p.Replicas[i].CountNodes()
|
||||
Count = p.Replicas[i].CountNodes(),
|
||||
Filter = Context.mainFilterName
|
||||
};
|
||||
|
||||
var nodes = c.GetSelection(s);
|
||||
|
||||
var arg = new List<List<FrostFsNodeInfo>>(nodes.Count);
|
||||
for (int j = 0; j < nodes.Count; j++)
|
||||
{
|
||||
arg[i] = nodes[j];
|
||||
}
|
||||
|
||||
result[i].AddRange(FlattenNodes(arg));
|
||||
result[i].AddRange(FlattenNodes(nodes));
|
||||
|
||||
if (unique)
|
||||
{
|
||||
|
@ -243,23 +231,16 @@ public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList<FrostFsNodeInfo> n
|
|||
else
|
||||
{
|
||||
var nodes = c.Selections[sName];
|
||||
|
||||
var arg = new List<List<FrostFsNodeInfo>>(nodes.Count);
|
||||
for (int j = 0; j < nodes.Count; j++)
|
||||
{
|
||||
arg[i] = nodes[j];
|
||||
}
|
||||
|
||||
result[i].AddRange(FlattenNodes(arg));
|
||||
result[i].AddRange(FlattenNodes(nodes));
|
||||
}
|
||||
}
|
||||
|
||||
var collection = new FrostFsNodeInfo[result.Count][];
|
||||
for(int i =0; i < result.Count; i++)
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
collection[i] = [.. result[i]];
|
||||
}
|
||||
|
||||
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,24 @@ using FrostFS.SDK.Client;
|
|||
|
||||
namespace FrostFS.SDK;
|
||||
|
||||
public struct FrostFsPlacementPolicy(bool unique, params FrostFsReplica[] replicas)
|
||||
public struct FrostFsPlacementPolicy(bool unique,
|
||||
uint backupFactor,
|
||||
Collection<FrostFsSelector> selectors,
|
||||
Collection<FrostFsFilter> filters,
|
||||
params FrostFsReplica[] replicas)
|
||||
: IEquatable<FrostFsPlacementPolicy>
|
||||
{
|
||||
private PlacementPolicy policy;
|
||||
|
||||
public FrostFsReplica[] Replicas { get; private set; } = replicas;
|
||||
public FrostFsReplica[] Replicas { get; } = replicas;
|
||||
|
||||
public Collection<FrostFsSelector> Selectors { get; } = [];
|
||||
public Collection<FrostFsSelector> Selectors { get; } = selectors;
|
||||
|
||||
public Collection<FrostFsFilter> Filters { get; } = [];
|
||||
public Collection<FrostFsFilter> Filters { get; } = filters;
|
||||
|
||||
public bool Unique { get; private set; } = unique;
|
||||
public bool Unique { get; } = unique;
|
||||
|
||||
public uint BackupFactor { get; set; }
|
||||
public uint BackupFactor { get; } = backupFactor;
|
||||
|
||||
public override readonly bool Equals(object obj)
|
||||
{
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
public class FrostFsSelector(string name)
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
public string Name { get; set; } = name;
|
||||
public uint Count { get; set; }
|
||||
public uint Clause { get; set; }
|
||||
public int Clause { get; set; }
|
||||
public string? Attribute { get; set; }
|
||||
public string? Filter { get; set; }
|
||||
}
|
||||
|
|
|
@ -104,20 +104,23 @@ internal struct Context
|
|||
return;
|
||||
}
|
||||
|
||||
if (filter.Operation == (int)Operation.EQ ||
|
||||
filter.Operation == (int)Operation.NE ||
|
||||
filter.Operation == (int)Operation.LIKE ||
|
||||
filter.Operation == (int)Operation.GT ||
|
||||
filter.Operation == (int)Operation.GE ||
|
||||
filter.Operation == (int)Operation.LT ||
|
||||
filter.Operation == (int)Operation.LE)
|
||||
switch (filter.Operation)
|
||||
{
|
||||
var n = uint.Parse(filter.Value, CultureInfo.InvariantCulture);
|
||||
NumCache[filter.Value] = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FrostFsException($"{errInvalidFilterOp}: {filter.Operation}");
|
||||
case (int)Operation.EQ:
|
||||
case (int)Operation.NE:
|
||||
case (int)Operation.LIKE:
|
||||
break;
|
||||
case (int)Operation.GT:
|
||||
case (int)Operation.GE:
|
||||
case (int)Operation.LT:
|
||||
case (int)Operation.LE:
|
||||
{
|
||||
var n = uint.Parse(filter.Value, CultureInfo.InvariantCulture);
|
||||
NumCache[filter.Value] = n;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new FrostFsException($"{errInvalidFilterOp}: {filter.Operation}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,7 +156,7 @@ internal struct Context
|
|||
// for the given selector.
|
||||
static (int bucketCount, int nodesInBucket) CalcNodesCount(FrostFsSelector selector)
|
||||
{
|
||||
return selector.Clause == (uint)FrostFsClause.Same
|
||||
return selector.Clause == (int)FrostFsClause.Same
|
||||
? (1, (int)selector.Count)
|
||||
: ((int)selector.Count, 1);
|
||||
}
|
||||
|
@ -211,11 +214,17 @@ internal struct Context
|
|||
{
|
||||
double[] ws = [];
|
||||
|
||||
foreach (var res in result)
|
||||
var sortedNodes = new NodeAttrPair[result.Count];
|
||||
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
Tools.AppendWeightsTo(res.nodes, weightFunc, ws);
|
||||
Tools.SortHasherSliceByWeightValue(res.nodes.ToList(), ws, HrwSeedHash);
|
||||
var res = result[i];
|
||||
Tools.AppendWeightsTo(res.nodes, weightFunc, ref ws);
|
||||
sortedNodes[i].nodes = Tools.SortHasherSliceByWeightValue(res.nodes.ToList(), ws, HrwSeedHash).ToArray();
|
||||
sortedNodes[i].attr = result[i].attr;
|
||||
}
|
||||
|
||||
return sortedNodes;
|
||||
}
|
||||
return [.. result];
|
||||
}
|
||||
|
@ -273,7 +282,7 @@ internal struct Context
|
|||
if (res.Count < bucketCount)
|
||||
{
|
||||
// Fallback to using minimum allowed backup factor (1).
|
||||
res = fallback;
|
||||
res.AddRange(fallback);
|
||||
|
||||
if (Strict && res.Count < bucketCount)
|
||||
{
|
||||
|
@ -293,7 +302,12 @@ internal struct Context
|
|||
}
|
||||
|
||||
var hashers = res.Select(r => new HasherList(r)).ToList();
|
||||
Tools.SortHasherSliceByWeightValue(hashers, weights, HrwSeedHash);
|
||||
hashers = Tools.SortHasherSliceByWeightValue(hashers, weights, HrwSeedHash);
|
||||
|
||||
for (int i = 0; i < res.Count; i++)
|
||||
{
|
||||
res[i] = hashers[i].Nodes;
|
||||
}
|
||||
}
|
||||
|
||||
if (res.Count < bucketCount)
|
||||
|
@ -331,7 +345,7 @@ internal struct Context
|
|||
switch (f.Operation)
|
||||
{
|
||||
case (int)Operation.EQ:
|
||||
return nodeInfo.Attributes[f.Key] == f.Value;
|
||||
return nodeInfo.Attributes.TryGetValue(f.Key, out var val) && val == f.Value;
|
||||
case (int)Operation.LIKE:
|
||||
{
|
||||
var hasPrefix = f.Value.StartsWith(likeWildcard, StringComparison.Ordinal);
|
||||
|
@ -357,12 +371,21 @@ internal struct Context
|
|||
return nodeInfo.Attributes[f.Key] != f.Value;
|
||||
default:
|
||||
{
|
||||
var attr = f.Key switch
|
||||
ulong attr;
|
||||
switch (f.Key)
|
||||
{
|
||||
FrostFsNodeInfo.AttrPrice => nodeInfo.Price,
|
||||
FrostFsNodeInfo.AttrCapacity => nodeInfo.GetCapacity(),
|
||||
_ => uint.Parse(nodeInfo.Attributes[f.Key], CultureInfo.InvariantCulture),
|
||||
};
|
||||
case FrostFsNodeInfo.AttrPrice:
|
||||
attr = nodeInfo.Price;
|
||||
break;
|
||||
|
||||
case FrostFsNodeInfo.AttrCapacity:
|
||||
attr = nodeInfo.GetCapacity();
|
||||
break;
|
||||
default:
|
||||
if (!ulong.TryParse(nodeInfo.Attributes[f.Key], NumberStyles.Integer, CultureInfo.InvariantCulture, out attr))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (f.Operation)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,14 @@ internal sealed class HasherList : IHasher
|
|||
_nodes = nodes;
|
||||
}
|
||||
|
||||
internal List<FrostFsNodeInfo> Nodes
|
||||
{
|
||||
get
|
||||
{
|
||||
return _nodes;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong Hash()
|
||||
{
|
||||
return _nodes.Count > 0 ? _nodes[0].Hash() : 0;
|
||||
|
|
|
@ -8,7 +8,7 @@ internal struct MeanAgg
|
|||
internal void Add(double n)
|
||||
{
|
||||
int c = count + 1;
|
||||
mean *= (double)count / c + n / c;
|
||||
mean = mean * count / c + n / c;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public static class Tools
|
|||
return x / (1 + x);
|
||||
}
|
||||
|
||||
internal static void AppendWeightsTo(FrostFsNodeInfo[] nodes, Func<FrostFsNodeInfo, double> wf, double[] weights)
|
||||
internal static void AppendWeightsTo(FrostFsNodeInfo[] nodes, Func<FrostFsNodeInfo, double> wf, ref double[] weights)
|
||||
{
|
||||
if (weights.Length < nodes.Length)
|
||||
{
|
||||
|
@ -49,11 +49,11 @@ public static class Tools
|
|||
}
|
||||
}
|
||||
|
||||
internal static void SortHasherSliceByWeightValue<T>(List<T> nodes, Span<double> weights, ulong hash) where T : IHasher
|
||||
internal static List<T> SortHasherSliceByWeightValue<T>(List<T> nodes, Span<double> weights, ulong hash) where T : IHasher
|
||||
{
|
||||
if (nodes.Count == 0)
|
||||
{
|
||||
return;
|
||||
return nodes;
|
||||
}
|
||||
|
||||
var allEquals = true;
|
||||
|
@ -70,7 +70,7 @@ public static class Tools
|
|||
}
|
||||
}
|
||||
|
||||
var dist = new ulong[nodes.Count];
|
||||
var dist = new double[nodes.Count];
|
||||
|
||||
if (allEquals)
|
||||
{
|
||||
|
@ -80,20 +80,19 @@ public static class Tools
|
|||
dist[i] = Distance(x, hash);
|
||||
}
|
||||
|
||||
SortHasherByDistance(nodes, dist, true);
|
||||
return;
|
||||
return SortHasherByDistance(nodes, dist, true);
|
||||
}
|
||||
|
||||
for (int i = 0; i < dist.Length; i++)
|
||||
{
|
||||
var d = Distance(nodes[i].Hash(), hash);
|
||||
dist[i] = ulong.MaxValue - (ulong)(d * weights[i]);
|
||||
dist[i] = (ulong.MaxValue - d) * weights[i];
|
||||
}
|
||||
|
||||
SortHasherByDistance(nodes, dist, false);
|
||||
return SortHasherByDistance(nodes, dist, false);
|
||||
}
|
||||
|
||||
internal static void SortHasherByDistance<T, N>(List<T> nodes, N[] dist, bool asc)
|
||||
internal static List<T> SortHasherByDistance<T, N>(List<T> nodes, N[] dist, bool asc)
|
||||
{
|
||||
IndexedValue<T, N>[] indexes = new IndexedValue<T, N>[nodes.Count];
|
||||
for (int i = 0; i < dist.Length; i++)
|
||||
|
@ -103,16 +102,15 @@ public static class Tools
|
|||
|
||||
if (asc)
|
||||
{
|
||||
nodes = new List<T>(indexes
|
||||
return new List<T>(indexes
|
||||
.OrderBy(x => x.dist)
|
||||
.Select(x => x.nodeInfo).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes = new List<T>(indexes
|
||||
return new List<T>(indexes
|
||||
.OrderByDescending(x => x.dist)
|
||||
.Select(x => x.nodeInfo)
|
||||
.ToArray());
|
||||
.Select(x => x.nodeInfo));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue