Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.4.21 Release #5096

Merged
merged 50 commits into from Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
3ebbfcf
Update RELEASE_NOTES.md (#5009)
Aaronontheweb May 12, 2021
0ef7f07
Merge pull request #5011 from brah-mcdude/dev
brah-mcdude May 14, 2021
bc5a2e4
Fix SonarQube's "IEnumerable LINQs should be simplified" / Code Smell…
marodev May 14, 2021
594eb7c
[RACY] LoggerSpec TestOutputLogger Make sure that event receive order…
Arkatufus May 14, 2021
e51d46b
Enable skipped batching SQLite specs, these runs just fine now. (#5017)
Arkatufus May 15, 2021
1875c32
Enable skipped specs, they're working now (#5018)
Arkatufus May 17, 2021
3f6182c
Fix AkkaProtocolStressTest spec (#5020)
Arkatufus May 18, 2021
0869e49
Increase timeout value to avoid timeout failure (#5021)
Arkatufus May 18, 2021
d0afa72
Bump BenchmarkDotNet from 0.12.1 to 0.13.0 (#5024)
dependabot[bot] May 20, 2021
b5f6552
Bump Google.Protobuf from 3.16.0 to 3.17.0 (#5012)
dependabot[bot] May 20, 2021
aa8a3de
adding ORSet benchmarks (#4990)
Aaronontheweb May 21, 2021
5294e9a
added `FastHash` benchmark (#5029)
Aaronontheweb May 21, 2021
61d08b7
Fix a potential race condition in FileSubscriber (#5032)
ismaelhamed May 22, 2021
99be70b
Consolidate intercept (#5033)
ismaelhamed May 24, 2021
6f5b003
remove `ActorPath.ToString` call from `ResolveActorRefWithLocalAddres…
Aaronontheweb May 24, 2021
3f8fa29
Improve MurmurHash string hash memory footprint (#5028)
Arkatufus May 24, 2021
431b69e
Turn on DData related ClusterSharding MNTR specs (#4926)
Arkatufus May 25, 2021
b888b40
A couple of fixes affecting the FileSubscriber (#5035)
ismaelhamed May 25, 2021
3dc2e35
Bump Google.Protobuf from 3.17.0 to 3.17.1 (#5036)
dependabot[bot] May 25, 2021
99afc0e
Bump Google.Protobuf from 3.17.1 to 3.17.2 (#5040)
dependabot[bot] May 26, 2021
c663bc2
Abstraction of ServiceProvider, Improving Akka.DependencyInjection (#…
SamEmber May 26, 2021
410066f
[MNTR] fix test dll loading error (#5044)
Arkatufus May 27, 2021
73d19af
Add ActorSystemSetup setup (#5045)
Arkatufus May 27, 2021
1938a20
added dedicated footprint for measuring Actor memory footprint (#5047)
Aaronontheweb May 28, 2021
98ef574
changed job configuration for ActorMemoryFootprintBenchmark (#5048)
Aaronontheweb May 28, 2021
c21289e
Remove `Uri.TryParse` and replace with `Span<char>` parsing (#5039)
Aaronontheweb May 28, 2021
566dbe3
Removed `StringBuilder` allocation from `Base64Encoding` (#5051)
Aaronontheweb May 29, 2021
f6da797
Bring UnfoldResourceSourceAsync up-to-date (#5052)
ismaelhamed Jun 1, 2021
83426ae
fixed incorrect deprecation message (#5054)
Aaronontheweb Jun 1, 2021
8982b64
remove ISSUE_TEMPLATE.md (#5057)
Aaronontheweb Jun 1, 2021
1bec20a
Fix DData infinite status/gossip round (#5056)
Arkatufus Jun 2, 2021
2715c36
Revert `ThreadPool.SetMinThreads(0,0)` (#5059)
Aaronontheweb Jun 2, 2021
15492d0
Reduced allocations around `ActorCell` and `Task` handling (#5053)
Aaronontheweb Jun 3, 2021
4bd3ef9
Remove `ThreadPool.SetMinThreads(0,0)` mention from dispatchers docum…
Aaronontheweb Jun 3, 2021
b1a9aa6
eliminate Lazy<string> from `Address` (#5068)
Aaronontheweb Jun 3, 2021
95cdfac
v1.4.21-beta1 Release Notes (#5071)
Aaronontheweb Jun 4, 2021
16fef39
Akka.Coordination: minor cleanup (#5074)
Aaronontheweb Jun 4, 2021
90afdb7
Use TimestampProvider in QueryExecutor.InsertABatchAsync, fixes #5076…
MartijnSchoemaker Jun 7, 2021
8565eaa
Fix documentation flaw for "Forward" (#5078)
wesselkranenborg Jun 8, 2021
8d13c3d
Bump NUnit3TestAdapter from 3.17.0 to 4.0.0 (#5079)
dependabot[bot] Jun 9, 2021
cf34e37
Bump Microsoft.Data.SQLite from 5.0.6 to 5.0.7 (#5081)
dependabot[bot] Jun 9, 2021
35b1bb5
added FQDN reproduction for `ActorPath.Parse` (#5084)
Aaronontheweb Jun 9, 2021
6a7373f
Fix Akka.Remote serialization exception bubbling and messages (#5072)
Arkatufus Jun 9, 2021
dfd7925
Return `T` from `IDependencyResolver.GetService<T>() (#5087)
Aaronontheweb Jun 10, 2021
c485d9e
cleaned up internal ActorCell and TestKit methods (#5091)
Aaronontheweb Jun 14, 2021
456d795
TestKit ReceiveWhile did not insert last inspected message properly. …
Arkatufus Jun 15, 2021
0210e21
Fix GSet elements needs to be sorted (#5093)
Arkatufus Jun 15, 2021
fa5ca50
Add failure handling for BatchingSqlJournal SelectCurrentPersistenceI…
Arkatufus Jun 15, 2021
5d557d0
Bump Microsoft.NET.Test.Sdk from 16.9.4 to 16.10.0 (#5041)
dependabot[bot] Jun 16, 2021
5c9f8a7
v1.4.21 final release notes (#5095)
Aaronontheweb Jun 16, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 0 additions & 6 deletions ISSUE_TEMPLATE.md

This file was deleted.

90 changes: 90 additions & 0 deletions RELEASE_NOTES.md
@@ -1,3 +1,93 @@
#### 1.4.21 June 16 2021 ####
**Maintenance Release for Akka.NET 1.4**

Akka.NET v1.4.21 is a significant release that includes major performance improvements, bug fixes, and a major update to the [Akka.DependencyInjection NuGet package](https://getakka.net/articles/actors/dependency-injection.html).

**Performance Improvements**
Akka.NET v1.4.21 includes some major performance fixes and improvements:

* [`Ask` is now ~10% faster](https://github.com/akkadotnet/akka.net/pull/5051)
* [`MurmurHash` is 33% faster and allocates 0 memory](https://github.com/akkadotnet/akka.net/pull/5028) - used _heavily_ in DData, Cluster Sharding, and Consistent Hash Routers
* `ActorPath.Parse` went from 1672 ns/op to 527 ns/op - a 68% improvement in throughput and a 50% reduction in memory. See [#5039](https://github.com/akkadotnet/akka.net/pull/5039) and [#5068](https://github.com/akkadotnet/akka.net/pull/5068).
* [Akka.Remote: remove `ActorPath.ToString` call from `ResolveActorRefWithLocalAddress`](https://github.com/akkadotnet/akka.net/pull/5034)
* **Important**: [Revert `ThreadPool.SetMinThreads(0,0)`](https://github.com/akkadotnet/akka.net/pull/5059) - based on the input from users on "[Akka.NET v1.4.19: ChannelExecutor performance data](https://github.com/akkadotnet/akka.net/discussions/4983)"

Our observed performance numbers for Akka.Remote show a significant increase in performance for v1.4.21 over v1.4.20:

*Before*

```
PS> dotnet run -c Release --framework netcoreapp3.1
OSVersion: Microsoft Windows NT 6.2.9200.0
ProcessorCount: 16
ClockSpeed: 0 MHZ
Actor Count: 32
Messages sent/received per client: 200000 (2e5)
Is Server GC: True
Thread count: 109

Num clients, Total [msg], Msgs/sec, Total [ms]
1, 200000, 113379, 1764.56
5, 1000000, 186429, 5364.05
10, 2000000, 185340, 10791.11
15, 3000000, 183218, 16374.06
20, 4000000, 179824, 22244.63
25, 5000000, 182716, 27365.89
30, 6000000, 182039, 32960.61
```

*After*

```
PS> dotnet run -c Release --framework netcoreapp3.1
OSVersion: Microsoft Windows NT 6.2.9200.0
ProcessorCount: 16
ClockSpeed: 0 MHZ
Actor Count: 32
Messages sent/received per client: 200000 (2e5)
Is Server GC: True
Thread count: 111

Num clients, Total [msg], Msgs/sec, Total [ms]
1, 200000, 109770, 1822.14
5, 1000000, 192902, 5184.79
10, 2000000, 191663, 10435.53
15, 3000000, 191339, 15679.11
20, 4000000, 192725, 20755.78
25, 5000000, 189754, 26350.14
30, 6000000, 189772, 31617.20
```

> N.B. these after numbers don't benefit from the performance benefits we observed in v1.4.20 when we invoked `ThreadPool.SetMinThreads(0,0)`, which makes them even more impressive.

**Akka.DependencyInjection Updates**
We had one major issue we implemented in v1.4.21 for Akka.DependencyInjection: [Abstraction of `ServiceProvider`, Improving Akka.DependencyInjection ](https://github.com/akkadotnet/akka.net/pull/4814)

What this change did was:

* Deprecate the `Akka.DependencyInjection.ServiceProvider` class in favor of the `Akka.DependencyInjection.DependencyResolver` class - to avoid namespace collision with Microsoft.Extensions.DependencyInjection.ServiceProvider;
* Deprecates the `Akka.DependencyInjection.ServiceProviderSetup` class in favor of the `Akka.DependencyInjection.DependencyResolverSetup` class for consistency reasons;
* `Akka.DependencyInjection.DependencyResolver` now takes an input of type [`IDependencyResolver`](https://getakka.net/api/Akka.DependencyInjection.IDependencyResolver.html), which allows users to abstract away the `IServiceProvider` and mock / replace it during unit testing; and
* Added some non-generic `Props` methods for dynamically spawning actors via DI.

All of these changes are backwards-compatible with v1.4.20 and earlier - and the deprecation warnings will appear in your code when you upgrade. If you run into any [issues upgrading to Akka.DependencyInjection v1.4.21 please reply on this thread](https://github.com/akkadotnet/akka.net/discussions/5070)!

**Other Changes and Fixes**

* [Akka.Streams: A couple of fixes affecting the `FileSubscriber`](https://github.com/akkadotnet/akka.net/pull/5035)
* [Akka.DistributedData: memory leak when recovering events from LMDB data store](https://github.com/akkadotnet/akka.net/issues/5022)
* [Akka.DistributedData: port `VectorClock` performance optimizations to `VersionVector` and similar types](https://github.com/akkadotnet/akka.net/issues/4956)

To see the [full set of fixes in Akka.NET v1.4.21, please see the milestone on Github](https://github.com/akkadotnet/akka.net/milestone/51).

| COMMITS | LOC+ | LOC- | AUTHOR |
| --- | --- | --- | --- |
| 5 | 34 | 24 | Aaron Stannard |
| 4 | 196 | 77 | Gregorius Soedharmo |
| 3 | 3 | 3 | dependabot[bot] |
| 1 | 2 | 2 | Wessel Kranenborg |
| 1 | 1 | 1 | Martijn Schoemaker |

#### 1.4.20 May 12 2021 ####
**Maintenance Release for Akka.NET 1.4**

Expand Down
5 changes: 1 addition & 4 deletions docs/articles/actors/dispatchers.md
Expand Up @@ -171,7 +171,7 @@ In Akka.NET v1.4.19 we will be introducing an opt-in feature, the `ChannelExecut

During its initial development and benchmarks, we observed the following:

1. The `ChannelExecutor` tremendously reduced idle CPU and max busy CPU even during peak message throughput, primarily as a result of dynamically shrinking the total `ThreadPool` to only the necessary size. This resolves one of the largest complaints large users of Akka.NET have today. However, **in order for this setting to be effective `ThreadPool.SetMin(0,0)` must also be set**. We are considering doing this inside the `ActorSystem.Create` method, those settings don't work for you you can easily override them by simply calling `ThreadPool.SetMin(yourValue, yourValue)` again after `ActorSystem.Create` has exited.
1. The `ChannelExecutor` tremendously reduced idle CPU and max busy CPU even during peak message throughput, primarily as a result of dynamically shrinking the total `ThreadPool` to only the necessary size. This resolves one of the largest complaints large users of Akka.NET have today.
2. The `ChannelExecutor` actually beat the `ForkJoinDispatcher` and others on performance even in environments like Docker and bare metal on Windows.

> [!NOTE]
Expand Down Expand Up @@ -220,9 +220,6 @@ akka.remote.backoff-remote-dispatcher {

This will enable the `ChannelExecutor` to run everywhere and all Akka.NET loads, with the exception of anything you manually allocate onto a `ForkJoinDispatcher` or `PinnedDispatcher`, will be managed by the `ThreadPool`.

> [!IMPORTANT]
> As of Akka.NET v1.4.19, we call `ThreadPool.SetMinThreads(0,0)` inside the `ActorSystem.Create` method as we've found that the default `ThreadPool` minimum values have a negative impact on performance. However, if this causes undesireable side effects for you inside your application you can always override those settings by calling `ThreadPool.SetMinThreads(yourValue, yourValue)` again after you've created your `ActorSystem`.

#### Common Dispatcher Configuration

The following configuration keys are available for any dispatcher configuration:
Expand Down
4 changes: 2 additions & 2 deletions docs/articles/actors/receive-actor-api.md
Expand Up @@ -405,10 +405,10 @@ For more information on Tasks, check out the [MSDN documentation](https://msdn.m
> When using task callbacks inside actors, you need to carefully avoid closing over the containing actor’s reference, i.e. do not call methods or access mutable state on the enclosing actor from within the callback. This would break the actor encapsulation and may introduce synchronization bugs and race conditions because the callback will be scheduled concurrently to the enclosing actor. Unfortunately there is not yet a way to detect these illegal accesses at compile time.

### Forward message
You can forward a message from one actor to another. This means that the original sender address/reference is maintained even though the message is going through a 'mediator'. This can be useful when writing actors that work as routers, load-balancers, replicators etc. You need to pass along your context variable as well.
You can forward a message from one actor to another. This means that the original sender address/reference is maintained even though the message is going through a 'mediator'. This can be useful when writing actors that work as routers, load-balancers, replicators etc.

```csharp
target.Forward(result, Context);
target.Forward(result);
```

## Receive messages
Expand Down
19 changes: 17 additions & 2 deletions src/Akka.sln
Expand Up @@ -240,9 +240,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.DependencyInjection.Te
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore", "{162F5991-EA57-4221-9B70-F9B6FEC18036}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Akka.AspNetCore", "examples\AspNetCore\Samples.Akka.AspNetCore\Samples.Akka.AspNetCore.csproj", "{D62F4AD6-318F-4ECC-B875-83FA9933A81B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Akka.AspNetCore", "examples\AspNetCore\Samples.Akka.AspNetCore\Samples.Akka.AspNetCore.csproj", "{D62F4AD6-318F-4ECC-B875-83FA9933A81B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerializationBenchmarks", "benchmark\SerializationBenchmarks\SerializationBenchmarks.csproj", "{2E4B9584-42CC-4D17-B719-9F462B16C94D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SerializationBenchmarks", "benchmark\SerializationBenchmarks\SerializationBenchmarks.csproj", "{2E4B9584-42CC-4D17-B719-9F462B16C94D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DDataStressTest", "examples\Cluster\DData\DDataStressTest\DDataStressTest.csproj", "{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -1123,6 +1125,18 @@ Global
{2E4B9584-42CC-4D17-B719-9F462B16C94D}.Release|x64.Build.0 = Release|Any CPU
{2E4B9584-42CC-4D17-B719-9F462B16C94D}.Release|x86.ActiveCfg = Release|Any CPU
{2E4B9584-42CC-4D17-B719-9F462B16C94D}.Release|x86.Build.0 = Release|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Debug|x64.ActiveCfg = Debug|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Debug|x64.Build.0 = Debug|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Debug|x86.ActiveCfg = Debug|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Debug|x86.Build.0 = Debug|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Release|Any CPU.Build.0 = Release|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Release|x64.ActiveCfg = Release|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Release|x64.Build.0 = Release|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Release|x86.ActiveCfg = Release|Any CPU
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1230,6 +1244,7 @@ Global
{162F5991-EA57-4221-9B70-F9B6FEC18036} = {D3AF8295-AEB5-4324-AA82-FCC0014AC310}
{D62F4AD6-318F-4ECC-B875-83FA9933A81B} = {162F5991-EA57-4221-9B70-F9B6FEC18036}
{2E4B9584-42CC-4D17-B719-9F462B16C94D} = {73108242-625A-4D7B-AA09-63375DBAE464}
{44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50} = {C50E1A9E-820C-4E75-AE39-6F96A99AC4A7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164}
Expand Down
@@ -0,0 +1,57 @@
//-----------------------------------------------------------------------
// <copyright file="ActorMemoryFootprintBenchmarks.cs" company="Akka.NET Project">
// Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.Benchmarks.Configurations;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;

namespace Akka.Benchmarks.Actor
{
[Config(typeof(MicroBenchmarkConfig))]
[SimpleJob(RunStrategy.Monitoring, targetCount: 25, warmupCount: 5)]
public class ActorMemoryFootprintBenchmark
{
public ActorSystem Sys;
public Props Props;

[Params(10_000)]
public int SpawnCount { get; set; }

[GlobalSetup]
public void Setup()
{
Sys = ActorSystem.Create("Bench");
Props = Props.Create(() => new TempActor());
}

private class TempActor : UntypedActor
{
protected override void OnReceive(object message)
{

}
}

[Benchmark]
public void SpawnActor()
{
for(var i = 0; i < SpawnCount; i++)
Sys.ActorOf(Props);
}

[GlobalCleanup]
public async Task Cleanup()
{
await Sys.Terminate();
}
}
}
22 changes: 19 additions & 3 deletions src/benchmark/Akka.Benchmarks/Actor/ActorPathBenchmarks.cs
Expand Up @@ -16,12 +16,16 @@ public class ActorPathBenchmarks
{
private ActorPath x;
private ActorPath y;
private ActorPath _childPath;
private Address _sysAdr = new Address("akka.tcp", "system", "127.0.0.1", 1337);
private Address _otherAdr = new Address("akka.tcp", "system", "127.0.0.1", 1338);

[GlobalSetup]
public void Setup()
{
x = new RootActorPath(new Address("akka.tcp", "system", "127.0.0.1", 1337), "user");
y = new RootActorPath(new Address("akka.tcp", "system", "127.0.0.1", 1337), "system");
x = new RootActorPath(_sysAdr, "user");
y = new RootActorPath(_sysAdr, "system");
_childPath = x / "parent" / "child";
}

[Benchmark]
Expand All @@ -45,7 +49,19 @@ public bool ActorPath_Equals()
[Benchmark]
public string ActorPath_ToString()
{
return x.ToString();
return _childPath.ToString();
}

[Benchmark]
public string ActorPath_ToSerializationFormat()
{
return _childPath.ToSerializationFormat();
}

[Benchmark]
public string ActorPath_ToSerializationFormatWithAddress()
{
return _childPath.ToSerializationFormatWithAddress(_otherAdr);
}
}
}
34 changes: 34 additions & 0 deletions src/benchmark/Akka.Benchmarks/Actor/NameAndUidBenchmarks.cs
@@ -0,0 +1,34 @@
//-----------------------------------------------------------------------
// <copyright file="NameAndUidBenchmarks.cs" company="Akka.NET Project">
// Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using Akka.Actor;
using Akka.Benchmarks.Configurations;
using BenchmarkDotNet.Attributes;

namespace Akka.Benchmarks.Actor
{
[Config(typeof(MicroBenchmarkConfig))]
public class NameAndUidBenchmarks
{
public const string ActorPath = "foo#11241311";

[Benchmark]
public NameAndUid ActorCell_SplitNameAndUid()
{
return ActorCell.SplitNameAndUid(ActorPath);
}

[Benchmark]
public (string name, int uid) ActorCell_GetNameAndUid()
{
return ActorCell.GetNameAndUid(ActorPath);
}
}
}
2 changes: 1 addition & 1 deletion src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<!-- FluentAssertions is used in some benchmarks to validate internal behaviors -->
Expand Down