Skip to content

Commit

Permalink
Feature: Improve Performance of MergeManyChangeSets with fewer change…
Browse files Browse the repository at this point in the history
…sets (#829)

* Reduce ChangeSets emitted by MergeManyChangeSets by using flag to determine if a parent changeset is currently being processed so that all changes initiated by a parent changeset are queued up until they've all been handled
* Updated unit tests with new expected message count
  • Loading branch information
dwcullop committed Jan 9, 2024
1 parent 0d1b41f commit 7585ae0
Show file tree
Hide file tree
Showing 13 changed files with 257 additions and 165 deletions.
26 changes: 24 additions & 2 deletions src/DynamicData.Tests/Cache/MergeManyChangeSetsCacheFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public void AllExistingSubItemsPresentInResult()
_marketCacheResults.Data.Count.Should().Be(MarketCount);
markets.Sum(m => m.PricesCache.Count).Should().Be(MarketCount * PricesPerMarket);
results.Data.Count.Should().Be(MarketCount * PricesPerMarket);
results.Messages.Count.Should().Be(MarketCount);
results.Messages.Count.Should().Be(1);
results.Summary.Overall.Adds.Should().Be(MarketCount * PricesPerMarket);
results.Summary.Overall.Removes.Should().Be(0);
results.Summary.Overall.Updates.Should().Be(0);
Expand Down Expand Up @@ -390,19 +390,41 @@ public void AnySourceItemRemovedRemovesAllSourceValues()
// having
var markets = Enumerable.Range(0, MarketCount).Select(n => new Market(n)).ToArray();
using var results = _marketCache.Connect().MergeManyChangeSets(m => m.LatestPrices, MarketPrice.EqualityComparer).AsAggregator();
_marketCache.AddOrUpdate(markets);
AddUniquePrices(markets);
_marketCache.AddOrUpdate(markets);

// when
_marketCache.Edit(updater => updater.RemoveKeys(updater.Keys.Take(RemoveCount)));

// then
_marketCacheResults.Data.Count.Should().Be(MarketCount - RemoveCount);
results.Messages.Count.Should().Be(2);
results.Data.Count.Should().Be((MarketCount - RemoveCount) * PricesPerMarket);
results.Summary.Overall.Adds.Should().Be(MarketCount * PricesPerMarket);
results.Summary.Overall.Removes.Should().Be(PricesPerMarket * RemoveCount);
}

[Fact]
public void ClearingParentEmitsSingleChangeSet()
{
// having
var markets = Enumerable.Range(0, MarketCount).Select(n => new Market(n)).ToArray();
using var results = _marketCache.Connect().MergeManyChangeSets(m => m.LatestPrices, MarketPrice.EqualityComparer).AsAggregator();
AddUniquePrices(markets);
_marketCache.AddOrUpdate(markets);

// when
_marketCache.Clear();

// then
_marketCacheResults.Data.Count.Should().Be(0);
results.Data.Count.Should().Be(0);
results.Messages.Count.Should().Be(2);
results.Summary.Overall.Adds.Should().Be(MarketCount * PricesPerMarket);
results.Summary.Overall.Removes.Should().Be(MarketCount * PricesPerMarket);
results.Summary.Overall.Updates.Should().Be(0);
}

[Fact]
public void ChangingSourceByUpdateRemovesPreviousAndAddsNewValues()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public void AllExistingSubItemsPresentInResult()
_marketCacheResults.Data.Count.Should().Be(MarketCount);
markets.Sum(m => m.PricesCache.Count).Should().Be(MarketCount * PricesPerMarket);
results.Data.Count.Should().Be(MarketCount * PricesPerMarket);
results.Messages.Count.Should().Be(MarketCount);
results.Messages.Count.Should().Be(1);
results.Summary.Overall.Adds.Should().Be(MarketCount * PricesPerMarket);
results.Summary.Overall.Removes.Should().Be(0);
results.Summary.Overall.Updates.Should().Be(0);
Expand Down Expand Up @@ -263,16 +263,16 @@ public void AllRefreshedSubItemsAreRefreshed()
// having
var markets = Enumerable.Range(0, MarketCount).Select(n => new Market(n)).ToArray();
using var results = ChangeSetByRating().AsAggregator();
_marketCache.AddOrUpdate(markets);
markets.Select((m, index) => new { Market = m, Index = index }).ForEach(m => m.Market.SetPrices(m.Index * ItemIdStride, (m.Index * ItemIdStride) + PricesPerMarket, GetRandomPrice));
_marketCache.AddOrUpdate(markets);

// when
markets.ForEach(m => m.RefreshAllPrices(GetRandomPrice));

// then
_marketCacheResults.Data.Count.Should().Be(MarketCount);
results.Data.Count.Should().Be(MarketCount * PricesPerMarket);
results.Messages.Count.Should().Be(MarketCount * 2);
results.Messages.Count.Should().Be(MarketCount + 1);
results.Summary.Overall.Adds.Should().Be(MarketCount * PricesPerMarket);
results.Summary.Overall.Removes.Should().Be(0);
results.Summary.Overall.Updates.Should().Be(0);
Expand Down
26 changes: 13 additions & 13 deletions src/DynamicData.Tests/Cache/MergeManyChangeSetsListFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public void ResultContainsAllInitialChildren()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(1);
CheckResultContents();
}

Expand All @@ -176,7 +176,7 @@ public void ResultContainsChildrenFromAddedParents()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
addThis.Animals.Items.ForEach(added => _animalResults.Data.Items.Should().Contain(added));
CheckResultContents();
}
Expand All @@ -192,7 +192,7 @@ public void ResultDoesNotContainChildrenFromParentsRemovedWithRemove()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount - 1);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
removeThis.Animals.Items.ForEach(removed => _animalResults.Data.Items.Should().NotContain(removed));
CheckResultContents();
removeThis.Dispose();
Expand All @@ -209,7 +209,7 @@ public void ResultDoesNotContainChildrenFromParentsBatchRemoved()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount - RemoveRangeSize);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + RemoveRangeSize);
_animalResults.Messages.Count.Should().Be(2);
removeThese.SelectMany(owner => owner.Animals.Items).ForEach(removed => _animalResults.Data.Items.Should().NotContain(removed));
CheckResultContents();
removeThese.ForEach(owner => owner.Dispose());
Expand All @@ -227,7 +227,7 @@ public void ResultContainsCorrectItemsAfterParentUpdate()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount); // Owner Count should not change
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 2); // +2 = 1 Message removing animals from old value, +1 message adding from new value
_animalResults.Messages.Count.Should().Be(2); // 2 = Initial Add and one changeset with remove old items / add new items
replaceThis.Animals.Items.ForEach(removed => _animalResults.Data.Items.Should().NotContain(removed));
withThis.Animals.Items.ForEach(added => _animalResults.Data.Items.Should().Contain(added));
CheckResultContents();
Expand Down Expand Up @@ -262,7 +262,7 @@ public void ResultContainsChildrenAddedWithAddRange()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount * 2);
_animalResults.Messages.Count.Should().Be(1 + InitialOwnerCount); // Initial + 1 for each Range Added
totalAdded.ForEach(animal => _animalResults.Data.Items.Should().Contain(animal));
_animalOwners.Items.Sum(owner => owner.Animals.Count).Should().Be(initialCount + totalAdded.Count);
CheckResultContents();
Expand All @@ -283,7 +283,7 @@ public void ResultContainsChildrenAddedWithInsert()
// Assert
randomOwner.Animals.Items.ElementAt(insertIndex).Should().Be(insertThis);
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
_animalResults.Data.Items.Should().Contain(insertThis);
_animalOwners.Items.Sum(owner => owner.Animals.Count).Should().Be(initialCount + 1);
CheckResultContents();
Expand All @@ -302,7 +302,7 @@ public void ResultDoesNotContainChildrenRemovedWithRemove()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
_animalResults.Data.Items.Should().NotContain(removeThis);
_animalOwners.Items.Sum(owner => owner.Animals.Count).Should().Be(initialCount - 1);
CheckResultContents();
Expand All @@ -322,7 +322,7 @@ public void ResultDoesNotContainChildrenRemovedWithRemoveAt()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
_animalResults.Data.Items.Should().NotContain(removeThis);
_animalOwners.Items.Sum(owner => owner.Animals.Count).Should().Be(initialCount - 1);
CheckResultContents();
Expand All @@ -342,7 +342,7 @@ public void ResultDoesNotContainChildrenRemovedWithRemoveRange()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
removeThese.ForEach(removed => randomOwner.Animals.Items.Should().NotContain(removed));
CheckResultContents();
}
Expand All @@ -360,7 +360,7 @@ public void ResultDoesNotContainChildrenRemovedWithRemoveMany()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
removeThese.ForEach(removed => randomOwner.Animals.Items.Should().NotContain(removed));
CheckResultContents();
}
Expand All @@ -378,7 +378,7 @@ public void ResultContainsCorrectItemsAfterChildReplacement()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
randomOwner.Animals.Items.Should().NotContain(replaceThis);
randomOwner.Animals.Items.Should().Contain(withThis);
CheckResultContents();
Expand All @@ -396,7 +396,7 @@ public void ResultContainsCorrectItemsAfterChildClear()

// Assert
_animalOwnerResults.Data.Count.Should().Be(InitialOwnerCount);
_animalResults.Messages.Count.Should().Be(InitialOwnerCount + 1);
_animalResults.Messages.Count.Should().Be(2);
randomOwner.Animals.Count.Should().Be(0);
removedAnimals.ForEach(removed => _animalResults.Data.Items.Should().NotContain(removed));
CheckResultContents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public void AllExistingSubItemsPresentInResult()
_marketListResults.Data.Count.Should().Be(MarketCount);
markets.Sum(m => m.PricesCache.Count).Should().Be(MarketCount * PricesPerMarket);
results.Data.Count.Should().Be(MarketCount * PricesPerMarket);
results.Messages.Count.Should().Be(MarketCount);
results.Messages.Count.Should().Be(1);
results.Summary.Overall.Adds.Should().Be(MarketCount * PricesPerMarket);
results.Summary.Overall.Removes.Should().Be(0);
results.Summary.Overall.Updates.Should().Be(0);
Expand Down Expand Up @@ -824,8 +824,6 @@ public void Dispose()
DisposeMarkets();
}

private void AddUniquePrices(Market[] markets) => markets.ForEach(m => m.AddUniquePrices(PricesPerMarket, _ => GetRandomPrice()));

private void CheckResultContents(ChangeSetAggregator<IMarket> marketResults, ChangeSetAggregator<MarketPrice, int> priceResults)
{
var expectedMarkets = _marketList.Items.ToList();
Expand Down

0 comments on commit 7585ae0

Please sign in to comment.