From 044115b1f2dc4a41cd58da38d42e2b5051fedf0a Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Tue, 19 Sep 2023 17:50:47 +0200 Subject: [PATCH 001/174] chore: Updated .gitignore --- .gitignore | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 8a30d25..9881e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -301,10 +301,6 @@ node_modules/ *.dsw *.dsp -# Visual Studio 6 technical files -*.ncb -*.aps - # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -396,3 +392,4 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml +.idea/ \ No newline at end of file From 2e8f2751e79f9d3bb9596ef041cec73d272a2591 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:28:28 +0200 Subject: [PATCH 002/174] chore: Added .editorconfig --- test/.editorconfig | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 test/.editorconfig diff --git a/test/.editorconfig b/test/.editorconfig new file mode 100644 index 0000000..79bfd7f --- /dev/null +++ b/test/.editorconfig @@ -0,0 +1,2 @@ +[*.cs] +dotnet_diagnostic.CA1707.severity = none From 1990652cfab63664208c1c1a386d667c44f69d4f Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:28:37 +0200 Subject: [PATCH 003/174] chore: Added .editorconfig --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f720d5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +[*] +charset = utf-8 +insert_final_newline = true +indent_size = tab +indent_style = space +tab_width = 4 + +[*.cs] +charset = utf-8-bom + +[*.{yml,yaml}] +tab_width = 2 From 4143a46e60405ae7cd12839bfb4a758600009dd2 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:28:49 +0200 Subject: [PATCH 004/174] chore: Added .globalconfig --- .globalconfig | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .globalconfig diff --git a/.globalconfig b/.globalconfig new file mode 100644 index 0000000..408c0ce --- /dev/null +++ b/.globalconfig @@ -0,0 +1,3 @@ +is_global = true + +dotnet_diagnostic.SA1135.severity = none From 977c291ded0a059bae85d32b330e898e2c8c046d Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:28:59 +0200 Subject: [PATCH 005/174] chore: Added AssemblyInfo.cs --- src/Clustering/Cassandra/Properties/AssemblyInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/Clustering/Cassandra/Properties/AssemblyInfo.cs diff --git a/src/Clustering/Cassandra/Properties/AssemblyInfo.cs b/src/Clustering/Cassandra/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14aeb7e --- /dev/null +++ b/src/Clustering/Cassandra/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +[assembly: CLSCompliant(false)] From 5d734f3a9b36809113f782472627b4e746007b64 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:29:17 +0200 Subject: [PATCH 006/174] chore: Added AssemblyInfo.cs --- src/Persistence/Cassandra/Properties/AssemblyInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/Persistence/Cassandra/Properties/AssemblyInfo.cs diff --git a/src/Persistence/Cassandra/Properties/AssemblyInfo.cs b/src/Persistence/Cassandra/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14aeb7e --- /dev/null +++ b/src/Persistence/Cassandra/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +[assembly: CLSCompliant(false)] From aef1209cc84320e74273c7a43f21f1a916f45316 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:29:27 +0200 Subject: [PATCH 007/174] chore: Added AssemblyInfo.cs --- src/Reminders/Cassandra/Properties/AssemblyInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/Reminders/Cassandra/Properties/AssemblyInfo.cs diff --git a/src/Reminders/Cassandra/Properties/AssemblyInfo.cs b/src/Reminders/Cassandra/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14aeb7e --- /dev/null +++ b/src/Reminders/Cassandra/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +[assembly: CLSCompliant(false)] From 20fadca6aa7f5f5fe650e4a5ff47ef70ef13e03e Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:29:38 +0200 Subject: [PATCH 008/174] chore: Added AssemblyInfo.cs --- test/Persistence/Cassandra/Properties/AssemblyInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test/Persistence/Cassandra/Properties/AssemblyInfo.cs diff --git a/test/Persistence/Cassandra/Properties/AssemblyInfo.cs b/test/Persistence/Cassandra/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14aeb7e --- /dev/null +++ b/test/Persistence/Cassandra/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +[assembly: CLSCompliant(false)] From caf66306cedf1b2f490c9b26050907bb021956ce Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:30:43 +0200 Subject: [PATCH 009/174] chore: Added build.yml --- .github/workflows/build.yml | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..877aadf --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +name: dotnet build + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-version: [ '7.0.x' ] + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: '0' + - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Restore .NET Packages + run: dotnet restore + - name: Build .NET Solution + run: dotnet build --configuration Release --no-restore + - name: Test .NET Solution + run: dotnet test --configuration Release --no-build --filter=Category=UnitTest --logger "trx;LogFileName=test-results.trx" + - uses: actions/upload-artifact@v2 + if: success() || failure() + with: + name: test-results + path: "**/test-results.trx" + - name: Pack .NET Solution + run: dotnet pack --configuration Release --no-build --output pack/ + - name: Publish .NET Solution to GitHub Packages + continue-on-error: true + run: dotnet nuget push pack/*.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" + - name: Publish .NET Solution to NuGet.org + env: + apikey: ${{ secrets.NUGET_ORG_KEY }} + if: ${{ env.apikey != '' }} + run: dotnet nuget push pack/*.nupkg --api-key ${{ secrets.NUGET_ORG_KEY }} --source nuget + continue-on-error: true From e24ea31075573e92832c83239f627bc0914fb3a1 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:31:22 +0200 Subject: [PATCH 010/174] feature: Added CassandraGrainStorageFactory.cs --- .../Storage/CassandraGrainStorageFactory.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/Persistence/Cassandra/Storage/CassandraGrainStorageFactory.cs diff --git a/src/Persistence/Cassandra/Storage/CassandraGrainStorageFactory.cs b/src/Persistence/Cassandra/Storage/CassandraGrainStorageFactory.cs new file mode 100644 index 0000000..0e62dcc --- /dev/null +++ b/src/Persistence/Cassandra/Storage/CassandraGrainStorageFactory.cs @@ -0,0 +1,34 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Storage; + +using Escendit.Extensions.Hosting.Cassandra; +using global::Orleans; +using Microsoft.Extensions.DependencyInjection; +using Options; + +/// +/// Cassandra Grain Storage Factory. +/// +public static class CassandraGrainStorageFactory +{ + /// + /// Create GrainStorage. + /// + /// The service provider. + /// The name. + /// The grain storage. + public static GrainStorageBase Create(IServiceProvider serviceProvider, string name) + { + var options = serviceProvider.GetOptionsByName(name); + var connectionOptions = serviceProvider.GetOptionsByName(name); + return options.Strategy switch + { + Strategy.SingleTable => ActivatorUtilities + .CreateInstance(serviceProvider, name, options, connectionOptions), + Strategy.TablePerGrain => throw new NotImplementedException(), + _ => throw new ArgumentOutOfRangeException(name), + }; + } +} From b540d77da9475a8756076ca34a6f9ca4c970f951 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:31:35 +0200 Subject: [PATCH 011/174] feature: Added CassandraMembershipTable.cs --- .../Cassandra/CassandraMembershipTable.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/Clustering/Cassandra/CassandraMembershipTable.cs diff --git a/src/Clustering/Cassandra/CassandraMembershipTable.cs b/src/Clustering/Cassandra/CassandraMembershipTable.cs new file mode 100644 index 0000000..d76334a --- /dev/null +++ b/src/Clustering/Cassandra/CassandraMembershipTable.cs @@ -0,0 +1,61 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +using global::Orleans; +using global::Orleans.Runtime; + +/// +/// Cassandra Membership Table. +/// +public class CassandraMembershipTable : IMembershipTable +{ + /// + public Task InitializeMembershipTable(bool tryInitTableVersion) + { + throw new NotImplementedException(); + } + + /// + public Task DeleteMembershipTableEntries(string clusterId) + { + throw new NotImplementedException(); + } + + /// + public Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate) + { + throw new NotImplementedException(); + } + + /// + public Task ReadRow(SiloAddress key) + { + throw new NotImplementedException(); + } + + /// + public Task ReadAll() + { + throw new NotImplementedException(); + } + + /// + public Task InsertRow(MembershipEntry entry, TableVersion tableVersion) + { + throw new NotImplementedException(); + } + + /// + public Task UpdateRow(MembershipEntry entry, string etag, TableVersion tableVersion) + { + throw new NotImplementedException(); + } + + /// + public Task UpdateIAmAlive(MembershipEntry entry) + { + throw new NotImplementedException(); + } +} From 30b0ca5e22b6a38086a4ec34056b74d8df005ff5 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:31:46 +0200 Subject: [PATCH 012/174] feature: Added CassandraRemindersTable.cs --- .../Cassandra/CassandraRemindersTable.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/Reminders/Cassandra/CassandraRemindersTable.cs diff --git a/src/Reminders/Cassandra/CassandraRemindersTable.cs b/src/Reminders/Cassandra/CassandraRemindersTable.cs new file mode 100644 index 0000000..8323614 --- /dev/null +++ b/src/Reminders/Cassandra/CassandraRemindersTable.cs @@ -0,0 +1,54 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra; + +using global::Orleans.Runtime; + +/// +/// Cassandra Reminders Table. +/// +public class CassandraRemindersTable : IReminderTable +{ + /// + public Task Init() + { + throw new NotImplementedException(); + } + + /// + public Task ReadRows(GrainId grainId) + { + throw new NotImplementedException(); + } + + /// + public Task ReadRows(uint begin, uint end) + { + throw new NotImplementedException(); + } + + /// + public Task ReadRow(GrainId grainId, string reminderName) + { + throw new NotImplementedException(); + } + + /// + public Task UpsertRow(ReminderEntry entry) + { + throw new NotImplementedException(); + } + + /// + public Task RemoveRow(GrainId grainId, string reminderName, string eTag) + { + throw new NotImplementedException(); + } + + /// + public Task TestOnlyClearTable() + { + throw new NotImplementedException(); + } +} From df1f55b4aeaa9041d654ca41348e9f65b36b8e44 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:31:56 +0200 Subject: [PATCH 013/174] feature: Added CassandraStorageException.cs --- .../Storage/CassandraStorageException.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/Persistence/Cassandra/Storage/CassandraStorageException.cs diff --git a/src/Persistence/Cassandra/Storage/CassandraStorageException.cs b/src/Persistence/Cassandra/Storage/CassandraStorageException.cs new file mode 100644 index 0000000..7258866 --- /dev/null +++ b/src/Persistence/Cassandra/Storage/CassandraStorageException.cs @@ -0,0 +1,50 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Storage; + +using System.Runtime.Serialization; +using global::Orleans; + +/// +/// Cassandra Storage Exception. +/// +[GenerateSerializer] +public class CassandraStorageException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public CassandraStorageException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public CassandraStorageException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public CassandraStorageException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The serialization info. + /// The streaming context. + protected CassandraStorageException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } +} From 04f682daa8dcdd7d94d07d136469e4a3c02e36b4 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:32:07 +0200 Subject: [PATCH 014/174] feature: Added CassandraStorageOptions.cs --- .../Options/CassandraStorageOptions.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/Persistence/Cassandra/Options/CassandraStorageOptions.cs diff --git a/src/Persistence/Cassandra/Options/CassandraStorageOptions.cs b/src/Persistence/Cassandra/Options/CassandraStorageOptions.cs new file mode 100644 index 0000000..5515146 --- /dev/null +++ b/src/Persistence/Cassandra/Options/CassandraStorageOptions.cs @@ -0,0 +1,40 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Options; + +using global::Orleans; +using global::Orleans.Storage; + +/// +/// Cassandra Storage Options. +/// +public class CassandraStorageOptions : IStorageProviderSerializerOptions +{ + /// + /// Gets or sets a value indicating whether delete state on clear. + /// + /// The flag to delete state on clear. + public bool DeleteStateOnClear { get; set; } + + /// + /// Gets or sets the initial stage. + /// + /// The initial stage. + public int InitialStage { get; set; } = ServiceLifecycleStage.ApplicationServices; + + /// + public IGrainStorageSerializer? GrainStorageSerializer { get; set; } + + /// + /// Gets or sets the strategy. + /// + /// The strategy. + public Strategy? Strategy { get; set; } = Options.Strategy.SingleTable; + + /// + /// Gets or sets the table name or prefix. + /// + /// The table name or prefix. + public string? TableNameOrPrefix { get; set; } +} From 57304e3c587c1f9b8e07bf4f05f0696d8b00edcc Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:32:17 +0200 Subject: [PATCH 015/174] feature: Added CassandraStorageOptionsValidator.cs --- .../CassandraStorageOptionsValidator.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Persistence/Cassandra/Options/CassandraStorageOptionsValidator.cs diff --git a/src/Persistence/Cassandra/Options/CassandraStorageOptionsValidator.cs b/src/Persistence/Cassandra/Options/CassandraStorageOptionsValidator.cs new file mode 100644 index 0000000..94675b2 --- /dev/null +++ b/src/Persistence/Cassandra/Options/CassandraStorageOptionsValidator.cs @@ -0,0 +1,31 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Options; + +using global::Orleans; + +/// +/// Cassandra Storage Options Validator. +/// +public class CassandraStorageOptionsValidator : IConfigurationValidator +{ + private readonly CassandraStorageOptions _options; + private readonly string _name; + + /// + /// Initializes a new instance of the class. + /// + /// The options. + /// The name. + public CassandraStorageOptionsValidator(CassandraStorageOptions options, string name) + { + _options = options; + _name = name; + } + + /// + public void ValidateConfiguration() + { + } +} From b4b4ae3b91e342d1587b5ac64335d513dce8ebcd Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:32:28 +0200 Subject: [PATCH 016/174] feature: Added DefaultCassandraPostConfigureOptions.cs --- .../DefaultCassandraPostConfigureOptions.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/Persistence/Cassandra/Options/DefaultCassandraPostConfigureOptions.cs diff --git a/src/Persistence/Cassandra/Options/DefaultCassandraPostConfigureOptions.cs b/src/Persistence/Cassandra/Options/DefaultCassandraPostConfigureOptions.cs new file mode 100644 index 0000000..6ca4309 --- /dev/null +++ b/src/Persistence/Cassandra/Options/DefaultCassandraPostConfigureOptions.cs @@ -0,0 +1,22 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Hosting; + +using global::Orleans.Storage; +using Options; + +/// +/// Default Cassandra Post Configure Options. +/// +public class DefaultCassandraPostConfigureOptions : DefaultStorageProviderSerializerOptionsConfigurator +{ + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + public DefaultCassandraPostConfigureOptions(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } +} From 9e90d68653de72972a8ee710bde447cbfafa7f90 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:32:37 +0200 Subject: [PATCH 017/174] chore: Added dependabot.yml --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..446b951 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "weekly" From 1ee79198dea6d92b6e9a850d95e6fcd984e19610 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:32:49 +0200 Subject: [PATCH 018/174] chore: Added Directory.Build.props --- test/Directory.Build.props | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/Directory.Build.props diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 0000000..92f64e3 --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,13 @@ + + + + $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\')) + + + + + + false + true + + From 91ad7fbca08058daf95039e9036287a6b19d726e Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:32:58 +0200 Subject: [PATCH 019/174] chore: Added Directory.Build.props --- Directory.Build.props | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 Directory.Build.props diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..43db09e --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,54 @@ + + + + net7.0 + enable + enable + + + true + + + git + https://github.com/escendit/cassandra-dotnet-extensions + + + $(NoWarn);CS1591 + $(SolutionDir) + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + From 43bf5ad6bd46a90ecf35b5c4f24cdd036ba0c2b2 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:33:16 +0200 Subject: [PATCH 020/174] chore: Added Directory.Packages.props --- test/Directory.Packages.props | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/Directory.Packages.props diff --git a/test/Directory.Packages.props b/test/Directory.Packages.props new file mode 100644 index 0000000..6200a17 --- /dev/null +++ b/test/Directory.Packages.props @@ -0,0 +1,36 @@ + + + + $([MSBuild]::GetPathOfFileAbove('Directory.Packages.props', '$(MSBuildThisFileDirectory)..\')) + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + \ No newline at end of file From 228ae9178ab74e56570de564a61479393cb0cc7c Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:33:30 +0200 Subject: [PATCH 021/174] chore: Added Directory.Packages.props --- Directory.Packages.props | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Directory.Packages.props diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..3c2a1a5 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,32 @@ + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 07e72d89d9f7f5ce4b94b5e7dcf77a5cbc65bd1d Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:34:02 +0200 Subject: [PATCH 022/174] chore: Added Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj --- ...cendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/AspNetCore/Clustering/Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj diff --git a/src/AspNetCore/Clustering/Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj b/src/AspNetCore/Clustering/Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj new file mode 100644 index 0000000..d6ca42c --- /dev/null +++ b/src/AspNetCore/Clustering/Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj @@ -0,0 +1 @@ + From 19b74eb4ba5b874a977da067a52a0afb897c8b21 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:34:22 +0200 Subject: [PATCH 023/174] chore: Added Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj --- ...endit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/AspNetCore/Persistence/Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj diff --git a/src/AspNetCore/Persistence/Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj b/src/AspNetCore/Persistence/Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj new file mode 100644 index 0000000..d6ca42c --- /dev/null +++ b/src/AspNetCore/Persistence/Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj @@ -0,0 +1 @@ + From fbe3c1af5f7a232e0cea5cbdc93fd0398633b5fe Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:34:50 +0200 Subject: [PATCH 024/174] chore: Added Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj --- .../Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/AspNetCore/Reminders/Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj diff --git a/src/AspNetCore/Reminders/Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj b/src/AspNetCore/Reminders/Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj new file mode 100644 index 0000000..d6ca42c --- /dev/null +++ b/src/AspNetCore/Reminders/Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj @@ -0,0 +1 @@ + From a9e45c5c45d72914c65dceb7396eca119203a2cc Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:35:05 +0200 Subject: [PATCH 025/174] chore: Added Escendit.Orleans.Clustering.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj diff --git a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj new file mode 100644 index 0000000..61b4c67 --- /dev/null +++ b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj @@ -0,0 +1,5 @@ + + + + + From 645f3498e459b2b33978f3d7659c00947ee2a23c Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:35:16 +0200 Subject: [PATCH 026/174] chore: Added Escendit.Orleans.Extensions.Cassandra.sln --- Escendit.Orleans.Extensions.Cassandra.sln | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Escendit.Orleans.Extensions.Cassandra.sln diff --git a/Escendit.Orleans.Extensions.Cassandra.sln b/Escendit.Orleans.Extensions.Cassandra.sln new file mode 100644 index 0000000..9cc2deb --- /dev/null +++ b/Escendit.Orleans.Extensions.Cassandra.sln @@ -0,0 +1,86 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BE03B4BC-8D74-42CA-81AC-014417FCFEB9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F719A264-551D-450A-9FBF-F3802858FFAE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clustering", "Clustering", "{5F161A73-5C42-4602-AB22-16EB85DEC048}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore", "{FED9F72C-209F-4539-B7C0-730DE379B1BF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Reminders", "Reminders", "{77E5F3C7-A347-42F4-BF93-51248B035D56}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Persistence", "Persistence", "{2950C6B5-E391-4569-AAB5-D8BEB92FF7B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra", "src\AspNetCore\Clustering\Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj", "{5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra", "src\AspNetCore\Persistence\Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj", "{68EE25C3-8397-4668-8DCB-971DF2430266}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.AspNetCore.Builder.Reminders.Cassandra", "src\AspNetCore\Reminders\Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj", "{648DA3BD-AF58-430F-8231-0E4D76BA19E4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Clustering.Cassandra", "src\Clustering\Cassandra\Escendit.Orleans.Clustering.Cassandra.csproj", "{D1643CDC-E454-441C-86D9-81207E8A0707}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Persistence.Cassandra", "src\Persistence\Cassandra\Escendit.Orleans.Persistence.Cassandra.csproj", "{C7BF8816-1982-4672-9ADB-55B2CC73C63B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Reminders.Cassandra", "src\Reminders\Cassandra\Escendit.Orleans.Reminders.Cassandra.csproj", "{6E3EEC03-C465-4C7F-93FB-E62A2ECC6847}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Persistence", "Persistence", "{9DC8501A-84DE-4272-A660-6615DA7C91B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Persistence.Cassandra.Tests", "test\Persistence\Cassandra\Escendit.Orleans.Persistence.Cassandra.Tests.csproj", "{7D40168C-40B6-4339-88A0-4F6E408F2EE7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5F161A73-5C42-4602-AB22-16EB85DEC048} = {BE03B4BC-8D74-42CA-81AC-014417FCFEB9} + {FED9F72C-209F-4539-B7C0-730DE379B1BF} = {BE03B4BC-8D74-42CA-81AC-014417FCFEB9} + {77E5F3C7-A347-42F4-BF93-51248B035D56} = {BE03B4BC-8D74-42CA-81AC-014417FCFEB9} + {2950C6B5-E391-4569-AAB5-D8BEB92FF7B8} = {BE03B4BC-8D74-42CA-81AC-014417FCFEB9} + {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A} = {FED9F72C-209F-4539-B7C0-730DE379B1BF} + {68EE25C3-8397-4668-8DCB-971DF2430266} = {FED9F72C-209F-4539-B7C0-730DE379B1BF} + {648DA3BD-AF58-430F-8231-0E4D76BA19E4} = {FED9F72C-209F-4539-B7C0-730DE379B1BF} + {D1643CDC-E454-441C-86D9-81207E8A0707} = {5F161A73-5C42-4602-AB22-16EB85DEC048} + {C7BF8816-1982-4672-9ADB-55B2CC73C63B} = {2950C6B5-E391-4569-AAB5-D8BEB92FF7B8} + {6E3EEC03-C465-4C7F-93FB-E62A2ECC6847} = {77E5F3C7-A347-42F4-BF93-51248B035D56} + {9DC8501A-84DE-4272-A660-6615DA7C91B0} = {F719A264-551D-450A-9FBF-F3802858FFAE} + {7D40168C-40B6-4339-88A0-4F6E408F2EE7} = {9DC8501A-84DE-4272-A660-6615DA7C91B0} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Release|Any CPU.Build.0 = Release|Any CPU + {68EE25C3-8397-4668-8DCB-971DF2430266}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68EE25C3-8397-4668-8DCB-971DF2430266}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68EE25C3-8397-4668-8DCB-971DF2430266}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68EE25C3-8397-4668-8DCB-971DF2430266}.Release|Any CPU.Build.0 = Release|Any CPU + {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Release|Any CPU.Build.0 = Release|Any CPU + {D1643CDC-E454-441C-86D9-81207E8A0707}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1643CDC-E454-441C-86D9-81207E8A0707}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1643CDC-E454-441C-86D9-81207E8A0707}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1643CDC-E454-441C-86D9-81207E8A0707}.Release|Any CPU.Build.0 = Release|Any CPU + {C7BF8816-1982-4672-9ADB-55B2CC73C63B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7BF8816-1982-4672-9ADB-55B2CC73C63B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7BF8816-1982-4672-9ADB-55B2CC73C63B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7BF8816-1982-4672-9ADB-55B2CC73C63B}.Release|Any CPU.Build.0 = Release|Any CPU + {6E3EEC03-C465-4C7F-93FB-E62A2ECC6847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E3EEC03-C465-4C7F-93FB-E62A2ECC6847}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E3EEC03-C465-4C7F-93FB-E62A2ECC6847}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E3EEC03-C465-4C7F-93FB-E62A2ECC6847}.Release|Any CPU.Build.0 = Release|Any CPU + {7D40168C-40B6-4339-88A0-4F6E408F2EE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D40168C-40B6-4339-88A0-4F6E408F2EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D40168C-40B6-4339-88A0-4F6E408F2EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D40168C-40B6-4339-88A0-4F6E408F2EE7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From 3d3a54671a32c77723653e593cf3a82c34082616 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:35:37 +0200 Subject: [PATCH 027/174] chore: Escendit.Orleans.Persistence.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj diff --git a/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj new file mode 100644 index 0000000..89672be --- /dev/null +++ b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj @@ -0,0 +1,6 @@ + + + + + + From 27d7fc6e7a6bf3df9bff03d57c5c8b3ef8ff7892 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:35:58 +0200 Subject: [PATCH 028/174] chore: Added Escendit.Orleans.Persistence.Cassandra.Tests.csproj --- ...cendit.Orleans.Persistence.Cassandra.Tests.csproj | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj diff --git a/test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj b/test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj new file mode 100644 index 0000000..33320f6 --- /dev/null +++ b/test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj @@ -0,0 +1,12 @@ + + + false + true + + + + + + + + From 45123d283c3f853ac0b50f9fa8eba83ab24bd190 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:36:08 +0200 Subject: [PATCH 029/174] chore: Added Escendit.Orleans.Reminders.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj diff --git a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj new file mode 100644 index 0000000..65d26f4 --- /dev/null +++ b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj @@ -0,0 +1,5 @@ + + + + + From ea5d6ef8c8f8fea27ef4ae9d353515b374e0646e Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:36:18 +0200 Subject: [PATCH 030/174] chore: Added GitVersion.yml --- GitVersion.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 GitVersion.yml diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..62167a1 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,4 @@ +branches: + master: + mode: ContinuousDeployment + tag: rc From ebd1d74ac4109a526a7dbaa72252e50fe3f25945 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:36:27 +0200 Subject: [PATCH 031/174] chore: Added global.json --- global.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 global.json diff --git a/global.json b/global.json new file mode 100644 index 0000000..2a8238c --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "7.0.202", + "rollForward": "latestMajor" + } +} From 5ba7c1e5d0c05daf267de7443106793be9025ed0 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:36:38 +0200 Subject: [PATCH 032/174] feature: Added GrainStorageBase.cs --- .../Cassandra/Storage/GrainStorageBase.cs | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 src/Persistence/Cassandra/Storage/GrainStorageBase.cs diff --git a/src/Persistence/Cassandra/Storage/GrainStorageBase.cs b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs new file mode 100644 index 0000000..3e748cf --- /dev/null +++ b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs @@ -0,0 +1,216 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Storage; + +using System.Runtime.CompilerServices; +using Escendit.Extensions.Hosting.Cassandra; +using global::Cassandra; +using global::Orleans; +using global::Orleans.Runtime; +using global::Orleans.Storage; +using Microsoft.Extensions.Logging; + +/// +/// Grain Storage Base. +/// +public abstract partial class GrainStorageBase : IGrainStorage, ILifecycleParticipant, IDisposable +{ + private readonly string _name; + private readonly ILogger _logger; + private readonly ICluster _cluster; + private readonly CassandraClientOptions _options; + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The logger. + /// The cluster. + /// The client options. + protected GrainStorageBase( + string name, + ILogger logger, + ICluster cluster, + CassandraClientOptions options) + { + _name = name; + _logger = logger; + _cluster = cluster; + _options = options; + } + + /// + /// Finalizes an instance of the class. + /// + ~GrainStorageBase() => Dispose(false); + + /// + /// Gets the session. + /// + /// The session. + protected ISession? Session { get; private set; } + + /// + public abstract Task ReadStateAsync(string stateName, GrainId grainId, IGrainState grainState); + + /// + public abstract Task WriteStateAsync(string stateName, GrainId grainId, IGrainState grainState); + + /// + public abstract Task ClearStateAsync(string stateName, GrainId grainId, IGrainState grainState); + + /// + public void Participate(ISiloLifecycle observer) + { + LogParticipate(_name); + observer.Subscribe($"{GetType().Name}:{_name}", ServiceLifecycleStage.ApplicationServices, Initialize); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// The init method. + /// + /// The cancellation token. + /// A representing the result of the asynchronous operation. + protected virtual async Task Initialize(CancellationToken cancellationToken) + { + LogInitialize(_name, _options.DefaultKeyspace!); + Session = await _cluster.ConnectAsync(string.Empty); + Session.CreateKeyspaceIfNotExists(_options.DefaultKeyspace); + Session.ChangeKeyspace(_options.DefaultKeyspace); + LogConnect(_name, _options.DefaultKeyspace!); + } + + /// + /// Dispose. + /// + /// The disposing. + protected virtual void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + Session?.Dispose(); + _cluster.Shutdown(); + _cluster.Dispose(); + } + + /// + /// Generate Type Name. + /// + /// The state name. + /// The grain id. + /// The grain type. + /// The string. + protected virtual string GenerateTypeName(string stateName, GrainId grainId) + { + return typeof(TGrain).FullName ?? typeof(TGrain).Name; + } + + /// + /// Generate State Name. + /// + /// The state name. + /// The grain id. + /// The grain type. + /// The string. + protected virtual string GenerateStateName(string stateName, GrainId grainId) + { + return stateName; + } + + /// + /// Generate Id. + /// + /// The state name. + /// The grain id. + /// The grain type. + /// The blob. + protected virtual byte[] GenerateId(string stateName, GrainId grainId) + { + return grainId.Key.Value.ToArray(); + } + + /// + /// Execute. + /// + /// The action. + /// The action name. + /// The task. + protected Task Execute(Func action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + return Execute(async () => + { + await action(); + return true; + }); + } + + /// + /// Execute. + /// + /// The type. + /// The action. + /// The action name. + /// A representing the result of the asynchronous operation. + protected Task Execute(Func> action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + + try + { + LogExecute(_name, typeof(T).FullName!, actionName); + return action(); + } + catch (Exception ex) + { + LogException(_name, ex, ex.Message, nameof(SingleTableGrainStorage), actionName); + throw; + } + } + + [LoggerMessage( + EventId = 0, + EventName = "Participate", + Level = LogLevel.Debug, + Message = "Participating {name} in Lifecycle")] + private partial void LogParticipate(string name); + + [LoggerMessage( + EventId = 100, + EventName = "Initialize", + Level = LogLevel.Information, + Message = "Initializing {name} to {keyspace}")] + private partial void LogInitialize(string name, string keyspace); + + [LoggerMessage( + EventId = 101, + EventName = "Connect", + Level = LogLevel.Information, + Message = "Connected {name} to {keyspace}")] + private partial void LogConnect(string name, string keyspace); + + [LoggerMessage( + EventId = 200, + EventName = "Execution", + Level = LogLevel.Debug, + Message = "Executing {name}#{type}.{action}")] + private partial void LogExecute(string name, string type, string action); + + [LoggerMessage( + EventId = 500, + EventName = "Exception", + Level = LogLevel.Error, + Message = "{name}#{type}.{action}")] + private partial void LogException(string name, Exception exception, string message, string type, string action); +} From 44ea638a838bad614b02d47ec6e60da6fab4fd08 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:36:50 +0200 Subject: [PATCH 033/174] feature: Added ISiloPersistenceBuilder.cs --- .../Options/ISiloPersistenceBuilder.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/Persistence/Cassandra/Options/ISiloPersistenceBuilder.cs diff --git a/src/Persistence/Cassandra/Options/ISiloPersistenceBuilder.cs b/src/Persistence/Cassandra/Options/ISiloPersistenceBuilder.cs new file mode 100644 index 0000000..2fc16b9 --- /dev/null +++ b/src/Persistence/Cassandra/Options/ISiloPersistenceBuilder.cs @@ -0,0 +1,18 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra; + +using global::Orleans.Hosting; + +/// +/// Silo Persistence Builder. +/// +public interface ISiloPersistenceBuilder : ISiloBuilder +{ + /// + /// Gets the client name. + /// + /// The client name. + public string Name { get; } +} From 485270c7154d167ffcbe3727988eb1d4be1b501c Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:37:05 +0200 Subject: [PATCH 034/174] chore: Added ISimpleGuidCompoundGrain.cs --- .../Cassandra/Grains/ISimpleGuidCompoundGrain.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/ISimpleGuidCompoundGrain.cs diff --git a/test/Persistence/Cassandra/Grains/ISimpleGuidCompoundGrain.cs b/test/Persistence/Cassandra/Grains/ISimpleGuidCompoundGrain.cs new file mode 100644 index 0000000..d8add98 --- /dev/null +++ b/test/Persistence/Cassandra/Grains/ISimpleGuidCompoundGrain.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +/// +/// Simple Guid Compound Grain Interface. +/// +public interface ISimpleGuidCompoundGrain : IGrainWithGuidCompoundKey, ITestGrain +{ +} From 5fd02f13ec9278f43fc92ece4e791c2029faeb99 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:37:17 +0200 Subject: [PATCH 035/174] chore: Added ISimpleGuidGrain.cs --- test/Persistence/Cassandra/Grains/ISimpleGuidGrain.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/ISimpleGuidGrain.cs diff --git a/test/Persistence/Cassandra/Grains/ISimpleGuidGrain.cs b/test/Persistence/Cassandra/Grains/ISimpleGuidGrain.cs new file mode 100644 index 0000000..bdd650c --- /dev/null +++ b/test/Persistence/Cassandra/Grains/ISimpleGuidGrain.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +/// +/// Simple Guid Grain Interface. +/// +public interface ISimpleGuidGrain : IGrainWithGuidKey, ITestGrain +{ +} From a56cefd2df56fe51cc6b28d9a3f7e7fc9da93985 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:37:29 +0200 Subject: [PATCH 036/174] chore: Added ISimpleIntegerCompoundGrain.cs --- .../Cassandra/Grains/ISimpleIntegerCompoundGrain.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/ISimpleIntegerCompoundGrain.cs diff --git a/test/Persistence/Cassandra/Grains/ISimpleIntegerCompoundGrain.cs b/test/Persistence/Cassandra/Grains/ISimpleIntegerCompoundGrain.cs new file mode 100644 index 0000000..67d5355 --- /dev/null +++ b/test/Persistence/Cassandra/Grains/ISimpleIntegerCompoundGrain.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +/// +/// Simple Integer Compound Grain Interface. +/// +public interface ISimpleIntegerCompoundGrain : IGrainWithIntegerCompoundKey +{ +} From 51502455c3879e8a70b0e3758176ce66edd1a078 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:37:45 +0200 Subject: [PATCH 037/174] chore: Added ISimpleIntegerGrain.cs --- .../Cassandra/Grains/ISimpleIntegerGrain.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/ISimpleIntegerGrain.cs diff --git a/test/Persistence/Cassandra/Grains/ISimpleIntegerGrain.cs b/test/Persistence/Cassandra/Grains/ISimpleIntegerGrain.cs new file mode 100644 index 0000000..3a604ba --- /dev/null +++ b/test/Persistence/Cassandra/Grains/ISimpleIntegerGrain.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +/// +/// Simple Integer Grain Interface. +/// +public interface ISimpleIntegerGrain : IGrainWithIntegerKey, ITestGrain +{ +} From 0fae9ad454486f66183458755766efd0edda22de Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:37:55 +0200 Subject: [PATCH 038/174] chore: Added ISimpleStringGrain.cs --- .../Cassandra/Grains/ISimpleStringGrain.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/ISimpleStringGrain.cs diff --git a/test/Persistence/Cassandra/Grains/ISimpleStringGrain.cs b/test/Persistence/Cassandra/Grains/ISimpleStringGrain.cs new file mode 100644 index 0000000..e162bf3 --- /dev/null +++ b/test/Persistence/Cassandra/Grains/ISimpleStringGrain.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +/// +/// Simple String Grain. +/// +public interface ISimpleStringGrain : IGrainWithStringKey, ITestGrain +{ +} From 6e906dfdd4311458417a3762d1cf87052c99e8f4 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:38:05 +0200 Subject: [PATCH 039/174] chore: Added ITestGrain.cs --- .../Cassandra/Grains/ITestGrain.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/ITestGrain.cs diff --git a/test/Persistence/Cassandra/Grains/ITestGrain.cs b/test/Persistence/Cassandra/Grains/ITestGrain.cs new file mode 100644 index 0000000..cb18dcb --- /dev/null +++ b/test/Persistence/Cassandra/Grains/ITestGrain.cs @@ -0,0 +1,29 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +/// +/// Test Grain. +/// +public interface ITestGrain +{ + /// + /// Read Simple State. + /// + /// A representing the result of the asynchronous operation. + Task ReadState(); + + /// + /// Write Simple State. + /// + /// The state. + /// The task. + Task WriteState(TestState state); + + /// + /// Clear State. + /// + /// The task. + Task ClearState(); +} From 02812443c86ca518d7610e79172accea346ed1d4 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:38:19 +0200 Subject: [PATCH 040/174] chore: Added nuget.config --- nuget.config | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 nuget.config diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..b3c1117 --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + From 1c8641ed6eba92548298efdebd3908a395fecab4 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:38:47 +0200 Subject: [PATCH 041/174] feature: Added ServiceCollectionExtensions.cs --- .../Hosting/ServiceCollectionExtensions.cs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/Persistence/Cassandra/Hosting/ServiceCollectionExtensions.cs diff --git a/src/Persistence/Cassandra/Hosting/ServiceCollectionExtensions.cs b/src/Persistence/Cassandra/Hosting/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..4640203 --- /dev/null +++ b/src/Persistence/Cassandra/Hosting/ServiceCollectionExtensions.cs @@ -0,0 +1,109 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Microsoft.Extensions.DependencyInjection; + +using Escendit.Orleans.Persistence.Cassandra.Hosting; +using Escendit.Orleans.Persistence.Cassandra.Options; +using Escendit.Orleans.Persistence.Cassandra.Storage; +using Extensions; +using Options; +using Orleans; +using Orleans.Configuration; +using Orleans.Providers; +using Orleans.Runtime; +using Orleans.Storage; + +/// +/// Service Collection Extensions. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Add Cassandra Grain Storage As Default. + /// + /// The initial service collection. + /// The configure options. + /// The updated service collection. + public static IServiceCollection AddCassandraGrainStorageAsDefault( + this IServiceCollection services, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configureOptions); + return services + .AddCassandraGrainStorage( + ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, + builder => builder.Configure(configureOptions)); + } + + /// + /// Add Cassandra Grain Storage As Default. + /// + /// The initial service collection. + /// The configure options. + /// The updated service collection. + public static IServiceCollection AddCassandraGrainStorageAsDefault( + this IServiceCollection services, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configureOptions); + return services + .AddCassandraGrainStorage( + ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, + configureOptions); + } + + /// + /// Add Cassandra Grain Storage. + /// + /// The initial service collection. + /// The name. + /// The configure options. + /// The updated service collection. + public static IServiceCollection AddCassandraGrainStorage( + this IServiceCollection services, + string name, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + return services + .AddCassandraGrainStorage(name, builder => builder.Configure(configureOptions)); + } + + /// + /// Add Cassandra Grain Storage. + /// + /// The initial service collection. + /// The name. + /// The configure options. + /// The updated service collection. + public static IServiceCollection AddCassandraGrainStorage( + this IServiceCollection services, + string name, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + configureOptions.Invoke(services.AddOptions(name)); + if (string.Equals(name, ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, StringComparison.Ordinal)) + { + services.TryAddSingleton(serviceProvider => + serviceProvider.GetRequiredServiceByName(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME)); + } + + return services + .AddTransient(sp => + new CassandraStorageOptionsValidator(sp.GetOptionsByName(name), name)) + .ConfigureNamedOptionForLogging(name) + .ConfigureOptions() + .AddSingletonNamedService(name, CassandraGrainStorageFactory.Create) + .AddSingletonNamedService>(name, (serviceProvider, providerName) => + (ILifecycleParticipant)serviceProvider + .GetRequiredServiceByName(providerName)); + } +} From 5e89a883f228b3a1fd2b842245cef78f8143592f Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:39:01 +0200 Subject: [PATCH 042/174] feature: Added SiloBuilderExtensions.cs --- .../Hosting/SiloBuilderExtensions.cs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/Persistence/Cassandra/Hosting/SiloBuilderExtensions.cs diff --git a/src/Persistence/Cassandra/Hosting/SiloBuilderExtensions.cs b/src/Persistence/Cassandra/Hosting/SiloBuilderExtensions.cs new file mode 100644 index 0000000..145b41d --- /dev/null +++ b/src/Persistence/Cassandra/Hosting/SiloBuilderExtensions.cs @@ -0,0 +1,91 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Escendit.Orleans.Persistence.Cassandra.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Providers; + +/// +/// Silo Builder Extensions. +/// +public static class SiloBuilderExtensions +{ +/// + /// Add Cassandra Grain Storage As Default. + /// + /// The initial service collection. + /// The configure options. + /// The updated service collection. + public static ISiloBuilder AddCassandraGrainStorageAsDefault( + this ISiloBuilder services, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configureOptions); + return services + .AddCassandraGrainStorage( + ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, + configureOptions); + } + + /// + /// Add Cassandra Grain Storage As Default. + /// + /// The initial service collection. + /// The configure options. + /// The updated service collection. + public static ISiloBuilder AddCassandraGrainStorageAsDefault( + this ISiloBuilder services, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configureOptions); + return services + .AddCassandraGrainStorage( + ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, + configureOptions); + } + + /// + /// Add Cassandra Grain Storage. + /// + /// The initial service collection. + /// The name. + /// The configure options. + /// The updated service collection. + public static ISiloBuilder AddCassandraGrainStorage( + this ISiloBuilder siloBuilder, + string name, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(siloBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + return siloBuilder + .ConfigureServices(services => services + .AddCassandraGrainStorage(name, configureOptions)); + } + + /// + /// Add Cassandra Grain Storage. + /// + /// The silo builder. + /// The name. + /// The configure options. + /// The updated service collection. + public static ISiloBuilder AddCassandraGrainStorage( + this ISiloBuilder siloBuilder, + string name, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(siloBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + return siloBuilder + .ConfigureServices(services => services + .AddCassandraGrainStorage(name, configureOptions)); + } +} From 2b804775465840938981d0b0a106727076a396b9 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:39:13 +0200 Subject: [PATCH 043/174] chore: Added SiloBuilderFixture.cs --- .../Cassandra/Fixtures/SiloBuilderFixture.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/Persistence/Cassandra/Fixtures/SiloBuilderFixture.cs diff --git a/test/Persistence/Cassandra/Fixtures/SiloBuilderFixture.cs b/test/Persistence/Cassandra/Fixtures/SiloBuilderFixture.cs new file mode 100644 index 0000000..64b6ef4 --- /dev/null +++ b/test/Persistence/Cassandra/Fixtures/SiloBuilderFixture.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Fixtures; + +/// +/// Silo Builder Fixture. +/// +public sealed class SiloBuilderFixture +{ +} From 18e6bc9544a646c7fcb02a661ad323077a9d135c Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:39:24 +0200 Subject: [PATCH 044/174] chore: Added SiloBuilderTests.cs --- .../Persistence/Cassandra/SiloBuilderTests.cs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 test/Persistence/Cassandra/SiloBuilderTests.cs diff --git a/test/Persistence/Cassandra/SiloBuilderTests.cs b/test/Persistence/Cassandra/SiloBuilderTests.cs new file mode 100644 index 0000000..8975f26 --- /dev/null +++ b/test/Persistence/Cassandra/SiloBuilderTests.cs @@ -0,0 +1,92 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests; + +using Collections; +using Fixtures; +using global::Orleans.TestingHost; +using Grains; +using Xunit; +using Xunit.Categories; + +/// +/// Silo Builder Tests. +/// +[Collection(SiloCollectionFixture.Name)] +public class SiloBuilderTests +{ + private readonly TestCluster _cluster; + + /// + /// Initializes a new instance of the class. + /// + /// The fixture. + /// the test cluster. + public SiloBuilderTests(SiloBuilderFixture fixture, TestClusterFixture testClusterFixture) + { + ArgumentNullException.ThrowIfNull(fixture); + ArgumentNullException.ThrowIfNull(testClusterFixture); + _cluster = testClusterFixture.Cluster; + } + + /// + /// Acquire Simple Guid Grain. + /// + /// A representing the asynchronous unit test. + [Fact] + [IntegrationTest] + public async Task Test_AcquireSimpleGuidGrain() + { + var grain = _cluster.GrainFactory.GetGrain(Guid.Empty); + await grain.ReadState(); + await grain.WriteState(new TestState + { + State = "Hello World!", + }); + var state = await grain.ReadState(); + Assert.NotNull(grain); + Assert.Equal("Hello World!", state.State); + await grain.ClearState(); + } + + /// + /// Acquire Simple Integer Grain. + /// + /// A representing the asynchronous unit test. + [Fact] + [IntegrationTest] + public async Task Test_AcquireSimpleIntegerGrain() + { + var grain = _cluster.GrainFactory.GetGrain(0); + await grain.ReadState(); + await grain.WriteState(new TestState + { + State = "Hello World!", + }); + var state = await grain.ReadState(); + Assert.NotNull(grain); + Assert.Equal("Hello World!", state.State); + await grain.ClearState(); + } + + /// + /// Acquire Simple Integer Grain. + /// + /// A representing the asynchronous unit test. + [Fact] + [IntegrationTest] + public async Task Test_AcquireSimpleStringGrain() + { + var grain = _cluster.GrainFactory.GetGrain("random_string_here"); + await grain.ReadState(); + await grain.WriteState(new TestState + { + State = "Hello World!", + }); + var state = await grain.ReadState(); + Assert.NotNull(grain); + Assert.Equal("Hello World!", state.State); + /* await grain.ClearState(); */ + } +} From 3b257d59b0cf788d084160bf04b7140f261cc114 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:39:33 +0200 Subject: [PATCH 045/174] chore: Added SiloCollectionFixture.cs --- .../Collections/SiloCollectionFixture.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/Persistence/Cassandra/Collections/SiloCollectionFixture.cs diff --git a/test/Persistence/Cassandra/Collections/SiloCollectionFixture.cs b/test/Persistence/Cassandra/Collections/SiloCollectionFixture.cs new file mode 100644 index 0000000..4ae210d --- /dev/null +++ b/test/Persistence/Cassandra/Collections/SiloCollectionFixture.cs @@ -0,0 +1,19 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Collections; + +using Fixtures; +using Xunit; + +/// +/// Silo Collection Fixture. +/// +[CollectionDefinition(Name)] +public class SiloCollectionFixture : ICollectionFixture, ICollectionFixture +{ + /// + /// Silo Connection Name. + /// + public const string Name = "SiloCollection"; +} From 646a0af7300805bfcc67d8a8561625fb193c92e7 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:39:45 +0200 Subject: [PATCH 046/174] chore: Added SiloPersistenceBuilder.cs --- .../Options/SiloPersistenceBuilder.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/Persistence/Cassandra/Options/SiloPersistenceBuilder.cs diff --git a/src/Persistence/Cassandra/Options/SiloPersistenceBuilder.cs b/src/Persistence/Cassandra/Options/SiloPersistenceBuilder.cs new file mode 100644 index 0000000..a75599d --- /dev/null +++ b/src/Persistence/Cassandra/Options/SiloPersistenceBuilder.cs @@ -0,0 +1,37 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +#pragma warning disable CA1812 + +namespace Escendit.Orleans.Persistence.Cassandra.Options; + +using Microsoft.Extensions.DependencyInjection; + +/// +/// Silo Persistence Builder. +/// +internal class SiloPersistenceBuilder : ISiloPersistenceBuilder +{ + /// + /// Initializes a new instance of the class. + /// + /// The service collection. + /// The name. + public SiloPersistenceBuilder(IServiceCollection services, string name) + { + Services = services; + Name = name; + } + + /// + /// Gets the services. + /// + /// The services. + public IServiceCollection Services { get; } + + /// + /// Gets the name. + /// + /// The name. + public string Name { get; } +} From 95ddccea0ef06d3b9060dbd7f88f45e170c07f79 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:39:59 +0200 Subject: [PATCH 047/174] chore: Added SiloPersistenceBuilderExtensions.cs --- .../SiloPersistenceBuilderExtensions.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/Persistence/Cassandra/Hosting/SiloPersistenceBuilderExtensions.cs diff --git a/src/Persistence/Cassandra/Hosting/SiloPersistenceBuilderExtensions.cs b/src/Persistence/Cassandra/Hosting/SiloPersistenceBuilderExtensions.cs new file mode 100644 index 0000000..4dd394c --- /dev/null +++ b/src/Persistence/Cassandra/Hosting/SiloPersistenceBuilderExtensions.cs @@ -0,0 +1,33 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Hosting; + +using Escendit.Extensions.Hosting.Cassandra; +using global::Orleans.Hosting; +using Microsoft.Extensions.DependencyInjection; + +/// +/// Silo Persistence Builder Extensions. +/// +public static class SiloPersistenceBuilderExtensions +{ + /// + /// Add Client. + /// + /// The initial silo persistence builder. + /// The configure options. + /// The updated silo persistence builder. + public static ISiloPersistenceBuilder UseClient( + this ISiloPersistenceBuilder siloPersistenceBuilder, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(siloPersistenceBuilder); + ArgumentNullException.ThrowIfNull(siloPersistenceBuilder.Name); + ArgumentNullException.ThrowIfNull(2); + siloPersistenceBuilder + .ConfigureServices(services => services + .AddCassandraClient(siloPersistenceBuilder.Name, configureOptions)); + return siloPersistenceBuilder; + } +} From 26cf6263c7289d719d7f7cb8e7b3e314ecb141ff Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:40:09 +0200 Subject: [PATCH 048/174] chore: Added SimpleGuidGrain.cs --- .../Cassandra/Grains/SimpleGuidGrain.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/SimpleGuidGrain.cs diff --git a/test/Persistence/Cassandra/Grains/SimpleGuidGrain.cs b/test/Persistence/Cassandra/Grains/SimpleGuidGrain.cs new file mode 100644 index 0000000..4595384 --- /dev/null +++ b/test/Persistence/Cassandra/Grains/SimpleGuidGrain.cs @@ -0,0 +1,43 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +using global::Orleans.Runtime; + +/// +/// Simple Guid Grain. +/// +public class SimpleGuidGrain : Grain, ISimpleGuidGrain +{ + private readonly IPersistentState _persistentState; + + /// + /// Initializes a new instance of the class. + /// + /// The persistent state. + public SimpleGuidGrain([PersistentState("SimpleGuidGrain")] IPersistentState persistentState) + { + _persistentState = persistentState; + } + + /// + public async Task ReadState() + { + await _persistentState.ReadStateAsync(); + return _persistentState.State; + } + + /// + public async Task WriteState(TestState state) + { + _persistentState.State = state; + await _persistentState.WriteStateAsync(); + } + + /// + public Task ClearState() + { + return _persistentState.ClearStateAsync(); + } +} From 9686293dd8d5ae171f8501bb1587dc68910c8b8d Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:40:38 +0200 Subject: [PATCH 049/174] chore: Added SimpleIntegerGrain.cs --- .../Cassandra/Grains/SimpleIntegerGrain.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/SimpleIntegerGrain.cs diff --git a/test/Persistence/Cassandra/Grains/SimpleIntegerGrain.cs b/test/Persistence/Cassandra/Grains/SimpleIntegerGrain.cs new file mode 100644 index 0000000..6334ce0 --- /dev/null +++ b/test/Persistence/Cassandra/Grains/SimpleIntegerGrain.cs @@ -0,0 +1,43 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +using global::Orleans.Runtime; + +/// +/// Simple Integer Grain. +/// +public class SimpleIntegerGrain : Grain, ISimpleIntegerGrain +{ + private readonly IPersistentState _persistentState; + + /// + /// Initializes a new instance of the class. + /// + /// The persistent state. + public SimpleIntegerGrain([PersistentState("SimpleIntegerGrain")] IPersistentState persistentState) + { + _persistentState = persistentState; + } + + /// + public async Task ReadState() + { + await _persistentState.ReadStateAsync(); + return _persistentState.State; + } + + /// + public Task WriteState(TestState state) + { + _persistentState.State = state; + return _persistentState.WriteStateAsync(); + } + + /// + public Task ClearState() + { + return _persistentState.ClearStateAsync(); + } +} From cfb4a744b132bea9c2a4930f3772db1e44a6a3dc Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:41:17 +0200 Subject: [PATCH 050/174] chore: Added SimpleStringGrain.cs --- .../Cassandra/Grains/SimpleStringGrain.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/SimpleStringGrain.cs diff --git a/test/Persistence/Cassandra/Grains/SimpleStringGrain.cs b/test/Persistence/Cassandra/Grains/SimpleStringGrain.cs new file mode 100644 index 0000000..7d44e9e --- /dev/null +++ b/test/Persistence/Cassandra/Grains/SimpleStringGrain.cs @@ -0,0 +1,43 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +using global::Orleans.Runtime; + +/// +/// Simple String Grain. +/// +public class SimpleStringGrain : Grain, ISimpleStringGrain +{ + private readonly IPersistentState _persistentState; + + /// + /// Initializes a new instance of the class. + /// + /// The persistent state. + public SimpleStringGrain([PersistentState("SimpleStringGrain")] IPersistentState persistentState) + { + _persistentState = persistentState; + } + + /// + public async Task ReadState() + { + await _persistentState.ReadStateAsync(); + return _persistentState.State; + } + + /// + public Task WriteState(TestState state) + { + _persistentState.State = state; + return _persistentState.WriteStateAsync(); + } + + /// + public Task ClearState() + { + return _persistentState.ClearStateAsync(); + } +} From 4ac860c5e55f0e90fa6669f91433d77961a25a68 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:41:36 +0200 Subject: [PATCH 051/174] feature: Added SingleTableGrainStorage.cs --- .../Storage/SingleTableGrainStorage.cs | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 src/Persistence/Cassandra/Storage/SingleTableGrainStorage.cs diff --git a/src/Persistence/Cassandra/Storage/SingleTableGrainStorage.cs b/src/Persistence/Cassandra/Storage/SingleTableGrainStorage.cs new file mode 100644 index 0000000..27fdc32 --- /dev/null +++ b/src/Persistence/Cassandra/Storage/SingleTableGrainStorage.cs @@ -0,0 +1,188 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +#pragma warning disable CA1812 + +namespace Escendit.Orleans.Persistence.Cassandra.Storage; + +using Escendit.Extensions.Hosting.Cassandra; +using global::Cassandra; +using global::Orleans; +using global::Orleans.Runtime; +using Microsoft.Extensions.Logging; +using Options; + +/// +/// Single Table Grain Storage. +/// +internal partial class SingleTableGrainStorage : GrainStorageBase +{ + private readonly ILogger _logger; + private readonly CassandraClientOptions _clientOptions; + private readonly CassandraStorageOptions _storageOptions; + private PreparedStatement? _readStatement; + private PreparedStatement? _writeStatement; + private PreparedStatement? _clearStatement; + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The logger. + /// The service provider. + /// The client options. + /// The storage options. + public SingleTableGrainStorage( + string name, + ILogger logger, + IServiceProvider serviceProvider, + CassandraClientOptions clientOptions, + CassandraStorageOptions storageOptions) + : base(name, logger, serviceProvider.GetRequiredCassandraClient(name), clientOptions) + { + _logger = logger; + _clientOptions = clientOptions; + _storageOptions = storageOptions; + } + + /// + public override Task ReadStateAsync(string stateName, GrainId grainId, IGrainState grainState) + { + ArgumentNullException.ThrowIfNull(stateName); + ArgumentNullException.ThrowIfNull(grainId); + ArgumentNullException.ThrowIfNull(grainState); + ArgumentNullException.ThrowIfNull(Session); + return ReadStateInternalAsync(stateName, grainId, grainState); + } + + /// + public override Task WriteStateAsync(string stateName, GrainId grainId, IGrainState grainState) + { + ArgumentNullException.ThrowIfNull(stateName); + ArgumentNullException.ThrowIfNull(grainId); + ArgumentNullException.ThrowIfNull(grainState); + ArgumentNullException.ThrowIfNull(Session); + return WriteStateInternalAsync(stateName, grainId, grainState); + } + + /// + public override Task ClearStateAsync(string stateName, GrainId grainId, IGrainState grainState) + { + ArgumentNullException.ThrowIfNull(stateName); + ArgumentNullException.ThrowIfNull(grainId); + ArgumentNullException.ThrowIfNull(grainState); + ArgumentNullException.ThrowIfNull(Session); + return ClearStateInternalAsync(stateName, grainId, grainState); + } + + /// + protected override async Task Initialize(CancellationToken cancellationToken) + { + await base.Initialize(cancellationToken); + + var results = await Execute( + () => Session! + .ExecuteAsync( + new SimpleStatement( + "SELECT table_name FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ? ALLOW FILTERING", + _clientOptions.DefaultKeyspace!, + _storageOptions.TableNameOrPrefix))); + + if (results.GetAvailableWithoutFetching() == 0) + { + var result = await Execute( + () => Session!.ExecuteAsync( + new SimpleStatement( + $""" + create table "{_storageOptions.TableNameOrPrefix}" ( + name varchar, + type blob, + id blob, + state blob, + etag varchar + primary key ((name, type, id)) + ) + """))); + + if (result.GetAvailableWithoutFetching() != 0) + { + LogTableCreated(_storageOptions.TableNameOrPrefix!); + } + } + + _readStatement = + await Execute( + () => Session!.PrepareAsync( + $"select name, type, id, state, etag, exists from \"{_storageOptions.TableNameOrPrefix}\" where name = :name and type = :type and id = :id ALLOW FILTERING")); + _writeStatement = + await Execute( + () => Session!.PrepareAsync( + $"insert into \"{_storageOptions.TableNameOrPrefix}\" (name, type, id, state, etag) VALUES (:name, :type, :id, :state, :etag)")); + _clearStatement = + await Execute( + () => Session!.PrepareAsync( + $"delete from \"{_storageOptions.TableNameOrPrefix}\" where name = :name and type = :type and id = :id")); + } + + private async Task ReadStateInternalAsync(string stateName, GrainId grainId, IGrainState grainState) + { + var results = await Execute( + () => Session!.ExecuteAsync(_readStatement!.Bind(new + { + id = GenerateId(stateName, grainId), + type = GenerateTypeName(stateName, grainId), + name = GenerateStateName(stateName, grainId), + }))); + + if (results.GetAvailableWithoutFetching() == 0) + { + grainState.State = default!; + grainState.ETag = default; + grainState.RecordExists = default; + return; + } + + var result = results.First(); + var state = result.GetValue("state"); + grainState.State = _storageOptions.GrainStorageSerializer!.Deserialize(new BinaryData(state)); + grainState.ETag = result.GetValue("etag"); + grainState.RecordExists = true; + } + + private async Task WriteStateInternalAsync(string stateName, GrainId grainId, IGrainState grainState) + { + var name = GenerateStateName(stateName, grainId); + var type = GenerateTypeName(stateName, grainId); + var id = GenerateId(stateName, grainId); + var state = _storageOptions.GrainStorageSerializer!.Serialize(grainState.State).ToArray(); + var etag = grainState.ETag; + await Execute( + () => Session!.ExecuteAsync(_writeStatement!.Bind(new { name, type, id, state, etag, }))); + } + + private async Task ClearStateInternalAsync(string stateName, GrainId grainId, IGrainState grainState) + { + var results = await + Execute(() => Session!.ExecuteAsync(_clearStatement!.Bind( + new + { + id = GenerateId(stateName, grainId), + type = GenerateTypeName(stateName, grainId), + name = GenerateStateName(stateName, grainId), + }))); + + if (results.GetAvailableWithoutFetching() != 0) + { + grainState.State = default!; + grainState.ETag = default; + grainState.RecordExists = default; + } + } + + [LoggerMessage( + EventId = 200, + EventName = "SingleTable Created", + Level = LogLevel.Information, + Message = "Strategy SingleTable '{tableName}' created")] + private partial void LogTableCreated(string tableName); +} From d6d0fc9381966cf899129ad0cdafb4d0fc51ab25 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:44:46 +0200 Subject: [PATCH 052/174] chore: Added sonarcloud.yml --- .github/workflows/sonarcloud.yml | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/workflows/sonarcloud.yml diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 0000000..8830405 --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,108 @@ +name: SonarCloud +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + name: Build and analyze + runs-on: windows-latest + strategy: + matrix: + dotnet-version: [ '7.0.x' ] + steps: + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0 + with: + versionSpec: '5.x' + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'zulu' + - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Determine Version + uses: gittools/actions/gitversion/execute@v0 + with: + useConfigFile: true + - name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache SonarCloud scanner + id: cache-sonar-scanner + uses: actions/cache@v3 + with: + path: .\.sonar\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + - name: Install SonarCloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\.sonar\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner + - name: Build and analyze [main] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + if: ${{ env.SONAR_TOKEN != null && github.event.pull_request.number == null }} + shell: powershell + run: | + .\.sonar\scanner\dotnet-sonarscanner begin ` + /k:"escendit_cassandra-dotnet-extensions" ` + /o:"escendit" ` + /v:${{ env.GitVersion_FullSemVer }} ` + /d:sonar.login="${{ secrets.SONAR_TOKEN }}" ` + /d:sonar.host.url="https://sonarcloud.io" ` + /d:sonar.cs.vstest.reportsPaths=**/TestResults/*.trx ` + /d:sonar.cs.opencover.reportsPaths=**/TestResults/*/coverage.opencover.xml ` + /d:sonar.coverage.exclusions="**Test*.cs" + dotnet build --configuration Release + dotnet test ` + --configuration Release ` + --no-build ` + --filter=Category=UnitTest ` + --collect "XPlat Code Coverage" ` + --results-directory TestResults/ ` + --logger "trx;LogFileName=test-results.trx" ` + -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" + - name: Build and analyze [pull request] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + if: ${{ env.SONAR_TOKEN != null && github.event.pull_request.number != null }} + shell: powershell + run: | + .\.sonar\scanner\dotnet-sonarscanner begin ` + /k:"escendit_cassandra-dotnet-extensions" ` + /o:"escendit" ` + /v:${{ env.GitVersion_FullSemVer }} ` + /d:sonar.pullrequest.key="${{ github.event.pull_request.number }}" ` + /d:sonar.login="${{ secrets.SONAR_TOKEN }}" ` + /d:sonar.host.url="https://sonarcloud.io" ` + /d:sonar.cs.vstest.reportsPaths=**/TestResults/*.trx ` + /d:sonar.cs.opencover.reportsPaths=**/TestResults/*/coverage.opencover.xml ` + /d:sonar.coverage.exclusions="**Test*.cs" + dotnet build --configuration Release + dotnet test ` + --configuration Release ` + --no-build ` + --filter=Category=UnitTest ` + --collect "XPlat Code Coverage" ` + --results-directory TestResults/ ` + --logger "trx;LogFileName=test-results.trx" ` + -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" + From 9240ba5cec822728a02743ceea940cb0225b77a2 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:45:00 +0200 Subject: [PATCH 053/174] feature: Added Strategy.cs --- src/Persistence/Cassandra/Options/Strategy.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/Persistence/Cassandra/Options/Strategy.cs diff --git a/src/Persistence/Cassandra/Options/Strategy.cs b/src/Persistence/Cassandra/Options/Strategy.cs new file mode 100644 index 0000000..a65a643 --- /dev/null +++ b/src/Persistence/Cassandra/Options/Strategy.cs @@ -0,0 +1,20 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Options; + +/// +/// Storage Strategy. +/// +public enum Strategy +{ + /// + /// Store data into single table. + /// + SingleTable, + + /// + /// Store data into multiple tables, each grain into its own table. + /// + TablePerGrain, +} From 099efc3fc2d5e515ca9e049c142a0b4cb0180121 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:45:11 +0200 Subject: [PATCH 054/174] chore: Added test-report.yml --- .github/workflows/test-report.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/test-report.yml diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml new file mode 100644 index 0000000..3040af1 --- /dev/null +++ b/.github/workflows/test-report.yml @@ -0,0 +1,16 @@ +name: dotnet test report +on: + workflow_run: + workflows: ['dotnet build'] + types: + - completed +jobs: + report: + runs-on: ubuntu-latest + steps: + - uses: dorny/test-reporter@v1 + with: + artifact: test-results + name: dotnet tests + path: '**/test-results.trx' + reporter: dotnet-trx From 34fc8ac258fe03fc6471774dd9434cb86bc768f9 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:45:23 +0200 Subject: [PATCH 055/174] chore: Added TestClusterFixture.cs --- .../Cassandra/Fixtures/TestClusterFixture.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/Persistence/Cassandra/Fixtures/TestClusterFixture.cs diff --git a/test/Persistence/Cassandra/Fixtures/TestClusterFixture.cs b/test/Persistence/Cassandra/Fixtures/TestClusterFixture.cs new file mode 100644 index 0000000..6e714e3 --- /dev/null +++ b/test/Persistence/Cassandra/Fixtures/TestClusterFixture.cs @@ -0,0 +1,57 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Fixtures; + +using Configuration; +using global::Orleans.TestingHost; + +/// +/// Test Cluster Fixture. +/// +public sealed class TestClusterFixture : IDisposable +{ + /// + /// Initializes a new instance of the class. + /// + public TestClusterFixture() + { + var builder = new TestClusterBuilder + { + Options = + { + ClusterId = "testCluster", + ServiceId = "testService", + }, + }; + builder.AddSiloBuilderConfigurator(); + Cluster = builder.Build(); + Cluster.Deploy(); + } + + /// + /// Finalizes an instance of the class. + /// + ~TestClusterFixture() => Dispose(false); + + /// + /// Gets the cluster. + /// + /// The cluster. + public TestCluster Cluster { get; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + Cluster.StopAllSilos(); + } + } +} From a6185186c1acf0002cf819fa2adc2a5abda196be Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:45:33 +0200 Subject: [PATCH 056/174] chore: Added TestSiloConfigurator.cs --- .../Configuration/TestSiloConfigurator.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/Persistence/Cassandra/Configuration/TestSiloConfigurator.cs diff --git a/test/Persistence/Cassandra/Configuration/TestSiloConfigurator.cs b/test/Persistence/Cassandra/Configuration/TestSiloConfigurator.cs new file mode 100644 index 0000000..c9a521b --- /dev/null +++ b/test/Persistence/Cassandra/Configuration/TestSiloConfigurator.cs @@ -0,0 +1,37 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Configuration; + +using global::Orleans.TestingHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Options; + +/// +/// Test Silo Configurator. +/// +public class TestSiloConfigurator : ISiloConfigurator +{ + /// + public void Configure(ISiloBuilder siloBuilder) + { + siloBuilder + .ConfigureLogging(options => + { + options.AddConsole(); + options.SetMinimumLevel(LogLevel.Debug); + }) + .ConfigureServices(services => services + .AddCassandraClientAsDefault(options => + { + options.Endpoints.Add("localhost"); + options.DefaultKeyspace = "test"; + })) + .AddCassandraGrainStorageAsDefault(builder => + { + builder.Strategy = Strategy.SingleTable; + builder.TableNameOrPrefix = "Default"; + }); + } +} From 117d30fcd0ff57321d0e6503cf9cd0d91613ef34 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 21 Sep 2023 22:45:44 +0200 Subject: [PATCH 057/174] chore: Added TestState.cs --- test/Persistence/Cassandra/Grains/TestState.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/Persistence/Cassandra/Grains/TestState.cs diff --git a/test/Persistence/Cassandra/Grains/TestState.cs b/test/Persistence/Cassandra/Grains/TestState.cs new file mode 100644 index 0000000..51e82fa --- /dev/null +++ b/test/Persistence/Cassandra/Grains/TestState.cs @@ -0,0 +1,18 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Grains; + +/// +/// Test Stage. +/// +[GenerateSerializer] +public class TestState +{ + /// + /// Gets or sets the state. + /// + /// The state. + [Id(0)] + public string? State { get; set; } +} From d5ddd5aa8e64148640c7b70378122f4253a407cd Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:36:57 +0200 Subject: [PATCH 058/174] feature: Added 1_SuspectTime.sql --- src/Clustering/Cassandra/Resources/SQL/1_SuspectTime.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/Clustering/Cassandra/Resources/SQL/1_SuspectTime.sql diff --git a/src/Clustering/Cassandra/Resources/SQL/1_SuspectTime.sql b/src/Clustering/Cassandra/Resources/SQL/1_SuspectTime.sql new file mode 100644 index 0000000..ba60894 --- /dev/null +++ b/src/Clustering/Cassandra/Resources/SQL/1_SuspectTime.sql @@ -0,0 +1,4 @@ +CREATE TYPE IF NOT EXISTS suspect_time ( + address varchar, + timestamp timestamp +); From 98784ac9d5335df83b1cdc2faa70cc6964bf5b0b Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:37:11 +0200 Subject: [PATCH 059/174] feature: Added 2_Silo.sql --- src/Clustering/Cassandra/Resources/SQL/2_Silo.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/Clustering/Cassandra/Resources/SQL/2_Silo.sql diff --git a/src/Clustering/Cassandra/Resources/SQL/2_Silo.sql b/src/Clustering/Cassandra/Resources/SQL/2_Silo.sql new file mode 100644 index 0000000..edb5a22 --- /dev/null +++ b/src/Clustering/Cassandra/Resources/SQL/2_Silo.sql @@ -0,0 +1,15 @@ +CREATE TYPE IF NOT EXISTS silo ( + address varchar, + alive_on timestamp, + role varchar, + etag varchar, + fault_zone int, + host varchar, + name varchar, + proxy_port int, + started_on timestamp, + status int, + suspect_times list>, + update_zone int, + timestamp timestamp +); From 185f21d54427e8b502cab7f1ec163e82d06716de Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:37:25 +0200 Subject: [PATCH 060/174] feature: Added 3_Membership.sql --- src/Clustering/Cassandra/Resources/SQL/3_Membership.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/Clustering/Cassandra/Resources/SQL/3_Membership.sql diff --git a/src/Clustering/Cassandra/Resources/SQL/3_Membership.sql b/src/Clustering/Cassandra/Resources/SQL/3_Membership.sql new file mode 100644 index 0000000..76893ac --- /dev/null +++ b/src/Clustering/Cassandra/Resources/SQL/3_Membership.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS membership ( + id varchar, + silos map>, + version int, + etag varchar, + primary key ( id ) +); From 9042ed96e82a3b9c5d3d695f1b917f578d51ae6d Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:37:38 +0200 Subject: [PATCH 061/174] chore: Added AssemblyInfo.cs --- test/Clustering/Cassandra/Properties/AssemblyInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test/Clustering/Cassandra/Properties/AssemblyInfo.cs diff --git a/test/Clustering/Cassandra/Properties/AssemblyInfo.cs b/test/Clustering/Cassandra/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14aeb7e --- /dev/null +++ b/test/Clustering/Cassandra/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +[assembly: CLSCompliant(false)] From d58cd80e5cb3c4c7f6c22e54aef5a22aaed24e29 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:38:10 +0200 Subject: [PATCH 062/174] feature: Added CassandraClusteringOptions.cs --- .../Options/CassandraClusteringOptions.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs diff --git a/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs b/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs new file mode 100644 index 0000000..0623a88 --- /dev/null +++ b/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra.Options; + +/// +/// Cassandra Clustering Options. +/// +public record CassandraClusteringOptions +{ + /// + /// Gets or sets the client name. + /// + /// The client name. + public string? ClientName { get; set; } +} From 4c463a7010aaa9277cd16e55e67769a811e2215e Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:38:46 +0200 Subject: [PATCH 063/174] feature: Added CassandraGatewayListProvider.cs --- .../Provider/CassandraGatewayListProvider.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs diff --git a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs new file mode 100644 index 0000000..cff7c69 --- /dev/null +++ b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs @@ -0,0 +1,37 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +using global::Orleans.Messaging; + +/// +/// Cassandra Gateway List Provider. +/// +public class CassandraGatewayListProvider : IGatewayListProvider +{ + /// + /// Initializes a new instance of the class. + /// + public CassandraGatewayListProvider() + { + } + + /// + public TimeSpan MaxStaleness { get; } = TimeSpan.Zero; + + /// + public bool IsUpdatable { get; } = true; + + /// + public Task InitializeGatewayListProvider() + { + throw new NotImplementedException(); + } + + /// + public Task> GetGateways() + { + throw new NotImplementedException(); + } +} From 47bad9ff0a7b8712d93de4d400deefa93ed3d4fa Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:38:57 +0200 Subject: [PATCH 064/174] feature: Added CassandraMembershipTable.cs --- .../Provider/CassandraMembershipTable.cs | 377 ++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs diff --git a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs new file mode 100644 index 0000000..bc65723 --- /dev/null +++ b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs @@ -0,0 +1,377 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +#pragma warning disable CA1812 + +namespace Escendit.Orleans.Clustering.Cassandra; + +using System.Reflection; +using System.Text; +using Escendit.Extensions.Hosting.Cassandra; +using global::Cassandra; +using global::Cassandra.Mapping; +using global::Orleans; +using global::Orleans.Configuration; +using global::Orleans.Runtime; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Options; + +/// +/// Cassandra Membership Table. +/// +internal sealed class CassandraMembershipTable : IMembershipTable, IDisposable +{ + private readonly Assembly _selfAssembly; + private readonly CassandraClientOptions _clientOptions; + private readonly ClusterOptions _clusterOptions; + private readonly ICluster _cluster; + private ISession? _session; + private IMapper? _mapper; + + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + /// The options. + public CassandraMembershipTable( + IServiceProvider serviceProvider, + CassandraClusteringOptions options) + { + ArgumentNullException.ThrowIfNull(serviceProvider); + ArgumentNullException.ThrowIfNull(options); + _clusterOptions = serviceProvider.GetRequiredService>().Value; + _cluster = serviceProvider.GetRequiredServiceByName(options.ClientName); + _clientOptions = serviceProvider.GetOptionsByName(options.ClientName); + _selfAssembly = Assembly.GetAssembly(typeof(CassandraMembershipTable))!; + } + + /// + /// Finalizes an instance of the class. + /// + ~CassandraMembershipTable() => Dispose(false); + + /// + public async Task InitializeMembershipTable(bool tryInitTableVersion) + { + var mappingConfiguration = new MappingConfiguration() + .Define(); + _session = await _cluster + .ConnectAsync(string.Empty) + .ConfigureAwait(false); + _session.CreateKeyspaceIfNotExists(_clientOptions.DefaultKeyspace); + _session.ChangeKeyspace(_clientOptions.DefaultKeyspace); + _mapper = new Mapper(_session, mappingConfiguration); + + await InitializeDatabaseAsync() + .ConfigureAwait(false); + + if (tryInitTableVersion) + { + await InitializeVersionDataAsync(_clusterOptions.ClusterId) + .ConfigureAwait(false); + } + + await _session.UserDefinedTypes.DefineAsync(UdtMap + .For("silo", _session.Keyspace) + .Map(p => p.FaultZone, "fault_zone") + .Map(p => p.ProxyPort, "proxy_port") + .Map(p => p.HostName, "host") + .Map(p => p.StartedOn, "started_on") + .Map(p => p.Address, "address") + .Map(p => p.Etag, "etag") + .Map(p => p.Name, "name") + .Map(p => p.Role, "role") + .Map(p => p.Status, "status") + .Map(p => p.Timestamp, "timestamp") + .Map(p => p.AliveOn, "alive_on") + .Map(p => p.SuspectTimes, "suspect_times") + .Map(p => p.UpdateZone, "update_zone")); + await _session.UserDefinedTypes.DefineAsync(UdtMap + .For("suspect_time", _session.Keyspace) + .Map(p => p.Timestamp, "timestamp") + .Map(p => p.Address, "address")) + .ConfigureAwait(false); + } + + /// + public async Task DeleteMembershipTableEntries(string clusterId) + { + ArgumentNullException.ThrowIfNull(clusterId); + await _mapper! + .DeleteAsync("WHERE id = ?", clusterId) + .ConfigureAwait(false); + } + + /// + public async Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate) + { + var membership = await ReadAll(); + var deadMembers = new List(); + foreach (var silo in membership.Members.Where(p => p.Item1.Status == SiloStatus.Dead && p.Item1.IAmAliveTime < beforeDate)) + { + deadMembers.Add(silo.Item1.SiloAddress.ToParsableString()); + } + + await _mapper! + .UpdateIfAsync("SET silos = silos - ? WHERE id = ?", deadMembers, _clusterOptions.ClusterId) + .ConfigureAwait(false); + } + + /// + public async Task ReadRow(SiloAddress key) + { + ArgumentNullException.ThrowIfNull(key); + var results = await _mapper! + .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId) + .ConfigureAwait(false); + + var memberships = results as Membership[] ?? results.ToArray(); + if (!memberships.Any()) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + var result = memberships.FirstOrDefault(); + + if (result is null) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + if (!result.Silos.Any()) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + var matchedSilo = MatchSiloKey(result.Silos, key); + var entry = MapEntry(matchedSilo); + return new MembershipTableData( + new Tuple(entry, matchedSilo.Value.Etag), + new TableVersion(result.Version, result.Etag)); + } + + /// + public async Task ReadAll() + { + var results = await _mapper! + .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId) + .ConfigureAwait(false); + + var result = results.FirstOrDefault(); + + if (result is null) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + var entries = result + .Silos + .ToList() + .ConvertAll(item => new Tuple( + MapEntry(item), + item.Value.Etag)); + return new MembershipTableData(entries, new TableVersion(result.Version, result.Etag)); + } + + /// + public async Task InsertRow(MembershipEntry entry, TableVersion tableVersion) + { + ArgumentNullException.ThrowIfNull(entry); + ArgumentNullException.ThrowIfNull(tableVersion); + var silos = await ReadAll(); + + var currentEntry = silos.Members.FirstOrDefault(w => w.Item1.SiloAddress.ToParsableString() == entry.SiloAddress.ToParsableString()); + + if (currentEntry is not null) + { + // already exists, fail. + return false; + } + + var appliedInfo = await _mapper!.UpdateIfAsync( + "SET silos[?] = ?, version = ?, etag = ? WHERE id = ?", + entry.SiloAddress.ToParsableString(), + MapSilo(entry, tableVersion), + tableVersion.Version, + tableVersion.VersionEtag, + _clusterOptions.ClusterId) + .ConfigureAwait(false); + return appliedInfo.Applied; + } + + /// + public async Task UpdateRow(MembershipEntry entry, string etag, TableVersion tableVersion) + { + ArgumentNullException.ThrowIfNull(entry); + ArgumentNullException.ThrowIfNull(etag); + ArgumentNullException.ThrowIfNull(tableVersion); + var row = await ReadRow(entry.SiloAddress) + .ConfigureAwait(false); + + if (!row.Members.Any()) + { + return false; + } + + var appliedInfo = await _mapper!.UpdateIfAsync( + "SET silos[?] = ?, version = ?, etag = ? WHERE id = ?", + entry.SiloAddress.ToParsableString(), + MapSilo(entry, etag), + tableVersion.Version, + tableVersion.VersionEtag, + _clusterOptions.ClusterId) + .ConfigureAwait(false); + + return appliedInfo.Applied; + } + + /// + public async Task UpdateIAmAlive(MembershipEntry entry) + { + ArgumentNullException.ThrowIfNull(entry); + var resultList = await _mapper! + .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId) + .ConfigureAwait(false); + + var results = resultList.ToList(); + if (!results.Any()) + { + return; + } + + var result = results.FirstOrDefault(); + + if (result is null) + { + return; + } + + var siloKey = entry.SiloAddress.ToParsableString(); + var siloExists = result.Silos.ContainsKey(siloKey); + + if (!siloExists) + { + return; + } + + var silo = result.Silos[siloKey]; + silo.AliveOn = entry.IAmAliveTime; + + await _mapper!.UpdateAsync( + "SET silos[?] = ? WHERE id = ?", + entry.SiloAddress.ToParsableString(), + silo, + _clusterOptions.ClusterId) + .ConfigureAwait(false); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private static KeyValuePair MatchSiloKey(IDictionary result, SiloAddress siloAddress) + { + return result + .FirstOrDefault(w => w.Key == siloAddress.ToParsableString()); + } + + private static MembershipEntry MapEntry(KeyValuePair entry) + { + return new MembershipEntry + { + SiloAddress = SiloAddress.FromParsableString(entry.Value.Address), + Status = (SiloStatus)entry.Value.Status, + FaultZone = entry.Value.FaultZone, + SuspectTimes = entry + .Value + .SuspectTimes + .ToList() + .ConvertAll(item => + new Tuple(SiloAddress.FromParsableString(item.Item1), item.Item2.DateTime)), + HostName = entry.Value.HostName, + ProxyPort = entry.Value.ProxyPort, + RoleName = entry.Value.Role, + SiloName = entry.Value.Name, + StartTime = entry.Value.StartedOn.DateTime, + UpdateZone = entry.Value.UpdateZone, + IAmAliveTime = entry.Value.AliveOn.DateTime, + }; + } + + private static Silo MapSilo(MembershipEntry entry, TableVersion tableVersion) + { + return MapSilo(entry, tableVersion.VersionEtag); + } + + private static Silo MapSilo(MembershipEntry entry, string etag) + { + return new Silo + { + Address = entry.SiloAddress.ToParsableString(), + HostName = entry.HostName, + ProxyPort = entry.ProxyPort, + Etag = etag, + Name = entry.SiloName, + Role = entry.RoleName, + SuspectTimes = entry.SuspectTimes is null + ? new List>() + : entry + .SuspectTimes + .ConvertAll(item => + new Tuple( + item.Item1.ToParsableString(), + item.Item2)), + Status = (int)entry.Status, + FaultZone = entry.FaultZone, + AliveOn = entry.IAmAliveTime, + StartedOn = entry.StartTime, + UpdateZone = entry.UpdateZone, + Timestamp = DateTime.UtcNow, + }; + } + + private void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + _session?.Dispose(); + _cluster.Dispose(); + } + + private async Task InitializeDatabaseAsync() + { + var suspectTimesTypeStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.1_SuspectTime.sql"); + var siloTypeStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.2_Silo.sql"); + var membershipTableStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.3_Membership.sql"); + + using var suspectTimesTypeReader = new StreamReader(suspectTimesTypeStream!, Encoding.UTF8, true); + using var siloTypeReader = new StreamReader(siloTypeStream!, Encoding.UTF8, true); + using var membershipTableReader = new StreamReader(membershipTableStream!, Encoding.UTF8, true); + var sqlSuspectTimesType = await suspectTimesTypeReader.ReadToEndAsync().ConfigureAwait(false); + var sqlSiloType = await siloTypeReader.ReadToEndAsync().ConfigureAwait(false); + var sqlMembershipTable = await membershipTableReader.ReadToEndAsync().ConfigureAwait(false); + + await _session!.ExecuteAsync(new SimpleStatement(sqlSuspectTimesType)); + await _session!.ExecuteAsync(new SimpleStatement(sqlSiloType)); + await _session!.ExecuteAsync(new SimpleStatement(sqlMembershipTable)); + } + + private async Task InitializeVersionDataAsync(string clusterId) + { + ArgumentNullException.ThrowIfNull(clusterId); + await _mapper! + .InsertIfNotExistsAsync(new Membership { Id = clusterId }) + .ConfigureAwait(false); + } +} From b6aad2f100e647117c948467661cc54a40cfe259 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:39:35 +0200 Subject: [PATCH 065/174] chore: Moved CassandraMembershipTable.cs --- .../Cassandra/CassandraMembershipTable.cs | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 src/Clustering/Cassandra/CassandraMembershipTable.cs diff --git a/src/Clustering/Cassandra/CassandraMembershipTable.cs b/src/Clustering/Cassandra/CassandraMembershipTable.cs deleted file mode 100644 index d76334a..0000000 --- a/src/Clustering/Cassandra/CassandraMembershipTable.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Escendit Ltd. All Rights Reserved. -// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. - -namespace Escendit.Orleans.Clustering.Cassandra; - -using global::Orleans; -using global::Orleans.Runtime; - -/// -/// Cassandra Membership Table. -/// -public class CassandraMembershipTable : IMembershipTable -{ - /// - public Task InitializeMembershipTable(bool tryInitTableVersion) - { - throw new NotImplementedException(); - } - - /// - public Task DeleteMembershipTableEntries(string clusterId) - { - throw new NotImplementedException(); - } - - /// - public Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate) - { - throw new NotImplementedException(); - } - - /// - public Task ReadRow(SiloAddress key) - { - throw new NotImplementedException(); - } - - /// - public Task ReadAll() - { - throw new NotImplementedException(); - } - - /// - public Task InsertRow(MembershipEntry entry, TableVersion tableVersion) - { - throw new NotImplementedException(); - } - - /// - public Task UpdateRow(MembershipEntry entry, string etag, TableVersion tableVersion) - { - throw new NotImplementedException(); - } - - /// - public Task UpdateIAmAlive(MembershipEntry entry) - { - throw new NotImplementedException(); - } -} From ffa2d6e815c8db392854bec1885c1a52c2ed7767 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:40:01 +0200 Subject: [PATCH 066/174] chore: Updated Directory.Packages.props --- Directory.Packages.props | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3c2a1a5..32e5936 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,9 @@ true + + From 07efd14ac80336e009148186ba5ebdca1bc7bac0 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:41:17 +0200 Subject: [PATCH 067/174] chore: Added Escendit.Orleans.Clustering.Cassandra.csproj --- .../Escendit.Orleans.Clustering.Cassandra.csproj | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj index 61b4c67..7bf32fa 100644 --- a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj +++ b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj @@ -1,5 +1,16 @@ - + + + + + + + + + + + + From ce258a55f32a7676ff4bbd4b28aef354f0ec1685 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:41:29 +0200 Subject: [PATCH 068/174] chore: Added Escendit.Orleans.Clustering.Cassandra.Tests.csproj --- .../Escendit.Orleans.Clustering.Cassandra.Tests.csproj | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj diff --git a/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj b/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj new file mode 100644 index 0000000..157810b --- /dev/null +++ b/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj @@ -0,0 +1,5 @@ + + + + + From 3a904c1b8016ac20c312d01d0b56c9821a0347f2 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:41:40 +0200 Subject: [PATCH 069/174] chore: Added Escendit.Orleans.Extensions.Cassandra.sln --- Escendit.Orleans.Extensions.Cassandra.sln | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Escendit.Orleans.Extensions.Cassandra.sln b/Escendit.Orleans.Extensions.Cassandra.sln index 9cc2deb..2b5c771 100644 --- a/Escendit.Orleans.Extensions.Cassandra.sln +++ b/Escendit.Orleans.Extensions.Cassandra.sln @@ -31,6 +31,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Persistence", "Persistence" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Persistence.Cassandra.Tests", "test\Persistence\Cassandra\Escendit.Orleans.Persistence.Cassandra.Tests.csproj", "{7D40168C-40B6-4339-88A0-4F6E408F2EE7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clustering", "Clustering", "{16F96498-E9B7-4747-898A-86AF7D6B44D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Clustering.Cassandra.Tests", "test\Clustering\Cassandra\Escendit.Orleans.Clustering.Cassandra.Tests.csproj", "{A9488ABE-E593-46FB-9C63-32A687EEFE38}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -52,6 +56,8 @@ Global {6E3EEC03-C465-4C7F-93FB-E62A2ECC6847} = {77E5F3C7-A347-42F4-BF93-51248B035D56} {9DC8501A-84DE-4272-A660-6615DA7C91B0} = {F719A264-551D-450A-9FBF-F3802858FFAE} {7D40168C-40B6-4339-88A0-4F6E408F2EE7} = {9DC8501A-84DE-4272-A660-6615DA7C91B0} + {16F96498-E9B7-4747-898A-86AF7D6B44D5} = {F719A264-551D-450A-9FBF-F3802858FFAE} + {A9488ABE-E593-46FB-9C63-32A687EEFE38} = {16F96498-E9B7-4747-898A-86AF7D6B44D5} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -82,5 +88,9 @@ Global {7D40168C-40B6-4339-88A0-4F6E408F2EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D40168C-40B6-4339-88A0-4F6E408F2EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D40168C-40B6-4339-88A0-4F6E408F2EE7}.Release|Any CPU.Build.0 = Release|Any CPU + {A9488ABE-E593-46FB-9C63-32A687EEFE38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9488ABE-E593-46FB-9C63-32A687EEFE38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9488ABE-E593-46FB-9C63-32A687EEFE38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9488ABE-E593-46FB-9C63-32A687EEFE38}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 7d762ce93eed60ba8d02e8caef97e3781ec82b17 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:41:57 +0200 Subject: [PATCH 070/174] chore: Updated Escendit.Orleans.Persistence.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj index 89672be..cac6be3 100644 --- a/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj +++ b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj @@ -3,4 +3,8 @@ + + + + From 3df43439110635afb89e867092f251c4a7939176 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:42:20 +0200 Subject: [PATCH 071/174] feature: Added Membership.cs --- src/Clustering/Cassandra/Schema/Membership.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/Clustering/Cassandra/Schema/Membership.cs diff --git a/src/Clustering/Cassandra/Schema/Membership.cs b/src/Clustering/Cassandra/Schema/Membership.cs new file mode 100644 index 0000000..931211a --- /dev/null +++ b/src/Clustering/Cassandra/Schema/Membership.cs @@ -0,0 +1,34 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +/// +/// Membership. +/// +public class Membership +{ + /// + /// Gets or sets the id. + /// + /// The id. + public string Id { get; set; } = default!; + + /// + /// Gets the silos. + /// + /// The silos. + public IDictionary Silos { get; init; } = new Dictionary(); + + /// + /// Gets or sets the version. + /// + /// The version. + public int Version { get; set; } + + /// + /// Gets or sets the etag. + /// + /// The etag. + public string Etag { get; set; } = Guid.Empty.ToString(); +} From a8cdd97519dc08d37faa7cf031f4cdd1a6113184 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:42:30 +0200 Subject: [PATCH 072/174] feature: Added MembershipMapping.cs --- .../Cassandra/Mapping/MembershipMapping.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/Clustering/Cassandra/Mapping/MembershipMapping.cs diff --git a/src/Clustering/Cassandra/Mapping/MembershipMapping.cs b/src/Clustering/Cassandra/Mapping/MembershipMapping.cs new file mode 100644 index 0000000..342b860 --- /dev/null +++ b/src/Clustering/Cassandra/Mapping/MembershipMapping.cs @@ -0,0 +1,27 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +using global::Cassandra.Mapping; + +/// +/// Membership Mapping. +/// +public class MembershipMapping : Mappings +{ + /// + /// Initializes a new instance of the class. + /// + public MembershipMapping() + { + For() + .TableName("membership") + .ClusteringKey(p => p.Id) + .ExplicitColumns() + .Column(p => p.Id, cm => cm.WithName("id")) + .Column(p => p.Etag, cm => cm.WithName("etag")) + .Column(p => p.Silos, cm => cm.WithName("silos")) + .Column(p => p.Version, cm => cm.WithName("version")); + } +} From c8193b94a6cdb77b26d51974b3cba9fe4285eae9 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:42:44 +0200 Subject: [PATCH 073/174] chore: Added MembershipTableTests.cs --- .../Cassandra/MembershipTableTests.cs | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 test/Clustering/Cassandra/MembershipTableTests.cs diff --git a/test/Clustering/Cassandra/MembershipTableTests.cs b/test/Clustering/Cassandra/MembershipTableTests.cs new file mode 100644 index 0000000..d761336 --- /dev/null +++ b/test/Clustering/Cassandra/MembershipTableTests.cs @@ -0,0 +1,133 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra.Tests; + +using System.Net; +using Collections; +using Fixtures; +using global::Orleans; +using global::Orleans.Runtime; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Categories; + +/// +/// Membership Table Tests. +/// +[Collection(SimulatorCollectionFixture.Name)] +public class MembershipTableTests +{ + private readonly SimulatorFixture _simulatorFixture; + + /// + /// Initializes a new instance of the class. + /// + /// The simulator fixture. + public MembershipTableTests(SimulatorFixture simulatorFixture) + { + _simulatorFixture = simulatorFixture; + } + + /// + /// Test Connect. + /// + /// The task. + [Fact] + [IntegrationTest] + public async Task Test_ConnectAsync() + { + var membershipTable = _simulatorFixture.Services.GetRequiredService(); + await membershipTable.InitializeMembershipTable(true); + Assert.NotNull(membershipTable); + } + + /// + /// Test Inserting a row. + /// + /// The task. + [Fact] + [IntegrationTest] + public async Task Test_InsertRow_Simple() + { + var membershipTable = _simulatorFixture.Services.GetRequiredService(); + await membershipTable.InitializeMembershipTable(false); + Assert.NotNull(membershipTable); + } + + /// + /// Test updating a row. + /// + /// The version. + /// The task. + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [IntegrationTest] + public async Task Test_InsertRow_Sequence(int version) + { + var membershipTable = _simulatorFixture.Services.GetRequiredService(); + await membershipTable.InitializeMembershipTable(false); + await membershipTable.InsertRow( + new MembershipEntry + { + Status = SiloStatus.Active, + SiloAddress = SiloAddress.New(IPAddress.Loopback, 11111, 0), + FaultZone = 0, + HostName = "localhost", + ProxyPort = 30000, + RoleName = "Test", + SiloName = "Test", + StartTime = DateTime.UtcNow, + UpdateZone = 0, + IAmAliveTime = DateTime.UtcNow, + }, + new TableVersion(version, Guid.NewGuid().ToString())); + Assert.NotNull(membershipTable); + } + + /// + /// Test UpdateRow Simple. + /// + /// The task. + [Fact] + [IntegrationTest] + public async Task Test_UpdateRow_Simple() + { + var membershipTable = _simulatorFixture.Services.GetRequiredService(); + await membershipTable.InitializeMembershipTable(false); + await membershipTable.InsertRow( + new MembershipEntry + { + Status = SiloStatus.Active, + SiloAddress = SiloAddress.New(IPAddress.Loopback, 11111, 1), + FaultZone = 0, + HostName = "localhost", + ProxyPort = 30000, + RoleName = "Test", + SiloName = "Test", + StartTime = DateTime.UtcNow, + UpdateZone = 0, + IAmAliveTime = DateTime.UtcNow, + }, + new TableVersion(3, Guid.NewGuid().ToString())); + await membershipTable.UpdateRow( + new MembershipEntry + { + Status = SiloStatus.Active, + SiloAddress = SiloAddress.New(IPAddress.Loopback, 11111, 1), + FaultZone = 0, + HostName = "localhost", + ProxyPort = 30000, + RoleName = "Test", + SiloName = "Test", + StartTime = DateTime.UtcNow, + UpdateZone = 0, + IAmAliveTime = DateTime.UtcNow, + }, + Guid.NewGuid().ToString(), + new TableVersion(4, Guid.NewGuid().ToString())); + Assert.NotNull(membershipTable); + } +} From 8c4a09e96bdaef189a4c179e48cc5e74fd0b1a9d Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:43:05 +0200 Subject: [PATCH 074/174] feature: Added ServiceCollectionExtensions.cs --- .../Hosting/ServiceCollectionExtensions.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs b/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..21bb14a --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs @@ -0,0 +1,48 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Microsoft.Extensions.DependencyInjection; + +using Escendit.Orleans.Clustering.Cassandra; +using Escendit.Orleans.Clustering.Cassandra.Options; +using Options; +using Orleans; + +/// +/// Service Collection Extensions. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Add Cassandra Clustering. + /// + /// The initial service collection. + /// The configure options. + /// The updated service collection. + public static IServiceCollection AddCassandraClustering( + this IServiceCollection services, + Action configureOptions) + { + return services + .AddCassandraClustering(builder => builder.Configure(configureOptions)); + } + + /// + /// Add Cassandra Clustering. + /// + /// The initial service collection. + /// The configure options. + /// The updated service collection. + public static IServiceCollection AddCassandraClustering( + this IServiceCollection services, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configureOptions); + configureOptions.Invoke(services.AddOptions()); + return services + .AddSingleton(serviceProvider => + new CassandraMembershipTable( + serviceProvider, serviceProvider.GetRequiredService>().Value)); + } +} From 117f78d277acbdadc38e227152042d8c12982436 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:43:39 +0200 Subject: [PATCH 075/174] feature: Added Silo.cs --- src/Clustering/Cassandra/Schema/Silo.cs | 88 +++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/Clustering/Cassandra/Schema/Silo.cs diff --git a/src/Clustering/Cassandra/Schema/Silo.cs b/src/Clustering/Cassandra/Schema/Silo.cs new file mode 100644 index 0000000..6d74f44 --- /dev/null +++ b/src/Clustering/Cassandra/Schema/Silo.cs @@ -0,0 +1,88 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +/// +/// Silo. +/// +public class Silo +{ + /// + /// Gets or sets the blob. + /// + /// The blob. + public string Address { get; set; } = default!; + + /// + /// Gets or sets the alive on. + /// + /// The alive on. + public DateTimeOffset AliveOn { get; set; } + + /// + /// Gets or sets the etag. + /// + /// The etag. + public string Etag { get; set; } = default!; + + /// + /// Gets or sets the fault zone. + /// + /// The fault zone. + public int FaultZone { get; set; } + + /// + /// Gets or sets the host name. + /// + /// The host name. + public string HostName { get; set; } = default!; + + /// + /// Gets or sets the name. + /// + /// The name. + public string Name { get; set; } = default!; + + /// + /// Gets or sets the proxy port. + /// + /// The proxy port. + public int ProxyPort { get; set; } = 30000; + + /// + /// Gets or sets the started on. + /// + /// The started on. + public DateTimeOffset StartedOn { get; set; } + + /// + /// Gets or sets the role. + /// + /// The role. + public string? Role { get; set; } + + /// + /// Gets or sets the status. + /// + /// The status. + public int Status { get; set; } + + /// + /// Gets the suspect times. + /// + /// The suspect times. + public IList> SuspectTimes { get; init; } = new List>(); + + /// + /// Gets or sets the update zone. + /// + /// The update zone. + public int UpdateZone { get; set; } + + /// + /// Gets or sets the timestamp. + /// + /// The timestamp. + public DateTimeOffset Timestamp { get; set; } +} From 3f7cf0b3572f86570d24929deda5bfe931ec991f Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:43:59 +0200 Subject: [PATCH 076/174] chore: Added SimulatorCollectionFixture.cs --- .../Collections/SimulatorCollectionFixture.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/Clustering/Cassandra/Collections/SimulatorCollectionFixture.cs diff --git a/test/Clustering/Cassandra/Collections/SimulatorCollectionFixture.cs b/test/Clustering/Cassandra/Collections/SimulatorCollectionFixture.cs new file mode 100644 index 0000000..79309d9 --- /dev/null +++ b/test/Clustering/Cassandra/Collections/SimulatorCollectionFixture.cs @@ -0,0 +1,19 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra.Tests.Collections; + +using Fixtures; +using Xunit; + +/// +/// Simulator Collection Fixture. +/// +[CollectionDefinition(Name)] +public class SimulatorCollectionFixture : ICollectionFixture +{ + /// + /// Name. + /// + public const string Name = "SimulatorCollection"; +} From e558a1aa986d3e01c041708bc32602f1963dacab Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:44:29 +0200 Subject: [PATCH 077/174] chore: Added SimulatorFixture.cs --- .../Cassandra/Fixtures/SimulatorFixture.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs diff --git a/test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs b/test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs new file mode 100644 index 0000000..d96f920 --- /dev/null +++ b/test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs @@ -0,0 +1,43 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra.Tests.Fixtures; + +using global::Orleans.Configuration; +using global::Orleans.Runtime; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Options; + +/// +/// Simulator Fixture. +/// +public sealed class SimulatorFixture +{ + /// + /// Initializes a new instance of the class. + /// + public SimulatorFixture() + { + var services = new ServiceCollection() + .AddCassandraClientAsDefault(options => + { + options + .Endpoints.Add("localhost"); + options.DefaultKeyspace = "test"; + }) + .Configure(options => options.ClusterId = "default") + .Configure(options => options.ClientName = "Default") + .AddCassandraClustering(options => options.ClientName = "Default"); + + services + .TryAddSingleton(typeof(IKeyedServiceCollection<,>), typeof(KeyedServiceCollection<,>)); + Services = services.BuildServiceProvider(); + } + + /// + /// Gets the services. + /// + /// The services. + public IServiceProvider Services { get; } +} From 36b2de477b4b1dabe33f41dd0a180da3b7f42bc2 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 21:44:41 +0200 Subject: [PATCH 078/174] feature: Added SuspectTime.cs --- .../Cassandra/Schema/SuspectTime.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/Clustering/Cassandra/Schema/SuspectTime.cs diff --git a/src/Clustering/Cassandra/Schema/SuspectTime.cs b/src/Clustering/Cassandra/Schema/SuspectTime.cs new file mode 100644 index 0000000..406672a --- /dev/null +++ b/src/Clustering/Cassandra/Schema/SuspectTime.cs @@ -0,0 +1,22 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +/// +/// Suspect Time. +/// +public class SuspectTime +{ + /// + /// Gets or sets the address. + /// + /// The address. + public string Address { get; set; } = default!; + + /// + /// Gets or sets the timestamp. + /// + /// The timestamp. + public DateTime Timestamp { get; set; } +} From d03e16a89e497ae9371a39866e9c5e831ce732c0 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 22:05:40 +0200 Subject: [PATCH 079/174] feature: Updated CassandraClusteringOptions.cs - introduced the MaxStaleness property --- .../Cassandra/Options/CassandraClusteringOptions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs b/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs index 0623a88..da07ed8 100644 --- a/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs +++ b/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs @@ -3,6 +3,8 @@ namespace Escendit.Orleans.Clustering.Cassandra.Options; +using global::Orleans.Messaging; + /// /// Cassandra Clustering Options. /// @@ -13,4 +15,11 @@ public record CassandraClusteringOptions /// /// The client name. public string? ClientName { get; set; } + + /// + /// Gets or sets the max staleness. + /// + /// The maximum staleness period. + /// + public TimeSpan MaxStaleness { get; set; } } From cc2b8608f8781b6b7c2b235d8b5e42359d905b3e Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 22:06:22 +0200 Subject: [PATCH 080/174] feature: Added CassandraGatewayListProvider.cs - simple implementation --- .../Provider/CassandraGatewayListProvider.cs | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs index cff7c69..0045a98 100644 --- a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs +++ b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs @@ -3,22 +3,36 @@ namespace Escendit.Orleans.Clustering.Cassandra; +using global::Orleans; using global::Orleans.Messaging; +using global::Orleans.Runtime; +using Microsoft.Extensions.Options; +using Options; /// /// Cassandra Gateway List Provider. /// public class CassandraGatewayListProvider : IGatewayListProvider { + private readonly IMembershipTable _membershipTable; + /// /// Initializes a new instance of the class. /// - public CassandraGatewayListProvider() + /// The membership table. + /// The options. + public CassandraGatewayListProvider( + IMembershipTable membershipTable, + IOptions options) { + ArgumentNullException.ThrowIfNull(membershipTable); + ArgumentNullException.ThrowIfNull(options); + _membershipTable = membershipTable; + MaxStaleness = options.Value.MaxStaleness; } /// - public TimeSpan MaxStaleness { get; } = TimeSpan.Zero; + public TimeSpan MaxStaleness { get; } /// public bool IsUpdatable { get; } = true; @@ -26,12 +40,19 @@ public CassandraGatewayListProvider() /// public Task InitializeGatewayListProvider() { - throw new NotImplementedException(); + return Task.CompletedTask; } /// - public Task> GetGateways() + public async Task> GetGateways() { - throw new NotImplementedException(); + var membershipTableData = await _membershipTable.ReadAll(); + return membershipTableData + .Members + .Where(w => w.Item1.Status == SiloStatus.Active) + .ToList() + .ConvertAll(item => item + .Item1 + .SiloAddress.ToGatewayUri()); } } From 556d554c88692339caabd415d29ee4aa876f8bef Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 22:06:51 +0200 Subject: [PATCH 081/174] feature: Updated CassandraMembershipTable.cs - simplify options parameters --- .../Cassandra/Provider/CassandraMembershipTable.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs index bc65723..6f9be66 100644 --- a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs +++ b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs @@ -36,13 +36,13 @@ internal sealed class CassandraMembershipTable : IMembershipTable, IDisposable /// The options. public CassandraMembershipTable( IServiceProvider serviceProvider, - CassandraClusteringOptions options) + IOptions options) { ArgumentNullException.ThrowIfNull(serviceProvider); ArgumentNullException.ThrowIfNull(options); _clusterOptions = serviceProvider.GetRequiredService>().Value; - _cluster = serviceProvider.GetRequiredServiceByName(options.ClientName); - _clientOptions = serviceProvider.GetOptionsByName(options.ClientName); + _cluster = serviceProvider.GetRequiredServiceByName(options.Value.ClientName); + _clientOptions = serviceProvider.GetOptionsByName(options.Value.ClientName); _selfAssembly = Assembly.GetAssembly(typeof(CassandraMembershipTable))!; } From e623ebc7ce06af436ff811bc3eb775cb5b798a2b Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Wed, 27 Sep 2023 22:07:22 +0200 Subject: [PATCH 082/174] feature: Updated ServiceCollectionExtensions.cs - introduced registration for GatewayListProvider --- .../Cassandra/Hosting/ServiceCollectionExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs b/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs index 21bb14a..60f378e 100644 --- a/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs +++ b/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs @@ -7,6 +7,7 @@ namespace Microsoft.Extensions.DependencyInjection; using Escendit.Orleans.Clustering.Cassandra.Options; using Options; using Orleans; +using Orleans.Messaging; /// /// Service Collection Extensions. @@ -41,8 +42,7 @@ public static IServiceCollection AddCassandraClustering( ArgumentNullException.ThrowIfNull(configureOptions); configureOptions.Invoke(services.AddOptions()); return services - .AddSingleton(serviceProvider => - new CassandraMembershipTable( - serviceProvider, serviceProvider.GetRequiredService>().Value)); + .AddSingleton() + .AddSingleton(); } } From 896e60fae7b068fa33ea8046f68b6018f1e7e71a Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:38:15 +0200 Subject: [PATCH 083/174] feature: Updated CassandraMembershipTable.cs --- .../Provider/CassandraMembershipTable.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs index 6f9be66..1de7ceb 100644 --- a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs +++ b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs @@ -7,6 +7,7 @@ namespace Escendit.Orleans.Clustering.Cassandra; using System.Reflection; using System.Text; + using Escendit.Extensions.Hosting.Cassandra; using global::Cassandra; using global::Cassandra.Mapping; @@ -14,14 +15,16 @@ namespace Escendit.Orleans.Clustering.Cassandra; using global::Orleans.Configuration; using global::Orleans.Runtime; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Options; /// /// Cassandra Membership Table. /// -internal sealed class CassandraMembershipTable : IMembershipTable, IDisposable +internal sealed partial class CassandraMembershipTable : IMembershipTable, IDisposable { + private readonly ILogger _logger; private readonly Assembly _selfAssembly; private readonly CassandraClientOptions _clientOptions; private readonly ClusterOptions _clusterOptions; @@ -32,14 +35,17 @@ internal sealed class CassandraMembershipTable : IMembershipTable, IDisposable /// /// Initializes a new instance of the class. /// + /// The logger. /// The service provider. /// The options. public CassandraMembershipTable( + ILogger logger, IServiceProvider serviceProvider, IOptions options) { ArgumentNullException.ThrowIfNull(serviceProvider); ArgumentNullException.ThrowIfNull(options); + _logger = logger; _clusterOptions = serviceProvider.GetRequiredService>().Value; _cluster = serviceProvider.GetRequiredServiceByName(options.Value.ClientName); _clientOptions = serviceProvider.GetOptionsByName(options.Value.ClientName); @@ -370,8 +376,22 @@ private async Task InitializeDatabaseAsync() private async Task InitializeVersionDataAsync(string clusterId) { ArgumentNullException.ThrowIfNull(clusterId); - await _mapper! - .InsertIfNotExistsAsync(new Membership { Id = clusterId }) - .ConfigureAwait(false); + try + { + await _mapper! + .InsertIfNotExistsAsync(new Membership { Id = clusterId }) + .ConfigureAwait(false); + } + catch (InvalidCastException ex) + { + LogException(ex, ex.Message); + } } + + [LoggerMessage( + EventId = 500, + EventName = "Log Exception", + Level = LogLevel.Error, + Message = "Error occurred: {message}")] + private partial void LogException(Exception ex, string message); } From 73379e5b260d96fae208b258735cc513d7733767 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:38:55 +0200 Subject: [PATCH 084/174] feature: Added ClientBuilderExtensions.cs --- .../Hosting/ClientBuilderExtensions.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs new file mode 100644 index 0000000..7c2e1e9 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Escendit.Orleans.Clustering.Cassandra; +using Messaging; +using Microsoft.Extensions.DependencyInjection; + +/// +/// Client Builder Extensions. +/// +public static class ClientBuilderExtensions +{ + /// + /// Use Cassandra Clustering. + /// + /// The initial silo builder. + /// The updated silo builder. + public static IClusteringClientBuilder UseCassandraClustering(this IClientBuilder clientBuilder) + { + ArgumentNullException.ThrowIfNull(clientBuilder); + return new ClusteringClientBuilder(clientBuilder + .Services + .AddSingleton()); + } +} From 7c13daea8c45fad1cea55496f63af9c5eb87d397 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:39:30 +0200 Subject: [PATCH 085/174] chore: Added ClusterCollectionFixture.cs --- .../Collections/ClusterCollectionFixture.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/Clustering/Cassandra/Collections/ClusterCollectionFixture.cs diff --git a/test/Clustering/Cassandra/Collections/ClusterCollectionFixture.cs b/test/Clustering/Cassandra/Collections/ClusterCollectionFixture.cs new file mode 100644 index 0000000..71f3285 --- /dev/null +++ b/test/Clustering/Cassandra/Collections/ClusterCollectionFixture.cs @@ -0,0 +1,19 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra.Tests.Collections; + +using Escendit.Orleans.Persistence.Cassandra.Tests.Fixtures; +using Xunit; + +/// +/// Cluster Collection Fixture. +/// +[CollectionDefinition(Name)] +public class ClusterCollectionFixture : ICollectionFixture +{ + /// + /// Name. + /// + public const string Name = "ClusterCollection"; +} From 08fe23d5ae7cdabb7d0082c6dadd44d85fcd8bbd Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:39:45 +0200 Subject: [PATCH 086/174] feature: Added ClusteringClientBuilder.cs --- .../Hosting/ClusteringClientBuilder.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/ClusteringClientBuilder.cs diff --git a/src/Clustering/Cassandra/Hosting/ClusteringClientBuilder.cs b/src/Clustering/Cassandra/Hosting/ClusteringClientBuilder.cs new file mode 100644 index 0000000..860c32a --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/ClusteringClientBuilder.cs @@ -0,0 +1,24 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Microsoft.Extensions.DependencyInjection; + +/// +/// Clustering Client Builder. +/// +internal class ClusteringClientBuilder : IClusteringClientBuilder +{ + /// + /// Initializes a new instance of the class. + /// + /// The service collection. + public ClusteringClientBuilder(IServiceCollection serviceCollection) + { + Services = serviceCollection; + } + + /// + public IServiceCollection Services { get; } +} From f8983c4c727398516b8efdf075286a482ce6480f Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:39:57 +0200 Subject: [PATCH 087/174] feature: Added ClusteringClientBuilderExtensions.cs --- .../ClusteringClientBuilderExtensions.cs | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs new file mode 100644 index 0000000..9ebf6bb --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs @@ -0,0 +1,172 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Escendit.Extensions.Hosting.Cassandra; +using Escendit.Orleans.Clustering.Cassandra.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +/// +/// Clustering Client Builder Extensions. +/// +public static class ClusteringClientBuilderExtensions +{ + /// + /// With Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithClientAsDefault( + this IClusteringClientBuilder clusteringClientBuilder, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringClientBuilder + .Services + .AddCassandraClientAsDefault(configureOptions) + .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); + return clusteringClientBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithClientAsDefault( + this IClusteringClientBuilder clusteringClientBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringClientBuilder + .Services + .AddCassandraClientAsDefault(configureOptions) + .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); + return clusteringClientBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The options name. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithClientFromOptionsAsDefault( + this IClusteringClientBuilder clusteringClientBuilder, + string optionsName) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(optionsName); + clusteringClientBuilder + .Services + .AddCassandraClientFromOptionsAsDefault(optionsName) + .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); + return clusteringClientBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithClient( + this IClusteringClientBuilder clusteringClientBuilder, + string name, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringClientBuilder + .Services + .AddCassandraClient(name, configureOptions) + .Configure(options => options.ClientName = name); + return clusteringClientBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithClient( + this IClusteringClientBuilder clusteringClientBuilder, + string name, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringClientBuilder.Configure(options => options.ClientName = name); + clusteringClientBuilder + .Services + .AddCassandraClient(name, configureOptions) + .Configure(options => options.ClientName = name); + return clusteringClientBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The options name. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithClientFromOptions( + this IClusteringClientBuilder clusteringClientBuilder, + string name, + string optionsName) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(optionsName); + clusteringClientBuilder + .Services + .AddCassandraClientFromOptions(name, optionsName) + .Configure(options => options.ClientName = name); + return clusteringClientBuilder; + } + + /// + /// With Options. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithOptions( + this IClusteringClientBuilder clusteringClientBuilder, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + return clusteringClientBuilder + .WithOptions(builder => + builder.Configure(configureOptions)); + } + + /// + /// With Options. + /// + /// The initial clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringClientBuilder WithOptions( + this IClusteringClientBuilder clusteringClientBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringClientBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + configureOptions.Invoke(clusteringClientBuilder.Services.AddOptions()); + return clusteringClientBuilder; + } +} From b141177825016ee25b5350de6cd43831bee91876 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:40:23 +0200 Subject: [PATCH 088/174] feature: Added ClusteringSiloBuilder.cs --- .../Hosting/ClusteringSiloBuilder.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/ClusteringSiloBuilder.cs diff --git a/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilder.cs b/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilder.cs new file mode 100644 index 0000000..a65eef2 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilder.cs @@ -0,0 +1,24 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Microsoft.Extensions.DependencyInjection; + +/// +/// Clustering Silo Builder. +/// +internal class ClusteringSiloBuilder : IClusteringSiloBuilder +{ + /// + /// Initializes a new instance of the class. + /// + /// The service collection. + public ClusteringSiloBuilder(IServiceCollection serviceCollection) + { + Services = serviceCollection; + } + + /// + public IServiceCollection Services { get; } +} From c6ae6a6329b8ad158035d89898fa15987f02eae8 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:40:33 +0200 Subject: [PATCH 089/174] feature: Added ClusteringSiloBuilderExtensions.cs --- .../ClusteringSiloBuilderExtensions.cs | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs new file mode 100644 index 0000000..9f5d891 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs @@ -0,0 +1,172 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Escendit.Extensions.Hosting.Cassandra; +using Escendit.Orleans.Clustering.Cassandra.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +/// +/// Clustering Silo Builder Extensions. +/// +public static class ClusteringSiloBuilderExtensions +{ + /// + /// With Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithClientAsDefault( + this IClusteringSiloBuilder clusteringSiloBuilder, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringSiloBuilder + .Services + .AddCassandraClientAsDefault(configureOptions) + .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); + return clusteringSiloBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithClientAsDefault( + this IClusteringSiloBuilder clusteringSiloBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringSiloBuilder + .Services + .AddCassandraClientAsDefault(configureOptions) + .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); + return clusteringSiloBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The options name. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithClientFromOptionsAsDefault( + this IClusteringSiloBuilder clusteringSiloBuilder, + string optionsName) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(optionsName); + clusteringSiloBuilder + .Services + .AddCassandraClientFromOptionsAsDefault(optionsName) + .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); + return clusteringSiloBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithClient( + this IClusteringSiloBuilder clusteringSiloBuilder, + string name, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringSiloBuilder + .Services + .AddCassandraClient(name, configureOptions) + .Configure(options => options.ClientName = name); + return clusteringSiloBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithClient( + this IClusteringSiloBuilder clusteringSiloBuilder, + string name, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + clusteringSiloBuilder.Configure(options => options.ClientName = name); + clusteringSiloBuilder + .Services + .AddCassandraClient(name, configureOptions) + .Configure(options => options.ClientName = name); + return clusteringSiloBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The options name. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithClientFromOptions( + this IClusteringSiloBuilder clusteringSiloBuilder, + string name, + string optionsName) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(optionsName); + clusteringSiloBuilder + .Services + .AddCassandraClientFromOptions(name, optionsName) + .Configure(options => options.ClientName = name); + return clusteringSiloBuilder; + } + + /// + /// With Options. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithOptions( + this IClusteringSiloBuilder clusteringSiloBuilder, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + return clusteringSiloBuilder + .WithOptions(builder => + builder.Configure(configureOptions)); + } + + /// + /// With Options. + /// + /// The initial clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClusteringSiloBuilder WithOptions( + this IClusteringSiloBuilder clusteringSiloBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + configureOptions.Invoke(clusteringSiloBuilder.Services.AddOptions()); + return clusteringSiloBuilder; + } +} From 2b73826ff2eb849ae15f563ccde1de0fbc5cc4dc Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:40:54 +0200 Subject: [PATCH 090/174] feature: Added DataProvider.cs --- .../Cassandra/Provider/DataProvider.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Clustering/Cassandra/Provider/DataProvider.cs diff --git a/src/Clustering/Cassandra/Provider/DataProvider.cs b/src/Clustering/Cassandra/Provider/DataProvider.cs new file mode 100644 index 0000000..a3daaca --- /dev/null +++ b/src/Clustering/Cassandra/Provider/DataProvider.cs @@ -0,0 +1,31 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +using Microsoft.Extensions.Logging; + +/// +/// Data Provider. +/// +public partial class DataProvider +{ + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public DataProvider( + ILogger logger) + { + _logger = logger; + } + + [LoggerMessage( + EventId = 100, + EventName = "", + Level = LogLevel.Information, + Message = "Fine")] + private partial void LogSomething(); +} From d60ba45275f874cf2fb7a77f660ab883a71de561 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:41:49 +0200 Subject: [PATCH 091/174] chore: Added Directory.Build.props --- src/Directory.Build.props | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/Directory.Build.props diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..c31a17c --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,9 @@ + + + + + + <_Parameter1>$(AssemblyName).Tests + + + From 835d44d5ba6180bf4f259a3eec65c039f159adc4 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:42:08 +0200 Subject: [PATCH 092/174] chore: Updated Directory.Packages.props --- Directory.Packages.props | 62 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 32e5936..11417ca 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,34 +1,34 @@ - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 4c30fd0df2bc6776f10f06e024a37323f0517754 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:42:22 +0200 Subject: [PATCH 093/174] chore: Updated Escendit.Orleans.Clustering.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj index 7bf32fa..23908c8 100644 --- a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj +++ b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj @@ -1,6 +1,7 @@ + From 7a57508d015c229ca7d983b4f1e3aa30523d20bb Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:42:35 +0200 Subject: [PATCH 094/174] chore: Updated Escendit.Orleans.Clustering.Cassandra.Tests.csproj --- .../Escendit.Orleans.Clustering.Cassandra.Tests.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj b/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj index 157810b..700691d 100644 --- a/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj +++ b/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj @@ -2,4 +2,7 @@ + + + From 698501bb45a53bd471af3bb9ce199b74f5f040ce Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:42:49 +0200 Subject: [PATCH 095/174] feature: Added IClusteringClientBuilder.cs --- .../Cassandra/Hosting/IClusteringClientBuilder.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/IClusteringClientBuilder.cs diff --git a/src/Clustering/Cassandra/Hosting/IClusteringClientBuilder.cs b/src/Clustering/Cassandra/Hosting/IClusteringClientBuilder.cs new file mode 100644 index 0000000..ebc6a77 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/IClusteringClientBuilder.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +/// +/// Clustering Client Builder. +/// +public interface IClusteringClientBuilder : IClientBuilder +{ +} From 2c513f5d6135f0b0a103ac605125c2b3922cfd6e Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:43:02 +0200 Subject: [PATCH 096/174] feature: Added IClusteringSiloBuilder.cs --- .../Cassandra/Hosting/IClusteringSiloBuilder.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/IClusteringSiloBuilder.cs diff --git a/src/Clustering/Cassandra/Hosting/IClusteringSiloBuilder.cs b/src/Clustering/Cassandra/Hosting/IClusteringSiloBuilder.cs new file mode 100644 index 0000000..fb88f88 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/IClusteringSiloBuilder.cs @@ -0,0 +1,11 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +/// +/// Clustering Silo Builder. +/// +public interface IClusteringSiloBuilder : ISiloBuilder +{ +} From bdec5b40f2001c0aa2e6908003f8ca09a1cff303 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:43:20 +0200 Subject: [PATCH 097/174] fix: Updated ServiceCollectionExtensions.cs --- src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs b/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs index 60f378e..a24bb7f 100644 --- a/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs +++ b/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.DependencyInjection; /// /// Service Collection Extensions. /// -public static class ServiceCollectionExtensions +internal static class ServiceCollectionExtensions { /// /// Add Cassandra Clustering. From 484c44582973786bf748a4ba13638ba42a35f6e8 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:43:44 +0200 Subject: [PATCH 098/174] chore: Updated Silo.cs --- src/Clustering/Cassandra/Schema/Silo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Clustering/Cassandra/Schema/Silo.cs b/src/Clustering/Cassandra/Schema/Silo.cs index 6d74f44..b3a66aa 100644 --- a/src/Clustering/Cassandra/Schema/Silo.cs +++ b/src/Clustering/Cassandra/Schema/Silo.cs @@ -9,9 +9,9 @@ namespace Escendit.Orleans.Clustering.Cassandra; public class Silo { /// - /// Gets or sets the blob. + /// Gets or sets the address. /// - /// The blob. + /// The address. public string Address { get; set; } = default!; /// From 2dc0a7a9c34c50bdd776620d9ba7586c95760b80 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:44:00 +0200 Subject: [PATCH 099/174] feature: Added SiloBuilderExtensions.cs --- .../Hosting/SiloBuilderExtensions.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs new file mode 100644 index 0000000..63ad847 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Escendit.Orleans.Clustering.Cassandra; +using Microsoft.Extensions.DependencyInjection; + +/// +/// Silo Builder Extensions. +/// +public static class SiloBuilderExtensions +{ + /// + /// Use Cassandra Clustering. + /// + /// The initial silo builder. + /// The updated silo builder. + public static IClusteringSiloBuilder UseCassandraClustering( + this ISiloBuilder siloBuilder) + { + ArgumentNullException.ThrowIfNull(siloBuilder); + return new ClusteringSiloBuilder(siloBuilder + .Services + .AddSingleton()); + } +} From b5914d8d92b13da95958126e92a72f6797a855a1 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:44:14 +0200 Subject: [PATCH 100/174] chore: Added TestClusterFixture.cs --- .../Cassandra/Fixtures/TestClusterFixture.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs diff --git a/test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs b/test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs new file mode 100644 index 0000000..6e714e3 --- /dev/null +++ b/test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs @@ -0,0 +1,57 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Fixtures; + +using Configuration; +using global::Orleans.TestingHost; + +/// +/// Test Cluster Fixture. +/// +public sealed class TestClusterFixture : IDisposable +{ + /// + /// Initializes a new instance of the class. + /// + public TestClusterFixture() + { + var builder = new TestClusterBuilder + { + Options = + { + ClusterId = "testCluster", + ServiceId = "testService", + }, + }; + builder.AddSiloBuilderConfigurator(); + Cluster = builder.Build(); + Cluster.Deploy(); + } + + /// + /// Finalizes an instance of the class. + /// + ~TestClusterFixture() => Dispose(false); + + /// + /// Gets the cluster. + /// + /// The cluster. + public TestCluster Cluster { get; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + Cluster.StopAllSilos(); + } + } +} From 4f3a8ac53c4a45801e4bbe04782ec228f0c9dc6a Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:44:27 +0200 Subject: [PATCH 101/174] chore: Added TestClusterTests.cs --- test/Clustering/Cassandra/TestClusterTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/Clustering/Cassandra/TestClusterTests.cs diff --git a/test/Clustering/Cassandra/TestClusterTests.cs b/test/Clustering/Cassandra/TestClusterTests.cs new file mode 100644 index 0000000..1b189b2 --- /dev/null +++ b/test/Clustering/Cassandra/TestClusterTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra.Tests; + +using Collections; +using Escendit.Orleans.Persistence.Cassandra.Tests.Fixtures; +using Xunit; +using Xunit.Categories; + +/// +/// Test Cluster Tests. +/// +[Collection(ClusterCollectionFixture.Name)] +public class TestClusterTests +{ + private readonly TestClusterFixture _testClusterFixture; + + /// + /// Initializes a new instance of the class. + /// + /// The cluster collection fixture. + public TestClusterTests(TestClusterFixture testClusterFixture) + { + _testClusterFixture = testClusterFixture; + } + + /// + /// Test A. + /// + /// The task. + [Fact] + [IntegrationTest] + public Task Test_A() + { + var cluster = _testClusterFixture.Cluster; + Assert.NotNull(cluster); + return Task.CompletedTask; + } +} From ad3cab0a55be1de9849cb127640f99c9d7872fd0 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 22:44:49 +0200 Subject: [PATCH 102/174] chore: Added TestSiloConfigurator.cs --- .../Configuration/TestSiloConfigurator.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs diff --git a/test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs b/test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs new file mode 100644 index 0000000..2bbc6eb --- /dev/null +++ b/test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs @@ -0,0 +1,31 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Configuration; + +using global::Orleans.TestingHost; +using Microsoft.Extensions.Logging; + +/// +/// Test Silo Configurator. +/// +public class TestSiloConfigurator : ISiloConfigurator +{ + /// + public void Configure(ISiloBuilder siloBuilder) + { + siloBuilder + .ConfigureLogging(options => + { + options.AddConsole(); + options.SetMinimumLevel(LogLevel.Debug); + }) + .UseCassandraClustering() + .WithClientAsDefault(options => + { + options.Endpoints.Add("localhost"); + options.DefaultKeyspace = "test"; + }) + .WithOptions(options => options.MaxStaleness = TimeSpan.FromDays(1)); + } +} From 915c788dd53020ae199f4c24bb730a51608be0b2 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:47:38 +0200 Subject: [PATCH 103/174] feature: Updated CassandraGatewayListProvider.cs - temporary fix --- .../Cassandra/Provider/CassandraGatewayListProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs index 0045a98..825beda 100644 --- a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs +++ b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs @@ -40,7 +40,7 @@ public CassandraGatewayListProvider( /// public Task InitializeGatewayListProvider() { - return Task.CompletedTask; + return _membershipTable.InitializeMembershipTable(false); } /// From 1282b52d6585e3f586c2cae77aaed5d19ef28e5b Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:48:10 +0200 Subject: [PATCH 104/174] fix: Updated CassandraMembershipTable.cs - suspect time issues --- .../Cassandra/Provider/CassandraMembershipTable.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs index 1de7ceb..d7bb4b4 100644 --- a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs +++ b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs @@ -301,7 +301,7 @@ private static MembershipEntry MapEntry(KeyValuePair entry) .SuspectTimes .ToList() .ConvertAll(item => - new Tuple(SiloAddress.FromParsableString(item.Item1), item.Item2.DateTime)), + new Tuple(SiloAddress.FromParsableString(item.Address), item.Timestamp)), HostName = entry.Value.HostName, ProxyPort = entry.Value.ProxyPort, RoleName = entry.Value.Role, @@ -328,13 +328,14 @@ private static Silo MapSilo(MembershipEntry entry, string etag) Name = entry.SiloName, Role = entry.RoleName, SuspectTimes = entry.SuspectTimes is null - ? new List>() + ? new List() : entry .SuspectTimes - .ConvertAll(item => - new Tuple( - item.Item1.ToParsableString(), - item.Item2)), + .ConvertAll(item => new SuspectTime + { + Address = item.Item1.ToParsableString(), + Timestamp = item.Item2, + }), Status = (int)entry.Status, FaultZone = entry.FaultZone, AliveOn = entry.IAmAliveTime, From 7f05fe3cfef8287b4ecb639a2b8a0c1bb42a5451 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:48:32 +0200 Subject: [PATCH 105/174] fix: Updated ClientBuilderExtensions.cs - temporary fix --- src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs index 7c2e1e9..4f0d388 100644 --- a/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs +++ b/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs @@ -22,6 +22,7 @@ public static IClusteringClientBuilder UseCassandraClustering(this IClientBuilde ArgumentNullException.ThrowIfNull(clientBuilder); return new ClusteringClientBuilder(clientBuilder .Services - .AddSingleton()); + .AddSingleton() + .AddSingleton()); } } From 08b9fc1f7e656798151b95fe7f436493bd982caa Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:48:59 +0200 Subject: [PATCH 106/174] fix: Updated GrainStorageBase.cs - added message to exception logging --- src/Persistence/Cassandra/Storage/GrainStorageBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persistence/Cassandra/Storage/GrainStorageBase.cs b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs index 3e748cf..0cca86d 100644 --- a/src/Persistence/Cassandra/Storage/GrainStorageBase.cs +++ b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs @@ -211,6 +211,6 @@ protected Task Execute(Func> action, [CallerMemberName] string act EventId = 500, EventName = "Exception", Level = LogLevel.Error, - Message = "{name}#{type}.{action}")] + Message = "{name}#{type}.{action} {message}")] private partial void LogException(string name, Exception exception, string message, string type, string action); } From 6883155804e3e679b973aa106fa20b4866eb1403 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:49:21 +0200 Subject: [PATCH 107/174] fix: Updated Silo.cs --- src/Clustering/Cassandra/Schema/Silo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Clustering/Cassandra/Schema/Silo.cs b/src/Clustering/Cassandra/Schema/Silo.cs index b3a66aa..e57e207 100644 --- a/src/Clustering/Cassandra/Schema/Silo.cs +++ b/src/Clustering/Cassandra/Schema/Silo.cs @@ -72,7 +72,7 @@ public class Silo /// Gets the suspect times. /// /// The suspect times. - public IList> SuspectTimes { get; init; } = new List>(); + public IList SuspectTimes { get; init; } = new List(); /// /// Gets or sets the update zone. From 052ac3b466e21b64157df0219c936a854db8b718 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:49:34 +0200 Subject: [PATCH 108/174] chore: Added TestClientBuilderConfigurator.cs --- .../TestClientBuilderConfigurator.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs diff --git a/test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs b/test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs new file mode 100644 index 0000000..1289f04 --- /dev/null +++ b/test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs @@ -0,0 +1,26 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Persistence.Cassandra.Tests.Configuration; + +using global::Orleans.TestingHost; +using Microsoft.Extensions.Configuration; + +/// +/// Test Client Builder Configurator. +/// +public class TestClientBuilderConfigurator : IClientBuilderConfigurator +{ + /// + public void Configure(IConfiguration configuration, IClientBuilder clientBuilder) + { + clientBuilder + .UseCassandraClustering() + .WithClientAsDefault(options => + { + options.Endpoints.Add("localhost"); + options.DefaultKeyspace = "test"; + }) + .WithOptions(options => options.MaxStaleness = TimeSpan.FromDays(1)); + } +} From 02b59af0ec480c91404aa0c38dbcd21cd615c597 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:49:59 +0200 Subject: [PATCH 109/174] chore: Updated TestClusterFixture.cs --- test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs b/test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs index 6e714e3..61e699d 100644 --- a/test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs +++ b/test/Clustering/Cassandra/Fixtures/TestClusterFixture.cs @@ -25,6 +25,7 @@ public TestClusterFixture() }, }; builder.AddSiloBuilderConfigurator(); + builder.AddClientBuilderConfigurator(); Cluster = builder.Build(); Cluster.Deploy(); } From e916971436f861db4fd116e19c40b20d59e86e99 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Thu, 28 Sep 2023 23:50:15 +0200 Subject: [PATCH 110/174] chore: Updated TestClusterTests.cs --- test/Clustering/Cassandra/TestClusterTests.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/Clustering/Cassandra/TestClusterTests.cs b/test/Clustering/Cassandra/TestClusterTests.cs index 1b189b2..6e0a488 100644 --- a/test/Clustering/Cassandra/TestClusterTests.cs +++ b/test/Clustering/Cassandra/TestClusterTests.cs @@ -26,15 +26,28 @@ public TestClusterTests(TestClusterFixture testClusterFixture) } /// - /// Test A. + /// Test Cluster. /// /// The task. [Fact] [IntegrationTest] - public Task Test_A() + public Task Test_Cluster() { var cluster = _testClusterFixture.Cluster; Assert.NotNull(cluster); return Task.CompletedTask; } + + /// + /// Test Client. + /// + /// The task. + [Fact] + [IntegrationTest] + public Task Test_Client() + { + var client = _testClusterFixture.Cluster.Client; + Assert.NotNull(client); + return Task.CompletedTask; + } } From b55e9d5e15a8630bdadf06ad6b33e270f641beff Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 16:30:21 +0200 Subject: [PATCH 111/174] feature: Updated CassandraGatewayListProvider.cs - refactored the provider for gateway list --- .../Provider/CassandraGatewayListProvider.cs | 49 +++++++++---------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs index 825beda..b598d20 100644 --- a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs +++ b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs @@ -3,56 +3,51 @@ namespace Escendit.Orleans.Clustering.Cassandra; -using global::Orleans; +using Escendit.Extensions.Hosting.Cassandra; +using global::Cassandra; +using global::Orleans.Configuration; using global::Orleans.Messaging; -using global::Orleans.Runtime; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Logging; using Options; /// /// Cassandra Gateway List Provider. /// -public class CassandraGatewayListProvider : IGatewayListProvider +internal class CassandraGatewayListProvider : SessionContextProviderBase, IGatewayListProvider { - private readonly IMembershipTable _membershipTable; - /// /// Initializes a new instance of the class. /// - /// The membership table. + /// The name (of client). + /// The logger. + /// The cluster (client). + /// The client options. + /// The cluster options. /// The options. public CassandraGatewayListProvider( - IMembershipTable membershipTable, - IOptions options) + string name, + ILogger logger, + ICluster cluster, + CassandraClientOptions clientOptions, + ClusterOptions clusterOptions, + CassandraGatewayListProviderOptions options) + : base(name, logger, cluster, clientOptions, clusterOptions) { - ArgumentNullException.ThrowIfNull(membershipTable); ArgumentNullException.ThrowIfNull(options); - _membershipTable = membershipTable; - MaxStaleness = options.Value.MaxStaleness; + MaxStaleness = options.MaxStaleness; } /// public TimeSpan MaxStaleness { get; } /// - public bool IsUpdatable { get; } = true; + public bool IsUpdatable => true; /// public Task InitializeGatewayListProvider() - { - return _membershipTable.InitializeMembershipTable(false); - } + => Initialize(); /// - public async Task> GetGateways() - { - var membershipTableData = await _membershipTable.ReadAll(); - return membershipTableData - .Members - .Where(w => w.Item1.Status == SiloStatus.Active) - .ToList() - .ConvertAll(item => item - .Item1 - .SiloAddress.ToGatewayUri()); - } + public Task> GetGateways() + => GetGatewayList(); } From 3531fb278bb8f3efc03698e2b23bca86bce17a50 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:03:24 +0200 Subject: [PATCH 112/174] feature: Updated CassandraGatewayListProvider.cs - Fixed Disposable pattern --- .../Cassandra/Provider/CassandraGatewayListProvider.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs index b598d20..fb8abeb 100644 --- a/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs +++ b/src/Clustering/Cassandra/Provider/CassandraGatewayListProvider.cs @@ -13,7 +13,7 @@ namespace Escendit.Orleans.Clustering.Cassandra; /// /// Cassandra Gateway List Provider. /// -internal class CassandraGatewayListProvider : SessionContextProviderBase, IGatewayListProvider +internal sealed class CassandraGatewayListProvider : SessionContextProviderBase, IGatewayListProvider { /// /// Initializes a new instance of the class. @@ -37,6 +37,11 @@ public CassandraGatewayListProvider( MaxStaleness = options.MaxStaleness; } + /// + /// Finalizes an instance of the class. + /// + ~CassandraGatewayListProvider() => Dispose(false); + /// public TimeSpan MaxStaleness { get; } From 0fe34ec0a2506a4d3eba31bc3ee9929c661170eb Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:04:15 +0200 Subject: [PATCH 113/174] feature: Updated CassandraGatewayListProviderOptions.cs - removed client name - refactored to be used in other part of setup --- ...tions.cs => CassandraGatewayListProviderOptions.cs} | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) rename src/Clustering/Cassandra/Options/{CassandraClusteringOptions.cs => CassandraGatewayListProviderOptions.cs} (68%) diff --git a/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs b/src/Clustering/Cassandra/Options/CassandraGatewayListProviderOptions.cs similarity index 68% rename from src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs rename to src/Clustering/Cassandra/Options/CassandraGatewayListProviderOptions.cs index da07ed8..c22b041 100644 --- a/src/Clustering/Cassandra/Options/CassandraClusteringOptions.cs +++ b/src/Clustering/Cassandra/Options/CassandraGatewayListProviderOptions.cs @@ -6,16 +6,10 @@ namespace Escendit.Orleans.Clustering.Cassandra.Options; using global::Orleans.Messaging; /// -/// Cassandra Clustering Options. +/// Clustering Gateway List Provider Options. /// -public record CassandraClusteringOptions +public record CassandraGatewayListProviderOptions { - /// - /// Gets or sets the client name. - /// - /// The client name. - public string? ClientName { get; set; } - /// /// Gets or sets the max staleness. /// From 2d510f68cc2052ad5135b8ecbe7179ff066b8f4b Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:05:17 +0200 Subject: [PATCH 114/174] feature: Updated CassandraMembershipTable.cs - refactor the membership table --- .../Provider/CassandraMembershipTable.cs | 376 ++---------------- 1 file changed, 26 insertions(+), 350 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs index d7bb4b4..6c9ade2 100644 --- a/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs +++ b/src/Clustering/Cassandra/Provider/CassandraMembershipTable.cs @@ -5,51 +5,34 @@ namespace Escendit.Orleans.Clustering.Cassandra; -using System.Reflection; -using System.Text; - using Escendit.Extensions.Hosting.Cassandra; using global::Cassandra; -using global::Cassandra.Mapping; using global::Orleans; using global::Orleans.Configuration; using global::Orleans.Runtime; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Options; /// /// Cassandra Membership Table. /// -internal sealed partial class CassandraMembershipTable : IMembershipTable, IDisposable +internal sealed class CassandraMembershipTable : SessionContextProviderBase, IMembershipTable { - private readonly ILogger _logger; - private readonly Assembly _selfAssembly; - private readonly CassandraClientOptions _clientOptions; - private readonly ClusterOptions _clusterOptions; - private readonly ICluster _cluster; - private ISession? _session; - private IMapper? _mapper; - /// /// Initializes a new instance of the class. /// + /// The name (of client). /// The logger. - /// The service provider. - /// The options. + /// The cluster. + /// The client options. + /// The cluster options. public CassandraMembershipTable( + string name, + ICluster cluster, ILogger logger, - IServiceProvider serviceProvider, - IOptions options) + CassandraClientOptions clientOptions, + ClusterOptions clusterOptions) + : base(name, logger, cluster, clientOptions, clusterOptions) { - ArgumentNullException.ThrowIfNull(serviceProvider); - ArgumentNullException.ThrowIfNull(options); - _logger = logger; - _clusterOptions = serviceProvider.GetRequiredService>().Value; - _cluster = serviceProvider.GetRequiredServiceByName(options.Value.ClientName); - _clientOptions = serviceProvider.GetOptionsByName(options.Value.ClientName); - _selfAssembly = Assembly.GetAssembly(typeof(CassandraMembershipTable))!; } /// @@ -58,341 +41,34 @@ public CassandraMembershipTable( ~CassandraMembershipTable() => Dispose(false); /// - public async Task InitializeMembershipTable(bool tryInitTableVersion) - { - var mappingConfiguration = new MappingConfiguration() - .Define(); - _session = await _cluster - .ConnectAsync(string.Empty) - .ConfigureAwait(false); - _session.CreateKeyspaceIfNotExists(_clientOptions.DefaultKeyspace); - _session.ChangeKeyspace(_clientOptions.DefaultKeyspace); - _mapper = new Mapper(_session, mappingConfiguration); - - await InitializeDatabaseAsync() - .ConfigureAwait(false); - - if (tryInitTableVersion) - { - await InitializeVersionDataAsync(_clusterOptions.ClusterId) - .ConfigureAwait(false); - } - - await _session.UserDefinedTypes.DefineAsync(UdtMap - .For("silo", _session.Keyspace) - .Map(p => p.FaultZone, "fault_zone") - .Map(p => p.ProxyPort, "proxy_port") - .Map(p => p.HostName, "host") - .Map(p => p.StartedOn, "started_on") - .Map(p => p.Address, "address") - .Map(p => p.Etag, "etag") - .Map(p => p.Name, "name") - .Map(p => p.Role, "role") - .Map(p => p.Status, "status") - .Map(p => p.Timestamp, "timestamp") - .Map(p => p.AliveOn, "alive_on") - .Map(p => p.SuspectTimes, "suspect_times") - .Map(p => p.UpdateZone, "update_zone")); - await _session.UserDefinedTypes.DefineAsync(UdtMap - .For("suspect_time", _session.Keyspace) - .Map(p => p.Timestamp, "timestamp") - .Map(p => p.Address, "address")) - .ConfigureAwait(false); - } - - /// - public async Task DeleteMembershipTableEntries(string clusterId) - { - ArgumentNullException.ThrowIfNull(clusterId); - await _mapper! - .DeleteAsync("WHERE id = ?", clusterId) - .ConfigureAwait(false); - } + public Task InitializeMembershipTable(bool tryInitTableVersion) + => Initialize(tryInitTableVersion); /// - public async Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate) - { - var membership = await ReadAll(); - var deadMembers = new List(); - foreach (var silo in membership.Members.Where(p => p.Item1.Status == SiloStatus.Dead && p.Item1.IAmAliveTime < beforeDate)) - { - deadMembers.Add(silo.Item1.SiloAddress.ToParsableString()); - } - - await _mapper! - .UpdateIfAsync("SET silos = silos - ? WHERE id = ?", deadMembers, _clusterOptions.ClusterId) - .ConfigureAwait(false); - } + public Task DeleteMembershipTableEntries(string clusterId) + => DeleteMemberships(clusterId); /// - public async Task ReadRow(SiloAddress key) - { - ArgumentNullException.ThrowIfNull(key); - var results = await _mapper! - .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId) - .ConfigureAwait(false); - - var memberships = results as Membership[] ?? results.ToArray(); - if (!memberships.Any()) - { - return new MembershipTableData( - new TableVersion(0, Guid.Empty.ToString())); - } - - var result = memberships.FirstOrDefault(); - - if (result is null) - { - return new MembershipTableData( - new TableVersion(0, Guid.Empty.ToString())); - } - - if (!result.Silos.Any()) - { - return new MembershipTableData( - new TableVersion(0, Guid.Empty.ToString())); - } - - var matchedSilo = MatchSiloKey(result.Silos, key); - var entry = MapEntry(matchedSilo); - return new MembershipTableData( - new Tuple(entry, matchedSilo.Value.Etag), - new TableVersion(result.Version, result.Etag)); - } + public Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate) + => CleanupDefunctMembers(beforeDate); /// - public async Task ReadAll() - { - var results = await _mapper! - .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId) - .ConfigureAwait(false); - - var result = results.FirstOrDefault(); - - if (result is null) - { - return new MembershipTableData( - new TableVersion(0, Guid.Empty.ToString())); - } - - var entries = result - .Silos - .ToList() - .ConvertAll(item => new Tuple( - MapEntry(item), - item.Value.Etag)); - return new MembershipTableData(entries, new TableVersion(result.Version, result.Etag)); - } + public Task ReadRow(SiloAddress key) + => ReadOne(key); /// - public async Task InsertRow(MembershipEntry entry, TableVersion tableVersion) - { - ArgumentNullException.ThrowIfNull(entry); - ArgumentNullException.ThrowIfNull(tableVersion); - var silos = await ReadAll(); - - var currentEntry = silos.Members.FirstOrDefault(w => w.Item1.SiloAddress.ToParsableString() == entry.SiloAddress.ToParsableString()); - - if (currentEntry is not null) - { - // already exists, fail. - return false; - } - - var appliedInfo = await _mapper!.UpdateIfAsync( - "SET silos[?] = ?, version = ?, etag = ? WHERE id = ?", - entry.SiloAddress.ToParsableString(), - MapSilo(entry, tableVersion), - tableVersion.Version, - tableVersion.VersionEtag, - _clusterOptions.ClusterId) - .ConfigureAwait(false); - return appliedInfo.Applied; - } + public Task ReadAll() + => ReadMany(); /// - public async Task UpdateRow(MembershipEntry entry, string etag, TableVersion tableVersion) - { - ArgumentNullException.ThrowIfNull(entry); - ArgumentNullException.ThrowIfNull(etag); - ArgumentNullException.ThrowIfNull(tableVersion); - var row = await ReadRow(entry.SiloAddress) - .ConfigureAwait(false); - - if (!row.Members.Any()) - { - return false; - } - - var appliedInfo = await _mapper!.UpdateIfAsync( - "SET silos[?] = ?, version = ?, etag = ? WHERE id = ?", - entry.SiloAddress.ToParsableString(), - MapSilo(entry, etag), - tableVersion.Version, - tableVersion.VersionEtag, - _clusterOptions.ClusterId) - .ConfigureAwait(false); - - return appliedInfo.Applied; - } + public Task InsertRow(MembershipEntry entry, TableVersion tableVersion) + => Insert(entry, tableVersion); /// - public async Task UpdateIAmAlive(MembershipEntry entry) - { - ArgumentNullException.ThrowIfNull(entry); - var resultList = await _mapper! - .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId) - .ConfigureAwait(false); - - var results = resultList.ToList(); - if (!results.Any()) - { - return; - } - - var result = results.FirstOrDefault(); - - if (result is null) - { - return; - } - - var siloKey = entry.SiloAddress.ToParsableString(); - var siloExists = result.Silos.ContainsKey(siloKey); - - if (!siloExists) - { - return; - } - - var silo = result.Silos[siloKey]; - silo.AliveOn = entry.IAmAliveTime; - - await _mapper!.UpdateAsync( - "SET silos[?] = ? WHERE id = ?", - entry.SiloAddress.ToParsableString(), - silo, - _clusterOptions.ClusterId) - .ConfigureAwait(false); - } + public Task UpdateRow(MembershipEntry entry, string etag, TableVersion tableVersion) + => Update(entry, etag, tableVersion); /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private static KeyValuePair MatchSiloKey(IDictionary result, SiloAddress siloAddress) - { - return result - .FirstOrDefault(w => w.Key == siloAddress.ToParsableString()); - } - - private static MembershipEntry MapEntry(KeyValuePair entry) - { - return new MembershipEntry - { - SiloAddress = SiloAddress.FromParsableString(entry.Value.Address), - Status = (SiloStatus)entry.Value.Status, - FaultZone = entry.Value.FaultZone, - SuspectTimes = entry - .Value - .SuspectTimes - .ToList() - .ConvertAll(item => - new Tuple(SiloAddress.FromParsableString(item.Address), item.Timestamp)), - HostName = entry.Value.HostName, - ProxyPort = entry.Value.ProxyPort, - RoleName = entry.Value.Role, - SiloName = entry.Value.Name, - StartTime = entry.Value.StartedOn.DateTime, - UpdateZone = entry.Value.UpdateZone, - IAmAliveTime = entry.Value.AliveOn.DateTime, - }; - } - - private static Silo MapSilo(MembershipEntry entry, TableVersion tableVersion) - { - return MapSilo(entry, tableVersion.VersionEtag); - } - - private static Silo MapSilo(MembershipEntry entry, string etag) - { - return new Silo - { - Address = entry.SiloAddress.ToParsableString(), - HostName = entry.HostName, - ProxyPort = entry.ProxyPort, - Etag = etag, - Name = entry.SiloName, - Role = entry.RoleName, - SuspectTimes = entry.SuspectTimes is null - ? new List() - : entry - .SuspectTimes - .ConvertAll(item => new SuspectTime - { - Address = item.Item1.ToParsableString(), - Timestamp = item.Item2, - }), - Status = (int)entry.Status, - FaultZone = entry.FaultZone, - AliveOn = entry.IAmAliveTime, - StartedOn = entry.StartTime, - UpdateZone = entry.UpdateZone, - Timestamp = DateTime.UtcNow, - }; - } - - private void Dispose(bool disposing) - { - if (!disposing) - { - return; - } - - _session?.Dispose(); - _cluster.Dispose(); - } - - private async Task InitializeDatabaseAsync() - { - var suspectTimesTypeStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.1_SuspectTime.sql"); - var siloTypeStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.2_Silo.sql"); - var membershipTableStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.3_Membership.sql"); - - using var suspectTimesTypeReader = new StreamReader(suspectTimesTypeStream!, Encoding.UTF8, true); - using var siloTypeReader = new StreamReader(siloTypeStream!, Encoding.UTF8, true); - using var membershipTableReader = new StreamReader(membershipTableStream!, Encoding.UTF8, true); - var sqlSuspectTimesType = await suspectTimesTypeReader.ReadToEndAsync().ConfigureAwait(false); - var sqlSiloType = await siloTypeReader.ReadToEndAsync().ConfigureAwait(false); - var sqlMembershipTable = await membershipTableReader.ReadToEndAsync().ConfigureAwait(false); - - await _session!.ExecuteAsync(new SimpleStatement(sqlSuspectTimesType)); - await _session!.ExecuteAsync(new SimpleStatement(sqlSiloType)); - await _session!.ExecuteAsync(new SimpleStatement(sqlMembershipTable)); - } - - private async Task InitializeVersionDataAsync(string clusterId) - { - ArgumentNullException.ThrowIfNull(clusterId); - try - { - await _mapper! - .InsertIfNotExistsAsync(new Membership { Id = clusterId }) - .ConfigureAwait(false); - } - catch (InvalidCastException ex) - { - LogException(ex, ex.Message); - } - } - - [LoggerMessage( - EventId = 500, - EventName = "Log Exception", - Level = LogLevel.Error, - Message = "Error occurred: {message}")] - private partial void LogException(Exception ex, string message); + public Task UpdateIAmAlive(MembershipEntry entry) + => Ping(entry); } From f01ca6dd91d09a5c8a8bfc1ed4bfe87a67768aa9 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:06:26 +0200 Subject: [PATCH 115/174] feature: Updated ClientBuilderExtensions.cs - refactor the setup process --- .../Hosting/ClientBuilderExtensions.cs | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs index 4f0d388..a70368f 100644 --- a/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs +++ b/src/Clustering/Cassandra/Hosting/ClientBuilderExtensions.cs @@ -3,9 +3,9 @@ namespace Orleans.Hosting; -using Escendit.Orleans.Clustering.Cassandra; -using Messaging; +using Escendit.Orleans.Clustering.Cassandra.Options; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; /// /// Client Builder Extensions. @@ -16,13 +16,43 @@ public static class ClientBuilderExtensions /// Use Cassandra Clustering. /// /// The initial silo builder. + /// The configure options. /// The updated silo builder. - public static IClusteringClientBuilder UseCassandraClustering(this IClientBuilder clientBuilder) + public static IClientClusteringBuilder UseCassandraClustering( + this IClientBuilder clientBuilder, + Action configureOptions) { ArgumentNullException.ThrowIfNull(clientBuilder); - return new ClusteringClientBuilder(clientBuilder - .Services - .AddSingleton() - .AddSingleton()); + clientBuilder + .UseCassandraClustering( + builder => builder.Configure(configureOptions)); + return new ClientClusteringBuilder(clientBuilder.Services); + } + + /// + /// Use Cassandra Clustering. + /// + /// The initial silo builder. + /// The configure options. + /// The updated silo builder. + public static IClientClusteringBuilder UseCassandraClustering( + this IClientBuilder clientBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clientBuilder); + configureOptions.Invoke(clientBuilder.Services.AddOptions()); + return new ClientClusteringBuilder(clientBuilder.Services); + } + + /// + /// Use Cassandra Clustering. + /// + /// The initial silo builder. + /// The updated silo builder. + public static IClientClusteringBuilder UseCassandraClustering(this IClientBuilder clientBuilder) + { + ArgumentNullException.ThrowIfNull(clientBuilder); + clientBuilder.Services.AddOptions(); + return new ClientClusteringBuilder(clientBuilder.Services); } } From 20be47c6cd9c8728bcd68bfc65eae10c27a9c850 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:06:39 +0200 Subject: [PATCH 116/174] fix: Renamed ClientClusteringBuilder.cs --- ...ringClientBuilder.cs => ClientClusteringBuilder.cs} | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) rename src/Clustering/Cassandra/Hosting/{ClusteringClientBuilder.cs => ClientClusteringBuilder.cs} (59%) diff --git a/src/Clustering/Cassandra/Hosting/ClusteringClientBuilder.cs b/src/Clustering/Cassandra/Hosting/ClientClusteringBuilder.cs similarity index 59% rename from src/Clustering/Cassandra/Hosting/ClusteringClientBuilder.cs rename to src/Clustering/Cassandra/Hosting/ClientClusteringBuilder.cs index 860c32a..8051fcd 100644 --- a/src/Clustering/Cassandra/Hosting/ClusteringClientBuilder.cs +++ b/src/Clustering/Cassandra/Hosting/ClientClusteringBuilder.cs @@ -3,22 +3,26 @@ namespace Orleans.Hosting; +using Escendit.Extensions.Hosting.Cassandra; using Microsoft.Extensions.DependencyInjection; /// /// Clustering Client Builder. /// -internal class ClusteringClientBuilder : IClusteringClientBuilder +internal class ClientClusteringBuilder : IClientClusteringBuilder { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The service collection. - public ClusteringClientBuilder(IServiceCollection serviceCollection) + public ClientClusteringBuilder(IServiceCollection serviceCollection) { Services = serviceCollection; } /// public IServiceCollection Services { get; } + + /// + public string Name { get; set; } = CassandraClientOptions.DefaultOptionsKey; } From 3850bbef64cedb0d77a8512515abe5819d3f618a Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:06:58 +0200 Subject: [PATCH 117/174] feature: Added ClientClusteringBuilderExtensions.cs --- .../ClientClusteringBuilderExtensions.cs | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/ClientClusteringBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ClientClusteringBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClientClusteringBuilderExtensions.cs new file mode 100644 index 0000000..b321a55 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/ClientClusteringBuilderExtensions.cs @@ -0,0 +1,158 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Cassandra; +using Escendit.Extensions.Hosting.Cassandra; +using Escendit.Orleans.Clustering.Cassandra; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Runtime; + +/// +/// Clustering Client Builder Extensions. +/// +public static class ClientClusteringBuilderExtensions +{ + /// + /// With Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClientClusteringBuilder WithClientAsDefault( + this IClientClusteringBuilder clientClusteringBuilder, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clientClusteringBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + clientClusteringBuilder.Name = CassandraClientOptions.DefaultOptionsKey; + clientClusteringBuilder + .Services + .AddCassandraClientAsDefault(configureOptions); + return clientClusteringBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static IClientClusteringBuilder WithClientAsDefault( + this IClientClusteringBuilder clientClusteringBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clientClusteringBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + clientClusteringBuilder.Name = CassandraClientOptions.DefaultOptionsKey; + clientClusteringBuilder + .Services + .AddCassandraClientAsDefault(configureOptions); + return clientClusteringBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The options name. + /// The updated clustering silo builder. + public static IClientClusteringBuilder WithClientFromOptionsAsDefault( + this IClientClusteringBuilder clientClusteringBuilder, + string optionsName) + { + ArgumentNullException.ThrowIfNull(clientClusteringBuilder); + ArgumentNullException.ThrowIfNull(optionsName); + clientClusteringBuilder.Name = CassandraClientOptions.DefaultOptionsKey; + clientClusteringBuilder + .Services + .AddCassandraClientFromOptionsAsDefault(optionsName); + return clientClusteringBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static IClientClusteringBuilder WithClient( + this IClientClusteringBuilder clientClusteringBuilder, + string name, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(clientClusteringBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + clientClusteringBuilder.Name = CassandraClientOptions.DefaultOptionsKey; + clientClusteringBuilder + .Services + .AddCassandraClient(name, configureOptions); + return clientClusteringBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static IClientClusteringBuilder WithClient( + this IClientClusteringBuilder clientClusteringBuilder, + string name, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(clientClusteringBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + clientClusteringBuilder.Name = name; + clientClusteringBuilder + .Services + .AddCassandraClient(name, configureOptions); + return clientClusteringBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The options name. + /// The updated clustering silo builder. + public static IClientClusteringBuilder WithClientFromOptions( + this IClientClusteringBuilder clientClusteringBuilder, + string name, + string optionsName) + { + ArgumentNullException.ThrowIfNull(clientClusteringBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(optionsName); + clientClusteringBuilder.Name = name; + clientClusteringBuilder + .Services + .AddCassandraClientFromOptions(name, optionsName); + return clientClusteringBuilder; + } + + /// + /// Build. + /// + /// The clustering silo builder. + /// The silo builder. + public static IClientBuilder Build(this IClientClusteringBuilder clientClusteringBuilder) + { + ArgumentNullException.ThrowIfNull(clientClusteringBuilder); + clientClusteringBuilder + .Services + .AddSingleton(serviceProvider => + ActivatorUtilities.CreateInstance( + serviceProvider, + clientClusteringBuilder.Name, + serviceProvider.GetRequiredServiceByName(clientClusteringBuilder.Name))); + return clientClusteringBuilder; + } +} From 78cd4f21be5777041aaeb59a8155f1b26e5567d3 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:07:12 +0200 Subject: [PATCH 118/174] feature: Deleted ClusteringClientBuilderExtensions.cs --- .../ClusteringClientBuilderExtensions.cs | 172 ------------------ 1 file changed, 172 deletions(-) delete mode 100644 src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs deleted file mode 100644 index 9ebf6bb..0000000 --- a/src/Clustering/Cassandra/Hosting/ClusteringClientBuilderExtensions.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) Escendit Ltd. All Rights Reserved. -// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. - -namespace Orleans.Hosting; - -using Escendit.Extensions.Hosting.Cassandra; -using Escendit.Orleans.Clustering.Cassandra.Options; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -/// -/// Clustering Client Builder Extensions. -/// -public static class ClusteringClientBuilderExtensions -{ - /// - /// With Client As Default. - /// - /// The clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithClientAsDefault( - this IClusteringClientBuilder clusteringClientBuilder, - Action configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringClientBuilder - .Services - .AddCassandraClientAsDefault(configureOptions) - .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); - return clusteringClientBuilder; - } - - /// - /// Add Client As Default. - /// - /// The clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithClientAsDefault( - this IClusteringClientBuilder clusteringClientBuilder, - Action> configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringClientBuilder - .Services - .AddCassandraClientAsDefault(configureOptions) - .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); - return clusteringClientBuilder; - } - - /// - /// Add Client As Default. - /// - /// The clustering silo builder. - /// The options name. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithClientFromOptionsAsDefault( - this IClusteringClientBuilder clusteringClientBuilder, - string optionsName) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(optionsName); - clusteringClientBuilder - .Services - .AddCassandraClientFromOptionsAsDefault(optionsName) - .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); - return clusteringClientBuilder; - } - - /// - /// Add Client. - /// - /// The initial clustering silo builder. - /// The name. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithClient( - this IClusteringClientBuilder clusteringClientBuilder, - string name, - Action configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringClientBuilder - .Services - .AddCassandraClient(name, configureOptions) - .Configure(options => options.ClientName = name); - return clusteringClientBuilder; - } - - /// - /// Add Client. - /// - /// The initial clustering silo builder. - /// The name. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithClient( - this IClusteringClientBuilder clusteringClientBuilder, - string name, - Action> configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringClientBuilder.Configure(options => options.ClientName = name); - clusteringClientBuilder - .Services - .AddCassandraClient(name, configureOptions) - .Configure(options => options.ClientName = name); - return clusteringClientBuilder; - } - - /// - /// Add Client. - /// - /// The initial clustering silo builder. - /// The name. - /// The options name. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithClientFromOptions( - this IClusteringClientBuilder clusteringClientBuilder, - string name, - string optionsName) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(optionsName); - clusteringClientBuilder - .Services - .AddCassandraClientFromOptions(name, optionsName) - .Configure(options => options.ClientName = name); - return clusteringClientBuilder; - } - - /// - /// With Options. - /// - /// The clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithOptions( - this IClusteringClientBuilder clusteringClientBuilder, - Action configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - return clusteringClientBuilder - .WithOptions(builder => - builder.Configure(configureOptions)); - } - - /// - /// With Options. - /// - /// The initial clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringClientBuilder WithOptions( - this IClusteringClientBuilder clusteringClientBuilder, - Action> configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringClientBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - configureOptions.Invoke(clusteringClientBuilder.Services.AddOptions()); - return clusteringClientBuilder; - } -} From 062b95b851ae699fc55fd4edc4a3e599aef88d78 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:07:27 +0200 Subject: [PATCH 119/174] feature: Removed ClusteringSiloBuilderExtensions.cs --- .../ClusteringSiloBuilderExtensions.cs | 172 ------------------ 1 file changed, 172 deletions(-) delete mode 100644 src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs deleted file mode 100644 index 9f5d891..0000000 --- a/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilderExtensions.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) Escendit Ltd. All Rights Reserved. -// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. - -namespace Orleans.Hosting; - -using Escendit.Extensions.Hosting.Cassandra; -using Escendit.Orleans.Clustering.Cassandra.Options; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -/// -/// Clustering Silo Builder Extensions. -/// -public static class ClusteringSiloBuilderExtensions -{ - /// - /// With Client As Default. - /// - /// The clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithClientAsDefault( - this IClusteringSiloBuilder clusteringSiloBuilder, - Action configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringSiloBuilder - .Services - .AddCassandraClientAsDefault(configureOptions) - .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); - return clusteringSiloBuilder; - } - - /// - /// Add Client As Default. - /// - /// The clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithClientAsDefault( - this IClusteringSiloBuilder clusteringSiloBuilder, - Action> configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringSiloBuilder - .Services - .AddCassandraClientAsDefault(configureOptions) - .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); - return clusteringSiloBuilder; - } - - /// - /// Add Client As Default. - /// - /// The clustering silo builder. - /// The options name. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithClientFromOptionsAsDefault( - this IClusteringSiloBuilder clusteringSiloBuilder, - string optionsName) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(optionsName); - clusteringSiloBuilder - .Services - .AddCassandraClientFromOptionsAsDefault(optionsName) - .Configure(options => options.ClientName = CassandraClientOptions.DefaultOptionsKey); - return clusteringSiloBuilder; - } - - /// - /// Add Client. - /// - /// The initial clustering silo builder. - /// The name. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithClient( - this IClusteringSiloBuilder clusteringSiloBuilder, - string name, - Action configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringSiloBuilder - .Services - .AddCassandraClient(name, configureOptions) - .Configure(options => options.ClientName = name); - return clusteringSiloBuilder; - } - - /// - /// Add Client. - /// - /// The initial clustering silo builder. - /// The name. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithClient( - this IClusteringSiloBuilder clusteringSiloBuilder, - string name, - Action> configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(configureOptions); - clusteringSiloBuilder.Configure(options => options.ClientName = name); - clusteringSiloBuilder - .Services - .AddCassandraClient(name, configureOptions) - .Configure(options => options.ClientName = name); - return clusteringSiloBuilder; - } - - /// - /// Add Client. - /// - /// The initial clustering silo builder. - /// The name. - /// The options name. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithClientFromOptions( - this IClusteringSiloBuilder clusteringSiloBuilder, - string name, - string optionsName) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(name); - ArgumentNullException.ThrowIfNull(optionsName); - clusteringSiloBuilder - .Services - .AddCassandraClientFromOptions(name, optionsName) - .Configure(options => options.ClientName = name); - return clusteringSiloBuilder; - } - - /// - /// With Options. - /// - /// The clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithOptions( - this IClusteringSiloBuilder clusteringSiloBuilder, - Action configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - return clusteringSiloBuilder - .WithOptions(builder => - builder.Configure(configureOptions)); - } - - /// - /// With Options. - /// - /// The initial clustering silo builder. - /// The configure options. - /// The updated clustering silo builder. - public static IClusteringSiloBuilder WithOptions( - this IClusteringSiloBuilder clusteringSiloBuilder, - Action> configureOptions) - { - ArgumentNullException.ThrowIfNull(clusteringSiloBuilder); - ArgumentNullException.ThrowIfNull(configureOptions); - configureOptions.Invoke(clusteringSiloBuilder.Services.AddOptions()); - return clusteringSiloBuilder; - } -} From 9c65a3578d4a6ccae1fc34882003fa9e12ca1ea4 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:07:38 +0200 Subject: [PATCH 120/174] feature: Removed DataProvider.cs --- .../Cassandra/Provider/DataProvider.cs | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/Clustering/Cassandra/Provider/DataProvider.cs diff --git a/src/Clustering/Cassandra/Provider/DataProvider.cs b/src/Clustering/Cassandra/Provider/DataProvider.cs deleted file mode 100644 index a3daaca..0000000 --- a/src/Clustering/Cassandra/Provider/DataProvider.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Escendit Ltd. All Rights Reserved. -// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. - -namespace Escendit.Orleans.Clustering.Cassandra; - -using Microsoft.Extensions.Logging; - -/// -/// Data Provider. -/// -public partial class DataProvider -{ - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public DataProvider( - ILogger logger) - { - _logger = logger; - } - - [LoggerMessage( - EventId = 100, - EventName = "", - Level = LogLevel.Information, - Message = "Fine")] - private partial void LogSomething(); -} From e969f8efb92e8bb44b5f82317610a0c3d815c7ec Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:08:18 +0200 Subject: [PATCH 121/174] feature: Updated Escendit.Orleans.Reminders.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj index 65d26f4..2a6506e 100644 --- a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj +++ b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj @@ -2,4 +2,12 @@ + + + + + + + + From abe66e96196a2c23c0edd1be4ed1c5ab41e9ef5a Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:08:45 +0200 Subject: [PATCH 122/174] feature: Updated IClientClusteringBuilder.cs - added name (of client) --- ...steringClientBuilder.cs => IClientClusteringBuilder.cs} | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) rename src/Clustering/Cassandra/Hosting/{IClusteringClientBuilder.cs => IClientClusteringBuilder.cs} (55%) diff --git a/src/Clustering/Cassandra/Hosting/IClusteringClientBuilder.cs b/src/Clustering/Cassandra/Hosting/IClientClusteringBuilder.cs similarity index 55% rename from src/Clustering/Cassandra/Hosting/IClusteringClientBuilder.cs rename to src/Clustering/Cassandra/Hosting/IClientClusteringBuilder.cs index ebc6a77..fb78ca6 100644 --- a/src/Clustering/Cassandra/Hosting/IClusteringClientBuilder.cs +++ b/src/Clustering/Cassandra/Hosting/IClientClusteringBuilder.cs @@ -6,6 +6,11 @@ namespace Orleans.Hosting; /// /// Clustering Client Builder. /// -public interface IClusteringClientBuilder : IClientBuilder +public interface IClientClusteringBuilder : IClientBuilder { + /// + /// Gets the name. + /// + /// The name. + public string Name { get; internal set; } } From e9efa7092a17b590dacaaccb7105b786600879ce Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:09:03 +0200 Subject: [PATCH 123/174] feature: Updated ISiloClusteringBuilder.cs - added name (of client) --- ...IClusteringSiloBuilder.cs => ISiloClusteringBuilder.cs} | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) rename src/Clustering/Cassandra/Hosting/{IClusteringSiloBuilder.cs => ISiloClusteringBuilder.cs} (55%) diff --git a/src/Clustering/Cassandra/Hosting/IClusteringSiloBuilder.cs b/src/Clustering/Cassandra/Hosting/ISiloClusteringBuilder.cs similarity index 55% rename from src/Clustering/Cassandra/Hosting/IClusteringSiloBuilder.cs rename to src/Clustering/Cassandra/Hosting/ISiloClusteringBuilder.cs index fb88f88..a2d8a51 100644 --- a/src/Clustering/Cassandra/Hosting/IClusteringSiloBuilder.cs +++ b/src/Clustering/Cassandra/Hosting/ISiloClusteringBuilder.cs @@ -6,6 +6,11 @@ namespace Orleans.Hosting; /// /// Clustering Silo Builder. /// -public interface IClusteringSiloBuilder : ISiloBuilder +public interface ISiloClusteringBuilder : ISiloBuilder { + /// + /// Gets the name. + /// + /// The name. + public string Name { get; internal set; } } From f5700ff957406f5082bccf15846db3c7fbb4b6cd Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:09:17 +0200 Subject: [PATCH 124/174] feature: Removed ServiceCollectionExtensions.cs --- .../Hosting/ServiceCollectionExtensions.cs | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs b/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs deleted file mode 100644 index a24bb7f..0000000 --- a/src/Clustering/Cassandra/Hosting/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Escendit Ltd. All Rights Reserved. -// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. - -namespace Microsoft.Extensions.DependencyInjection; - -using Escendit.Orleans.Clustering.Cassandra; -using Escendit.Orleans.Clustering.Cassandra.Options; -using Options; -using Orleans; -using Orleans.Messaging; - -/// -/// Service Collection Extensions. -/// -internal static class ServiceCollectionExtensions -{ - /// - /// Add Cassandra Clustering. - /// - /// The initial service collection. - /// The configure options. - /// The updated service collection. - public static IServiceCollection AddCassandraClustering( - this IServiceCollection services, - Action configureOptions) - { - return services - .AddCassandraClustering(builder => builder.Configure(configureOptions)); - } - - /// - /// Add Cassandra Clustering. - /// - /// The initial service collection. - /// The configure options. - /// The updated service collection. - public static IServiceCollection AddCassandraClustering( - this IServiceCollection services, - Action> configureOptions) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(configureOptions); - configureOptions.Invoke(services.AddOptions()); - return services - .AddSingleton() - .AddSingleton(); - } -} From 01fd6c21c4b6a763268b6295d9394f337efb7cb3 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:12:44 +0200 Subject: [PATCH 125/174] feature: Added SessionContextProviderBase.cs --- .../Provider/SessionContextProviderBase.cs | 539 ++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs diff --git a/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs new file mode 100644 index 0000000..7bd8983 --- /dev/null +++ b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs @@ -0,0 +1,539 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Clustering.Cassandra; + +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using Escendit.Extensions.Hosting.Cassandra; +using global::Cassandra; +using global::Cassandra.Mapping; +using global::Orleans; +using global::Orleans.Configuration; +using global::Orleans.Runtime; +using Microsoft.Extensions.Logging; + +/// +/// Data Provider. +/// +internal partial class SessionContextProviderBase : IDisposable +{ + private readonly string _name; + private readonly ILogger _logger; + private readonly ICluster _cluster; + private readonly CassandraClientOptions _clientOptions; + private readonly ClusterOptions _clusterOptions; + private readonly Assembly _selfAssembly; + private ISession? _session; + private IMapper? _mapper; + + /// + /// Initializes a new instance of the class. + /// + /// The name [of client]. + /// The logger. + /// The cassandra cluster. + /// The client options. + /// The cluster options. + protected SessionContextProviderBase( + string name, + ILogger logger, + ICluster cluster, + CassandraClientOptions clientOptions, + ClusterOptions clusterOptions) + { + _name = name; + _logger = logger; + _cluster = cluster; + _clientOptions = clientOptions; + _clusterOptions = clusterOptions; + _selfAssembly = typeof(SessionContextProviderBase).Assembly; + } + + /// + /// Finalizes an instance of the class. + /// + ~SessionContextProviderBase() => Dispose(false); + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Initialize the cluster. + /// + /// The try initialize data in the cluster. + /// A representing the asynchronous operation. + protected async Task Initialize(bool tryInitializeData = false) + { + LogInitialize(_name, _clientOptions.DefaultKeyspace!); + var mappingConfiguration = new MappingConfiguration() + .Define(); + _session = await Execute(() => _cluster + .ConnectAsync(string.Empty)) + .ConfigureAwait(false); + LogConnect(_name, string.Empty); + _session.CreateKeyspaceIfNotExists(_clientOptions.DefaultKeyspace); + _session.ChangeKeyspace(_clientOptions.DefaultKeyspace); + LogConnect(_name, _clientOptions.DefaultKeyspace!); + _mapper = new Mapper(_session, mappingConfiguration); + + await Execute(InitializeDatabaseAsync) + .ConfigureAwait(false); + + if (tryInitializeData) + { + await Execute(() => InitializeVersionDataAsync(_clusterOptions.ClusterId)) + .ConfigureAwait(false); + } + + await Execute(() => _session.UserDefinedTypes.DefineAsync(UdtMap + .For("silo", _session.Keyspace) + .Map(p => p.FaultZone, "fault_zone") + .Map(p => p.ProxyPort, "proxy_port") + .Map(p => p.HostName, "host") + .Map(p => p.StartedOn, "started_on") + .Map(p => p.Address, "address") + .Map(p => p.Etag, "etag") + .Map(p => p.Name, "name") + .Map(p => p.Role, "role") + .Map(p => p.Status, "status") + .Map(p => p.Timestamp, "timestamp") + .Map(p => p.AliveOn, "alive_on") + .Map(p => p.SuspectTimes, "suspect_times") + .Map(p => p.UpdateZone, "update_zone"))) + .ConfigureAwait(false); + await Execute(() => _session.UserDefinedTypes.DefineAsync(UdtMap + .For("suspect_time", _session.Keyspace) + .Map(p => p.Timestamp, "timestamp") + .Map(p => p.Address, "address"))) + .ConfigureAwait(false); + } + + /// + /// Delete Memberships. + /// + /// The cluster id to delete from. + /// A representing the result of the asynchronous operation. + protected async Task DeleteMemberships(string clusterId) + { + ArgumentNullException.ThrowIfNull(clusterId); + await Execute(() => _mapper! + .DeleteAsync("WHERE id = ?", clusterId)) + .ConfigureAwait(false); + } + + /// + /// Cleanup Defunct Members. + /// + /// The before date. + /// A representing the result of the asynchronous operation. + protected async Task CleanupDefunctMembers(DateTimeOffset beforeDate) + { + var membership = await Execute(ReadMany) + .ConfigureAwait(false); + var deadMembers = membership + .Members + .Where(p => + p.Item1.Status == SiloStatus.Dead && p.Item1.IAmAliveTime < beforeDate) + .Select(silo => silo.Item1.SiloAddress.ToParsableString()) + .ToList(); + + await Execute(() => _mapper! + .UpdateIfAsync("SET silos = silos - ? WHERE id = ?", deadMembers, _clusterOptions.ClusterId)) + .ConfigureAwait(false); + } + + /// + /// Read One Member. + /// + /// The silo address. + /// A representing the result of the asynchronous operation. + protected async Task ReadOne(SiloAddress address) + { + ArgumentNullException.ThrowIfNull(address); + var results = await Execute(() => _mapper! + .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId)) + .ConfigureAwait(false); + + var memberships = results as Membership[] ?? results.ToArray(); + if (!memberships.Any()) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + var result = memberships.FirstOrDefault(); + + if (result is null) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + if (!result.Silos.Any()) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + var matchedSilo = MatchSiloKey(result.Silos, address); + var entry = MapEntry(matchedSilo); + return new MembershipTableData( + new Tuple(entry, matchedSilo.Value.Etag), + new TableVersion(result.Version, result.Etag)); + } + + /// + /// Read Many. + /// + /// The membership table data. + protected async Task ReadMany() + { + var results = await Execute(() => _mapper! + .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId)) + .ConfigureAwait(false); + + var result = results.FirstOrDefault(); + + if (result is null) + { + return new MembershipTableData( + new TableVersion(0, Guid.Empty.ToString())); + } + + var entries = result + .Silos + .ToList() + .ConvertAll(item => new Tuple( + MapEntry(item), + item.Value.Etag)); + return new MembershipTableData(entries, new TableVersion(result.Version, result.Etag)); + } + + /// + /// Insert One. + /// + /// The entry. + /// The table version. + /// A representing the result of the asynchronous operation. + protected async Task Insert(MembershipEntry entry, TableVersion tableVersion) + { + ArgumentNullException.ThrowIfNull(entry); + ArgumentNullException.ThrowIfNull(tableVersion); + var silos = await Execute(ReadMany); + + var currentEntry = silos.Members.FirstOrDefault(w => w.Item1.SiloAddress.ToParsableString() == entry.SiloAddress.ToParsableString()); + + if (currentEntry is not null) + { + // already exists, fail. + return false; + } + + var appliedInfo = await Execute(() => _mapper! + .UpdateIfAsync( + "SET silos[?] = ?, version = ?, etag = ? WHERE id = ?", + entry.SiloAddress.ToParsableString(), + MapSilo(entry, tableVersion), + tableVersion.Version, + tableVersion.VersionEtag, + _clusterOptions.ClusterId)) + .ConfigureAwait(false); + return appliedInfo.Applied; + } + + /// + /// Update One. + /// + /// The entry. + /// The etag. + /// The table version. + /// A representing the result of the asynchronous operation. + protected async Task Update(MembershipEntry entry, string etag, TableVersion tableVersion) + { + ArgumentNullException.ThrowIfNull(entry); + ArgumentNullException.ThrowIfNull(etag); + ArgumentNullException.ThrowIfNull(tableVersion); + var row = await Execute(() => + ReadOne(entry.SiloAddress)) + .ConfigureAwait(false); + + if (!row.Members.Any()) + { + return false; + } + + var appliedInfo = await Execute(() => _mapper! + .UpdateIfAsync( + "SET silos[?] = ?, version = ?, etag = ? WHERE id = ?", + entry.SiloAddress.ToParsableString(), + MapSilo(entry, etag), + tableVersion.Version, + tableVersion.VersionEtag, + _clusterOptions.ClusterId)) + .ConfigureAwait(false); + + return appliedInfo.Applied; + } + + /// + /// Ping. Pong. (IAmAlive). + /// + /// The entry. + /// A representing the asynchronous operation. + protected async Task Ping(MembershipEntry entry) + { + ArgumentNullException.ThrowIfNull(entry); + var resultList = await Execute(() => _mapper! + .FetchAsync("WHERE id = ?", _clusterOptions.ClusterId)) + .ConfigureAwait(false); + + var results = resultList.ToList(); + if (!results.Any()) + { + return; + } + + var result = results.FirstOrDefault(); + + if (result is null) + { + return; + } + + var siloKey = entry.SiloAddress.ToParsableString(); + var siloExists = result.Silos.ContainsKey(siloKey); + + if (!siloExists) + { + return; + } + + var silo = result.Silos[siloKey]; + silo.AliveOn = entry.IAmAliveTime; + + await Execute(() => _mapper! + .UpdateAsync( + "SET silos[?] = ? WHERE id = ?", + entry.SiloAddress.ToParsableString(), + silo, + _clusterOptions.ClusterId)) + .ConfigureAwait(false); + } + + /// + /// Get Gateway List. + /// + /// A representing the result of the asynchronous operation. + protected async Task> GetGatewayList() + { + var membershipTableData = await Execute(ReadMany); + return membershipTableData + .Members + .Where(w => w.Item1.Status == SiloStatus.Active) + .ToList() + .ConvertAll(item => item + .Item1 + .SiloAddress.ToGatewayUri()); + } + + /// + /// Dispose. + /// + /// The disposing. + protected virtual void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + _session?.Dispose(); + _cluster.Dispose(); + } + + /// + /// Execute. + /// + /// The action. + /// The action name. + /// The task. + protected Task Execute(Func action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + return Execute(async () => + { + await action(); + return true; + }); + } + + /// + /// Execute. + /// + /// The type. + /// The action. + /// The action name. + /// A representing the result of the asynchronous operation. + protected Task Execute(Func> action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + + try + { + var type = typeof(T); + var typeName = type.Name; + if (type.GenericTypeArguments.Any()) + { + var arguments = string.Join(", ", type.GenericTypeArguments.ToList().ConvertAll(item => item.Name)); + typeName = $"{typeName}<{arguments}>"; + } + + var stopwatch = Stopwatch.StartNew(); + var result = action(); + LogExecute(_name, typeName, actionName, stopwatch.ElapsedMilliseconds); + return result; + } + catch (Exception ex) + { + LogException(_name, ex, ex.Message, typeof(T).FullName!, actionName); + throw; + } + } + + private static KeyValuePair MatchSiloKey(IDictionary result, SiloAddress siloAddress) + { + return result + .FirstOrDefault(w => w.Key == siloAddress.ToParsableString()); + } + + private static MembershipEntry MapEntry(KeyValuePair entry) + { + return new MembershipEntry + { + SiloAddress = SiloAddress.FromParsableString(entry.Value.Address), + Status = (SiloStatus)entry.Value.Status, + FaultZone = entry.Value.FaultZone, + SuspectTimes = entry + .Value + .SuspectTimes + .ToList() + .ConvertAll(item => + new Tuple(SiloAddress.FromParsableString(item.Address), item.Timestamp)), + HostName = entry.Value.HostName, + ProxyPort = entry.Value.ProxyPort, + RoleName = entry.Value.Role, + SiloName = entry.Value.Name, + StartTime = entry.Value.StartedOn.DateTime, + UpdateZone = entry.Value.UpdateZone, + IAmAliveTime = entry.Value.AliveOn.DateTime, + }; + } + + private static Silo MapSilo(MembershipEntry entry, TableVersion tableVersion) + { + return MapSilo(entry, tableVersion.VersionEtag); + } + + private static Silo MapSilo(MembershipEntry entry, string etag) + { + return new Silo + { + Address = entry.SiloAddress.ToParsableString(), + HostName = entry.HostName, + ProxyPort = entry.ProxyPort, + Etag = etag, + Name = entry.SiloName, + Role = entry.RoleName, + SuspectTimes = entry.SuspectTimes is null + ? new List() + : entry + .SuspectTimes + .ConvertAll(item => new SuspectTime + { + Address = item.Item1.ToParsableString(), + Timestamp = item.Item2, + }), + Status = (int)entry.Status, + FaultZone = entry.FaultZone, + AliveOn = entry.IAmAliveTime, + StartedOn = entry.StartTime, + UpdateZone = entry.UpdateZone, + Timestamp = DateTime.UtcNow, + }; + } + + private async Task InitializeVersionDataAsync(string clusterId) + { + ArgumentNullException.ThrowIfNull(clusterId); + try + { + await Execute(() => _mapper! + .InsertIfNotExistsAsync(new Membership { Id = clusterId })) + .ConfigureAwait(false); + } + catch (InvalidCastException ex) + { + LogException(ex, ex.Message); + } + } + + private async Task InitializeDatabaseAsync() + { + var suspectTimesTypeStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.1_SuspectTime.sql"); + var siloTypeStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.2_Silo.sql"); + var membershipTableStream = _selfAssembly.GetManifestResourceStream("Escendit.Orleans.Clustering.Cassandra.Resources.SQL.3_Membership.sql"); + + using var suspectTimesTypeReader = new StreamReader(suspectTimesTypeStream!, Encoding.UTF8, true); + using var siloTypeReader = new StreamReader(siloTypeStream!, Encoding.UTF8, true); + using var membershipTableReader = new StreamReader(membershipTableStream!, Encoding.UTF8, true); + var sqlSuspectTimesType = await suspectTimesTypeReader.ReadToEndAsync().ConfigureAwait(false); + var sqlSiloType = await siloTypeReader.ReadToEndAsync().ConfigureAwait(false); + var sqlMembershipTable = await membershipTableReader.ReadToEndAsync().ConfigureAwait(false); + + await _session!.ExecuteAsync(new SimpleStatement(sqlSuspectTimesType)); + await _session!.ExecuteAsync(new SimpleStatement(sqlSiloType)); + await _session!.ExecuteAsync(new SimpleStatement(sqlMembershipTable)); + } + + [LoggerMessage( + EventId = 100, + EventName = "Initialize", + Level = LogLevel.Information, + Message = "Initializing {name} to {keyspace}")] + private partial void LogInitialize(string name, string keyspace); + + [LoggerMessage( + EventId = 101, + EventName = "Connect", + Level = LogLevel.Information, + Message = "Connected {name} to {keyspace}")] + private partial void LogConnect(string name, string keyspace); + + [LoggerMessage( + EventId = 200, + EventName = "Execution", + Level = LogLevel.Debug, + Message = "Executing {name}#{type}.{action} took {duration}ms")] + private partial void LogExecute(string name, string type, string action, long duration); + + [LoggerMessage( + EventId = 500, + EventName = "Log Exception", + Level = LogLevel.Error, + Message = "Error occurred: {message}")] + private partial void LogException(Exception ex, string message); + + [LoggerMessage( + EventId = 500, + EventName = "Exception", + Level = LogLevel.Error, + Message = "{name}#{type}.{action} {message}")] + private partial void LogException(string name, Exception exception, string message, string type, string action); +} From 9390c68737b6a8cf2a22c1535931ba82e2f67a29 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:13:02 +0200 Subject: [PATCH 126/174] feature: Updated SiloBuilderExtensions.cs - refactor --- src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs index 63ad847..88d4798 100644 --- a/src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs +++ b/src/Clustering/Cassandra/Hosting/SiloBuilderExtensions.cs @@ -16,12 +16,10 @@ public static class SiloBuilderExtensions /// /// The initial silo builder. /// The updated silo builder. - public static IClusteringSiloBuilder UseCassandraClustering( + public static ISiloClusteringBuilder UseCassandraClustering( this ISiloBuilder siloBuilder) { ArgumentNullException.ThrowIfNull(siloBuilder); - return new ClusteringSiloBuilder(siloBuilder - .Services - .AddSingleton()); + return new SiloClusteringBuilder(siloBuilder.Services); } } From c3686e0b82722d95475df64fe631e70e4c8417e3 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:13:31 +0200 Subject: [PATCH 127/174] feature: Updated SiloClusteringBuilder.cs --- ...usteringSiloBuilder.cs => SiloClusteringBuilder.cs} | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) rename src/Clustering/Cassandra/Hosting/{ClusteringSiloBuilder.cs => SiloClusteringBuilder.cs} (59%) diff --git a/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilder.cs b/src/Clustering/Cassandra/Hosting/SiloClusteringBuilder.cs similarity index 59% rename from src/Clustering/Cassandra/Hosting/ClusteringSiloBuilder.cs rename to src/Clustering/Cassandra/Hosting/SiloClusteringBuilder.cs index a65eef2..9828188 100644 --- a/src/Clustering/Cassandra/Hosting/ClusteringSiloBuilder.cs +++ b/src/Clustering/Cassandra/Hosting/SiloClusteringBuilder.cs @@ -3,22 +3,26 @@ namespace Orleans.Hosting; +using Escendit.Extensions.Hosting.Cassandra; using Microsoft.Extensions.DependencyInjection; /// /// Clustering Silo Builder. /// -internal class ClusteringSiloBuilder : IClusteringSiloBuilder +internal class SiloClusteringBuilder : ISiloClusteringBuilder { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The service collection. - public ClusteringSiloBuilder(IServiceCollection serviceCollection) + public SiloClusteringBuilder(IServiceCollection serviceCollection) { Services = serviceCollection; } /// public IServiceCollection Services { get; } + + /// + public string Name { get; set; } = CassandraClientOptions.DefaultOptionsKey; } From a4efcdd27b2d619e4ccd954a86d5a6dab0519d5b Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:13:51 +0200 Subject: [PATCH 128/174] feature: Added SiloClusteringBuilderExtensions.cs --- .../SiloClusteringBuilderExtensions.cs | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/Clustering/Cassandra/Hosting/SiloClusteringBuilderExtensions.cs diff --git a/src/Clustering/Cassandra/Hosting/SiloClusteringBuilderExtensions.cs b/src/Clustering/Cassandra/Hosting/SiloClusteringBuilderExtensions.cs new file mode 100644 index 0000000..74a5d84 --- /dev/null +++ b/src/Clustering/Cassandra/Hosting/SiloClusteringBuilderExtensions.cs @@ -0,0 +1,162 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Cassandra; +using Configuration.Overrides; +using Escendit.Extensions.Hosting.Cassandra; +using Escendit.Orleans.Clustering.Cassandra; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Runtime; + +/// +/// Clustering Silo Builder Extensions. +/// +public static class SiloClusteringBuilderExtensions +{ + /// + /// With Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static ISiloClusteringBuilder WithClientAsDefault( + this ISiloClusteringBuilder siloClusteringBuilder, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(siloClusteringBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + siloClusteringBuilder.Name = CassandraClientOptions.DefaultOptionsKey; + siloClusteringBuilder + .Services + .AddCassandraClientAsDefault(configureOptions); + return siloClusteringBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The configure options. + /// The updated clustering silo builder. + public static ISiloClusteringBuilder WithClientAsDefault( + this ISiloClusteringBuilder siloClusteringBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(siloClusteringBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + siloClusteringBuilder.Name = CassandraClientOptions.DefaultOptionsKey; + siloClusteringBuilder + .Services + .AddCassandraClientAsDefault(configureOptions); + return siloClusteringBuilder; + } + + /// + /// Add Client As Default. + /// + /// The clustering silo builder. + /// The options name. + /// The updated clustering silo builder. + public static ISiloClusteringBuilder WithClientFromOptionsAsDefault( + this ISiloClusteringBuilder siloClusteringBuilder, + string optionsName) + { + ArgumentNullException.ThrowIfNull(siloClusteringBuilder); + ArgumentNullException.ThrowIfNull(optionsName); + siloClusteringBuilder.Name = CassandraClientOptions.DefaultOptionsKey; + siloClusteringBuilder + .Services + .AddCassandraClientFromOptionsAsDefault(optionsName); + return siloClusteringBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static ISiloClusteringBuilder WithClient( + this ISiloClusteringBuilder siloClusteringBuilder, + string name, + Action configureOptions) + { + ArgumentNullException.ThrowIfNull(siloClusteringBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + siloClusteringBuilder.Name = name; + siloClusteringBuilder + .Services + .AddCassandraClient(name, configureOptions); + return siloClusteringBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The configure options. + /// The updated clustering silo builder. + public static ISiloClusteringBuilder WithClient( + this ISiloClusteringBuilder siloClusteringBuilder, + string name, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(siloClusteringBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configureOptions); + siloClusteringBuilder.Name = name; + siloClusteringBuilder + .Services + .AddCassandraClient(name, configureOptions); + return siloClusteringBuilder; + } + + /// + /// Add Client. + /// + /// The initial clustering silo builder. + /// The name. + /// The options name. + /// The updated clustering silo builder. + public static ISiloClusteringBuilder WithClientFromOptions( + this ISiloClusteringBuilder siloClusteringBuilder, + string name, + string optionsName) + { + ArgumentNullException.ThrowIfNull(siloClusteringBuilder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(optionsName); + siloClusteringBuilder.Name = name; + siloClusteringBuilder + .Services + .AddCassandraClientFromOptions(name, optionsName); + return siloClusteringBuilder; + } + + /// + /// Build. + /// + /// The silo clustering builder. + /// The updated silo builder. + public static ISiloBuilder Build( + this ISiloClusteringBuilder siloClusteringBuilder) + { + ArgumentNullException.ThrowIfNull(siloClusteringBuilder); + siloClusteringBuilder + .Services + .AddSingleton(serviceProvider => + ActivatorUtilities.CreateInstance( + serviceProvider, + siloClusteringBuilder.Name, + serviceProvider.GetRequiredServiceByName(siloClusteringBuilder.Name), + serviceProvider.GetOptionsByName(siloClusteringBuilder.Name), + serviceProvider.GetProviderClusterOptions(siloClusteringBuilder.Name).Value)); + return siloClusteringBuilder; + } +} From 3d38111ac56236b998540bd2d7c596e07ee929d1 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:14:46 +0200 Subject: [PATCH 129/174] fix: Updated SimulatorFixture.cs - removed options --- test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs b/test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs index d96f920..b9299d1 100644 --- a/test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs +++ b/test/Clustering/Cassandra/Fixtures/SimulatorFixture.cs @@ -26,9 +26,7 @@ public SimulatorFixture() .Endpoints.Add("localhost"); options.DefaultKeyspace = "test"; }) - .Configure(options => options.ClusterId = "default") - .Configure(options => options.ClientName = "Default") - .AddCassandraClustering(options => options.ClientName = "Default"); + .Configure(options => options.ClusterId = "default"); services .TryAddSingleton(typeof(IKeyedServiceCollection<,>), typeof(KeyedServiceCollection<,>)); From 52c23d76f3d7fc809129a5736a70d268ac6d6ca3 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:15:09 +0200 Subject: [PATCH 130/174] feature: Updated TestClientBuilderConfigurator.cs - refactor --- .../Cassandra/Configuration/TestClientBuilderConfigurator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs b/test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs index 1289f04..865396e 100644 --- a/test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs +++ b/test/Clustering/Cassandra/Configuration/TestClientBuilderConfigurator.cs @@ -21,6 +21,6 @@ public void Configure(IConfiguration configuration, IClientBuilder clientBuilder options.Endpoints.Add("localhost"); options.DefaultKeyspace = "test"; }) - .WithOptions(options => options.MaxStaleness = TimeSpan.FromDays(1)); + .Build(); } } From 7c94149e48326e0d97184451daa2121fc0a2f691 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:15:35 +0200 Subject: [PATCH 131/174] feature: Updated TestClusterTests.cs - temporarily disabled a test --- test/Clustering/Cassandra/TestClusterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Clustering/Cassandra/TestClusterTests.cs b/test/Clustering/Cassandra/TestClusterTests.cs index 6e0a488..7832623 100644 --- a/test/Clustering/Cassandra/TestClusterTests.cs +++ b/test/Clustering/Cassandra/TestClusterTests.cs @@ -29,14 +29,14 @@ public TestClusterTests(TestClusterFixture testClusterFixture) /// Test Cluster. /// /// The task. - [Fact] + /*[Fact] [IntegrationTest] public Task Test_Cluster() { var cluster = _testClusterFixture.Cluster; Assert.NotNull(cluster); return Task.CompletedTask; - } + }*/ /// /// Test Client. From 30264f1eaeeadad548c2054c8bd403d8c4d75760 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:15:58 +0200 Subject: [PATCH 132/174] chore: Updated TestSiloConfigurator.cs - refactor process in test. --- .../Cassandra/Configuration/TestSiloConfigurator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs b/test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs index 2bbc6eb..a92c697 100644 --- a/test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs +++ b/test/Clustering/Cassandra/Configuration/TestSiloConfigurator.cs @@ -18,6 +18,8 @@ public void Configure(ISiloBuilder siloBuilder) .ConfigureLogging(options => { options.AddConsole(); + options.AddFilter("Orleans", LogLevel.Information); + options.AddFilter("Escendit", LogLevel.Trace); options.SetMinimumLevel(LogLevel.Debug); }) .UseCassandraClustering() @@ -26,6 +28,6 @@ public void Configure(ISiloBuilder siloBuilder) options.Endpoints.Add("localhost"); options.DefaultKeyspace = "test"; }) - .WithOptions(options => options.MaxStaleness = TimeSpan.FromDays(1)); + .Build(); } } From 3bd12b1081b2518b703b1032ee28488a3a1321f0 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 17:31:13 +0200 Subject: [PATCH 133/174] fix: Updated SessionContextProviderBase.cs - protected to private, and passing caller --- .../Provider/SessionContextProviderBase.cs | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs index 7bd8983..fdda14f 100644 --- a/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs +++ b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs @@ -358,55 +358,6 @@ protected virtual void Dispose(bool disposing) _cluster.Dispose(); } - /// - /// Execute. - /// - /// The action. - /// The action name. - /// The task. - protected Task Execute(Func action, [CallerMemberName] string actionName = default!) - { - ArgumentNullException.ThrowIfNull(action); - return Execute(async () => - { - await action(); - return true; - }); - } - - /// - /// Execute. - /// - /// The type. - /// The action. - /// The action name. - /// A representing the result of the asynchronous operation. - protected Task Execute(Func> action, [CallerMemberName] string actionName = default!) - { - ArgumentNullException.ThrowIfNull(action); - - try - { - var type = typeof(T); - var typeName = type.Name; - if (type.GenericTypeArguments.Any()) - { - var arguments = string.Join(", ", type.GenericTypeArguments.ToList().ConvertAll(item => item.Name)); - typeName = $"{typeName}<{arguments}>"; - } - - var stopwatch = Stopwatch.StartNew(); - var result = action(); - LogExecute(_name, typeName, actionName, stopwatch.ElapsedMilliseconds); - return result; - } - catch (Exception ex) - { - LogException(_name, ex, ex.Message, typeof(T).FullName!, actionName); - throw; - } - } - private static KeyValuePair MatchSiloKey(IDictionary result, SiloAddress siloAddress) { return result @@ -469,6 +420,58 @@ private static Silo MapSilo(MembershipEntry entry, string etag) }; } + /// + /// Execute. + /// + /// The action. + /// The action name. + /// The task. + private Task Execute(Func action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + return Execute( + async () => + { + await action(); + return true; + }, + actionName); + } + + /// + /// Execute. + /// + /// The type. + /// The action. + /// The action name. + /// A representing the result of the asynchronous operation. + private Task Execute(Func> action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + + try + { + var type = typeof(T); + var typeName = type.Name; + if (type.GenericTypeArguments.Any()) + { + var arguments = string.Join(", ", type.GenericTypeArguments.ToList().ConvertAll(item => item.Name)); + typeName = $"{typeName}<{arguments}>"; + } + + var stopwatch = Stopwatch.StartNew(); + var result = action(); + stopwatch.Stop(); + LogExecute(_name, typeName, actionName, stopwatch.ElapsedMilliseconds); + return result; + } + catch (Exception ex) + { + LogException(_name, ex, ex.Message, typeof(T).FullName!, actionName); + throw; + } + } + private async Task InitializeVersionDataAsync(string clusterId) { ArgumentNullException.ThrowIfNull(clusterId); From 44ff1326b3341601d35a6fc2607cded0852da6ce Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:45:49 +0200 Subject: [PATCH 134/174] feature: Added 1_Reminders.sql --- src/Reminders/Cassandra/Resources/SQL/1_Reminders.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/Reminders/Cassandra/Resources/SQL/1_Reminders.sql diff --git a/src/Reminders/Cassandra/Resources/SQL/1_Reminders.sql b/src/Reminders/Cassandra/Resources/SQL/1_Reminders.sql new file mode 100644 index 0000000..4d00d38 --- /dev/null +++ b/src/Reminders/Cassandra/Resources/SQL/1_Reminders.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS reminders ( + type blob, + id blob, + hash bigint, + name varchar, + start_on timestamp, + period bigint, + etag varchar, + primary key (type, id, name) +); From 4de6ab8109c1f944e015529c2e9a064f351e0bf8 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:46:10 +0200 Subject: [PATCH 135/174] feature: Moved CassandraRemindersTable.cs - implementation --- .../Cassandra/CassandraRemindersTable.cs | 54 ---- .../Provider/CassandraRemindersTable.cs | 262 ++++++++++++++++++ 2 files changed, 262 insertions(+), 54 deletions(-) delete mode 100644 src/Reminders/Cassandra/CassandraRemindersTable.cs create mode 100644 src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs diff --git a/src/Reminders/Cassandra/CassandraRemindersTable.cs b/src/Reminders/Cassandra/CassandraRemindersTable.cs deleted file mode 100644 index 8323614..0000000 --- a/src/Reminders/Cassandra/CassandraRemindersTable.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Escendit Ltd. All Rights Reserved. -// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. - -namespace Escendit.Orleans.Reminders.Cassandra; - -using global::Orleans.Runtime; - -/// -/// Cassandra Reminders Table. -/// -public class CassandraRemindersTable : IReminderTable -{ - /// - public Task Init() - { - throw new NotImplementedException(); - } - - /// - public Task ReadRows(GrainId grainId) - { - throw new NotImplementedException(); - } - - /// - public Task ReadRows(uint begin, uint end) - { - throw new NotImplementedException(); - } - - /// - public Task ReadRow(GrainId grainId, string reminderName) - { - throw new NotImplementedException(); - } - - /// - public Task UpsertRow(ReminderEntry entry) - { - throw new NotImplementedException(); - } - - /// - public Task RemoveRow(GrainId grainId, string reminderName, string eTag) - { - throw new NotImplementedException(); - } - - /// - public Task TestOnlyClearTable() - { - throw new NotImplementedException(); - } -} diff --git a/src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs b/src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs new file mode 100644 index 0000000..2979659 --- /dev/null +++ b/src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs @@ -0,0 +1,262 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Provider; + +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using Escendit.Extensions.Hosting.Cassandra; +using global::Cassandra; +using global::Cassandra.Mapping; +using global::Orleans.Runtime; +using Microsoft.Extensions.Logging; +using Schema; + +/// +/// Cassandra Reminders Table. +/// +internal partial class CassandraRemindersTable : IReminderTable +{ + private readonly string _name; + private readonly ILogger _logger; + private readonly ICluster _cluster; + private readonly CassandraClientOptions _clientOptions; + private readonly Assembly _selfAssembly; + private ISession? _session; + private IMapper? _mapper; + + /// + /// Initializes a new instance of the class. + /// + /// The name (of client). + /// The logger. + /// The cluster. + /// The client options. + public CassandraRemindersTable( + string name, + ILogger logger, + ICluster cluster, + CassandraClientOptions clientOptions) + { + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(cluster); + ArgumentNullException.ThrowIfNull(clientOptions); + _name = name; + _logger = logger; + _cluster = cluster; + _clientOptions = clientOptions; + _selfAssembly = typeof(CassandraRemindersTable).Assembly; + } + + /// + public async Task Init() + { + _session = await _cluster.ConnectAsync(string.Empty); + _session.CreateKeyspaceIfNotExists(_clientOptions.DefaultKeyspace); + _session.ChangeKeyspace(_clientOptions.DefaultKeyspace); + _mapper = new Mapper(_session); + await InitializeDatabase(); + } + + /// + public async Task ReadRows(GrainId grainId) + { + var (type, id) = GenerateDatabaseIds(grainId); + var results = await _mapper!.FetchAsync("WHERE type = ? AND id = ?", type, id); + if (results is null) + { + return new ReminderTableData(); + } + + return new ReminderTableData(results + .ToList() + .ConvertAll(item => new ReminderEntry + { + GrainId = grainId, + ReminderName = item.Name, + ETag = item.Etag, + Period = TimeSpan.FromTicks(item.Period), + StartAt = item.StartOn.UtcDateTime, + })); + } + + /// + public async Task ReadRows(uint begin, uint end) + { + var results = await _mapper!.FetchAsync("WHERE hash > ? and hash <= ?", (long)begin, (long)end); + if (results is null) + { + return new ReminderTableData(); + } + + var items = results.ToList(); + return new ReminderTableData(items.ConvertAll(item => new ReminderEntry + { + GrainId = BuildGrainId(item.Type, item.Id), + ReminderName = item.Name, + ETag = item.Etag, + Period = TimeSpan.FromTicks(item.Period), + StartAt = item.StartOn.UtcDateTime, + })); + } + + /// + public async Task ReadRow(GrainId grainId, string reminderName) + { + ArgumentNullException.ThrowIfNull(reminderName); + var (type, id) = GenerateDatabaseIds(grainId); + var results + = await _mapper! + .FetchAsync("WHERE type = ? AND id = ? AND name = ?", type, id, reminderName); + + if (results is null) + { + return new ReminderEntry(); + } + + var items = results.ToList(); + + if (!items.Any()) + { + return new ReminderEntry(); + } + + var item = items.First()!; + return new ReminderEntry + { + GrainId = grainId, + ReminderName = reminderName, + ETag = item.Etag, + Period = TimeSpan.FromTicks(item.Period), + StartAt = item.StartOn.UtcDateTime, + }; + } + + /// + public async Task UpsertRow(ReminderEntry entry) + { + ArgumentNullException.ThrowIfNull(entry); + var etag = Guid.NewGuid(); + var (type, id) = GenerateDatabaseIds(entry.GrainId); + + var row = new Reminder( + type, + id, + entry.GrainId.GetUniformHashCode(), + entry.ReminderName, + new DateTimeOffset(entry.StartAt), + entry.Period.Ticks, + entry.ETag); + + await _mapper!.InsertAsync(row); + + return entry.ETag; + } + + /// + public async Task RemoveRow(GrainId grainId, string reminderName, string eTag) + { + ArgumentNullException.ThrowIfNull(reminderName); + ArgumentNullException.ThrowIfNull(eTag); + var info = await _mapper!.DeleteIfAsync("WHERE type = ? AND id = ? AND name = ? AND etag = ?"); + return info.Applied; + } + + /// + public Task TestOnlyClearTable() + { + return _mapper!.DeleteAsync("1=1"); + } + + private static (byte[] Type, byte[] Id) GenerateDatabaseIds(GrainId grainId) + { + return (grainId.Type.Value.Value.ToArray(), grainId.Key.Value.ToArray()); + } + + private static GrainId BuildGrainId(byte[] type, byte[] id) + { + return GrainId.Create(new GrainType(type), new IdSpan(id)); + } + + private async Task InitializeDatabase() + { + var remindersTableStream = + _selfAssembly.GetManifestResourceStream( + "Escendit.Orleans.Reminders.Cassandra.Resources.SQL.1_Reminders.sql"); + + using var remindersTableReader = new StreamReader(remindersTableStream!, Encoding.UTF8, true); + var sqlRemindersTable = await remindersTableReader.ReadToEndAsync().ConfigureAwait(false); + + await _session!.ExecuteAsync(new SimpleStatement(sqlRemindersTable)); + } + + /// + /// Execute. + /// + /// The action. + /// The action name. + /// The task. + private Task Execute(Func action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + return Execute( + async () => + { + await action(); + return true; + }, + actionName); + } + + /// + /// Execute. + /// + /// The type. + /// The action. + /// The action name. + /// A representing the result of the asynchronous operation. + private Task Execute(Func> action, [CallerMemberName] string actionName = default!) + { + ArgumentNullException.ThrowIfNull(action); + var returnType = typeof(T); + var selfType = GetType(); + var returnTypeName = returnType.Name; + var selfTypeName = selfType.Name; + if (returnType.GenericTypeArguments.Any()) + { + var arguments = string.Join(", ", returnType.GenericTypeArguments.ToList().ConvertAll(item => item.Name)); + returnTypeName = $"{returnTypeName}<{arguments}>"; + } + + try + { + var stopwatch = Stopwatch.StartNew(); + var result = action(); + stopwatch.Stop(); + LogExecute(_name, returnTypeName, selfTypeName, actionName, stopwatch.ElapsedMilliseconds); + return result; + } + catch (Exception ex) + { + LogException(_name, ex, ex.Message, returnTypeName, selfTypeName, actionName); + throw; + } + } + + [LoggerMessage( + EventId = 200, + EventName = "Execution", + Level = LogLevel.Debug, + Message = "Executing with client {name} > {returnType} {type}.{action} completed in {elapsed}")] + private partial void LogExecute(string name, string returnType, string selfType, string action, long elapsed); + + [LoggerMessage( + EventId = 500, + EventName = "Exception", + Level = LogLevel.Error, + Message = "Exception with client {name} > {returnType} {type}.{action} {message}")] + private partial void LogException(string name, Exception exception, string message, string returnType, string type, string action); +} From 0aaa27d27891cfa1e26bf3e368e9b8acd138ae8c Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:46:26 +0200 Subject: [PATCH 136/174] chore: Updated Escendit.Orleans.Reminders.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj index 2a6506e..9946974 100644 --- a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj +++ b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj @@ -1,13 +1,9 @@ + - - - - - From cb7179c91305ac2b54829d7b325c5787da6b42ba Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:47:03 +0200 Subject: [PATCH 137/174] feature: Updated GrainStorageBase.cs - refactor, improved logging messages --- .../Cassandra/Storage/GrainStorageBase.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Persistence/Cassandra/Storage/GrainStorageBase.cs b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs index 0cca86d..beb6622 100644 --- a/src/Persistence/Cassandra/Storage/GrainStorageBase.cs +++ b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs @@ -3,6 +3,7 @@ namespace Escendit.Orleans.Persistence.Cassandra.Storage; +using System.Diagnostics; using System.Runtime.CompilerServices; using Escendit.Extensions.Hosting.Cassandra; using global::Cassandra; @@ -166,15 +167,27 @@ protected Task Execute(Func action, [CallerMemberName] string actionName = protected Task Execute(Func> action, [CallerMemberName] string actionName = default!) { ArgumentNullException.ThrowIfNull(action); + var returnType = typeof(T); + var selfType = GetType(); + var returnTypeName = returnType.Name; + var selfTypeName = selfType.Name; + if (returnType.GenericTypeArguments.Any()) + { + var arguments = string.Join(", ", returnType.GenericTypeArguments.ToList().ConvertAll(item => item.Name)); + returnTypeName = $"{returnTypeName}<{arguments}>"; + } try { - LogExecute(_name, typeof(T).FullName!, actionName); - return action(); + var stopwatch = Stopwatch.StartNew(); + var result = action(); + stopwatch.Stop(); + LogExecute(_name, returnTypeName, selfTypeName, actionName, stopwatch.ElapsedMilliseconds); + return result; } catch (Exception ex) { - LogException(_name, ex, ex.Message, nameof(SingleTableGrainStorage), actionName); + LogException(_name, ex, ex.Message, returnTypeName, selfTypeName, actionName); throw; } } @@ -204,13 +217,13 @@ protected Task Execute(Func> action, [CallerMemberName] string act EventId = 200, EventName = "Execution", Level = LogLevel.Debug, - Message = "Executing {name}#{type}.{action}")] - private partial void LogExecute(string name, string type, string action); + Message = "Executing with client {name} > {returnType} {type}.{action} completed in {elapsed}")] + private partial void LogExecute(string name, string returnType, string selfType, string action, long elapsed); [LoggerMessage( EventId = 500, EventName = "Exception", Level = LogLevel.Error, - Message = "{name}#{type}.{action} {message}")] - private partial void LogException(string name, Exception exception, string message, string type, string action); + Message = "Exception with client {name} > {returnType} {type}.{action} {message}")] + private partial void LogException(string name, Exception exception, string message, string returnType, string type, string action); } From 69253da36a63d778c523f371c03081cded0eeb13 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:47:20 +0200 Subject: [PATCH 138/174] feature: Added Reminder.cs --- src/Reminders/Cassandra/Schema/Reminder.cs | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/Reminders/Cassandra/Schema/Reminder.cs diff --git a/src/Reminders/Cassandra/Schema/Reminder.cs b/src/Reminders/Cassandra/Schema/Reminder.cs new file mode 100644 index 0000000..87c3f3d --- /dev/null +++ b/src/Reminders/Cassandra/Schema/Reminder.cs @@ -0,0 +1,73 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Schema; + +/// +/// Reminder. +/// +public record Reminder +{ + /// + /// Initializes a new instance of the class. + /// + /// The grain id's type. + /// The grain id's id. + /// The hash. + /// The name. + /// The start on. + /// The period. + /// The etag. + public Reminder(byte[] type, byte[] id, long hash, string name, DateTimeOffset startOn, long period, string etag) + { + Type = type; + Id = id; + Hash = hash; + Name = name; + StartOn = startOn; + Period = period; + Etag = etag; + } + + /// + /// Gets or sets the type. + /// + /// The type component. + public byte[] Type { get; set; } + + /// + /// Gets or sets the id. + /// + /// The id. + public byte[] Id { get; set; } + + /// + /// Gets or sets the hash. + /// + /// The hash. + public long Hash { get; set; } + + /// + /// Gets or sets the name. + /// + /// The name. + public string Name { get; set; } + + /// + /// Gets or sets the start on. + /// + /// The start on. + public DateTimeOffset StartOn { get; set; } = DateTimeOffset.UtcNow; + + /// + /// Gets or sets the period. + /// + /// The period. + public long Period { get; set; } + + /// + /// Gets or sets the etag. + /// + /// The etag. + public string Etag { get; set; } +} From e2b257f3f7dcdbebdf42ce59950f826cb8fb67a1 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:47:52 +0200 Subject: [PATCH 139/174] feature: Added ReminderMapping.cs --- .../Cassandra/Mapping/ReminderMapping.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/Reminders/Cassandra/Mapping/ReminderMapping.cs diff --git a/src/Reminders/Cassandra/Mapping/ReminderMapping.cs b/src/Reminders/Cassandra/Mapping/ReminderMapping.cs new file mode 100644 index 0000000..a502af7 --- /dev/null +++ b/src/Reminders/Cassandra/Mapping/ReminderMapping.cs @@ -0,0 +1,30 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Mapping; + +using global::Cassandra.Mapping; +using Schema; + +/// +/// Reminder Mapping. +/// +public class ReminderMapping : Mappings +{ + /// + /// Initializes a new instance of the class. + /// + public ReminderMapping() + { + For() + .TableName("reminders") + .ClusteringKey("type", "id", "name") + .Column(p => p.Type, cm => cm.WithName("type")) + .Column(p => p.Id, cm => cm.WithName("id")) + .Column(p => p.Hash, cm => cm.WithName("hash")) + .Column(p => p.Name, cm => cm.WithName("name")) + .Column(p => p.StartOn, cm => cm.WithName("start_on")) + .Column(p => p.Period, cm => cm.WithName("period")) + .Column(p => p.Etag, cm => cm.WithName("etag")); + } +} From 7a8426440f8a30d425f5afa93b9c712ecce804de Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:48:22 +0200 Subject: [PATCH 140/174] feature: Updated SessionContextProviderBase.cs - refactor, improved logging --- .../Provider/SessionContextProviderBase.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs index fdda14f..5ffe6c6 100644 --- a/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs +++ b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs @@ -448,26 +448,27 @@ private Task Execute(Func action, [CallerMemberName] string actionName = d private Task Execute(Func> action, [CallerMemberName] string actionName = default!) { ArgumentNullException.ThrowIfNull(action); + var returnType = typeof(T); + var selfType = GetType(); + var returnTypeName = returnType.Name; + var selfTypeName = selfType.Name; + if (returnType.GenericTypeArguments.Any()) + { + var arguments = string.Join(", ", returnType.GenericTypeArguments.ToList().ConvertAll(item => item.Name)); + returnTypeName = $"{returnTypeName}<{arguments}>"; + } try { - var type = typeof(T); - var typeName = type.Name; - if (type.GenericTypeArguments.Any()) - { - var arguments = string.Join(", ", type.GenericTypeArguments.ToList().ConvertAll(item => item.Name)); - typeName = $"{typeName}<{arguments}>"; - } - var stopwatch = Stopwatch.StartNew(); var result = action(); stopwatch.Stop(); - LogExecute(_name, typeName, actionName, stopwatch.ElapsedMilliseconds); + LogExecute(_name, returnTypeName, selfTypeName, actionName, stopwatch.ElapsedMilliseconds); return result; } catch (Exception ex) { - LogException(_name, ex, ex.Message, typeof(T).FullName!, actionName); + LogException(_name, ex, ex.Message, returnTypeName, selfTypeName, actionName); throw; } } @@ -523,8 +524,8 @@ private async Task InitializeDatabaseAsync() EventId = 200, EventName = "Execution", Level = LogLevel.Debug, - Message = "Executing {name}#{type}.{action} took {duration}ms")] - private partial void LogExecute(string name, string type, string action, long duration); + Message = "Executing with client {name} > {returnType} {type}.{action} completed in {elapsed}")] + private partial void LogExecute(string name, string returnType, string selfType, string action, long elapsed); [LoggerMessage( EventId = 500, @@ -537,6 +538,6 @@ private async Task InitializeDatabaseAsync() EventId = 500, EventName = "Exception", Level = LogLevel.Error, - Message = "{name}#{type}.{action} {message}")] - private partial void LogException(string name, Exception exception, string message, string type, string action); + Message = "Exception with client {name} > {returnType} {type}.{action} {message}")] + private partial void LogException(string name, Exception exception, string message, string returnType, string type, string action); } From 7102b18eee89414a6f8b803ee512d5aa8893c5c3 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sat, 30 Sep 2023 23:48:52 +0200 Subject: [PATCH 141/174] feature: Added SiloBuilderExtensions.cs --- .../Hosting/SiloBuilderExtensions.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/Reminders/Cassandra/Hosting/SiloBuilderExtensions.cs diff --git a/src/Reminders/Cassandra/Hosting/SiloBuilderExtensions.cs b/src/Reminders/Cassandra/Hosting/SiloBuilderExtensions.cs new file mode 100644 index 0000000..855ff6c --- /dev/null +++ b/src/Reminders/Cassandra/Hosting/SiloBuilderExtensions.cs @@ -0,0 +1,122 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Orleans.Hosting; + +using Cassandra; +using Escendit.Extensions.Hosting.Cassandra; +using Escendit.Orleans.Reminders.Cassandra.Provider; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Runtime; + +/// +/// Silo Builder Extensions. +/// +public static class SiloBuilderExtensions +{ + /// + /// Use Cassandra Reminder Service. + /// + /// The initial silo builder. + /// The configure options. + /// The updated silo builder. + public static ISiloBuilder UseCassandraReminderService( + this ISiloBuilder siloBuilder, + Action configureOptions) + { + return siloBuilder + .UseCassandraReminderService(builder => builder + .Configure(configureOptions)); + } + + /// + /// Use Cassandra Reminder Service. + /// + /// The initial silo builder. + /// The configure options. + /// The updated silo builder. + public static ISiloBuilder UseCassandraReminderService( + this ISiloBuilder siloBuilder, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(siloBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + siloBuilder + .Services + .AddCassandraClientAsDefault(configureOptions) + .AddSingleton(serviceProvider => + Create(serviceProvider, CassandraClientOptions.DefaultOptionsKey)) + .AddReminders(); + return siloBuilder; + } + + /// + /// Use Cassandra Reminder Service. + /// + /// The initial silo builder. + /// The name (of client). + /// The configure options. + /// The updated silo builder. + public static ISiloBuilder UseCassandraReminderService( + this ISiloBuilder siloBuilder, + string name, + Action configureOptions) + { + return siloBuilder + .UseCassandraReminderService(name, builder => builder + .Configure(configureOptions)); + } + + /// + /// Use Cassandra Reminder Service. + /// + /// The initial silo builder. + /// The name (of client). + /// The configure options. + /// The updated silo builder. + public static ISiloBuilder UseCassandraReminderService( + this ISiloBuilder siloBuilder, + string name, + Action> configureOptions) + { + ArgumentNullException.ThrowIfNull(siloBuilder); + ArgumentNullException.ThrowIfNull(configureOptions); + siloBuilder + .Services + .AddCassandraClient(name, configureOptions) + .AddSingleton(serviceProvider => Create(serviceProvider, name)) + .AddReminders(); + return siloBuilder; + } + + /// + /// Use Cassandra Reminder Service. + /// + /// + /// Use if we already created the client. + /// + /// The initial silo builder. + /// The client name. + /// The updated silo builder. + public static ISiloBuilder UseCassandraReminderService( + this ISiloBuilder siloBuilder, + string clientName) + { + ArgumentNullException.ThrowIfNull(siloBuilder); + ArgumentNullException.ThrowIfNull(clientName); + siloBuilder + .Services + .AddSingleton(serviceProvider => Create(serviceProvider, clientName)) + .AddReminders(); + return siloBuilder; + } + + private static CassandraRemindersTable Create(IServiceProvider serviceProvider, string name) + => ActivatorUtilities + .CreateInstance( + serviceProvider, + name, + serviceProvider.GetRequiredServiceByName(name), + serviceProvider.GetOptionsByName(name)); +} From 47bb021e38cca7733744e37ca31efbaeb8b415f2 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 13:57:23 +0200 Subject: [PATCH 142/174] chore: Added AssemblyInfo.cs --- test/Reminders/Cassandra/Properties/AssemblyInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test/Reminders/Cassandra/Properties/AssemblyInfo.cs diff --git a/test/Reminders/Cassandra/Properties/AssemblyInfo.cs b/test/Reminders/Cassandra/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14aeb7e --- /dev/null +++ b/test/Reminders/Cassandra/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +[assembly: CLSCompliant(false)] From d35104da857a6f0bb8114f0353e828d80134be1b Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 13:58:16 +0200 Subject: [PATCH 143/174] feature: Updated CassandraRemindersTable.cs - refactor hash filtering CSHARP-999 report for bug in LINQ and Mapper --- .../Provider/CassandraRemindersTable.cs | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs b/src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs index 2979659..00b3fbb 100644 --- a/src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs +++ b/src/Reminders/Cassandra/Provider/CassandraRemindersTable.cs @@ -11,6 +11,7 @@ namespace Escendit.Orleans.Reminders.Cassandra.Provider; using global::Cassandra; using global::Cassandra.Mapping; using global::Orleans.Runtime; +using Mapping; using Microsoft.Extensions.Logging; using Schema; @@ -24,8 +25,10 @@ internal partial class CassandraRemindersTable : IReminderTable private readonly ICluster _cluster; private readonly CassandraClientOptions _clientOptions; private readonly Assembly _selfAssembly; + private readonly MappingConfiguration _mapping; private ISession? _session; private IMapper? _mapper; + private PreparedStatement? _hashLookup; /// /// Initializes a new instance of the class. @@ -49,6 +52,7 @@ public CassandraRemindersTable( _cluster = cluster; _clientOptions = clientOptions; _selfAssembly = typeof(CassandraRemindersTable).Assembly; + _mapping = new MappingConfiguration().Define(); } /// @@ -57,8 +61,9 @@ public async Task Init() _session = await _cluster.ConnectAsync(string.Empty); _session.CreateKeyspaceIfNotExists(_clientOptions.DefaultKeyspace); _session.ChangeKeyspace(_clientOptions.DefaultKeyspace); - _mapper = new Mapper(_session); + _mapper = new Mapper(_session, _mapping); await InitializeDatabase(); + _hashLookup = await _session.PrepareAsync("SELECT * FROM reminders WHERE hash > ? AND hash <= ? ALLOW FILTERING"); } /// @@ -86,21 +91,29 @@ public async Task ReadRows(GrainId grainId) /// public async Task ReadRows(uint begin, uint end) { - var results = await _mapper!.FetchAsync("WHERE hash > ? and hash <= ?", (long)begin, (long)end); - if (results is null) + var bigBegin = Convert.ToInt64(begin); + var bigEnd = Convert.ToInt64(end); + + var resultSet = await _session!.ExecuteAsync(_hashLookup!.Bind(bigBegin, bigEnd)); + if (resultSet.GetAvailableWithoutFetching() == 0) { return new ReminderTableData(); } - var items = results.ToList(); - return new ReminderTableData(items.ConvertAll(item => new ReminderEntry + var items = new List(); + foreach (var row in resultSet.GetRows()) { - GrainId = BuildGrainId(item.Type, item.Id), - ReminderName = item.Name, - ETag = item.Etag, - Period = TimeSpan.FromTicks(item.Period), - StartAt = item.StartOn.UtcDateTime, - })); + items.Add(new ReminderEntry + { + GrainId = BuildGrainId(row.GetValue("type"), row.GetValue("id")), + ReminderName = row.GetValue("name"), + StartAt = row.GetValue("start_on").UtcDateTime, + Period = TimeSpan.FromTicks(row.GetValue("period")), + ETag = row.GetValue("etag"), + }); + } + + return new ReminderTableData(items); } /// @@ -139,7 +152,7 @@ var results public async Task UpsertRow(ReminderEntry entry) { ArgumentNullException.ThrowIfNull(entry); - var etag = Guid.NewGuid(); + var etag = Encoding.UTF8.GetString(GrainIdKeyExtensions.CreateGuidKey(Guid.NewGuid()).Value.ToArray()); var (type, id) = GenerateDatabaseIds(entry.GrainId); var row = new Reminder( @@ -149,11 +162,10 @@ public async Task UpsertRow(ReminderEntry entry) entry.ReminderName, new DateTimeOffset(entry.StartAt), entry.Period.Ticks, - entry.ETag); + etag); await _mapper!.InsertAsync(row); - - return entry.ETag; + return etag; } /// @@ -250,13 +262,13 @@ private Task Execute(Func> action, [CallerMemberName] string actio EventId = 200, EventName = "Execution", Level = LogLevel.Debug, - Message = "Executing with client {name} > {returnType} {type}.{action} completed in {elapsed}")] + Message = "Executing with client {name} > {returnType} {selfType}.{action} completed in {elapsed}")] private partial void LogExecute(string name, string returnType, string selfType, string action, long elapsed); [LoggerMessage( EventId = 500, EventName = "Exception", Level = LogLevel.Error, - Message = "Exception with client {name} > {returnType} {type}.{action} {message}")] - private partial void LogException(string name, Exception exception, string message, string returnType, string type, string action); + Message = "Exception with client {name} > {returnType} {selfType}.{action} {message}")] + private partial void LogException(string name, Exception exception, string message, string returnType, string selfType, string action); } From 4c903ad7067a73d8edbdcf4edfca34f40401e98e Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 13:58:30 +0200 Subject: [PATCH 144/174] chore: Added ClusterCollectionFixture.cs --- .../Collections/ClusterCollectionFixture.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/Reminders/Cassandra/Collections/ClusterCollectionFixture.cs diff --git a/test/Reminders/Cassandra/Collections/ClusterCollectionFixture.cs b/test/Reminders/Cassandra/Collections/ClusterCollectionFixture.cs new file mode 100644 index 0000000..1bf408b --- /dev/null +++ b/test/Reminders/Cassandra/Collections/ClusterCollectionFixture.cs @@ -0,0 +1,18 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Tests.Collections; + +using Fixtures; + +/// +/// Cluster Collection Fixture. +/// +[CollectionDefinition(Name)] +public class ClusterCollectionFixture : ICollectionFixture +{ + /// + /// Name. + /// + public const string Name = "ClusterCollection"; +} From 7b9024db2e6c36f1f196b8a9ba413d780cd8bcef Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 13:58:59 +0200 Subject: [PATCH 145/174] chore: Updated Directory.Build.props - moved from packages --- test/Directory.Build.props | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 92f64e3..00aff81 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -4,10 +4,35 @@ $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\')) - + false true + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + From 221a0da628e563ad96ab4065f306265cdd25959a Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:00:23 +0200 Subject: [PATCH 146/174] chore: Updated Directory.Packages.props - removed references and moved to build props --- test/Directory.Packages.props | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/test/Directory.Packages.props b/test/Directory.Packages.props index 6200a17..d811dfc 100644 --- a/test/Directory.Packages.props +++ b/test/Directory.Packages.props @@ -6,31 +6,4 @@ - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - \ No newline at end of file + From 4a49e16d18e40e70fc6d6cf386820e99ae519668 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:00:50 +0200 Subject: [PATCH 147/174] chore: Updated Directory.Packages.props - added Orleans's TestingHost --- Directory.Packages.props | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 11417ca..32726c9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,10 +25,11 @@ + - \ No newline at end of file + From 2ac6f8f18ca53fda6f64fbb736059f1d061be867 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:01:10 +0200 Subject: [PATCH 148/174] chore: Updated Escendit.Orleans.Clustering.Cassandra.Tests.csproj --- .../Escendit.Orleans.Clustering.Cassandra.Tests.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj b/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj index 700691d..157810b 100644 --- a/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj +++ b/test/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.Tests.csproj @@ -2,7 +2,4 @@ - - - From 9231f73fa2cc443dedf0425a9712b0c29d7876ad Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:01:20 +0200 Subject: [PATCH 149/174] chore: Updated Escendit.Orleans.Extensions.Cassandra.sln --- Escendit.Orleans.Extensions.Cassandra.sln | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Escendit.Orleans.Extensions.Cassandra.sln b/Escendit.Orleans.Extensions.Cassandra.sln index 2b5c771..78f776b 100644 --- a/Escendit.Orleans.Extensions.Cassandra.sln +++ b/Escendit.Orleans.Extensions.Cassandra.sln @@ -35,6 +35,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clustering", "Clustering", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Clustering.Cassandra.Tests", "test\Clustering\Cassandra\Escendit.Orleans.Clustering.Cassandra.Tests.csproj", "{A9488ABE-E593-46FB-9C63-32A687EEFE38}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Reminders", "Reminders", "{051C13BE-64CE-42A2-A67C-46A7DCA55095}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Reminders.Cassandra.Tests", "test\Reminders\Cassandra\Escendit.Orleans.Reminders.Cassandra.Tests.csproj", "{F14B85E6-725E-4F78-BDAB-0240DB573333}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -58,6 +62,8 @@ Global {7D40168C-40B6-4339-88A0-4F6E408F2EE7} = {9DC8501A-84DE-4272-A660-6615DA7C91B0} {16F96498-E9B7-4747-898A-86AF7D6B44D5} = {F719A264-551D-450A-9FBF-F3802858FFAE} {A9488ABE-E593-46FB-9C63-32A687EEFE38} = {16F96498-E9B7-4747-898A-86AF7D6B44D5} + {051C13BE-64CE-42A2-A67C-46A7DCA55095} = {F719A264-551D-450A-9FBF-F3802858FFAE} + {F14B85E6-725E-4F78-BDAB-0240DB573333} = {051C13BE-64CE-42A2-A67C-46A7DCA55095} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -92,5 +98,9 @@ Global {A9488ABE-E593-46FB-9C63-32A687EEFE38}.Debug|Any CPU.Build.0 = Debug|Any CPU {A9488ABE-E593-46FB-9C63-32A687EEFE38}.Release|Any CPU.ActiveCfg = Release|Any CPU {A9488ABE-E593-46FB-9C63-32A687EEFE38}.Release|Any CPU.Build.0 = Release|Any CPU + {F14B85E6-725E-4F78-BDAB-0240DB573333}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F14B85E6-725E-4F78-BDAB-0240DB573333}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F14B85E6-725E-4F78-BDAB-0240DB573333}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F14B85E6-725E-4F78-BDAB-0240DB573333}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From d717cd3c809638c9b209954aec2cdc0a99002269 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:01:32 +0200 Subject: [PATCH 150/174] chore: Updated Escendit.Orleans.Persistence.Cassandra.Tests.csproj --- .../Escendit.Orleans.Persistence.Cassandra.Tests.csproj | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj b/test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj index 33320f6..02d0cfb 100644 --- a/test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj +++ b/test/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.Tests.csproj @@ -1,11 +1,4 @@ - - false - true - - - - From f1e49786d191b0bfcdb03f785aced87c59e15f43 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:02:15 +0200 Subject: [PATCH 151/174] chore: Updated Escendit.Orleans.Reminders.Cassandra.csproj - SQL embedded reminders --- .../Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj index 9946974..8eb4731 100644 --- a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj +++ b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj @@ -6,4 +6,8 @@ + + + + From c604ec1871505ee199585b98f7b7fb7c7efdf431 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:02:34 +0200 Subject: [PATCH 152/174] chore: Added Escendit.Orleans.Reminders.Cassandra.Tests.csproj --- .../Escendit.Orleans.Reminders.Cassandra.Tests.csproj | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.Tests.csproj diff --git a/test/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.Tests.csproj b/test/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.Tests.csproj new file mode 100644 index 0000000..52b25a0 --- /dev/null +++ b/test/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.Tests.csproj @@ -0,0 +1,5 @@ + + + + + From 54e5a45e5bd4f9154cebe3dde44bc40c95f805fb Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:02:46 +0200 Subject: [PATCH 153/174] chore: Added ITestGrain.cs --- test/Reminders/Cassandra/Grains/ITestGrain.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/Reminders/Cassandra/Grains/ITestGrain.cs diff --git a/test/Reminders/Cassandra/Grains/ITestGrain.cs b/test/Reminders/Cassandra/Grains/ITestGrain.cs new file mode 100644 index 0000000..9005cb4 --- /dev/null +++ b/test/Reminders/Cassandra/Grains/ITestGrain.cs @@ -0,0 +1,17 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Tests.Grains; + +/// +/// Test Grain. +/// +public interface ITestGrain : IGrainWithIntegerKey +{ + /// + /// Remind me with message. + /// + /// The message. + /// A representing the result of the asynchronous operation. + public Task RemindMe(string message); +} From 1f335640380f924e42fba5be8cbc55963e187bb4 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:03:17 +0200 Subject: [PATCH 154/174] chore: Added TestClusterFixture.cs --- .../Cassandra/Fixtures/TestClusterFixture.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/Reminders/Cassandra/Fixtures/TestClusterFixture.cs diff --git a/test/Reminders/Cassandra/Fixtures/TestClusterFixture.cs b/test/Reminders/Cassandra/Fixtures/TestClusterFixture.cs new file mode 100644 index 0000000..1ceb9ff --- /dev/null +++ b/test/Reminders/Cassandra/Fixtures/TestClusterFixture.cs @@ -0,0 +1,57 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Tests.Fixtures; + +using Configuration; +using global::Orleans.TestingHost; + +/// +/// Test Cluster Fixture. +/// +public sealed class TestClusterFixture : IDisposable +{ + /// + /// Initializes a new instance of the class. + /// + public TestClusterFixture() + { + var builder = new TestClusterBuilder + { + Options = + { + ClusterId = "testCluster", + ServiceId = "testService", + }, + }; + builder.AddSiloBuilderConfigurator(); + Cluster = builder.Build(); + Cluster.Deploy(); + } + + /// + /// Finalizes an instance of the class. + /// + ~TestClusterFixture() => Dispose(false); + + /// + /// Gets the cluster. + /// + /// The cluster. + public TestCluster Cluster { get; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + Cluster.StopAllSilos(); + } + } +} From 6a77324ca4aa046e792b961f4557a3075e613a12 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:03:33 +0200 Subject: [PATCH 155/174] chore: Added TestClusterTests.cs --- test/Reminders/Cassandra/TestClusterTests.cs | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/Reminders/Cassandra/TestClusterTests.cs diff --git a/test/Reminders/Cassandra/TestClusterTests.cs b/test/Reminders/Cassandra/TestClusterTests.cs new file mode 100644 index 0000000..dfdfa3f --- /dev/null +++ b/test/Reminders/Cassandra/TestClusterTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Tests; + +using Collections; +using Fixtures; +using Grains; + +/// +/// Test Cluster Tests. +/// +[Collection(ClusterCollectionFixture.Name)] +public class TestClusterTests +{ + private readonly TestClusterFixture _testClusterFixture; + + /// + /// Initializes a new instance of the class. + /// + /// The test cluster fixture. + public TestClusterTests(TestClusterFixture testClusterFixture) + { + _testClusterFixture = testClusterFixture; + } + + /// + /// Test spin up cluster. + /// + [Fact] + public void Test_Load() + { + Assert.NotNull(_testClusterFixture.Cluster); + } + + /// + /// Test Reminder. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Test_Reminder() + { + var cluster = _testClusterFixture.Cluster; + var testGrain = cluster.GrainFactory.GetGrain(0); + await testGrain.RemindMe("test"); + Assert.NotNull(testGrain); + await Task.Delay(TimeSpan.FromSeconds(70)); + Assert.NotNull(testGrain); + } +} From f59b5f462bf266113740083801f831f0ee97d25b Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:03:47 +0200 Subject: [PATCH 156/174] chore: Added TestGrain.cs --- test/Reminders/Cassandra/Grains/TestGrain.cs | 43 ++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/Reminders/Cassandra/Grains/TestGrain.cs diff --git a/test/Reminders/Cassandra/Grains/TestGrain.cs b/test/Reminders/Cassandra/Grains/TestGrain.cs new file mode 100644 index 0000000..e2a05d3 --- /dev/null +++ b/test/Reminders/Cassandra/Grains/TestGrain.cs @@ -0,0 +1,43 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Tests.Grains; + +using global::Orleans.Runtime; +using Microsoft.Extensions.Logging; + +/// +/// Test Grain. +/// +public class TestGrain : IGrainBase, ITestGrain, IRemindable +{ + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// The grain context. + public TestGrain(ILogger logger, IGrainContext grainContext) + { + _logger = logger; + GrainContext = grainContext; + } + + /// + public IGrainContext GrainContext { get; } + + /// + public Task RemindMe(string message) + { + ArgumentNullException.ThrowIfNull(message); + return this.RegisterOrUpdateReminder("rememberMe", TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); + } + + /// + public Task ReceiveReminder(string reminderName, TickStatus status) + { + _logger.LogInformation("Reminder triggered for {Name}: {Tick1} {Tick2}", reminderName, status.FirstTickTime, status.CurrentTickTime); + return Task.CompletedTask; + } +} From 4c83295a44346e29835e00656cf05d28a00c08bd Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:03:58 +0200 Subject: [PATCH 157/174] chore: Added TestSiloConfigurator.cs --- .../Configuration/TestSiloConfigurator.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/Reminders/Cassandra/Configuration/TestSiloConfigurator.cs diff --git a/test/Reminders/Cassandra/Configuration/TestSiloConfigurator.cs b/test/Reminders/Cassandra/Configuration/TestSiloConfigurator.cs new file mode 100644 index 0000000..c47b23f --- /dev/null +++ b/test/Reminders/Cassandra/Configuration/TestSiloConfigurator.cs @@ -0,0 +1,34 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +namespace Escendit.Orleans.Reminders.Cassandra.Tests.Configuration; + +using global::Orleans.TestingHost; +using Microsoft.Extensions.Logging; + +/// +/// Test Silo Configurator. +/// +public class TestSiloConfigurator : ISiloConfigurator +{ + /// + public void Configure(ISiloBuilder siloBuilder) + { + siloBuilder + .ConfigureLogging(options => + { + options.AddConsole(); + options.AddFilter("Orleans", LogLevel.Information); + options.AddFilter("Escendit", LogLevel.Trace); + options.SetMinimumLevel(LogLevel.Debug); + }) + .AddMemoryGrainStorageAsDefault() + .UseCassandraReminderService(options => + { + options + .Endpoints + .Add("localhost"); + options.DefaultKeyspace = "test"; + }); + } +} From 3f7e67dedb6ea7014501e720df5e2459f224e87a Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 14:04:07 +0200 Subject: [PATCH 158/174] chore: Added Usings.cs --- test/Reminders/Cassandra/Usings.cs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 test/Reminders/Cassandra/Usings.cs diff --git a/test/Reminders/Cassandra/Usings.cs b/test/Reminders/Cassandra/Usings.cs new file mode 100644 index 0000000..96673ea --- /dev/null +++ b/test/Reminders/Cassandra/Usings.cs @@ -0,0 +1,6 @@ +// Copyright (c) Escendit Ltd. All Rights Reserved. +// Licensed under the MIT. See LICENSE.txt file in the solution root for full license information. + +#pragma warning disable SA1200 + +global using Xunit; From 384d40301f1868fa01d31b54a862f43a15d8adbf Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:02:38 +0200 Subject: [PATCH 159/174] chore: Updated README --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7afc842..e796f4b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,46 @@ -# cassandra-orleans-extensions -Cassandra & ScyllaDB Orleans Storage Providers +# Orleans Cassandra Extensions + +Orleans Cassandra & ScyllaDB (or any other database implementing CQL specification) Extensions allow you to easily configure +and manage providers for: +- Grain Storage +- Clustering +- Reminders + +## Installation + +Choose from three `Cassandra` provider types: + +### [Grain Storage][grain-storage] + +To install `Escendit.Orleans.Persistence.Cassandra`, run the following command in the Package Manager Console: + +```powershell +Install-Package Escendit.Orleans.Persistence.Cassandra +``` + +### [Clustering][clustering] + +To install `Escendit.Orleans.Clustering.Cassandra`, run the following command in the Package Manager Console: + +```powershell +Install-Package Escendit.Orleans.Clustering.Cassandra +``` + +### [Reminders][reminders] + +To install `Escendit.Orleans.Reminders.Cassandra`, run the following command in the Package Manager Console: + +```powershell +Install-Package Escendit.Orleans.Reminders.Cassandra +``` + +## Contributing + +If you'd like to contribute to [`cassandra-orleans-extensions`][self], +please fork the repository and make changes as you'd like. +Pull requests are warmly welcome. + +[self]: https://github.com/escendit/cassandra-orleans-extensions +[grain-storage]: src/Persistence/Cassandra +[clustering]: src/Clustering/Cassandra +[reminders]: src/Reminders/Cassandra From d73c9c479ce275331b1b75d9c15a146719941447 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:44:30 +0200 Subject: [PATCH 160/174] chore: Updated Escendit.Orleans.Clustering.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj index 23908c8..7a7e0e4 100644 --- a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj +++ b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj @@ -14,4 +14,7 @@ + + + From 83ebb2e108b98bfe96bdbfc3f1d416d2d3a092d6 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:44:50 +0200 Subject: [PATCH 161/174] chore: Updated Escendit.Orleans.Persistence.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj index cac6be3..ba6bf35 100644 --- a/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj +++ b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj @@ -7,4 +7,7 @@ + + + From ff57af7852cf24fa6f36e002f85787d505be49d5 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:45:04 +0200 Subject: [PATCH 162/174] chore: Updated Escendit.Orleans.Reminders.Cassandra.csproj --- .../Escendit.Orleans.Reminders.Cassandra.csproj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj index 8eb4731..4396b44 100644 --- a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj +++ b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj @@ -1,13 +1,16 @@ - + - + - - + + + + + From 526b6db0f805a560a3f8582e4a68356d07fa957c Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:45:16 +0200 Subject: [PATCH 163/174] chore: Added README.md --- src/Clustering/Cassandra/README.md | 47 ++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/Clustering/Cassandra/README.md diff --git a/src/Clustering/Cassandra/README.md b/src/Clustering/Cassandra/README.md new file mode 100644 index 0000000..80ea69e --- /dev/null +++ b/src/Clustering/Cassandra/README.md @@ -0,0 +1,47 @@ +# Escendit.Orleans.Clustering.Cassandra + +`Escendit.Orleans.Clustering.Cassandra` is a NuGet package that provides the ability to register a Clustering provider +for Orleans to use for Cluster coordination. + +## Installation + +To install `Escendit.Orleans.Persistence.Cassandra`, run the following command in the Package Manager Console: + +```powershell +Install-Package Escendit.Orleans.Persistence.Cassandra +``` + +## Usage + +### Silo + +#### Simple Usage +```csharp +Host + .CreateDefaultBuilder() + .UseOrleans(siloBuilder => siloBuilder + .UseCassandraClustering() + .WithClientAsDefault(...) + .Build()); +``` + +### Client + +#### Simple Usage + +```csharp +Host + .CreateDefaultBuilder() + .UseOrleansClient(clientBuilder => clientBuilder + .UseCassandraClustering() + .WithClientAsDefault(...) + .Build()); +``` + +## Contributing + +If you'd like to contribute to [`cassandra-orleans-extensions`][self], +please fork the repository and make changes as you'd like. +Pull requests are warmly welcome. + +[self]: https://github.com/escendit/cassandra-orleans-extensions From f50aff8879777eb16c4eb3f86b6dd199c3444f10 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:45:19 +0200 Subject: [PATCH 164/174] chore: Added README.md --- src/Persistence/Cassandra/README.md | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Persistence/Cassandra/README.md diff --git a/src/Persistence/Cassandra/README.md b/src/Persistence/Cassandra/README.md new file mode 100644 index 0000000..8da1de4 --- /dev/null +++ b/src/Persistence/Cassandra/README.md @@ -0,0 +1,48 @@ +# Escendit.Orleans.Persistence.Cassandra + +`Escendit.Orleans.Persistence.Cassandra` is a NuGet package that provides the ability to register a Grain Storage +provider for Orleans State Management. + +## Installation + +To install `Escendit.Orleans.Persistence.Cassandra`, run the following command in the Package Manager Console: + +```powershell +Install-Package Escendit.Orleans.Persistence.Cassandra +``` + +## Usage + +### Silo + +#### Simple Usage + +##### Default + +```csharp +Host + .CreateDefaultBuilder() + .ConfigureServices(services => services + .AddCassandraClientAsDefault(...)); + .UseOrleans(siloBuilder => siloBuilder + .AddCassandraGrainStorageAsDefault(...)); +``` + +##### Named + +```csharp +Host + .CreateDefaultBuilder() + .ConfigureServices(services => services + .AddCassandraClient("name", ...)); + .UseOrleans(siloBuilder => siloBuilder + .AddCassandraGrainStorage("name", ...)); +``` + +## Contributing + +If you'd like to contribute to [`cassandra-orleans-extensions`][self], +please fork the repository and make changes as you'd like. +Pull requests are warmly welcome. + +[self]: https://github.com/escendit/cassandra-orleans-extensions From 1bd04dd058bedda6e4877faddcc43f90d8a0c961 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:45:23 +0200 Subject: [PATCH 165/174] chore: Added README.md --- src/Reminders/Cassandra/README.md | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/Reminders/Cassandra/README.md diff --git a/src/Reminders/Cassandra/README.md b/src/Reminders/Cassandra/README.md new file mode 100644 index 0000000..68b6027 --- /dev/null +++ b/src/Reminders/Cassandra/README.md @@ -0,0 +1,56 @@ +# Escendit.Orleans.Reminders.Cassandra + +`Escendit.Orleans.Reminders.Cassandra` is a NuGet package that provides the ability to register a Grain Storage +provider for Orleans State Management. + +## Installation + +To install `Escendit.Orleans.Reminders.Cassandra`, run the following command in the Package Manager Console: + +```powershell +Install-Package Escendit.Orleans.Reminders.Cassandra +``` + +## Usage + +### Silo + +#### Register with Default Client + +⚠️ This method tries to create a new client with options, and may override your settings + +```csharp +Host + .CreateDefaultBuilder() + .UseCassandraReminderService(...); +``` + +#### Register with Named Client + +If you have multiple clients you can pick the client which you want to use + +⚠️ This method tries to create a new client with options, and may override your settings + +```csharp +Host + .CreateDefaultBuilder() + .UseCassandraReminderService("new_name", ...); +``` + +#### Use Existing Client + +⚠️ If you already use client, you can reuse the client. + +```csharp +Host + .CreateDefaultBuilder() + .UseCassandraReminderService("existing_client"); +``` + +## Contributing + +If you'd like to contribute to [`cassandra-orleans-extensions`][self], +please fork the repository and make changes as you'd like. +Pull requests are warmly welcome. + +[self]: https://github.com/escendit/cassandra-orleans-extensions From 24639dd88c568001a0d5b7cd45169dd5736e1ec7 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:45:56 +0200 Subject: [PATCH 166/174] fix: Updated SessionContextProviderBase.cs - fixed logging variable names --- .../Cassandra/Provider/SessionContextProviderBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs index 5ffe6c6..1265a4d 100644 --- a/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs +++ b/src/Clustering/Cassandra/Provider/SessionContextProviderBase.cs @@ -524,7 +524,7 @@ private async Task InitializeDatabaseAsync() EventId = 200, EventName = "Execution", Level = LogLevel.Debug, - Message = "Executing with client {name} > {returnType} {type}.{action} completed in {elapsed}")] + Message = "Executing with client {name} > {returnType} {selfType}.{action} completed in {elapsed}")] private partial void LogExecute(string name, string returnType, string selfType, string action, long elapsed); [LoggerMessage( @@ -538,6 +538,6 @@ private async Task InitializeDatabaseAsync() EventId = 500, EventName = "Exception", Level = LogLevel.Error, - Message = "Exception with client {name} > {returnType} {type}.{action} {message}")] - private partial void LogException(string name, Exception exception, string message, string returnType, string type, string action); + Message = "Exception with client {name} > {returnType} {selfType}.{action} {message}")] + private partial void LogException(string name, Exception exception, string message, string returnType, string selfType, string action); } From abad797f821f93d2c7d4b97cb06a1de8fe2b7a6c Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:49:36 +0200 Subject: [PATCH 167/174] fix: Updated GrainStorageBase.cs - fixed logging parameter names --- src/Persistence/Cassandra/Storage/GrainStorageBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Persistence/Cassandra/Storage/GrainStorageBase.cs b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs index beb6622..4c47555 100644 --- a/src/Persistence/Cassandra/Storage/GrainStorageBase.cs +++ b/src/Persistence/Cassandra/Storage/GrainStorageBase.cs @@ -217,13 +217,13 @@ protected Task Execute(Func> action, [CallerMemberName] string act EventId = 200, EventName = "Execution", Level = LogLevel.Debug, - Message = "Executing with client {name} > {returnType} {type}.{action} completed in {elapsed}")] + Message = "Executing with client {name} > {returnType} {selfType}.{action} completed in {elapsed}")] private partial void LogExecute(string name, string returnType, string selfType, string action, long elapsed); [LoggerMessage( EventId = 500, EventName = "Exception", Level = LogLevel.Error, - Message = "Exception with client {name} > {returnType} {type}.{action} {message}")] - private partial void LogException(string name, Exception exception, string message, string returnType, string type, string action); + Message = "Exception with client {name} > {returnType} {selfType}.{action} {message}")] + private partial void LogException(string name, Exception exception, string message, string returnType, string selfType, string action); } From 9ca36ba256a4870d42f3e1b8239d3d1c93659eff Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:51:19 +0200 Subject: [PATCH 168/174] fix: Updated CassandraStorageException.cs - set serializable attribute --- src/Persistence/Cassandra/Storage/CassandraStorageException.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Persistence/Cassandra/Storage/CassandraStorageException.cs b/src/Persistence/Cassandra/Storage/CassandraStorageException.cs index 7258866..22cc549 100644 --- a/src/Persistence/Cassandra/Storage/CassandraStorageException.cs +++ b/src/Persistence/Cassandra/Storage/CassandraStorageException.cs @@ -9,6 +9,7 @@ namespace Escendit.Orleans.Persistence.Cassandra.Storage; /// /// Cassandra Storage Exception. /// +[Serializable] [GenerateSerializer] public class CassandraStorageException : Exception { From eae241b4b38843351bccc0ab535fb478c73de86d Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 18:56:25 +0200 Subject: [PATCH 169/174] chore: Updated Escendit.Orleans.Extensions.Cassandra.sln - removed currently unused projects --- Escendit.Orleans.Extensions.Cassandra.sln | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/Escendit.Orleans.Extensions.Cassandra.sln b/Escendit.Orleans.Extensions.Cassandra.sln index 78f776b..aaf02eb 100644 --- a/Escendit.Orleans.Extensions.Cassandra.sln +++ b/Escendit.Orleans.Extensions.Cassandra.sln @@ -15,12 +15,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Reminders", "Reminders", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Persistence", "Persistence", "{2950C6B5-E391-4569-AAB5-D8BEB92FF7B8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra", "src\AspNetCore\Clustering\Escendit.AspNetCore.Builder.Orleans.Clustering.Cassandra.csproj", "{5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra", "src\AspNetCore\Persistence\Escendit.AspNetCore.Builder.Orleans.Persistence.Cassandra.csproj", "{68EE25C3-8397-4668-8DCB-971DF2430266}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.AspNetCore.Builder.Reminders.Cassandra", "src\AspNetCore\Reminders\Escendit.AspNetCore.Builder.Reminders.Cassandra.csproj", "{648DA3BD-AF58-430F-8231-0E4D76BA19E4}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Clustering.Cassandra", "src\Clustering\Cassandra\Escendit.Orleans.Clustering.Cassandra.csproj", "{D1643CDC-E454-441C-86D9-81207E8A0707}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Escendit.Orleans.Persistence.Cassandra", "src\Persistence\Cassandra\Escendit.Orleans.Persistence.Cassandra.csproj", "{C7BF8816-1982-4672-9ADB-55B2CC73C63B}" @@ -52,9 +46,6 @@ Global {FED9F72C-209F-4539-B7C0-730DE379B1BF} = {BE03B4BC-8D74-42CA-81AC-014417FCFEB9} {77E5F3C7-A347-42F4-BF93-51248B035D56} = {BE03B4BC-8D74-42CA-81AC-014417FCFEB9} {2950C6B5-E391-4569-AAB5-D8BEB92FF7B8} = {BE03B4BC-8D74-42CA-81AC-014417FCFEB9} - {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A} = {FED9F72C-209F-4539-B7C0-730DE379B1BF} - {68EE25C3-8397-4668-8DCB-971DF2430266} = {FED9F72C-209F-4539-B7C0-730DE379B1BF} - {648DA3BD-AF58-430F-8231-0E4D76BA19E4} = {FED9F72C-209F-4539-B7C0-730DE379B1BF} {D1643CDC-E454-441C-86D9-81207E8A0707} = {5F161A73-5C42-4602-AB22-16EB85DEC048} {C7BF8816-1982-4672-9ADB-55B2CC73C63B} = {2950C6B5-E391-4569-AAB5-D8BEB92FF7B8} {6E3EEC03-C465-4C7F-93FB-E62A2ECC6847} = {77E5F3C7-A347-42F4-BF93-51248B035D56} @@ -66,18 +57,6 @@ Global {F14B85E6-725E-4F78-BDAB-0240DB573333} = {051C13BE-64CE-42A2-A67C-46A7DCA55095} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B3BE032-E088-4FB8-A5AC-5BC0BF40F14A}.Release|Any CPU.Build.0 = Release|Any CPU - {68EE25C3-8397-4668-8DCB-971DF2430266}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68EE25C3-8397-4668-8DCB-971DF2430266}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68EE25C3-8397-4668-8DCB-971DF2430266}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68EE25C3-8397-4668-8DCB-971DF2430266}.Release|Any CPU.Build.0 = Release|Any CPU - {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {648DA3BD-AF58-430F-8231-0E4D76BA19E4}.Release|Any CPU.Build.0 = Release|Any CPU {D1643CDC-E454-441C-86D9-81207E8A0707}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D1643CDC-E454-441C-86D9-81207E8A0707}.Debug|Any CPU.Build.0 = Debug|Any CPU {D1643CDC-E454-441C-86D9-81207E8A0707}.Release|Any CPU.ActiveCfg = Release|Any CPU From f2d75b1ec4583210a827ad0d37bb6f1e7d4035f3 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 19:02:08 +0200 Subject: [PATCH 170/174] chore: Updated Escendit.Orleans.Clustering.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj index 7a7e0e4..ac2c1de 100644 --- a/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj +++ b/src/Clustering/Cassandra/Escendit.Orleans.Clustering.Cassandra.csproj @@ -1,4 +1,7 @@ + + Cassandra Clustering Provider for Orleans Clustering + From a3848284b87fa669597397c890918361a04cdad8 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 19:02:19 +0200 Subject: [PATCH 171/174] chore: Updated Escendit.Orleans.Persistence.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj index ba6bf35..394751d 100644 --- a/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj +++ b/src/Persistence/Cassandra/Escendit.Orleans.Persistence.Cassandra.csproj @@ -1,4 +1,7 @@ + + Cassandra Grain Storage Provider for Orleans Persistence + From 78e9cf021929d7bf7510f554881b4a55fd75e712 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 19:02:29 +0200 Subject: [PATCH 172/174] chore: Updated Escendit.Orleans.Reminders.Cassandra.csproj --- .../Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj index 4396b44..61a105d 100644 --- a/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj +++ b/src/Reminders/Cassandra/Escendit.Orleans.Reminders.Cassandra.csproj @@ -1,4 +1,7 @@ + + Cassandra Reminder Service Provider for Orleans Reminders + From 06b98323b534140865dda54d2bd745e3ff2909d1 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 19:16:05 +0200 Subject: [PATCH 173/174] chore: Updated Directory.Build.props - fixed the repository url --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 43db09e..70fc4e0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,7 +10,7 @@ git - https://github.com/escendit/cassandra-dotnet-extensions + https://github.com/escendit/cassandra-orleans-extensions $(NoWarn);CS1591 From c13e74c32994f3ee61a72c274dbc9a2c47379ca5 Mon Sep 17 00:00:00 2001 From: Simon Novak Date: Sun, 1 Oct 2023 20:24:46 +0200 Subject: [PATCH 174/174] chore: Updated sonarcloud.yml - fixed sonarcloud key --- .github/workflows/sonarcloud.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 8830405..dd90e80 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -60,7 +60,7 @@ jobs: shell: powershell run: | .\.sonar\scanner\dotnet-sonarscanner begin ` - /k:"escendit_cassandra-dotnet-extensions" ` + /k:"escendit_cassandra-orleans-extensions" ` /o:"escendit" ` /v:${{ env.GitVersion_FullSemVer }} ` /d:sonar.login="${{ secrets.SONAR_TOKEN }}" ` @@ -86,7 +86,7 @@ jobs: shell: powershell run: | .\.sonar\scanner\dotnet-sonarscanner begin ` - /k:"escendit_cassandra-dotnet-extensions" ` + /k:"escendit_cassandra-orleans-extensions" ` /o:"escendit" ` /v:${{ env.GitVersion_FullSemVer }} ` /d:sonar.pullrequest.key="${{ github.event.pull_request.number }}" `