Skip to content

Commit

Permalink
Merge pull request #226 from petabridge/dev
Browse files Browse the repository at this point in the history
0.9.0 Release
  • Loading branch information
Aaronontheweb authored Jul 21, 2022
2 parents 4ab138d + f099e17 commit a2a5dc6
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 8 deletions.
10 changes: 10 additions & 0 deletions Akka.Persistence.Azure.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{320BFA6C
build.sh = build.sh
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Azure.Hosting", "src\Akka.Persistence.Azure.Hosting\Akka.Persistence.Azure.Hosting.csproj", "{64C6B877-9262-456B-8A1C-60C4F272DA19}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -35,6 +37,14 @@ Global
{CAE7CA7C-0D0C-4FDA-BDE9-BE16A27343EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAE7CA7C-0D0C-4FDA-BDE9-BE16A27343EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAE7CA7C-0D0C-4FDA-BDE9-BE16A27343EF}.Release|Any CPU.Build.0 = Release|Any CPU
{FE4C3232-1DB9-40E5-B0BE-BD70ACB4C1F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE4C3232-1DB9-40E5-B0BE-BD70ACB4C1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE4C3232-1DB9-40E5-B0BE-BD70ACB4C1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE4C3232-1DB9-40E5-B0BE-BD70ACB4C1F3}.Release|Any CPU.Build.0 = Release|Any CPU
{64C6B877-9262-456B-8A1C-60C4F272DA19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64C6B877-9262-456B-8A1C-60C4F272DA19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64C6B877-9262-456B-8A1C-60C4F272DA19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64C6B877-9262-456B-8A1C-60C4F272DA19}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,47 @@ Akka.Persistence implementation that uses Windows Azure table and blob storage.

## Configuration

### Easy Mode: Using Akka.Hosting

[Akka.Hosting](https://github.com/akkadotnet/Akka.Hosting) can make configuring Akka.Persistence.Azure trivially easy and HOCON-less.

First, install the `Akka.Persistence.Azure.Hosting` NuGet package:

```shell
PS> install-package Akka.Persistence.Azure.Hosting

```

Next, add the `WithAzurePersistence` method calls to your `AkkaConfigurationBuilder` (from Akka.Hosting):

```csharp
var conn = Environment.GetEnvironmentVariable("AZURE_CONNECTION_STR");
var host = new HostBuilder()
.ConfigureServices(collection =>
{
collection.AddAkka("MyActorSys", builder =>
{
// enables both journal and snapshot store
builder.WithAzurePersistence(conn);
builder.StartActors((system, registry) =>
{
var myActor = system.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1");
registry.Register<MyPersistenceActor>(myActor);
});
});
}).Build();

await host.StartAsync();
return host;
```

You can also call the following methods to activate the journal / snapshot stores independently:

* ` WithAzureTableJournal`
* `WithAzureBlobsSnapshotStore`

### Custom Mode: HOCON

Here is a default configuration used by this plugin: https://github.com/petabridge/Akka.Persistence.Azure/blob/dev/src/Akka.Persistence.Azure/reference.conf

You will need to provide connection string and Azure Table name for journal, and connection string with container name for Azure Blob Store:
Expand Down
38 changes: 38 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
#### 0.9.0 July 21 2022 ####
Added [Akka.Hosting](https://github.com/akkadotnet/Akka.Hosting) support to Akka.Persistence.Azure, which you can activate via the following:

First, install the `Akka.Persistence.Azure.Hosting` NuGet package:

```shell
PS> install-package Akka.Persistence.Azure.Hosting

```

Next, add the `WithAzurePersistence` method calls to your `AkkaConfigurationBuilder` (from Akka.Hosting):

```csharp
var conn = Environment.GetEnvironmentVariable("AZURE_CONNECTION_STR");
var host = new HostBuilder()
.ConfigureServices(collection =>
{
collection.AddAkka("MyActorSys", builder =>
{
// enables both journal and snapshot store
builder.WithAzurePersistence(conn);
builder.StartActors((system, registry) =>
{
var myActor = system.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1");
registry.Register<MyPersistenceActor>(myActor);
});
});
}).Build();

await host.StartAsync();
return host;
```

You can also call the following methods to activate the journal / snapshot stores independently:

* ` WithAzureTableJournal`
* `WithAzureBlobsSnapshotStore`

#### 0.8.4 June 2 2022 ####
* Upgraded to [Akka.NET 1.4.39](https://github.com/akkadotnet/akka.net/releases/tag/1.4.39)
* [Update Azure.Identity to 1.6.0](https://github.com/petabridge/Akka.Persistence.Azure/pull/205)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\common.props" />
<PropertyGroup>
<TargetFramework>$(NetStandardLibVersion)</TargetFramework>
<Description>Akka.Hosting support for Akka.Persistence.Azure.</Description>
<LangVersion>8.0</LangVersion>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Akka.Persistence.Azure\Akka.Persistence.Azure.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Akka.Persistence.Hosting" Version="0.4.1" />
</ItemGroup>

</Project>
102 changes: 102 additions & 0 deletions src/Akka.Persistence.Azure.Hosting/AzurePersistenceExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using Akka.Actor;
using Akka.Configuration;
using Akka.Hosting;
using Akka.Persistence.Azure.Query;
using Akka.Persistence.Hosting;

namespace Akka.Persistence.Azure.Hosting
{
/// <summary>
/// Extension methods for Akka.Hosting and Akka.Azure.Persistence
/// </summary>
public static class AzurePersistenceExtensions
{
public const string DefaultTableName = "AkkaPersistenceDefaultTable";
public const string DefaultBlobContainerName = "akka-persistence-default-container";

private static string ToHocon(bool b)
{
return b ? "on" : "off";
}


public static AkkaConfigurationBuilder WithAzureTableJournal(this AkkaConfigurationBuilder builder,
string connectionString, bool autoInitialize = true, string tableName = DefaultTableName, Action<AkkaPersistenceJournalBuilder> configurator = null)
{
Config journalConfiguration = @$"
akka.persistence {{
journal {{
plugin = ""akka.persistence.journal.azure-table""
azure-table {{
class = ""Akka.Persistence.Azure.Journal.AzureTableStorageJournal, Akka.Persistence.Azure""
connection-string = ""{connectionString}""
# the name of the Windows Azure Table used to persist journal events
table-name = ""{tableName}""
auto-initialize = {ToHocon(autoInitialize)}
}}
}}
}}";

var finalConfig = journalConfiguration;
builder.AddHocon(finalConfig, HoconAddMode.Prepend);

// PUSH DEFAULT CONFIG TO END
builder.AddHocon(AzurePersistence.DefaultConfig, HoconAddMode.Append);

if (configurator != null) // configure event adapters
{
builder.WithJournal("azure-table", configurator);
}

return builder;
}

public static AkkaConfigurationBuilder WithAzureBlobsSnapshotStore(this AkkaConfigurationBuilder builder,
string connectionString, bool autoInitialize = true, string containerName = DefaultBlobContainerName)
{
Config journalConfiguration = @$"
akka.persistence {{
snapshot-store {{
plugin = ""akka.persistence.snapshot-store.azure-blob-store""
azure-blob-store {{
class = ""Akka.Persistence.Azure.Snapshot.AzureBlobSnapshotStore, Akka.Persistence.Azure""
connection-string = ""{connectionString}""
# the name of the Windows Azure Table used to persist journal events
container-name = ""{containerName}""
auto-initialize = {ToHocon(autoInitialize)}
}}
}}
}}";

var finalConfig = journalConfiguration;
builder.AddHocon(finalConfig, HoconAddMode.Prepend);

// PUSH DEFAULT CONFIG TO END
builder.AddHocon(AzurePersistence.DefaultConfig, HoconAddMode.Append);

return builder;
}

/// <summary>
/// Adds both AzureTableStorage journal and AzureBlobStorage snapshot-store as the default Akka.Persistence
/// implementations for a given <see cref="ActorSystem"/>.
/// </summary>
/// <param name="builder"></param>
/// <param name="connectionString"></param>
/// <param name="autoInitialize"></param>
/// <param name="containerName"></param>
/// <param name="tableName"></param>
/// <param name="configurator"></param>
/// <returns></returns>
public static AkkaConfigurationBuilder WithAzurePersistence(this AkkaConfigurationBuilder builder,
string connectionString, bool autoInitialize = true, string containerName = DefaultBlobContainerName,
string tableName = DefaultTableName, Action<AkkaPersistenceJournalBuilder> configurator = null)
{
builder.WithAzureTableJournal(connectionString, autoInitialize, tableName, configurator);
builder.WithAzureBlobsSnapshotStore(connectionString, autoInitialize, containerName);

return builder;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="Akka.Persistence.TCK" Version="$(AkkaVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="Docker.DotNet" Version="3.125.5" />
<PackageReference Include="Docker.DotNet" Version="3.125.10" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
Expand All @@ -16,6 +16,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Akka.Persistence.Azure.Hosting\Akka.Persistence.Azure.Hosting.csproj" />
<ProjectReference Include="..\Akka.Persistence.Azure\Akka.Persistence.Azure.csproj" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.Event;
using Akka.Hosting;
using Akka.Persistence.Azure.Hosting;
using Akka.TestKit.Xunit2.Internals;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;
using Xunit.Abstractions;

namespace Akka.Persistence.Azure.Tests.Hosting
{
public class AzurePersistenceHostingSanityCheck
{
public static async Task<IHost> StartHost(Action<AkkaConfigurationBuilder> testSetup)
{
var conn = Environment.GetEnvironmentVariable("AZURE_CONNECTION_STR");
var host = new HostBuilder()
.ConfigureServices(collection =>
{
collection.AddAkka("MyActorSys", builder =>
{
builder.WithAzurePersistence(conn);
testSetup(builder);
});
}).Build();

await host.StartAsync();
return host;
}

public sealed class MyPersistenceActor : ReceivePersistentActor
{
private List<int> _values = new List<int>();

public MyPersistenceActor(string persistenceId)
{
PersistenceId = persistenceId;

Recover<SnapshotOffer>(offer =>
{
if (offer.Snapshot is IEnumerable<int> ints)
{
_values = new List<int>(ints);
}
});

Recover<int>(i => { _values.Add(i); });

Command<int>(i =>
{
Persist(i, i1 =>
{
_values.Add(i);
if (LastSequenceNr % 2 == 0)
{
SaveSnapshot(_values);
}
Sender.Tell("ACK");
});
});

Command<string>(str => str.Equals("getall"), s => { Sender.Tell(_values.ToArray()); });

Command<SaveSnapshotSuccess>(s => { });
}

public override string PersistenceId { get; }
}

private readonly ITestOutputHelper _output;

public AzurePersistenceHostingSanityCheck(ITestOutputHelper output)
{
_output = output;
}

[Fact]
public async Task ShouldLaunchAzurePersistence()
{
// arrange
using var host = await StartHost(builder => {
builder.StartActors((system, registry) =>
{
var myActor = system.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1");
registry.Register<MyPersistenceActor>(myActor);
})
.WithActors((system, registry) =>
{
var extSystem = (ExtendedActorSystem)system;
var logger = extSystem.SystemActorOf(Props.Create(() => new TestOutputLogger(_output)), "log-test");
logger.Tell(new InitializeLogger(system.EventStream));
});
});

var actorSystem = host.Services.GetRequiredService<ActorSystem>();
var actorRegistry = host.Services.GetRequiredService<ActorRegistry>();
var myPersistentActor = actorRegistry.Get<MyPersistenceActor>();

// act
var resp1 = await myPersistentActor.Ask<string>(1, TimeSpan.FromSeconds(3));
var resp2 = await myPersistentActor.Ask<string>(2, TimeSpan.FromSeconds(3));
var snapshot = await myPersistentActor.Ask<int[]>("getall", TimeSpan.FromSeconds(3));

// assert
snapshot.Should().BeEquivalentTo(new[] {1, 2});

// kill + recreate actor with same PersistentId
await myPersistentActor.GracefulStop(TimeSpan.FromSeconds(3));
var myPersistentActor2 = actorSystem.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1a");

var snapshot2 = await myPersistentActor2.Ask<int[]>("getall", TimeSpan.FromSeconds(3));
snapshot2.Should().BeEquivalentTo(new[] {1, 2});

// validate configs
var config = actorSystem.Settings.Config;
config.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.azure-table");
config.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.azure-blob-store");
}
}
}
2 changes: 1 addition & 1 deletion src/Akka.Persistence.Azure/Akka.Persistence.Azure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<ItemGroup>
<PackageReference Include="Akka.Persistence.Query" Version="$(AkkaVersion)" />
<PackageReference Include="Azure.Data.Tables" Version="12.5.0" />
<PackageReference Include="Azure.Data.Tables" Version="12.6.1" />
<PackageReference Include="Azure.Identity" Version="1.6.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.12.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
Expand Down
Loading

0 comments on commit a2a5dc6

Please sign in to comment.