-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Docs: defining Akka.Analyzer rules (#7039)
* defining Akka.Analyzer rules These will be shown from the tooltips inside Visual Studio whenever an error is flagged. * attempting to debug Akka.Persistence.TestKit.Tests * Revert "attempting to debug Akka.Persistence.TestKit.Tests" This reverts commit df57633. * added AK1001.md page * added `AK2000` * added links to all rules to index * added TOC for rules * fixed markdown linting issues
- Loading branch information
1 parent
0486f97
commit a2c0df4
Showing
8 changed files
with
204 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
uid: akka-analyzers | ||
title: Akka.Analyzers - Roslyn Analyzers and Code Fixes for Akka.NET | ||
--- | ||
|
||
# Akka.Analyzers | ||
|
||
As of Akka.NET v1.5.15 we now include [Akka.Analyzers](https://github.com/akkadotnet/akka.analyzers) as a package dependency for the core Akka library, which means any projects that reference anything depending on [`Akka`](https://www.nuget.org/packages/Akka) will automatically pull in all of Akka.Analyzer's rules and code fixes. | ||
|
||
Akka.Analyzer is a [Roslyn Analysis and Code Fix](https://learn.microsoft.com/en-us/visualstudio/extensibility/getting-started-with-roslyn-analyzers) package, which means that it leverages the .NET compiler platform ("Roslyn") to detect Akka.NET-specific anti-patterns and problems during _compilation_, rather than at run-time. | ||
|
||
## Supported Rules | ||
|
||
| Id | Title | Severity | Category | | ||
|--------|--------------------------------------------------------|----------|--------------| | ||
| [AK1000](xref:AK1000) | Do not use `new` to create actors. | Error | Actor Design | | ||
| [AK1001](xref:AK1001) | Should always close over `Sender` when using `PipeTo`. | Error | Actor Design | | ||
| [AK2000](xref:AK2000) | Do not use `Ask` with `TimeSpan.Zero` for timeout. | Error | API Usage | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
--- | ||
uid: AK1000 | ||
title: Akka.Analyzers Rule AK1000 - "Do not use `new` to create actors" | ||
--- | ||
|
||
# AK1000 - Error | ||
|
||
Do not use the `new` keyword to create Akka.NET actors. | ||
|
||
## Cause | ||
|
||
A class inheriting from [`Akka.Actor.ActorBase`](xref:Akka.Actor.ActorBase) or one of its descendants was instantiated directly via the `new` keyword, which is illegal. Actors can only be instantiated inside a [`Props.Create` method](xref:Akka.Actor.Props) or via an [`IIndirectActorProducer`](xref:Akka.Actor.IIndirectActorProducer) implementation (such as [`Akka.DependencyInjection`](xref:dependency-injection).) | ||
|
||
An example: | ||
|
||
```csharp | ||
using Akka.Actor; | ||
|
||
class MyActor : ActorBase { | ||
protected override bool Receive(object message) { | ||
return true; | ||
} | ||
} | ||
|
||
class Test | ||
{ | ||
void Method() | ||
{ | ||
MyActor actorInstance = new MyActor(); // not supported by Akka.NET | ||
} | ||
} | ||
``` | ||
|
||
## Resolution | ||
|
||
The correct way to get a reference back to an actor is to wrap your actor's constructor call inside a `Props.Create` method and then pass that into either [`ActorSystem.ActorOf`](xref:Akka.Actor.ActorSystem) (when creating a top-level actor) or [`Context.ActorOf`](xref:Akka.Actor.IActorRefFactory#Akka_Actor_IActorRefFactory_ActorOf_Akka_Actor_Props_System_String_) (when creating a child actor.) | ||
|
||
Here's an example below: | ||
|
||
```csharp | ||
using Akka.Actor; | ||
|
||
class MyActor : ReceiveActor { | ||
private readonly string _name; | ||
private readonly int _myVar; | ||
|
||
public MyActor(string name, int myVar){ | ||
_name = name; | ||
_myVar = myVar; | ||
ReceiveAny(_ => { | ||
Sender.Tell(_name + _myVar); | ||
}); | ||
} | ||
} | ||
|
||
class Test | ||
{ | ||
void Method() | ||
{ | ||
// obviously, don't create a new ActorSystem every time - this is just an example. | ||
ActorSystem sys = ActorSystem.Create("MySys"); | ||
Akka.Actor.Props props = Akka.Actor.Props.Create(() => new MyActor("foo", 1)); | ||
IActorRef realActorInstance = sys.ActorOf(props); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
--- | ||
uid: AK1001 | ||
title: Akka.Analyzers Rule AK1001 - "Should always close over `Sender` when using `PipeTo`" | ||
--- | ||
|
||
# AK1000 - Error | ||
|
||
You should always close over [`Context.Sender`](xref:Akka.Actor.IActorContext#Akka_Actor_IActorContext_Sender) when using [`PipeTo`](xref:Akka.Actor.PipeToSupport#Akka_Actor_PipeToSupport_PipeTo_System_Threading_Tasks_Task_Akka_Actor_ICanTell_Akka_Actor_IActorRef_System_Func_System_Object__System_Func_System_Exception_System_Object__) | ||
|
||
## Cause | ||
|
||
When using `PipeTo`, you must always close over `Sender` to ensure that the actor's `Sender` property is captured at the time you're scheduling the `PipeTo`, as this value may change asynchronously. | ||
|
||
This is a concurrent programming problem: `PipeTo` will be evaluated and executed at some point in the future because it's an asynchronous continuation, therefore the `Context.Sender` property, which is _mutable and changes each time the original actor processes a message_, may change. | ||
|
||
An example: | ||
|
||
```csharp | ||
using Akka.Actor; | ||
using System.Threading.Tasks; | ||
using System; | ||
|
||
public sealed class MyActor : UntypedActor{ | ||
|
||
protected override void OnReceive(object message){ | ||
async Task<int> LocalFunction(){ | ||
await Task.Delay(10); | ||
return message.ToString().Length; | ||
} | ||
|
||
// potentially unsafe use of Context.Sender | ||
LocalFunction().PipeTo(Sender); | ||
} | ||
} | ||
``` | ||
|
||
## Resolution | ||
|
||
To avoid this entire category of problem, we should close over the `Context.Sender` property in a local variable. | ||
|
||
Here's an example below: | ||
|
||
```csharp | ||
using Akka.Actor; | ||
using System.Threading.Tasks; | ||
using System; | ||
|
||
public sealed class MyActor : UntypedActor{ | ||
|
||
protected override void OnReceive(object message){ | ||
async Task<int> LocalFunction(){ | ||
await Task.Delay(10); | ||
return message.ToString().Length; | ||
} | ||
|
||
var sender = Sender; | ||
LocalFunction().PipeTo(sender); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
--- | ||
uid: AK2000 | ||
title: Akka.Analyzers Rule AK2000 - "Do not use `Ask` with `TimeSpan.Zero` for timeout." | ||
--- | ||
|
||
# AK2000 - Error | ||
|
||
Do not use [`Ask<T>`](xref:Akka.Actor.Futures#Akka_Actor_Futures_Ask__1_Akka_Actor_ICanTell_System_Object_System_Nullable_System_TimeSpan__) or [`Ask`](xref:Akka.Actor.Futures#Akka_Actor_Futures_Ask_Akka_Actor_ICanTell_System_Object_System_Nullable_System_TimeSpan__) with `TimeSpan.Zero` for timeout. | ||
|
||
## Cause | ||
|
||
When using `Ask`, you must always specify a timeout value greater than `TimeSpan.Zero` otherwise the process might deadlock. See [https://github.com/akkadotnet/akka.net/issues/6131](https://github.com/akkadotnet/akka.net/issues/6131) for details. | ||
|
||
> [!IMPORTANT] | ||
> This rule is not exhaustive - Roslyn can't scan every possible variable value at compilation time, so it's still possible to pass in a `TimeSpan.Zero` value even with this rule present. | ||
An example: | ||
|
||
```csharp | ||
using Akka.Actor; | ||
using System.Threading.Tasks; | ||
using System; | ||
|
||
public static class MyActorCaller{ | ||
public static Task<string> Call(IActorRef actor){ | ||
return actor.Ask<string>(""hello"", TimeSpan.Zero); | ||
} | ||
} | ||
``` | ||
|
||
## Resolution | ||
|
||
The right way to fix this issue is to pass in a non-zero value or to use the `Ask<T>` overload that accepts a `CancellationToken`. | ||
|
||
Here's an example below: | ||
|
||
```csharp | ||
using Akka.Actor; | ||
using System.Threading.Tasks; | ||
using System; | ||
|
||
public static class MyActorCaller{ | ||
public static Task<string> Call(IActorRef actor){ | ||
return actor.Ask<string>(""hello"", TimeSpan.FromSeconds(1)); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
- name: AK1000 | ||
href: AK1000.md | ||
- name: AK1001 | ||
href: AK1001.md | ||
- name: AK2000 | ||
href: AK2000.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
- name: Akka.NET Roslyn Analyzers | ||
href: akka-analyzers.md | ||
- name: Analysis Rules | ||
href: rules/toc.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters