|
|
@ -4,109 +4,144 @@ using ZeroLevel.Services.PartitionStorage;
|
|
|
|
|
|
|
|
|
|
|
|
namespace PartitionFileStorageTest
|
|
|
|
namespace PartitionFileStorageTest
|
|
|
|
{
|
|
|
|
{
|
|
|
|
public class CallRecordParser
|
|
|
|
internal class Program
|
|
|
|
{
|
|
|
|
{
|
|
|
|
private static HashSet<char> _partsOfNumbers = new HashSet<char> { '*', '#', '+', '(', ')', '-' };
|
|
|
|
private class Metadata
|
|
|
|
private StringBuilder sb = new StringBuilder();
|
|
|
|
{
|
|
|
|
private const string NO_VAL = null;
|
|
|
|
public DateTime Date { get; set; }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string ReadNumber(string line)
|
|
|
|
private static ulong Generate(Random r)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
sb.Clear();
|
|
|
|
var num = new StringBuilder();
|
|
|
|
var started = false;
|
|
|
|
num.Append("79");
|
|
|
|
foreach (var ch in line)
|
|
|
|
num.Append(r.Next(99).ToString("D2"));
|
|
|
|
|
|
|
|
num.Append(r.Next(999).ToString("D7"));
|
|
|
|
|
|
|
|
return ulong.Parse(num.ToString());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void BuildStore(string root)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var options = new StoreOptions<ulong, ulong, byte[], Metadata>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (char.IsDigit(ch))
|
|
|
|
Index = new IndexOptions { Enabled = true, FileIndexCount = 64 },
|
|
|
|
|
|
|
|
RootFolder = root,
|
|
|
|
|
|
|
|
FilePartition = new StoreFilePartition<ulong, Metadata>("Last three digits", (ctn, date) => (ctn % 128).ToString()),
|
|
|
|
|
|
|
|
MergeFunction = list =>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (started)
|
|
|
|
ulong s = 0;
|
|
|
|
{
|
|
|
|
return Compressor.GetEncodedBytes(list.OrderBy(c => c), ref s);
|
|
|
|
sb.Append(ch);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
Partitions = new List<StoreCatalogPartition<Metadata>>
|
|
|
|
else if (ch != '0')
|
|
|
|
{
|
|
|
|
{
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Date.ToString("yyyyMMdd"))
|
|
|
|
sb.Append(ch);
|
|
|
|
},
|
|
|
|
started = true;
|
|
|
|
KeyComparer = (left, right) => left == right ? 0 : (left < right) ? -1 : 1,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var store = new Store<ulong, ulong, byte[], Metadata>(options);
|
|
|
|
|
|
|
|
var storePart1 = store.CreateBuilder(new Metadata { Date = new DateTime(2022, 11, 08) });
|
|
|
|
|
|
|
|
var storePart2 = store.CreateBuilder(new Metadata { Date = new DateTime(2022, 11, 09) });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var sw = new Stopwatch();
|
|
|
|
|
|
|
|
sw.Start();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var r = new Random(Environment.TickCount);
|
|
|
|
|
|
|
|
for (int i = 0; i < 1000000; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var s = Generate(r);
|
|
|
|
|
|
|
|
var count = r.Next(300);
|
|
|
|
|
|
|
|
for (int j = 0; j < count; j++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var t = Generate(r);
|
|
|
|
|
|
|
|
storePart1.Store(s, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (char.IsWhiteSpace(ch) || _partsOfNumbers.Contains(ch)) continue;
|
|
|
|
|
|
|
|
else return NO_VAL;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sb.Length == 11 && sb[0] == '8') sb[0] = '7';
|
|
|
|
for (int i = 0; i < 1000000; i++)
|
|
|
|
if (sb.Length == 3 || sb.Length == 4 || sb.Length > 10)
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
|
|
|
return NO_VAL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private HashSet<string> ReadNumbers(string line)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var result = new HashSet<string>();
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(line) == false)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
char STX = (char)0x0002;
|
|
|
|
var s = Generate(r);
|
|
|
|
var values = line.Split(STX);
|
|
|
|
var count = r.Next(300);
|
|
|
|
if (values.Length > 0)
|
|
|
|
for (int j = 0; j < count; j++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
foreach (var val in values)
|
|
|
|
var t = Generate(r);
|
|
|
|
{
|
|
|
|
storePart2.Store(s, t);
|
|
|
|
var number = ReadNumber(val);
|
|
|
|
|
|
|
|
if (number != null)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
result.Add(number);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
|
|
/// Парсинг строки исходного файла
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
/// <param name="line"></param>
|
|
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
|
|
public CallRecord Parse(string line)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var parts = line.Split('\t');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (parts.Length != 2) return null;
|
|
|
|
sw.Stop();
|
|
|
|
|
|
|
|
Console.WriteLine($"Fill journal: {sw.ElapsedMilliseconds}ms");
|
|
|
|
var msisdn = ReadNumber(parts[0].Trim());
|
|
|
|
sw.Restart();
|
|
|
|
|
|
|
|
storePart1.CompleteAddingAndCompress();
|
|
|
|
|
|
|
|
storePart2.CompleteAddingAndCompress();
|
|
|
|
|
|
|
|
sw.Stop();
|
|
|
|
|
|
|
|
Console.WriteLine($"Rebuild journal to store: {sw.ElapsedMilliseconds}ms");
|
|
|
|
|
|
|
|
sw.Restart();
|
|
|
|
|
|
|
|
storePart1.RebuildIndex();
|
|
|
|
|
|
|
|
storePart2.RebuildIndex();
|
|
|
|
|
|
|
|
sw.Stop();
|
|
|
|
|
|
|
|
Console.WriteLine($"Rebuild indexes: {sw.ElapsedMilliseconds}ms");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(msisdn) == false)
|
|
|
|
private static void SmallFullTest(string root)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var r = new Random(Environment.TickCount);
|
|
|
|
|
|
|
|
var options = new StoreOptions<ulong, ulong, byte[], Metadata>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var numbers = ReadNumbers(parts[1]);
|
|
|
|
Index = new IndexOptions { Enabled = true, FileIndexCount = 64 },
|
|
|
|
if (numbers != null && numbers.Count > 0)
|
|
|
|
RootFolder = root,
|
|
|
|
|
|
|
|
FilePartition = new StoreFilePartition<ulong, Metadata>("Last three digits", (ctn, date) => (ctn % 128).ToString()),
|
|
|
|
|
|
|
|
MergeFunction = list =>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return new CallRecord
|
|
|
|
ulong s = 0;
|
|
|
|
{
|
|
|
|
return Compressor.GetEncodedBytes(list.OrderBy(c => c), ref s);
|
|
|
|
Msisdn = msisdn,
|
|
|
|
},
|
|
|
|
Msisdns = numbers
|
|
|
|
Partitions = new List<StoreCatalogPartition<Metadata>>
|
|
|
|
};
|
|
|
|
{
|
|
|
|
}
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Date.ToString("yyyyMMdd"))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
return null;
|
|
|
|
KeyComparer = (left, right) => left == right ? 0 : (left < right) ? -1 : 1,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
var store = new Store<ulong, ulong, byte[], Metadata>(options);
|
|
|
|
|
|
|
|
var storePart = store.CreateBuilder(new Metadata { Date = new DateTime(2022, 11, 08) });
|
|
|
|
|
|
|
|
|
|
|
|
public class CallRecord
|
|
|
|
Console.WriteLine("Small test start");
|
|
|
|
{
|
|
|
|
var c1 = (ulong)(86438 * 128);
|
|
|
|
public string Msisdn;
|
|
|
|
var c2 = (ulong)(83438 * 128);
|
|
|
|
public HashSet<string> Msisdns;
|
|
|
|
var c3 = (ulong)(831238 * 128);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal class Program
|
|
|
|
storePart.Store(c1, Generate(r));
|
|
|
|
{
|
|
|
|
storePart.Store(c1, Generate(r));
|
|
|
|
private class Metadata
|
|
|
|
storePart.Store(c1, Generate(r));
|
|
|
|
{
|
|
|
|
storePart.Store(c2, Generate(r));
|
|
|
|
public DateTime Date { get; set; }
|
|
|
|
storePart.Store(c2, Generate(r));
|
|
|
|
public bool Incoming { get; set; }
|
|
|
|
storePart.Store(c2, Generate(r));
|
|
|
|
|
|
|
|
storePart.Store(c2, Generate(r));
|
|
|
|
|
|
|
|
storePart.Store(c2, Generate(r));
|
|
|
|
|
|
|
|
storePart.Store(c3, Generate(r));
|
|
|
|
|
|
|
|
storePart.Store(c3, Generate(r));
|
|
|
|
|
|
|
|
storePart.Store(c3, Generate(r));
|
|
|
|
|
|
|
|
storePart.CompleteAddingAndCompress();
|
|
|
|
|
|
|
|
var readPart = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08) });
|
|
|
|
|
|
|
|
Console.WriteLine("Data:");
|
|
|
|
|
|
|
|
foreach (var e in readPart.Iterate())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"{e.Key}: {e.Value.Length}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
readPart.RemoveKey(c1);
|
|
|
|
|
|
|
|
Console.WriteLine("Data after remove:");
|
|
|
|
|
|
|
|
foreach (var e in readPart.Iterate())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"{e.Key}: {e.Value.Length}");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void BuildStore(string source, string root)
|
|
|
|
private static void TestBuildRemoveStore(string root)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
var r = new Random(Environment.TickCount);
|
|
|
|
var options = new StoreOptions<ulong, ulong, byte[], Metadata>
|
|
|
|
var options = new StoreOptions<ulong, ulong, byte[], Metadata>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Index = new IndexOptions { Enabled = true, FileIndexCount = 256 },
|
|
|
|
Index = new IndexOptions { Enabled = true, FileIndexCount = 64 },
|
|
|
|
RootFolder = root,
|
|
|
|
RootFolder = root,
|
|
|
|
FilePartition = new StoreFilePartition<ulong, Metadata>("Last three digits", (ctn, date) => (ctn % 512).ToString()),
|
|
|
|
FilePartition = new StoreFilePartition<ulong, Metadata>("Last three digits", (ctn, date) => (ctn % 128).ToString()),
|
|
|
|
MergeFunction = list =>
|
|
|
|
MergeFunction = list =>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ulong s = 0;
|
|
|
|
ulong s = 0;
|
|
|
@ -114,55 +149,127 @@ namespace PartitionFileStorageTest
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Partitions = new List<StoreCatalogPartition<Metadata>>
|
|
|
|
Partitions = new List<StoreCatalogPartition<Metadata>>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Date.ToString("yyyyMMdd")),
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Date.ToString("yyyyMMdd"))
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Incoming ? "incoming" : "outcoming")
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
KeyComparer = (left, right) => left == right ? 0 : (left < right) ? -1 : 1,
|
|
|
|
KeyComparer = (left, right) => left == right ? 0 : (left < right) ? -1 : 1,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
var store = new Store<ulong, ulong, byte[], Metadata>(options);
|
|
|
|
var store = new Store<ulong, ulong, byte[], Metadata>(options);
|
|
|
|
|
|
|
|
var storePart = store.CreateBuilder(new Metadata { Date = new DateTime(2022, 11, 08) });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var storeIncoming = store.CreateBuilder(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = true });
|
|
|
|
|
|
|
|
var storeOutcoming = store.CreateBuilder(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = false });
|
|
|
|
|
|
|
|
var parser = new CallRecordParser();
|
|
|
|
|
|
|
|
var sw = new Stopwatch();
|
|
|
|
var sw = new Stopwatch();
|
|
|
|
sw.Start();
|
|
|
|
sw.Start();
|
|
|
|
using (FileStream fs = File.Open(source, FileMode.Open, FileAccess.Read, FileShare.None))
|
|
|
|
|
|
|
|
|
|
|
|
var testKeys1 = new List<ulong>();
|
|
|
|
|
|
|
|
var testKeys2 = new List<ulong>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 1000000; i++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using (BufferedStream bs = new BufferedStream(fs, 1024 * 1024 * 64))
|
|
|
|
var s = Generate(r);
|
|
|
|
|
|
|
|
var count = r.Next(300);
|
|
|
|
|
|
|
|
for (int j = 0; j < count; j++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using (StreamReader sr = new StreamReader(bs))
|
|
|
|
var t = Generate(r);
|
|
|
|
{
|
|
|
|
storePart.Store(s, t);
|
|
|
|
string line;
|
|
|
|
}
|
|
|
|
while ((line = sr.ReadLine()) != null)
|
|
|
|
if (s % 11217 == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var record = parser.Parse(line);
|
|
|
|
testKeys1.Add(s);
|
|
|
|
if (record == null) continue;
|
|
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(record?.Msisdn ?? string.Empty) && ulong.TryParse(record.Msisdn, out var n))
|
|
|
|
if (s % 11219 == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var ctns = record.Msisdns.ParseMsisdns().ToArray();
|
|
|
|
testKeys2.Add(s);
|
|
|
|
foreach (var ctn in ctns)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
storeIncoming.Store(n, ctn);
|
|
|
|
|
|
|
|
storeOutcoming.Store(ctn, n);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sw.Stop();
|
|
|
|
sw.Stop();
|
|
|
|
Console.WriteLine($"Fill journal: {sw.ElapsedMilliseconds}ms");
|
|
|
|
Console.WriteLine($"Fill journal: {sw.ElapsedMilliseconds}ms");
|
|
|
|
sw.Restart();
|
|
|
|
sw.Restart();
|
|
|
|
storeIncoming.CompleteAddingAndCompress();
|
|
|
|
storePart.CompleteAddingAndCompress();
|
|
|
|
storeOutcoming.CompleteAddingAndCompress();
|
|
|
|
|
|
|
|
sw.Stop();
|
|
|
|
sw.Stop();
|
|
|
|
Console.WriteLine($"Rebuild journal to store: {sw.ElapsedMilliseconds}ms");
|
|
|
|
Console.WriteLine($"Compress: {sw.ElapsedMilliseconds}ms");
|
|
|
|
sw.Restart();
|
|
|
|
sw.Restart();
|
|
|
|
storeIncoming.RebuildIndex();
|
|
|
|
storePart.RebuildIndex();
|
|
|
|
storeOutcoming.RebuildIndex();
|
|
|
|
|
|
|
|
sw.Stop();
|
|
|
|
sw.Stop();
|
|
|
|
Console.WriteLine($"Rebuild indexes: {sw.ElapsedMilliseconds}ms");
|
|
|
|
Console.WriteLine($"Rebuild indexes: {sw.ElapsedMilliseconds}ms");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Start merge test");
|
|
|
|
|
|
|
|
sw.Restart();
|
|
|
|
|
|
|
|
var merger = store.CreateMergeAccessor(new Metadata { Date = new DateTime(2022, 11, 08) }, data => Compressor.DecodeBytesContent(data));
|
|
|
|
|
|
|
|
for (int i = 0; i < 1000000; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var s = Generate(r);
|
|
|
|
|
|
|
|
var count = r.Next(300);
|
|
|
|
|
|
|
|
for (int j = 0; j < count; j++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var t = Generate(r);
|
|
|
|
|
|
|
|
merger.Store(s, t);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Console.WriteLine($"Merge journal filled: {sw.ElapsedMilliseconds}ms");
|
|
|
|
|
|
|
|
merger.CompleteAddingAndCompress();
|
|
|
|
|
|
|
|
sw.Stop();
|
|
|
|
|
|
|
|
Console.WriteLine($"Compress after merge: {sw.ElapsedMilliseconds}ms");
|
|
|
|
|
|
|
|
sw.Restart();
|
|
|
|
|
|
|
|
merger.RebuildIndex();
|
|
|
|
|
|
|
|
sw.Stop();
|
|
|
|
|
|
|
|
Console.WriteLine($"Rebuild indexes after merge: {sw.ElapsedMilliseconds}ms");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Test #1 reading");
|
|
|
|
|
|
|
|
var readPart = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08) });
|
|
|
|
|
|
|
|
foreach (var key in testKeys1)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"\tKey: {key}");
|
|
|
|
|
|
|
|
var result = readPart.Find(key);
|
|
|
|
|
|
|
|
Console.WriteLine($"\t\tFound: {result.Found}. {result.Value?.Length ?? 0} bytes");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Console.WriteLine("Press to continue");
|
|
|
|
|
|
|
|
Console.ReadKey();
|
|
|
|
|
|
|
|
Console.WriteLine("Test #1 remove by keys");
|
|
|
|
|
|
|
|
for (int i = 0; i < testKeys1.Count; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (i % 100 == 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
readPart.RemoveKey(testKeys1[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Console.WriteLine("Test #1 reading after remove");
|
|
|
|
|
|
|
|
foreach (var key in testKeys1)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"\tKey: {key}");
|
|
|
|
|
|
|
|
var result = readPart.Find(key);
|
|
|
|
|
|
|
|
Console.WriteLine($"\t\tFound: {result.Found}. {result.Value?.Length ?? 0} bytes");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Console.WriteLine("Press to continue");
|
|
|
|
|
|
|
|
Console.ReadKey();
|
|
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
|
|
Console.WriteLine("---------------------------------------");
|
|
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
|
|
Console.WriteLine("Test #2 reading");
|
|
|
|
|
|
|
|
foreach (var key in testKeys2)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"\tKey: {key}");
|
|
|
|
|
|
|
|
var result = readPart.Find(key);
|
|
|
|
|
|
|
|
Console.WriteLine($"\t\tFound: {result.Found}. {result.Value?.Length ?? 0} bytes");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Console.WriteLine("Press to continue");
|
|
|
|
|
|
|
|
Console.ReadKey();
|
|
|
|
|
|
|
|
Console.WriteLine("Test #2 remove keys batch");
|
|
|
|
|
|
|
|
readPart.RemoveKeys(testKeys2);
|
|
|
|
|
|
|
|
Console.WriteLine("Test #2 reading after remove");
|
|
|
|
|
|
|
|
foreach (var key in testKeys2)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"\tKey: {key}");
|
|
|
|
|
|
|
|
var result = readPart.Find(key);
|
|
|
|
|
|
|
|
Console.WriteLine($"\t\tFound: {result.Found}. {result.Value?.Length ?? 0} bytes");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Press to continue for iteration");
|
|
|
|
|
|
|
|
Console.ReadKey();
|
|
|
|
|
|
|
|
foreach (var e in readPart.Iterate())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"{e.Key}: {e.Value.Length}");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void TestReading(string root)
|
|
|
|
private static void TestReading(string root)
|
|
|
@ -179,8 +286,7 @@ namespace PartitionFileStorageTest
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Partitions = new List<StoreCatalogPartition<Metadata>>
|
|
|
|
Partitions = new List<StoreCatalogPartition<Metadata>>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Date.ToString("yyyyMMdd")),
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Date.ToString("yyyyMMdd"))
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Incoming ? "incoming" : "outcoming")
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
KeyComparer = (left, right) => left == right ? 0 : (left < right) ? -1 : 1,
|
|
|
|
KeyComparer = (left, right) => left == right ? 0 : (left < right) ? -1 : 1,
|
|
|
|
};
|
|
|
|
};
|
|
|
@ -191,26 +297,25 @@ namespace PartitionFileStorageTest
|
|
|
|
{
|
|
|
|
{
|
|
|
|
new PartitionSearchRequest<ulong, Metadata>
|
|
|
|
new PartitionSearchRequest<ulong, Metadata>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Info = new Metadata { Date = new DateTime(2022, 11, 08), Incoming = true },
|
|
|
|
Info = new Metadata { Date = new DateTime(2022, 11, 08) },
|
|
|
|
Keys = new ulong[] { }
|
|
|
|
Keys = new ulong[] { }
|
|
|
|
},
|
|
|
|
},
|
|
|
|
new PartitionSearchRequest<ulong, Metadata>
|
|
|
|
new PartitionSearchRequest<ulong, Metadata>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Info = new Metadata { Date = new DateTime(2022, 11, 08), Incoming = false },
|
|
|
|
Info = new Metadata { Date = new DateTime(2022, 11, 09) },
|
|
|
|
Keys = new ulong[] { }
|
|
|
|
Keys = new ulong[] { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
var storeIncoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = true });
|
|
|
|
var storeIncoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08) });
|
|
|
|
Console.WriteLine($"Incoming data files: {storeIncoming.CountDataFiles()}");
|
|
|
|
Console.WriteLine($"Incoming data files: {storeIncoming.CountDataFiles()}");
|
|
|
|
var storeOutcoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = false });
|
|
|
|
var storeOutcoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 09) });
|
|
|
|
Console.WriteLine($"Outcoming data files: {storeOutcoming.CountDataFiles()}");
|
|
|
|
Console.WriteLine($"Outcoming data files: {storeOutcoming.CountDataFiles()}");
|
|
|
|
var sw = new Stopwatch();
|
|
|
|
var sw = new Stopwatch();
|
|
|
|
sw.Start();
|
|
|
|
sw.Start();
|
|
|
|
var result = store.Search(request).Result;
|
|
|
|
var result = store.Search(request).Result;
|
|
|
|
foreach (var r in result.Results)
|
|
|
|
foreach (var r in result.Results)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Console.WriteLine($"Incoming: {r.Key.Incoming}");
|
|
|
|
|
|
|
|
foreach (var mr in r.Value)
|
|
|
|
foreach (var mr in r.Value)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Console.WriteLine($"\tKey: {mr.Key}. Sucess: {mr.Found}");
|
|
|
|
Console.WriteLine($"\tKey: {mr.Key}. Sucess: {mr.Found}");
|
|
|
@ -225,29 +330,104 @@ namespace PartitionFileStorageTest
|
|
|
|
Console.WriteLine($"Search time: {sw.ElapsedMilliseconds}ms");
|
|
|
|
Console.WriteLine($"Search time: {sw.ElapsedMilliseconds}ms");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private struct KeyIndex<TKey>
|
|
|
|
private static void TestIterations(string root)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
public TKey Key { get; set; }
|
|
|
|
var options = new StoreOptions<ulong, ulong, byte[], Metadata>
|
|
|
|
public long Offset { get; set; }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static KeyIndex<long>[] Generate(int count)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var arr = new KeyIndex<long>[count];
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
arr[i] = new KeyIndex<long> { Key = i * 3, Offset = i * 17 };
|
|
|
|
Index = new IndexOptions { Enabled = true, FileIndexCount = 256 },
|
|
|
|
|
|
|
|
RootFolder = root,
|
|
|
|
|
|
|
|
FilePartition = new StoreFilePartition<ulong, Metadata>("Last three digits", (ctn, date) => (ctn % 512).ToString()),
|
|
|
|
|
|
|
|
MergeFunction = list =>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ulong s = 0;
|
|
|
|
|
|
|
|
return Compressor.GetEncodedBytes(list.OrderBy(c => c), ref s);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Partitions = new List<StoreCatalogPartition<Metadata>>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
new StoreCatalogPartition<Metadata>("Date", m => m.Date.ToString("yyyyMMdd"))
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
KeyComparer = (left, right) => left == right ? 0 : (left < right) ? -1 : 1,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
var store = new Store<ulong, ulong, byte[], Metadata>(options);
|
|
|
|
|
|
|
|
var storeIncoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08) });
|
|
|
|
|
|
|
|
foreach (var r in storeIncoming.Iterate())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"{r.Key}: {r.Value.Length}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return arr;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Main(string[] args)
|
|
|
|
static void Main(string[] args)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var root = @"H:\temp";
|
|
|
|
var root = @"H:\temp";
|
|
|
|
var source = @"H:\319a9c31-d823-4dd1-89b0-7fb1bb9c4859.txt";
|
|
|
|
SmallFullTest(root);
|
|
|
|
//BuildStore(source, root);
|
|
|
|
//TestBuildRemoveStore(root);
|
|
|
|
TestReading(root);
|
|
|
|
//BuildStore(root);
|
|
|
|
|
|
|
|
//TestReading(root);
|
|
|
|
|
|
|
|
//TestIterations(root);
|
|
|
|
|
|
|
|
//TestRangeCompressionAndInversion();
|
|
|
|
Console.ReadKey();
|
|
|
|
Console.ReadKey();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void TestRangeCompressionAndInversion()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var list = new List<FilePositionRange>();
|
|
|
|
|
|
|
|
list.Add(new FilePositionRange { Start = 1, End = 36 });
|
|
|
|
|
|
|
|
list.Add(new FilePositionRange { Start = 36, End = 63 });
|
|
|
|
|
|
|
|
list.Add(new FilePositionRange { Start = 63, End = 89 });
|
|
|
|
|
|
|
|
list.Add(new FilePositionRange { Start = 93, End = 118 });
|
|
|
|
|
|
|
|
list.Add(new FilePositionRange { Start = 126, End = 199 });
|
|
|
|
|
|
|
|
list.Add(new FilePositionRange { Start = 199, End = 216 });
|
|
|
|
|
|
|
|
list.Add(new FilePositionRange { Start = 277, End = 500 });
|
|
|
|
|
|
|
|
RangeCompression(list);
|
|
|
|
|
|
|
|
foreach (var r in list)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"{r.Start}: {r.End}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Console.WriteLine("Invert ranges");
|
|
|
|
|
|
|
|
var inverted = RangeInversion(list, 500);
|
|
|
|
|
|
|
|
foreach (var r in inverted)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine($"{r.Start}: {r.End}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void RangeCompression(List<FilePositionRange> ranges)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (var i = 0; i < ranges.Count - 1; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var current = ranges[i];
|
|
|
|
|
|
|
|
var next = ranges[i + 1];
|
|
|
|
|
|
|
|
if (current.End == next.Start)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
current.End = next.End;
|
|
|
|
|
|
|
|
ranges.RemoveAt(i + 1);
|
|
|
|
|
|
|
|
i--;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static List<FilePositionRange> RangeInversion(List<FilePositionRange> ranges, long length)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if ((ranges?.Count ?? 0) == 0) return new List<FilePositionRange> { new FilePositionRange { Start = 0, End = length } };
|
|
|
|
|
|
|
|
var inverted = new List<FilePositionRange>();
|
|
|
|
|
|
|
|
var current = new FilePositionRange { Start = 0, End = ranges[0].Start };
|
|
|
|
|
|
|
|
for (var i = 0; i < ranges.Count; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
current.End = ranges[i].Start;
|
|
|
|
|
|
|
|
if (current.Start != current.End)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
inverted.Add(new FilePositionRange { Start = current.Start, End = current.End });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
current.Start = ranges[i].End;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current.End != length)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (current.Start != length)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
inverted.Add(new FilePositionRange { Start = current.Start, End = length });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return inverted;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|