diff --git a/benchmarks/NexusMods.MnemonicDB.Benchmarks/Benchmarks/ObserveAllBenchmarks.cs b/benchmarks/NexusMods.MnemonicDB.Benchmarks/Benchmarks/ObserveAllBenchmarks.cs new file mode 100644 index 0000000..8703660 --- /dev/null +++ b/benchmarks/NexusMods.MnemonicDB.Benchmarks/Benchmarks/ObserveAllBenchmarks.cs @@ -0,0 +1,57 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using NexusMods.MnemonicDB.Abstractions.TxFunctions; +using NexusMods.MnemonicDB.TestModel; + +namespace NexusMods.MnemonicDB.Benchmarks.Benchmarks; + +[MemoryDiagnoser] +[SimpleJob(runStrategy: RunStrategy.Monitoring, warmupCount: 1)] +public class ObserveAllBenchmarks : ABenchmark +{ + private string[] _names = []; + + [Params(100, 1_000, 10_000)] + public int N { get; set; } + + [GlobalSetup] + public async ValueTask GlobalSetup() + { + await InitializeAsync(); + _names = Enumerable.Range(start: 0, count: N).Select(i => $"Loadout {i}").ToArray(); + } + + [IterationCleanup] + public void IterationCleanup() + { + using var tx = Connection.BeginTransaction(); + + foreach (var entity in Loadout.All(Connection.Db)) + { + tx.Delete(entity, recursive: false); + } + + tx.Commit().GetAwaiter().GetResult(); + } + + [Benchmark] + public async ValueTask ObserveAll() + { + using var disposable = Loadout.ObserveAll(Connection).Subscribe(_ => { }); + + using var tx = Connection.BeginTransaction(); + + foreach (var name in _names) + { + _ = new Loadout.New(tx) + { + Name = name, + }; + } + + await tx.Commit(); + } +} diff --git a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt index bd13ebb..97e440a 100644 --- a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt +++ b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt @@ -14,3 +14,4 @@ + | 0200000000000001 | (0015) Hash | 0x00000000DEADBEEF | 0100000000000002 + | 0200000000000003 | (0018) Name | Test Mod 1 | 0100000000000002 + | 0200000000000005 | (0018) Name | Test Mod 2 | 0100000000000002 ++ | 0200000000000004 | (001A) Name | Test Loadout 1 | 0100000000000002 diff --git a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt index bd13ebb..97e440a 100644 --- a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt +++ b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt @@ -14,3 +14,4 @@ + | 0200000000000001 | (0015) Hash | 0x00000000DEADBEEF | 0100000000000002 + | 0200000000000003 | (0018) Name | Test Mod 1 | 0100000000000002 + | 0200000000000005 | (0018) Name | Test Mod 2 | 0100000000000002 ++ | 0200000000000004 | (001A) Name | Test Loadout 1 | 0100000000000002 diff --git a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt index 801a1a1..6b4a33a 100644 --- a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt +++ b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/InMemoryTests.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt @@ -12,3 +12,4 @@ + | 0200000000000001 | (0015) Hash | 0x00000000DEADBEEF | 0100000000000002 + | 0200000000000003 | (0018) Name | Test Mod 1 | 0100000000000002 + | 0200000000000005 | (0018) Name | Test Mod 2 | 0100000000000002 ++ | 0200000000000004 | (001A) Name | Test Loadout 1 | 0100000000000002 diff --git a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt index bd13ebb..97e440a 100644 --- a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt +++ b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETCurrent.verified.txt @@ -14,3 +14,4 @@ + | 0200000000000001 | (0015) Hash | 0x00000000DEADBEEF | 0100000000000002 + | 0200000000000003 | (0018) Name | Test Mod 1 | 0100000000000002 + | 0200000000000005 | (0018) Name | Test Mod 2 | 0100000000000002 ++ | 0200000000000004 | (001A) Name | Test Loadout 1 | 0100000000000002 diff --git a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt index bd13ebb..97e440a 100644 --- a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt +++ b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.HistoricalQueriesReturnAllDataSorted_type=AVETHistory.verified.txt @@ -14,3 +14,4 @@ + | 0200000000000001 | (0015) Hash | 0x00000000DEADBEEF | 0100000000000002 + | 0200000000000003 | (0018) Name | Test Mod 1 | 0100000000000002 + | 0200000000000005 | (0018) Name | Test Mod 2 | 0100000000000002 ++ | 0200000000000004 | (001A) Name | Test Loadout 1 | 0100000000000002 diff --git a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt index 801a1a1..6b4a33a 100644 --- a/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt +++ b/tests/NexusMods.MnemonicDB.Storage.Tests/BackendTestVerifyData/RocksDB.InsertedDatomsShowUpInTheIndex_type=AVETCurrent.verified.txt @@ -12,3 +12,4 @@ + | 0200000000000001 | (0015) Hash | 0x00000000DEADBEEF | 0100000000000002 + | 0200000000000003 | (0018) Name | Test Mod 1 | 0100000000000002 + | 0200000000000005 | (0018) Name | Test Mod 2 | 0100000000000002 ++ | 0200000000000004 | (001A) Name | Test Loadout 1 | 0100000000000002 diff --git a/tests/NexusMods.MnemonicDB.TestModel/Loadout.cs b/tests/NexusMods.MnemonicDB.TestModel/Loadout.cs index 4c6379a..4a33ff3 100644 --- a/tests/NexusMods.MnemonicDB.TestModel/Loadout.cs +++ b/tests/NexusMods.MnemonicDB.TestModel/Loadout.cs @@ -7,9 +7,8 @@ namespace NexusMods.MnemonicDB.TestModel; public partial class Loadout : IModelDefinition { private const string Namespace = "NexusMods.MnemonicDB.TestModel.Loadout"; - public static readonly StringAttribute Name = new(Namespace, nameof(Name)); + public static readonly StringAttribute Name = new(Namespace, nameof(Name)) { IsIndexed = true }; public static readonly BackReferenceAttribute Mods = new(Mod.Loadout); public static readonly BackReferenceAttribute Collections = new(Collection.Loadout); - public static readonly AbsolutePathAttribute GamePath = new(Namespace, nameof(GamePath)) - { IsOptional = true }; + public static readonly AbsolutePathAttribute GamePath = new(Namespace, nameof(GamePath)) { IsOptional = true }; } diff --git a/tests/NexusMods.MnemonicDB.Tests/ObservableTests.cs b/tests/NexusMods.MnemonicDB.Tests/ObservableTests.cs new file mode 100644 index 0000000..eff50c7 --- /dev/null +++ b/tests/NexusMods.MnemonicDB.Tests/ObservableTests.cs @@ -0,0 +1,97 @@ +using DynamicData; +using NexusMods.MnemonicDB.Abstractions.TxFunctions; +using NexusMods.MnemonicDB.TestModel; + +namespace NexusMods.MnemonicDB.Tests; + +public class ObservableTests : AMnemonicDBTest +{ + public ObservableTests(IServiceProvider provider) : base(provider) { } + + [Fact] + public async Task TestObserveAll() + { + using var disposable = Loadout + .ObserveAll(Connection) + .Transform(loadout => loadout.Name) + .Bind(out var list) + .Subscribe(); + + list.Should().BeEmpty(); + + await Add("Loadout 1"); + + list.Should().ContainSingle(static name => name.Equals("Loadout 1")); + + await Add("Loadout 2"); + + list.Should().HaveCount(2).And.ContainInOrder(["Loadout 1", "Loadout 2"]); + + await Add("Loadout 3", "Loadout 4"); + + list.Should().HaveCount(4).And.ContainInOrder(["Loadout 1", "Loadout 2", "Loadout 3", "Loadout 4"]); + + await Delete("Loadout 2"); + + list.Should().HaveCount(3).And.ContainInOrder(["Loadout 1", "Loadout 3", "Loadout 4"]); + + await Delete("Loadout 1", "Loadout 4"); + + list.Should().ContainSingle(static name => name.Equals("Loadout 3")); + + await Delete("Loadout 3"); + + list.Should().BeEmpty(); + } + + [Fact] + public async Task StressTest() + { + using var disposable = Loadout + .ObserveAll(Connection) + .Transform(loadout => loadout.Name) + .Bind(out var list) + .Subscribe(); + + var stress = Enumerable.Range(start: 0, count: 100).Select(i => $"Loadout {i}").ToArray(); + await Add(stress); + + list.Should().ContainInOrder(stress); + + await Delete(stress); + + list.Should().BeEmpty(); + } + + private async ValueTask Add(params string[] names) + { + using var tx = Connection.BeginTransaction(); + + foreach (var name in names) + { + _ = new Loadout.New(tx) + { + Name = name, + }; + } + + + await tx.Commit(); + } + + private async ValueTask Delete(params string[] names) + { + var db = Connection.Db; + using var tx = Connection.BeginTransaction(); + + foreach (var name in names) + { + var entities = Loadout.FindByName(db, name); + entities.Should().ContainSingle(); + + tx.Delete(entities.First(), recursive: false); + } + + await tx.Commit(); + } +}