diff --git a/PartitionFileStorageTest/Program.cs b/PartitionFileStorageTest/Program.cs index e8ea52e..5f6e2a3 100644 --- a/PartitionFileStorageTest/Program.cs +++ b/PartitionFileStorageTest/Program.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Text; using ZeroLevel.Services.PartitionStorage; -using ZeroLevel.Services.Serialization; namespace PartitionFileStorageTest { @@ -92,6 +91,7 @@ namespace PartitionFileStorageTest public string Msisdn; public HashSet Msisdns; } + internal class Program { private class Metadata @@ -116,11 +116,12 @@ namespace PartitionFileStorageTest new StoreCatalogPartition("Date", m => m.Date.ToString("yyyyMMdd")), new StoreCatalogPartition("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, }; + options.Index.Enabled = true; var store = new Store(options); - - + + var storeIncoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = true }); var storeOutcoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = false }); var parser = new CallRecordParser(); @@ -157,12 +158,22 @@ namespace PartitionFileStorageTest storeOutcoming.CompleteStoreAndRebuild(); sw.Stop(); Console.WriteLine($"Rebuild journal to store: {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + storeIncoming.RebuildIndex(); + storeOutcoming.RebuildIndex(); + sw.Stop(); + Console.WriteLine($"Rebuild indexes: {sw.ElapsedMilliseconds}ms"); } private static void TestReading(string source, string root) { var options = new IStoreOptions { + Index = new IndexOptions + { + Enabled = false, + FileIndexCount = 64 + }, RootFolder = root, FilePartition = new StoreFilePartition("Last three digits", (ctn, date) => (ctn % 1000).ToString()), MergeFunction = list => @@ -185,16 +196,15 @@ namespace PartitionFileStorageTest new PartitionSearchRequest { Info = new Metadata { Date = new DateTime(2022, 11, 08), Incoming = true }, - Keys = new ulong[] { 79645090604 } + Keys = new ulong[] { 79645090604, 79645100604, 79643090604 } }, new PartitionSearchRequest { Info = new Metadata { Date = new DateTime(2022, 11, 08), Incoming = false }, - Keys = new ulong[] { 79645090604 } + Keys = new ulong[] { 79645090604, 79645100604, 79643090604 } } } }; - var storeIncoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = true }); Console.WriteLine($"Incoming data files: {storeIncoming.CountDataFiles()}"); var storeOutcoming = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08), Incoming = false }); @@ -219,12 +229,77 @@ namespace PartitionFileStorageTest Console.WriteLine($"Search time: {sw.ElapsedMilliseconds}ms"); } + private struct KeyIndex + { + public TKey Key { get; set; } + public long Offset { get; set; } + } + + static KeyIndex[] Generate(int count) + { + var arr = new KeyIndex[count]; + for (int i = 0; i < count; i++) + { + arr[i] = new KeyIndex { Key = i * 3, Offset = i * 17 }; + } + return arr; + } + + private static KeyIndex BinarySearchInIndex(KeyIndex[] index, + long key, + Func keyComparer, + ref int position) + { + if (index == null || index.Length == 0) + { + return new KeyIndex { Key = key, Offset = 0 }; + } + int left = position; + int right = index.Length - 1; + int mid = 0; + long test; + while (left <= right) + { + mid = (int)Math.Floor((right + left) / 2d); + test = index[mid].Key; + var c = keyComparer(test, key); + if (c < 0) + { + left = mid + 1; + } + else if (c > 0) + { + right = mid - 1; + } + else + { + position = mid; + return index[mid]; + } + } + position = mid; + return index[mid]; + } + static void Main(string[] args) { var root = @"H:\temp"; var source = @"H:\319a9c31-d823-4dd1-89b0-7fb1bb9c4859.txt"; - BuildStore(source, root); + //BuildStore(source, root); TestReading(source, root); + + /* + Func keyComparer = + (left, right) => + (left == right) ? 0 : (left < right) ? -1 : 1; + var indexes = Generate(77); + int position = 0; + for (long i = 65; i < 700; i++) + { + var ind = BinarySearchInIndex(indexes, i, keyComparer, ref position); + Console.WriteLine($"{i}: {ind.Offset}. [{ind.Key}]"); + } + */ Console.ReadKey(); } } diff --git a/ZeroLevel.sln b/ZeroLevel.sln index 58bceed..2a2b46d 100644 --- a/ZeroLevel.sln +++ b/ZeroLevel.sln @@ -65,9 +65,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoLoader", "AutoLoader", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppContainer", "AutoLoader\AppContainer\AppContainer.csproj", "{9DE345EA-955B-41A8-93AF-277C0B5A9AC5}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PartitionTest", "PartitionTest", "{BAD88A91-1AFA-48A8-8D39-4846A65B4167}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PartitionFileStorageTest", "PartitionFileStorageTest\PartitionFileStorageTest.csproj", "{EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PartitionFileStorageTest", "PartitionFileStorageTest\PartitionFileStorageTest.csproj", "{6BCAB578-52F0-48F7-903E-B1B284F2D5AE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -319,18 +317,18 @@ Global {9DE345EA-955B-41A8-93AF-277C0B5A9AC5}.Release|x64.Build.0 = Release|Any CPU {9DE345EA-955B-41A8-93AF-277C0B5A9AC5}.Release|x86.ActiveCfg = Release|Any CPU {9DE345EA-955B-41A8-93AF-277C0B5A9AC5}.Release|x86.Build.0 = Release|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Debug|x64.ActiveCfg = Debug|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Debug|x64.Build.0 = Debug|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Debug|x86.ActiveCfg = Debug|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Debug|x86.Build.0 = Debug|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Release|Any CPU.Build.0 = Release|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Release|x64.ActiveCfg = Release|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Release|x64.Build.0 = Release|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Release|x86.ActiveCfg = Release|Any CPU - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9}.Release|x86.Build.0 = Release|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Debug|x64.ActiveCfg = Debug|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Debug|x64.Build.0 = Debug|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Debug|x86.ActiveCfg = Debug|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Debug|x86.Build.0 = Debug|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Release|Any CPU.Build.0 = Release|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Release|x64.ActiveCfg = Release|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Release|x64.Build.0 = Release|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Release|x86.ActiveCfg = Release|Any CPU + {6BCAB578-52F0-48F7-903E-B1B284F2D5AE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -345,7 +343,6 @@ Global {F70842E7-9A1D-4CC4-9F55-0953AEC9C7C8} = {03ACF314-93FC-46FE-9FB8-3F46A01A5A15} {2C33D5A3-6CD4-4AAA-A716-B3CD65036E25} = {D5207A5A-2F27-4992-9BA5-0BDCFC59F133} {9DE345EA-955B-41A8-93AF-277C0B5A9AC5} = {2EF83101-63BC-4397-A005-A747189143D4} - {EB85B2E0-03EE-4F71-B6E9-B6D6B34524B9} = {BAD88A91-1AFA-48A8-8D39-4846A65B4167} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A65DB16F-877D-4586-A9F3-8BBBFBAF5CEB} diff --git a/ZeroLevel/Services/PartitionStorage/IStoreOptions.cs b/ZeroLevel/Services/PartitionStorage/IStoreOptions.cs index 2ca81fc..d3ea172 100644 --- a/ZeroLevel/Services/PartitionStorage/IStoreOptions.cs +++ b/ZeroLevel/Services/PartitionStorage/IStoreOptions.cs @@ -5,6 +5,12 @@ using ZeroLevel.Services.FileSystem; namespace ZeroLevel.Services.PartitionStorage { + public class IndexOptions + { + public bool Enabled { get; set; } + public int FileIndexCount { get; set; } = 64; + } + /// /// Options /// @@ -26,7 +32,7 @@ namespace ZeroLevel.Services.PartitionStorage /// /// Maximum degree of parallelis /// - public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount / 2; + public int MaxDegreeOfParallelism { get; set; } = 64; /// /// Function for translating a list of TInput into one TValue /// @@ -40,6 +46,8 @@ namespace ZeroLevel.Services.PartitionStorage /// public StoreFilePartition FilePartition { get; set; } + public IndexOptions Index { get; set; } = new IndexOptions { Enabled = false, FileIndexCount = 64 }; + internal string GetFileName(TKey key, TMeta info) { return FilePartition.PathExtractor(key, info); diff --git a/ZeroLevel/Services/PartitionStorage/IStorePartitionAccessor.cs b/ZeroLevel/Services/PartitionStorage/IStorePartitionAccessor.cs index 42315f0..0c7a068 100644 --- a/ZeroLevel/Services/PartitionStorage/IStorePartitionAccessor.cs +++ b/ZeroLevel/Services/PartitionStorage/IStorePartitionAccessor.cs @@ -22,6 +22,10 @@ namespace ZeroLevel.Services.PartitionStorage /// void CompleteStoreAndRebuild(); /// + /// Rebuild indexes + /// + void RebuildIndex(); + /// /// Find in catalog partition by key /// StorePartitionKeyValueSearchResult Find(TKey key); @@ -29,6 +33,8 @@ namespace ZeroLevel.Services.PartitionStorage /// Find in catalog partition by keys /// IEnumerable> Find(IEnumerable keys); + IEnumerable> Iterate(); + IEnumerable> IterateKeyBacket(TKey key); /// /// Has any files /// diff --git a/ZeroLevel/Services/PartitionStorage/IStorePartitionIndex.cs b/ZeroLevel/Services/PartitionStorage/IStorePartitionIndex.cs new file mode 100644 index 0000000..bf4eb35 --- /dev/null +++ b/ZeroLevel/Services/PartitionStorage/IStorePartitionIndex.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace ZeroLevel.Services.PartitionStorage +{ + internal interface IStorePartitionIndex + { + KeyIndex GetOffset(TKey key); + KeyIndex[] GetOffset(TKey[] keys, bool inOneGroup); + } +} diff --git a/ZeroLevel/Services/PartitionStorage/Store.cs b/ZeroLevel/Services/PartitionStorage/Store.cs index 906a921..5c94151 100644 --- a/ZeroLevel/Services/PartitionStorage/Store.cs +++ b/ZeroLevel/Services/PartitionStorage/Store.cs @@ -35,7 +35,8 @@ namespace ZeroLevel.Services.PartitionStorage if (searchRequest.PartitionSearchRequests?.Any() ?? false) { var partitionsSearchInfo = searchRequest.PartitionSearchRequests.ToDictionary(r => r.Info, r => r.Keys); - var options = new ParallelOptions { MaxDegreeOfParallelism = _options.MaxDegreeOfParallelism }; + //var options = new ParallelOptions { MaxDegreeOfParallelism = _options.MaxDegreeOfParallelism }; + var options = new ParallelOptions { MaxDegreeOfParallelism = 1 }; await Parallel.ForEachAsync(partitionsSearchInfo, options, async (pair, _) => { using (var accessor = CreateAccessor(pair.Key)) diff --git a/ZeroLevel/Services/PartitionStorage/StorePartitionAccessor.cs b/ZeroLevel/Services/PartitionStorage/StorePartitionAccessor.cs index abcaaae..d1f348e 100644 --- a/ZeroLevel/Services/PartitionStorage/StorePartitionAccessor.cs +++ b/ZeroLevel/Services/PartitionStorage/StorePartitionAccessor.cs @@ -12,6 +12,8 @@ namespace ZeroLevel.Services.PartitionStorage public class StorePartitionAccessor : IStorePartitionAccessor { + private readonly ConcurrentDictionary _writeStreams + = new ConcurrentDictionary(); private readonly IStoreOptions _options; private readonly string _catalog; private readonly TMeta _info; @@ -32,25 +34,126 @@ namespace ZeroLevel.Services.PartitionStorage public StorePartitionKeyValueSearchResult Find(TKey key) { var fileName = _options.GetFileName(key, _info); - using (var reader = GetReadStream(fileName)) + if (File.Exists(Path.Combine(_catalog, fileName))) { - while (reader.EOS == false) + using (var reader = GetReadStream(fileName)) { - var k = reader.ReadCompatible(); - var v = reader.ReadCompatible(); - var c = _options.KeyComparer(key, k); - if (c == 0) return new StorePartitionKeyValueSearchResult { Key = key, Value = v, Found = true }; - if (c == -1) break; + while (reader.EOS == false) + { + var k = reader.ReadCompatible(); + var v = reader.ReadCompatible(); + var c = _options.KeyComparer(key, k); + if (c == 0) return new StorePartitionKeyValueSearchResult + { + Key = key, + Value = v, + Found = true + }; + if (c == -1) + { + break; + } + } + } + } + return new StorePartitionKeyValueSearchResult + { + Key = key, + Found = false, + Value = default + }; + } + + private IEnumerable> Find(string fileName, + TKey[] keys) + { + if (File.Exists(Path.Combine(_catalog, fileName))) + { + if (_options.Index.Enabled) + { + var index = new StorePartitionIndex(_catalog, _info, _options.FilePartition, _options.KeyComparer); + var offsets = index.GetOffset(keys, true); + using (var reader = GetReadStream(fileName)) + { + for (int i = 0; i < keys.Length; i++) + { + var searchKey = keys[i]; + var off = offsets[i]; + + reader.Stream.Seek(off.Offset, SeekOrigin.Begin); + while (reader.EOS == false) + { + var k = reader.ReadCompatible(); + var v = reader.ReadCompatible(); + var c = _options.KeyComparer(searchKey, k); + if (c == 0) + { + yield return new StorePartitionKeyValueSearchResult + { + Key = searchKey, + Value = v, + Found = true + }; + break; + } + else if (c == -1) + { + break; + } + } + } + } + } + else + { + using (var reader = GetReadStream(fileName)) + { + int index = 0; + var keys_arr = keys.OrderBy(k => k).ToArray(); + while (reader.EOS == false && index < keys_arr.Length) + { + var k = reader.ReadCompatible(); + var v = reader.ReadCompatible(); + var c = _options.KeyComparer(keys_arr[index], k); + if (c == 0) + { + yield return new StorePartitionKeyValueSearchResult + { + Key = keys_arr[index], + Value = v, + Found = true + }; + index++; + } + else if (c == -1) + { + do + { + index++; + if (index < keys_arr.Length) + { + c = _options.KeyComparer(keys_arr[index], k); + } + } while (index < keys_arr.Length && c == -1); + } + } + } } } - return new StorePartitionKeyValueSearchResult { Key = key, Found = false, Value = default }; } public IEnumerable> Find(IEnumerable keys) { - foreach (var key in keys) + var results = keys + .GroupBy( + k => _options.GetFileName(k, _info), + k => k, (key, g) => new { FileName = key, Keys = g.ToArray() }); + foreach (var group in results) { - yield return Find(key); + foreach (var r in Find(group.FileName, group.Keys)) + { + yield return r; + } } } @@ -111,8 +214,6 @@ namespace ZeroLevel.Services.PartitionStorage stream.SerializeCompatible(key); stream.SerializeCompatible(value); } - - private readonly ConcurrentDictionary _writeStreams = new ConcurrentDictionary(); private MemoryStreamWriter GetWriteStream(string fileName) { return _writeStreams.GetOrAdd(fileName, k => @@ -128,20 +229,96 @@ namespace ZeroLevel.Services.PartitionStorage var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096 * 1024); return new MemoryStreamReader(stream); } - - public void Dispose() - { - } - public int CountDataFiles() { var files = Directory.GetFiles(_catalog); return files?.Length ?? 0; } - public void DropData() { FSUtils.CleanAndTestFolder(_catalog); } + public IEnumerable> Iterate() + { + var files = Directory.GetFiles(_catalog); + if (files != null && files.Length > 1) + { + foreach (var file in files) + { + using (var reader = GetReadStream(Path.GetFileName(file))) + { + while (reader.EOS == false) + { + var k = reader.ReadCompatible(); + var v = reader.ReadCompatible(); + yield return new StorePartitionKeyValueSearchResult { Key = k, Value = v, Found = true }; + } + } + } + } + } + public IEnumerable> IterateKeyBacket(TKey key) + { + var fileName = _options.GetFileName(key, _info); + if (File.Exists(Path.Combine(_catalog, fileName))) + { + using (var reader = GetReadStream(fileName)) + { + while (reader.EOS == false) + { + var k = reader.ReadCompatible(); + var v = reader.ReadCompatible(); + yield return new StorePartitionKeyValueSearchResult { Key = k, Value = v, Found = true }; + } + } + } + } + public void RebuildIndex() + { + if (_options.Index.Enabled) + { + var indexFolder = Path.Combine(_catalog, "__indexes__"); + FSUtils.CleanAndTestFolder(indexFolder); + var files = Directory.GetFiles(_catalog); + if (files != null && files.Length > 1) + { + var dict = new Dictionary(); + foreach (var file in files) + { + dict.Clear(); + using (var reader = GetReadStream(Path.GetFileName(file))) + { + while (reader.EOS == false) + { + var pos = reader.Stream.Position; + var k = reader.ReadCompatible(); + dict[k] = pos; + reader.ReadCompatible(); + } + } + if (dict.Count > _options.Index.FileIndexCount * 8) + { + var step = (int)Math.Round(((float)dict.Count / (float)_options.Index.FileIndexCount), MidpointRounding.ToZero); + var index_file = Path.Combine(indexFolder, Path.GetFileName(file)); + var d_arr = dict.OrderBy(p => p.Key).ToArray(); + using (var writer = new MemoryStreamWriter( + new FileStream(index_file, FileMode.Create, FileAccess.Write, FileShare.None))) + { + for (int i = 0; i < _options.Index.FileIndexCount; i++) + { + var pair = d_arr[i * step]; + writer.WriteCompatible(pair.Key); + writer.WriteLong(pair.Value); + } + } + } + } + } + } + } + + public void Dispose() + { + } } } diff --git a/ZeroLevel/Services/PartitionStorage/StorePartitionIndex.cs b/ZeroLevel/Services/PartitionStorage/StorePartitionIndex.cs new file mode 100644 index 0000000..d4a4c17 --- /dev/null +++ b/ZeroLevel/Services/PartitionStorage/StorePartitionIndex.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using ZeroLevel.Services.Serialization; + +namespace ZeroLevel.Services.PartitionStorage +{ + internal struct KeyIndex + { + public TKey Key { get; set; } + public long Offset { get; set; } + } + + internal class StorePartitionIndex + : IStorePartitionIndex + { + private readonly Dictionary[]> _indexCachee + = new Dictionary[]>(1024); + + private readonly StoreFilePartition _filePartition; + private readonly Func _keyComparer; + private readonly string _indexFolder; + private readonly bool _indexExists = false; + private readonly TMeta _meta; + public StorePartitionIndex(string partitionFolder, TMeta meta, + StoreFilePartition filePartition, + Func keyComparer) + { + _indexFolder = Path.Combine(partitionFolder, "__indexes__"); + _indexExists = Directory.Exists(_indexFolder); + _meta = meta; + _keyComparer = keyComparer; + _filePartition = filePartition; + } + + public KeyIndex GetOffset(TKey key) + { + if (_indexExists) + { + var index = GetFileIndex(key); + int pos = 0; + if (index != null && index.Length > 0) + { + return BinarySearchInIndex(index, key, ref pos); + } + } + return new KeyIndex { Key = key, Offset = 0 }; + } + + public KeyIndex[] GetOffset(TKey[] keys, bool inOneGroup) + { + var result = new KeyIndex[keys.Length]; + int position = 0; + if (inOneGroup) + { + var index = GetFileIndex(keys[0]); + for (int i = 0; i < keys.Length; i++) + { + result[i] = BinarySearchInIndex(index, keys[i], ref position); + } + } + else + { + for (int i = 0; i < keys.Length; i++) + { + var index = GetFileIndex(keys[i]); + result[i] = BinarySearchInIndex(index, keys[i], ref position); + } + } + return result; + } + + private KeyIndex BinarySearchInIndex(KeyIndex[] index, TKey key, ref int position) + { + if (index == null || index.Length == 0) + { + return new KeyIndex { Key = key, Offset = 0 }; + } + int left = position; + int right = index.Length - 1; + int mid = 0; + TKey test; + while (left <= right) + { + mid = (int)Math.Floor((right + left) / 2d); + test = index[mid].Key; + var c = _keyComparer(test, key); + if (c < 0) + { + left = mid + 1; + } + else if (c > 0) + { + right = mid - 1; + } + else + { + break; + } + } + position = mid; + while (_keyComparer(index[position].Key, key) > 0 && position > 0) position--; + return index[position]; + } + + private KeyIndex[] GetFileIndex(TKey key) + { + var indexName = _filePartition.PathExtractor.Invoke(key, _meta); + if (_indexCachee.TryGetValue(indexName, out var index)) return index; + + var file = Path.Combine(_indexFolder, indexName); + if (File.Exists(file)) + { + var list = new List>(); + using (var reader = new MemoryStreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))) + { + while (reader.EOS == false) + { + var k = reader.ReadCompatible(); + var o = reader.ReadLong(); + list.Add(new KeyIndex { Key = k, Offset = o }); + } + } + _indexCachee[indexName] = list.ToArray(); + return _indexCachee[indexName]; + } + return null; + } + } +} diff --git a/ZeroLevel/ZeroLevel.csproj b/ZeroLevel/ZeroLevel.csproj index bbde94c..815787d 100644 --- a/ZeroLevel/ZeroLevel.csproj +++ b/ZeroLevel/ZeroLevel.csproj @@ -6,16 +6,16 @@ ogoun ogoun - 3.3.7.1 - PartitionStorage new methods + 3.3.7.3 + PartitionStorage append indexes https://github.com/ogoun/Zero/wiki Copyright Ogoun 2022 https://github.com/ogoun/Zero git - 3.3.7.1 - 3.3.7.1 + 3.3.7.3 + 3.3.7.3 AnyCPU;x64;x86 zero.png full