Skip to content

Commit

Permalink
Manual: Persistent Actor. #9
Browse files Browse the repository at this point in the history
  • Loading branch information
veblush committed Jun 7, 2016
1 parent 141e8ff commit 73e7f8d
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 2 deletions.
92 changes: 91 additions & 1 deletion docs/PersistentActor.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,94 @@

## PersistentActor

- InterfacedPersistentActor = InterfacedActor + PersistentActor
`UntypedPersistentActor` is the special base class the provides the persistency
to derived actors using eventsourcing.
`InterfacedPersistentActor` is a `UntypedPersistentActor` with features of `InterfacedActor`.

- `InterfacedActor` = the interfaced class of `UntypedActor`
- `InterfacedPersistentActor` = the interfaced class of `UntypedPersistentActor`

If you are not familar `Akka.Persistence`, read [Persistent Actors](http://getakka.net/docs/persistence/persistent-actors)

#### Example

At first, you need to define event classes and a (optional) state class.
The event journal will handle those classes to keep an actor persistent.

```csharp
class GreetEvent { public string Name; }

class GreeterState
{
public int GreetCount;
public int TotalNameLength;

public void OnGreet(GreetEvent e)
{
GreetCount += 1;
TotalNameLength += e.Name.Length;
}
}
```

Define `PersistentGreetingActor` that inherits `InterfacedPersistentActor` and
implements `IGreeter`. This greeting class will be persistent with `OnRecover` method
which recover the actor state from event messages or (optional) snapshot message.

```csharp
class PersistentGreetingActor : InterfacedPersistentActor, IGreeter
{
private GreeterState _state = new GreeterState();

public override string PersistenceId { get; }

public PersistentGreetingActor(string id)
{
PersistenceId = id;
}

[MessageHandler] void OnRecover(SnapshotOffer snapshot)
{
_state = (GreeterState)snapshot.Snapshot;
}

[MessageHandler] void OnRecover(GreetEvent e)
{
_state.OnGreet(e);
}

async Task<string> IGreeter.Greet(string name)
{
var e = new GreetEvent { Name = name };
await PersistTaskAsync(e);
_state.OnGreet(e);
return $"Hello {name}!";
}

Task<int> IGreeter.GetCount()
{
return Task.FromResult(_state.GreetCount);
}
}
```

You can use `PersistentGreetingActor` as a normal greeting actor but the state of
greeting actor is kept when it restarts.

```csharp
// create actor, change state of it, and destroy it.
var a = _system.ActorOf(Props.Create(() => new PersistentGreetingActor("greeter1")));
var g = new GreeterRef(a);
await g.Greet("World");
await g.Greet("Actor");
Console.WriteLine("1st: " + await g.GetCount()); // Output: 1st: 2
await a.GracefulStop(TimeSpan.FromMinutes(1), InterfacedPoisonPill.Instance);

// create actor, and check saved state.
var a2 = _system.ActorOf(Props.Create(() => new PersistentGreetingActor("greeter1")));
var g2 = new GreeterRef(a2);
Console.WriteLine("2nd: " + await g2.GetCount()); // Output: 2nd: 2
await g2.Greet("More");
Console.WriteLine("3rd: " + await g2.GetCount()); // Output: 3rd: 3
await a2.GracefulStop(TimeSpan.FromMinutes(1), InterfacedPoisonPill.Instance);
```
93 changes: 93 additions & 0 deletions samples/Manual/Program/DemoPersistentActor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.Interfaced;
using Akka.Interfaced.Persistence;
using Akka.Persistence;

namespace Manual
{
public class DemoPersistentActor
{
private ActorSystem _system;

public DemoPersistentActor(ActorSystem system, string[] args)
{
_system = system;
}

public class GreetEvent
{
public string Name;
}

public class GreeterState
{
public int GreetCount;
public int TotalNameLength;

public void OnGreet(GreetEvent e)
{
GreetCount += 1;
TotalNameLength += e.Name.Length;
}
}

public class PersistentGreetingActor : InterfacedPersistentActor, IGreeter
{
private GreeterState _state = new GreeterState();

public override string PersistenceId { get; }

public PersistentGreetingActor(string id)
{
PersistenceId = id;
}

[MessageHandler]
private void OnRecover(SnapshotOffer snapshot)
{
_state = (GreeterState)snapshot.Snapshot;
}

[MessageHandler]
private void OnRecover(GreetEvent e)
{
_state.OnGreet(e);
}

async Task<string> IGreeter.Greet(string name)
{
var e = new GreetEvent { Name = name };
await PersistTaskAsync(e);
_state.OnGreet(e);
return $"Hello {name}!";
}

Task<int> IGreeter.GetCount()
{
return Task.FromResult(_state.GreetCount);
}
}

private async Task DemoRun()
{
// create actor, change state of it, and destroy it.
var a = _system.ActorOf(Props.Create(() => new PersistentGreetingActor("greeter1")));
var g = new GreeterRef(a);
await g.Greet("World");
await g.Greet("Actor");
Console.WriteLine("1st: " + await g.GetCount());
await a.GracefulStop(TimeSpan.FromMinutes(1), InterfacedPoisonPill.Instance);

// create actor, and check saved state.
var a2 = _system.ActorOf(Props.Create(() => new PersistentGreetingActor("greeter1")));
var g2 = new GreeterRef(a2);
Console.WriteLine("2nd: " + await g2.GetCount());
await g2.Greet("More");
Console.WriteLine("3rd: " + await g2.GetCount());
await a2.GracefulStop(TimeSpan.FromMinutes(1), InterfacedPoisonPill.Instance);
}
}
}
2 changes: 1 addition & 1 deletion samples/Manual/Program/DemoStartEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ protected override async Task OnStart(bool restarted)
}

[MessageHandler]
protected async Task Handle(string message)
protected Task Handle(string message)
{
Console.WriteLine($"Handle({message}) Throw");
throw new InvalidOperationException(message);
Expand Down
17 changes: 17 additions & 0 deletions samples/Manual/Program/Manual.Program.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
<HintPath>..\..\..\packages\Akka.1.0.8\lib\net45\Akka.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Akka.Persistence, Version=1.0.8.25, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Akka.Persistence.1.0.8.25-beta\lib\net45\Akka.Persistence.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Google.ProtocolBuffers, Version=2.4.1.555, Culture=neutral, PublicKeyToken=55f7125234beb589, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Google.ProtocolBuffers.Serialization, Version=2.4.1.555, Culture=neutral, PublicKeyToken=55f7125234beb589, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.Serialization.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
Expand All @@ -63,6 +75,7 @@
<Compile Include="DemoLogFilter.cs" />
<Compile Include="DemoMessageHandler.cs" />
<Compile Include="DemoObserver.cs" />
<Compile Include="DemoPersistentActor.cs" />
<Compile Include="DemoReentrantHandler.cs" />
<Compile Include="DemoRunTask.cs" />
<Compile Include="DemoStartEvent.cs" />
Expand All @@ -83,6 +96,10 @@
<Project>{5cbda46b-b22c-474a-8b99-e2e855a8bca8}</Project>
<Name>Akka.Interfaced</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\plugins\Akka.Interfaced.Persistence\Akka.Interfaced.Persistence.csproj">
<Project>{6364051d-a683-4239-bbbe-88412a8f4e8b}</Project>
<Name>Akka.Interfaced.Persistence</Name>
</ProjectReference>
<ProjectReference Include="..\Interface\Manual.Interface.csproj">
<Project>{9eecbf54-7b2d-4a41-bbde-cbfbb344a142}</Project>
<Name>Manual.Interface</Name>
Expand Down
2 changes: 2 additions & 0 deletions samples/Manual/Program/packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Akka" version="1.0.8" targetFramework="net452" />
<package id="Akka.Persistence" version="1.0.8.25-beta" targetFramework="net452" />
<package id="Google.ProtocolBuffers" version="2.4.1.555" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net452" />
<package id="StyleCop.Analyzers" version="1.0.0" targetFramework="net452" developmentDependency="true" />
<package id="System.Collections.Immutable" version="1.1.36" targetFramework="net452" />
Expand Down

0 comments on commit 73e7f8d

Please sign in to comment.