Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
8e82a50
Updated actors prototypes
WhitWaldo Jun 30, 2026
c6249f7
Updated dapr.proto as well to include actor service changes
WhitWaldo Jun 30, 2026
999a1ee
Added methods to serializer to skip a serialization hop and convert d…
WhitWaldo Jul 3, 2026
08e435b
Tweaks to support app port/channel specifics
WhitWaldo Jul 3, 2026
f6c2b65
Added settings file for code coverage configuration
WhitWaldo Jul 3, 2026
9fa2d4e
Added benchmarking for Dapr.Actors.Next implementation (validates .NE…
WhitWaldo Jul 3, 2026
0b4b77e
Base implementation of the Dapr.Actors.Next SDK to provide a wire-con…
WhitWaldo Jul 3, 2026
a7f65bc
Adding support for one-line PubSub streaming subscription support. By…
WhitWaldo Jul 3, 2026
178d1a3
Adds a broad collection of analyzers that automatically identify unsa…
WhitWaldo Jul 3, 2026
3fe7c65
Testing Dapr actors has historically been very difficult due to the p…
WhitWaldo Jul 3, 2026
01c3f91
Workflows are great for running stateful, multi-step operations, but …
WhitWaldo Jul 3, 2026
a3522ec
This is a twist on the state machine concept paired with a dynamic au…
WhitWaldo Jul 3, 2026
9b07e9d
Adding reference to Dapr.Actors.Benchmarks from `Dapr.Actors` project
WhitWaldo Jul 3, 2026
80724db
Building all the `Dapr.Actors.Next` projects into a single shared ass…
WhitWaldo Jul 3, 2026
a776b92
Adding unit, integration and smoke testing (for the meta package gene…
WhitWaldo Jul 3, 2026
1eb05c5
Implemented example/tutorial project to show a basic actor implementa…
WhitWaldo Jul 3, 2026
60f56a0
Added example/tutorial project to demonstrate how state migration wor…
WhitWaldo Jul 3, 2026
a1b615f
Updated to honor configured web JSON options in the generic fast path
WhitWaldo Jul 3, 2026
da3de11
Route extraction now matching payload properties case-insensitively
WhitWaldo Jul 3, 2026
3db858b
Test to validate that route extraction is no longer case-sensitive
WhitWaldo Jul 3, 2026
75d8d81
Renaming and setting new purpose to example project #5
WhitWaldo Jul 3, 2026
343efda
Update serialization tests
WhitWaldo Jul 3, 2026
8e50ee4
Added example/tutorial project to demonstrate using actors with a sub…
WhitWaldo Jul 3, 2026
31e2d9f
Updated XML documentation to warn against using ActorProxy.Create/Con…
WhitWaldo Jul 3, 2026
7f93668
Updated reentrancy implementation so it's automatically applied in th…
WhitWaldo Jul 3, 2026
edb27fe
Added tutorial/example project demonstrating a state machine actor il…
WhitWaldo Jul 3, 2026
e720fc6
Where example 4 demonstrated a state machine built out in the actor l…
WhitWaldo Jul 3, 2026
0db7452
Fixing stale unit test
WhitWaldo Jul 3, 2026
f327315
Updated to allow multiple actors to implement the same interface, but…
WhitWaldo Jul 3, 2026
69e6553
Patch for smoke test project CI failure
WhitWaldo Jul 3, 2026
b910c38
Applying attribute so Dapr.Actors.Next tests run only on 1.18 or late…
WhitWaldo Jul 3, 2026
ce30e1b
Addressing smoke test failure in GitHub
WhitWaldo Jul 4, 2026
402ec7e
Implementing overhaul to actor state migration using source generator…
WhitWaldo Jul 6, 2026
2de6286
Adding updated benchmarking logic to disable actor state migration ch…
WhitWaldo Jul 6, 2026
80edf95
Added mechanism to force an actor to flush its cache (including with …
WhitWaldo Jul 6, 2026
3aef578
Added information-level analyzer that fires when an actor reads or wr…
WhitWaldo Jul 6, 2026
469e5a7
Adding missing flush state options
WhitWaldo Jul 6, 2026
2c71f72
Adding analyzer to automatically flag the a type decorated with `[Dap…
WhitWaldo Jul 6, 2026
2be5dea
Fleshed out reminder and timer APIs + tests
WhitWaldo Jul 6, 2026
c3e3a21
Adding get, list and cancel support for reminders
WhitWaldo Jul 6, 2026
fdb9688
Excluding the Dapr.Actors.Next test projects from CI example-build te…
WhitWaldo Jul 6, 2026
5aebf87
Narrowing old integration tests to build only the few packages it's a…
WhitWaldo Jul 6, 2026
64cfed6
Removing tests project outputs to fix CI build
WhitWaldo Jul 7, 2026
b71f690
Added mechanism to the state machine to deliberately reinitialize it …
WhitWaldo Jul 8, 2026
25edede
Corrected name of test to clarify it's using timers, not a reminder
WhitWaldo Jul 8, 2026
40fe5ed
Added another example to elaborate on example 5's use of dynamic stat…
WhitWaldo Jul 8, 2026
0ccfcd8
Added generator support so that actors can implement one or more inte…
WhitWaldo Jul 8, 2026
c9fa5a1
Added more overload options to registering timers and reminders to su…
WhitWaldo Jul 10, 2026
dd33b03
Renamed Flush to Evict for state management purposes. Added method fo…
WhitWaldo Jul 10, 2026
8386bf3
Adding analyzer to validate that the callback method specified in a r…
WhitWaldo Jul 10, 2026
0dfee55
Adding Dapr copyright header to all .cs files
WhitWaldo Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 17 additions & 3 deletions .github/workflows/itests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,24 @@ jobs:
dotnet-version: '10.0.x'
dotnet-quality: 'ga'
- name: Restore dependencies
run: dotnet restore
run: |
dotnet restore ${{ github.workspace }}/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj
dotnet restore ${{ github.workspace }}/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj
- name: Build
# disable deterministic builds, just for test run. Deterministic builds break coverage for some reason
run: dotnet build --configuration release --no-restore /p:GITHUB_ACTIONS=false
# Disable deterministic builds, just for test run. Deterministic builds break coverage for some reason.
# Build only the projects this workflow executes; the full solution includes example test projects
# that are covered by other CI jobs and should not participate in this integration-test workflow.
run: |
dotnet build ${{ github.workspace }}/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj \
--configuration Release \
--framework ${{ matrix.framework }} \
--no-restore \
/p:GITHUB_ACTIONS=false
dotnet build ${{ github.workspace }}/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj \
--configuration Release \
--framework ${{ matrix.framework }} \
--no-restore \
/p:GITHUB_ACTIONS=false
- name: Run General Integration Tests
id: tests
continue-on-error: true # proceed if tests fail, the report step will report the failure with more details.
Expand Down
21 changes: 21 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Deterministic>true</Deterministic>
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>

<PropertyGroup Condition="'$(MSBuildProjectDirectory.Contains(`\src\`))' == 'true' Or '$(MSBuildProjectDirectory.EndsWith(`\src`))' == 'true'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<PropertyGroup Condition="'$(DaprActorsNextEnableTrimAotAnalysis)' == 'true'">
<IsAotCompatible>true</IsAotCompatible>
<IsTrimmable>true</IsTrimmable>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
</PropertyGroup>
</Project>
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="5.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
Expand Down
536 changes: 536 additions & 0 deletions all.sln

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions benchmarks/Dapr.Actors.Benchmarks/ActorPackage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Dapr.Actors.Benchmarks;

public enum ActorPackage
{
DaprActors,
DaprActorsNext,
}
22 changes: 22 additions & 0 deletions benchmarks/Dapr.Actors.Benchmarks/ActorsNextAotConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Toolchains.NativeAot;

namespace Dapr.Actors.Benchmarks;

public sealed class ActorsNextAotConfig : ManualConfig
{
public ActorsNextAotConfig()
{
AddJob(Job.ShortRun
.WithRuntime(CoreRuntime.Core10_0)
.WithId(".NET 10 JIT")
.AsBaseline());

AddJob(Job.ShortRun
.WithRuntime(NativeAotRuntime.Net10_0)
.WithToolchain(NativeAotToolchain.Net10_0)
.WithId(".NET 10 Native AOT"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.DependencyInjection;

namespace Dapr.Actors.Benchmarks;

[Config(typeof(ActorsNextAotConfig))]
[MemoryDiagnoser]
public class ActorsNextAotInvocationBenchmarks
{
private const int ParallelCalls = 32;
private ServiceProvider provider = null!;
private int nextActorId;

[GlobalSetup]
public async Task Setup()
{
provider = BenchmarkHostFactory.CreateActorsNextServiceProvider(actorTypes: 1, useAotSerializer: true);
await BenchmarkHostFactory.InvokeAsync(provider, ActorPackage.DaprActorsNext, "warm").ConfigureAwait(false);
}

[GlobalCleanup]
public void Cleanup()
{
provider.Dispose();
}

[Benchmark]
public Task<int> WarmActorCall()
{
return BenchmarkHostFactory.InvokeAsync(provider, ActorPackage.DaprActorsNext, "warm");
}

[Benchmark]
public Task<int> ColdActorActivationCall()
{
var id = Interlocked.Increment(ref nextActorId).ToString();
return BenchmarkHostFactory.InvokeAsync(provider, ActorPackage.DaprActorsNext, id);
}

[Benchmark]
public async Task<int> ParallelFanOut()
{
var start = Interlocked.Add(ref nextActorId, ParallelCalls);
var tasks = new Task<int>[ParallelCalls];
for (var i = 0; i < tasks.Length; i++)
{
tasks[i] = BenchmarkHostFactory.InvokeAsync(provider, ActorPackage.DaprActorsNext, "aot-parallel-" + (start - i));
}

var results = await Task.WhenAll(tasks).ConfigureAwait(false);
return results.Sum();
}
}
104 changes: 104 additions & 0 deletions benchmarks/Dapr.Actors.Benchmarks/BenchmarkActors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using Dapr.Actors.Next.Abstractions.Attributes;
using Dapr.Actors.Next.Abstractions.State;
using Dapr.Actors.Next.Core.Activation;
using LegacyActor = Dapr.Actors.Runtime.Actor;
using LegacyActorHost = Dapr.Actors.Runtime.ActorHost;
using LegacyIActor = Dapr.Actors.IActor;
using NextActor = Dapr.Actors.Next.Abstractions.Actor;
using NextActorId = Dapr.Actors.Next.Abstractions.ActorId;
using NextIActor = Dapr.Actors.Next.Abstractions.IActor;

namespace Dapr.Actors.Benchmarks;

public interface ILegacyBenchmarkActor : LegacyIActor
{
Task<int> AddAsync(int value);
}

public interface INextBenchmarkActor : NextIActor
{
Task<int> AddAsync(int value, CancellationToken cancellationToken = default);
}

[GenerateActorClient]
public interface INextBenchmarkActor01 : INextBenchmarkActor;

[GenerateActorClient]
public interface INextBenchmarkActor02 : INextBenchmarkActor;

[GenerateActorClient]
public interface INextBenchmarkActor03 : INextBenchmarkActor;

[GenerateActorClient]
public interface INextBenchmarkActor04 : INextBenchmarkActor;

[GenerateActorClient]
public interface INextBenchmarkActor05 : INextBenchmarkActor;

[GenerateActorClient]
public interface INextBenchmarkActor06 : INextBenchmarkActor;

[GenerateActorClient]
public interface INextBenchmarkActor07 : INextBenchmarkActor;

[GenerateActorClient]
public interface INextBenchmarkActor08 : INextBenchmarkActor;

public abstract class LegacyBenchmarkActor(LegacyActorHost host) : LegacyActor(host), ILegacyBenchmarkActor
{
private int value;

public Task<int> AddAsync(int value)
{
this.value += value;
return Task.FromResult(this.value);
}
}

public abstract class NextBenchmarkActor(ActorActivationContext context) : NextActor
{
private int value;

protected override NextActorId Id => context.ActorId;

protected override IActorStateAccessor State => context.State;

public Task<int> AddAsync(int value, CancellationToken cancellationToken = default)
{
this.value += value;
return Task.FromResult(this.value);
}
}

public sealed class LegacyBenchmarkActor01(LegacyActorHost host) : LegacyBenchmarkActor(host);
public sealed class LegacyBenchmarkActor02(LegacyActorHost host) : LegacyBenchmarkActor(host);
public sealed class LegacyBenchmarkActor03(LegacyActorHost host) : LegacyBenchmarkActor(host);
public sealed class LegacyBenchmarkActor04(LegacyActorHost host) : LegacyBenchmarkActor(host);
public sealed class LegacyBenchmarkActor05(LegacyActorHost host) : LegacyBenchmarkActor(host);
public sealed class LegacyBenchmarkActor06(LegacyActorHost host) : LegacyBenchmarkActor(host);
public sealed class LegacyBenchmarkActor07(LegacyActorHost host) : LegacyBenchmarkActor(host);
public sealed class LegacyBenchmarkActor08(LegacyActorHost host) : LegacyBenchmarkActor(host);

[DaprActor("BenchmarkActor01")]
public sealed class NextBenchmarkActor01(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor01;

[DaprActor("BenchmarkActor02")]
public sealed class NextBenchmarkActor02(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor02;

[DaprActor("BenchmarkActor03")]
public sealed class NextBenchmarkActor03(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor03;

[DaprActor("BenchmarkActor04")]
public sealed class NextBenchmarkActor04(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor04;

[DaprActor("BenchmarkActor05")]
public sealed class NextBenchmarkActor05(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor05;

[DaprActor("BenchmarkActor06")]
public sealed class NextBenchmarkActor06(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor06;

[DaprActor("BenchmarkActor07")]
public sealed class NextBenchmarkActor07(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor07;

[DaprActor("BenchmarkActor08")]
public sealed class NextBenchmarkActor08(ActorActivationContext context) : NextBenchmarkActor(context), INextBenchmarkActor08;
46 changes: 46 additions & 0 deletions benchmarks/Dapr.Actors.Benchmarks/BenchmarkAotSerializers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapr.Actors.Next.Core.Serialization;
using Dapr.Common.Serialization;

namespace Dapr.Actors.Benchmarks;

public sealed class BenchmarkAotWireSerializer : IActorWireSerializer
{
public byte[] JsonToBytes(string? json) =>
string.IsNullOrEmpty(json) ? [] : Encoding.UTF8.GetBytes(json);

public string? BytesToJson(ReadOnlyMemory<byte> bytes) =>
bytes.IsEmpty ? null : Encoding.UTF8.GetString(bytes.Span);

public byte[] SerializeToBytes<T>(T value) =>
JsonSerializer.SerializeToUtf8Bytes(value, typeof(T), BenchmarkAotJsonContext.Default);

public T? DeserializeFromBytes<T>(ReadOnlyMemory<byte> bytes) =>
bytes.IsEmpty ? default : (T?)JsonSerializer.Deserialize(bytes.Span, typeof(T), BenchmarkAotJsonContext.Default);
}

public sealed class BenchmarkAotDaprSerializer : IDaprSerializer
{
public string Serialize<T>(T value) =>
JsonSerializer.Serialize(value, typeof(T), BenchmarkAotJsonContext.Default);

[RequiresUnreferencedCode("Runtime-type serialization is not used by this AOT benchmark.")]
[RequiresDynamicCode("Runtime-type serialization is not used by this AOT benchmark.")]
public string Serialize(object? value, Type? inputType = null) =>
JsonSerializer.Serialize(value, inputType ?? value?.GetType() ?? typeof(object), BenchmarkAotJsonContext.Default);

public T? Deserialize<T>(string? data) =>
string.IsNullOrEmpty(data) ? default : (T?)JsonSerializer.Deserialize(data, typeof(T), BenchmarkAotJsonContext.Default);

[RequiresUnreferencedCode("Runtime-type deserialization is not used by this AOT benchmark.")]
[RequiresDynamicCode("Runtime-type deserialization is not used by this AOT benchmark.")]
public object? Deserialize(string? data, Type returnType) =>
string.IsNullOrEmpty(data) ? null : JsonSerializer.Deserialize(data, returnType, BenchmarkAotJsonContext.Default);
}

[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(object))]
internal sealed partial class BenchmarkAotJsonContext : JsonSerializerContext;
Loading
Loading