From 660f63741cfe50934f2753229f5422871655240f Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sun, 9 Jan 2022 15:09:21 -0500 Subject: [PATCH 01/20] index creation app example --- .../Akka.Linq2Db.Sandbox.csproj | 15 ++ ....Persistence.Linq2Db.IndexHelperApp.csproj | 25 ++++ .../Options.cs | 22 +++ .../Program.cs | 141 ++++++++++++++++++ .../example.hocon | 15 ++ ....Persistence.Linq2Db.IndexHelperLib.csproj | 15 ++ .../Class1.cs | 50 +++++++ Akka.Persistence.Linq2Db.sln | 18 +++ 8 files changed, 301 insertions(+) create mode 100644 Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj create mode 100644 Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj create mode 100644 Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs create mode 100644 Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs create mode 100644 Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon create mode 100644 Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj create mode 100644 Akka.Persistence.Linq2Db.IndexHelperLib/Class1.cs diff --git a/Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj b/Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj new file mode 100644 index 00000000..8019ffa3 --- /dev/null +++ b/Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp3.1 + enable + enable + + + + + + + + + diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj b/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj new file mode 100644 index 00000000..caf07276 --- /dev/null +++ b/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj @@ -0,0 +1,25 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + + + + Always + + + + diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs b/Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs new file mode 100644 index 00000000..b8ebaa58 --- /dev/null +++ b/Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs @@ -0,0 +1,22 @@ +using CommandLine; + +namespace Akka.Persistence.Linq2Db.IndexHelperApp +{ + public class Options + { + [Option('f',"file", Required=true, HelpText = "Specify the HOCON file to use")] + public string File { get; set; } + + [Option('p',"path", Required = true, HelpText = "The Path to the Akka.Persistence.Linq2Db Config in the HOCON.")] + public string HoconPath { get; set; } + + [Option("OrderingIdx", Required = true, Group = "IndexType", HelpText = "Generates the SQL Text for an Ordering index")] + public bool GenerateOrdering { get; set; } + + [Option("PidSeqNoIdx", Required = true, Group = "IndexType", HelpText = "Generates the SQL Text for an index on PersistenceID and SeqNo")] + public bool GeneratePidSeqNo { get; set; } + + [Option("TimeStampIdx", Required = true, Group = "IndexType", HelpText = "Generates the SQL Text for a Timestamp Index")] + public bool GenerateTimestamp { get; set; } + } +} \ No newline at end of file diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs b/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs new file mode 100644 index 00000000..14a386b5 --- /dev/null +++ b/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs @@ -0,0 +1,141 @@ +using System; +using System.IO; +using Akka.Configuration; +using Akka.Persistence.Linq2Db.IndexHelperLib; +using Akka.Persistence.Sql.Linq2Db.Config; +using Akka.Persistence.Sql.Linq2Db.Tests; +using CommandLine; +using FluentMigrator.Expressions; +using FluentMigrator.Runner.Generators; +using FluentMigrator.Runner.Generators.Generic; +using FluentMigrator.Runner.Generators.MySql; +using FluentMigrator.Runner.Generators.Oracle; +using FluentMigrator.Runner.Generators.Postgres; +using FluentMigrator.Runner.Generators.Postgres92; +using FluentMigrator.Runner.Generators.SQLite; +using FluentMigrator.Runner.Generators.SqlServer; +using FluentMigrator.Runner.Processors.Postgres; +using LinqToDB; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; + +namespace Akka.Persistence.Linq2Db.IndexHelperApp +{ + class Program + { + + static void Main(string[] args) + { + Parser.Default.ParseArguments(args) + .WithParsed(opts => + { + //var str = Linq2DbJournalDefaultSpecConfig.customConfig("testGen", + // "journalTbl", "metadataTbl", ProviderName.SqlServer, + // "connStr"); + var conf = + ConfigurationFactory.ParseString( + File.ReadAllText(opts.File)); + + var journalConf = + new Akka.Persistence.Sql.Linq2Db.Config.JournalConfig( + conf.GetConfig( + opts.HoconPath + //"akka.persistence.journal.linq2db.testGen" + ) + .WithFallback(Akka.Persistence.Sql.Linq2Db + .Journal + .Linq2DbWriteJournal.DefaultConfiguration)); + var generator = getGenerator(journalConf.ProviderName); + var helper = new JournalIndexHelper(); + CreateIndexExpression expr = null; + GeneratePerOptions(opts, helper, journalConf, generator); + }); + } + + private static void GeneratePerOptions(Options opts, JournalIndexHelper helper, + JournalConfig journalConf, GenericGenerator generator) + { + CreateIndexExpression expr; + if (opts.GeneratePidSeqNo) + { + expr = new CreateIndexExpression() + { + Index = helper.JournalOrdering(journalConf.TableConfig.TableName, + journalConf.TableConfig.ColumnNames.Ordering, + journalConf.TableConfig.SchemaName) + }; + GenerateWithHeaderAndFooter(generator, expr, "Ordering"); + } + + if (opts.GeneratePidSeqNo) + { + expr = new CreateIndexExpression() + { + Index = helper.DefaultJournalIndex( + journalConf.TableConfig.TableName, + journalConf.TableConfig.ColumnNames.PersistenceId, + journalConf.TableConfig.ColumnNames.SequenceNumber, + journalConf.TableConfig.SchemaName) + }; + GenerateWithHeaderAndFooter(generator, expr, "PidAndSequenceNo"); + } + + if (opts.GenerateTimestamp) + { + expr = new CreateIndexExpression() + { + Index = helper.JournalTimestamp(journalConf.TableConfig.TableName, + journalConf.TableConfig.ColumnNames.Created, + journalConf.TableConfig.SchemaName) + }; + GenerateWithHeaderAndFooter(generator, expr, "Timestamp"); + } + } + + private static void GenerateWithHeaderAndFooter(GenericGenerator generator, + CreateIndexExpression expr, string indexType) + { + Console.WriteLine("-------"); + Console.WriteLine($"----{indexType} Index Create Below"); + Console.WriteLine(generator.Generate(expr)); + Console.WriteLine($"----{indexType} Index Create Above"); + Console.WriteLine("-------"); + } + + static GenericGenerator getGenerator(string dbArg) + { + if (dbArg.StartsWith("sqlserver", + StringComparison.InvariantCultureIgnoreCase)) + { + return new SqlServer2008Generator(); + } + else if (dbArg.Contains("sqlite", + StringComparison.InvariantCultureIgnoreCase)) + { + return new SQLiteGenerator(); + } + else if (dbArg.Contains("postgres", + StringComparison.InvariantCultureIgnoreCase)) + { + return new Postgres92Generator( + new PostgresQuoter(new PostgresOptions()), + new OptionsWrapper( + new GeneratorOptions())); + } + else if (dbArg.Contains("mysql", + StringComparison.InvariantCultureIgnoreCase)) + { + return new MySql5Generator(); + } + else if (dbArg.Contains("oracle", + StringComparison.InvariantCultureIgnoreCase)) + { + return new OracleGenerator(); + } + else + { + throw new Exception("IDK what to do with this!"); + } + } + } +} \ No newline at end of file diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon b/Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon new file mode 100644 index 00000000..a382c24c --- /dev/null +++ b/Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon @@ -0,0 +1,15 @@ +akka.persistence.journal.linq2db{ + testGen { + class = "Akka.Persistence.Sql.Linq2Db.Journal.Linq2DbWriteJournal, Akka.Persistence.Sql.Linq2Db" + provider-name = "SqlServer" + connection-string = "connStr" + tables{ + journal{ + auto-init = true + warn-on-auto-init-fail = false + table-name = "journalTbl" + metadata-table-name = "metadataTbl" + } + } +} +} \ No newline at end of file diff --git a/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj b/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj new file mode 100644 index 00000000..49c19454 --- /dev/null +++ b/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/Akka.Persistence.Linq2Db.IndexHelperLib/Class1.cs b/Akka.Persistence.Linq2Db.IndexHelperLib/Class1.cs new file mode 100644 index 00000000..65f5b08e --- /dev/null +++ b/Akka.Persistence.Linq2Db.IndexHelperLib/Class1.cs @@ -0,0 +1,50 @@ +using System; +using FluentMigrator.Model; + +namespace Akka.Persistence.Linq2Db.IndexHelperLib +{ + public class JournalIndexHelper + { + public IndexDefinition DefaultJournalIndex(string tableName, string persistenceIdCol, string sequenceNoCol, string schemaName = null) + { + var idx = beginCreateIndex(tableName, schemaName, $"UX_{tableName}_PID_SEQNO"); + //short name for easy compat with all dbs. (*cough* oracle *cough*) + idx.Columns.Add(new IndexColumnDefinition(){ Name = persistenceIdCol }); + idx.Columns.Add(new IndexColumnDefinition(){Name = sequenceNoCol, Direction = Direction.Ascending}); + idx.IsUnique = true; + return idx; + } + + public IndexDefinition JournalOrdering(string tableName, + string orderingCol, string schemaName = null) + { + var idx = beginCreateIndex(tableName, schemaName,$"IX_{tableName}_Ordering"); + idx.Columns.Add(new IndexColumnDefinition(){Name = orderingCol}); + //Should it be? + //idx.IsUnique = true; + return idx; + } + + public IndexDefinition JournalTimestamp(string tableName, + string timestampCol, string schemaName = null) + { + var idx = beginCreateIndex(tableName, schemaName, + $"IX_{tableName}_TimeStamp"); + idx.Columns.Add(new IndexColumnDefinition(){Name = timestampCol}); + //Not unique by any stretch. + return idx; + } + + private static IndexDefinition beginCreateIndex(string tableName, string schemaName, string indexName) + { + var idx = new IndexDefinition(); + if (string.IsNullOrWhiteSpace(schemaName) == false) + { + idx.SchemaName = schemaName; + } + idx.TableName = tableName; + idx.Name = indexName; + return idx; + } + } +} \ No newline at end of file diff --git a/Akka.Persistence.Linq2Db.sln b/Akka.Persistence.Linq2Db.sln index f84ac72e..81a30e5c 100644 --- a/Akka.Persistence.Linq2Db.sln +++ b/Akka.Persistence.Linq2Db.sln @@ -36,6 +36,12 @@ EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests", "Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests\Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests.csproj", "{170698FA-DA1E-40BC-896D-AFA67976C0EB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Linq2Db.IndexHelperLib", "Akka.Persistence.Linq2Db.IndexHelperLib\Akka.Persistence.Linq2Db.IndexHelperLib.csproj", "{AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Linq2Db.IndexHelperApp", "Akka.Persistence.Linq2Db.IndexHelperApp\Akka.Persistence.Linq2Db.IndexHelperApp.csproj", "{D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Linq2Db.Sandbox", "Akka.Linq2Db.Sandbox\Akka.Linq2Db.Sandbox.csproj", "{697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -78,6 +84,18 @@ Global {170698FA-DA1E-40BC-896D-AFA67976C0EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {170698FA-DA1E-40BC-896D-AFA67976C0EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {170698FA-DA1E-40BC-896D-AFA67976C0EB}.Release|Any CPU.Build.0 = Release|Any CPU + {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Release|Any CPU.Build.0 = Release|Any CPU + {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Release|Any CPU.Build.0 = Release|Any CPU + {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From fec6cf57c3e44ccc7d670269feeaa0c96a1d0b3f Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 19 Mar 2022 16:52:30 -0400 Subject: [PATCH 02/20] WIP Tag Table with backwards compatibility --- .../Config/BaseByteArrayJournalDaoConfig.cs | 3 +- .../Config/JournalConfig.cs | 4 +- .../Config/JournalTableConfig.cs | 39 +++- .../Config/ReadJournalPluginConfig.cs | 29 ++- .../AkkaPersistenceDataConnectionFactory.cs | 7 +- .../Journal/DAO/BaseByteArrayJournalDao.cs | 147 ++++++++++++-- .../DAO/BaseJournalDaoWithReadMessages.cs | 29 ++- .../Journal/DAO/ByteArrayJournalSerializer.cs | 77 ++++++-- .../Journal/Types/JournalRow.cs | 7 + .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 185 ++++++++++++++++-- .../Serialization/PersistentReprSerializer.cs | 92 ++++++--- 11 files changed, 527 insertions(+), 92 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/BaseByteArrayJournalDaoConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/BaseByteArrayJournalDaoConfig.cs index 0410df3d..5e6499a1 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/BaseByteArrayJournalDaoConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/BaseByteArrayJournalDaoConfig.cs @@ -10,6 +10,7 @@ public BaseByteArrayJournalDaoConfig(Configuration.Config config) BufferSize = config.GetInt("buffer-size", 5000); BatchSize = config.GetInt("batch-size", 100); DbRoundTripBatchSize = config.GetInt("db-round-trip-max-batch-size", 1000); + DbRoundTripTagBatchSize = config.GetInt("db-round-trip-max-tag-batch-size", 1000); PreferParametersOnMultiRowInsert = config.GetBoolean("prefer-parameters-on-multirow-insert", false); @@ -43,6 +44,6 @@ public BaseByteArrayJournalDaoConfig(Configuration.Config config) public int BufferSize { get; protected set; } public bool SqlCommonCompatibilityMode { get; protected set; } - + public int DbRoundTripTagBatchSize { get; set; } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalConfig.cs index ac40d71c..52708066 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalConfig.cs @@ -21,7 +21,6 @@ public JournalConfig(Configuration.Config config) UseSharedDb = string.IsNullOrWhiteSpace(dbConf) ? null : dbConf; UseCloneConnection = config.GetBoolean("use-clone-connection", false); - } public string MaterializerDispatcher { get; protected set; } @@ -44,6 +43,7 @@ public IDaoConfig IDaoConfig public string ProviderName { get; } public string ConnectionString { get; } public bool UseCloneConnection { get; set; } + } public interface IProviderConfig @@ -61,4 +61,6 @@ public interface IDaoConfig bool SqlCommonCompatibilityMode { get; } int Parallelism { get; } } + + } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs index 4d680076..f5b532d8 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs @@ -3,6 +3,13 @@ namespace Akka.Persistence.Sql.Linq2Db.Config { + [Flags] + public enum TagWriteMode + { + CommaSeparatedArray = 1, + TagTable = 2, + CommaSeparatedArrayAndTagTable = 3, + } public class JournalTableConfig { @@ -13,6 +20,8 @@ public class JournalTableConfig public string MetadataTableName { get; protected set; } public MetadataTableColumnNames MetadataColumnNames { get; protected set; } public bool WarnOnAutoInitializeFail { get; } + + public TagWriteMode TagWriteMode { get; } public JournalTableConfig(Configuration.Config config) { @@ -28,6 +37,31 @@ public JournalTableConfig(Configuration.Config config) AutoInitialize = localcfg.GetBoolean("auto-init", false); WarnOnAutoInitializeFail = localcfg.GetBoolean("warn-on-auto-init-fail", true); + + var s = config.GetString("tag-write-mode", "default"); + if (Enum.TryParse(s, true, out TagWriteMode res)) + { + + } + else if (s.Equals("default", StringComparison.InvariantCultureIgnoreCase)) + { + res = TagWriteMode.CommaSeparatedArray; + } + else if (s.Equals("migration", + StringComparison.InvariantCultureIgnoreCase)) + { + res = TagWriteMode.CommaSeparatedArrayAndTagTable; + } + else if (s.Equals("tagtableonly", + StringComparison.InvariantCultureIgnoreCase)) + { + res = TagWriteMode.TagTable; + } + else + { + res = TagWriteMode.CommaSeparatedArray; + } + TagWriteMode = res; } @@ -40,7 +74,8 @@ protected bool Equals(JournalTableConfig other) AutoInitialize == other.AutoInitialize && MetadataTableName == other.MetadataTableName && WarnOnAutoInitializeFail == other.WarnOnAutoInitializeFail && - Equals(MetadataColumnNames, other.MetadataColumnNames); + Equals(MetadataColumnNames, other.MetadataColumnNames) && + TagWriteMode== other.TagWriteMode; } public override bool Equals(object obj) @@ -54,7 +89,7 @@ public override bool Equals(object obj) public override int GetHashCode() { return HashCode.Combine(ColumnNames, TableName, SchemaName, - AutoInitialize, MetadataTableName, MetadataColumnNames); + AutoInitialize, MetadataTableName, MetadataColumnNames, TagWriteMode); } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs index 691d3e9d..10394f2c 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs @@ -1,4 +1,6 @@ -namespace Akka.Persistence.Sql.Linq2Db.Config +using System; + +namespace Akka.Persistence.Sql.Linq2Db.Config { public class ReadJournalPluginConfig { @@ -7,11 +9,34 @@ public ReadJournalPluginConfig(Configuration.Config config) TagSeparator = config.GetString("tag-separator", ","); Dao = config.GetString("dao", "akka.persistence.sql.linq2db.dao.bytea.readjournal.bytearrayreadjournaldao"); - + var tagReadStr = config.GetString("tag-read-mode", "defaultconcatvarchar"); + if (Enum.TryParse(tagReadStr,true,out TagReadMode tgr)) + { + + } + else if (tagReadStr.Equals("default", StringComparison.InvariantCultureIgnoreCase)) + { + tgr = TagReadMode.DefaultConcatVarchar; + } + else if (tagReadStr.Equals("migrate", StringComparison.InvariantCultureIgnoreCase)) + { + tgr = TagReadMode.MigrateToTagTable; + } + + TagReadMode = tgr; } public string Dao { get; set; } public string TagSeparator { get; set; } + public TagReadMode TagReadMode { get; set; } + } + + [Flags] + public enum TagReadMode + { + DefaultConcatVarchar = 1, + MigrateToTagTable = 3, + TagTableOnly = 2 } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index 5a63fef1..c29a33c3 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -135,7 +135,12 @@ private static void MapJournalRow(IProviderConfig config, .HasColumnName(tableConfig.ColumnNames.SequenceNumber) .Member(r => r.Timestamp) .HasColumnName(tableConfig.ColumnNames.Created); - + //We can skip writing tags the old way by ignoring the column in mapping. + if ((tableConfig.TagWriteMode & + TagWriteMode.CommaSeparatedArray) == 0) + { + journalRowBuilder.Member(r => r.tags).IsNotColumn(); + } if (config.ProviderName.ToLower().Contains("sqlite")) { journalRowBuilder.Member(r => r.ordering).IsPrimaryKey().HasDbType("INTEGER") diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs index 2c749fa4..1cc8a141 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Data; @@ -23,6 +24,18 @@ namespace Akka.Persistence.Sql.Linq2Db.Journal.DAO { + public static class EnumerableExtensions + { + public static IEnumerable SelectStateful( + this IEnumerable enumerable, TState state, + Func selector) + { + foreach (var item in enumerable) + { + yield return selector(item,state); + } + } + } public abstract class BaseByteArrayJournalDao : BaseJournalDaoWithReadMessages, IJournalDaoWithUpdates @@ -149,15 +162,75 @@ private async Task WriteJournalRows(Seq xs) private async Task InsertSingle(Seq xs) { - //If we are writing a single row, - //we don't need to worry about transactions. - using (var db = _connectionFactory.GetConnection()) + if ((_journalConfig.TableConfig.TagWriteMode & TagWriteMode.TagTable)!=0 && xs.Head.tagArr.Length>0) + { + //Lazy fallback; do the InsertMultiple call here and leave it at that. + await InsertMultiple(xs); + } + else + { + //If we are writing a single row, + //we don't need to worry about transactions. + using (var db = _connectionFactory.GetConnection()) + { + await db.InsertAsync(xs.Head); + } + } + + + } + + private async Task BulkInsertWithTagTableTags(DataConnection dc, + Seq xs) + { + var tagsToInsert = new List(xs.Count); + foreach (var journalRow in xs) { - await db.InsertAsync(xs.Head); + var dbid = await dc.InsertWithInt64IdentityAsync(journalRow); + tagsToInsert.AddRange(journalRow.tagArr.SelectStateful(dbid, + (tag, id) => new JournalTagRow() + { JournalOrderingId = id, TagValue = tag })); } + + await dc.GetTable() + .BulkCopyAsync(new BulkCopyOptions() + { + BulkCopyType = BulkCopyType.MultipleRows, + UseParameters = _journalConfig.DaoConfig + .PreferParametersOnMultiRowInsert, + MaxBatchSize = _journalConfig.DaoConfig.DbRoundTripTagBatchSize + }, tagsToInsert); + } + private async Task BulkInsertNoTagTableTags(DataConnection dc, Seq xs) + { + await dc.GetTable() + .BulkCopyAsync( + new BulkCopyOptions() + { + BulkCopyType = + xs.Count > _journalConfig.DaoConfig + .MaxRowByRowSize + ? BulkCopyType.Default + : BulkCopyType.MultipleRows, + UseParameters = _journalConfig.DaoConfig.PreferParametersOnMultiRowInsert, + MaxBatchSize = _journalConfig.DaoConfig.DbRoundTripBatchSize + }, xs); } private async Task InsertMultiple(Seq xs) + { + if ((_journalConfig.TableConfig.TagWriteMode & TagWriteMode.TagTable) !=0) + { + await HandleTagTableInsert(xs); + } + else + { + await HandleDefaultInsert(xs); + } + + } + + private async Task HandleDefaultInsert(Seq xs) { using (var db = _connectionFactory.GetConnection()) { @@ -165,18 +238,7 @@ private async Task InsertMultiple(Seq xs) { await db.BeginTransactionAsync(IsolationLevel .ReadCommitted); - await db.GetTable() - .BulkCopyAsync( - new BulkCopyOptions() - { - BulkCopyType = - xs.Count > _journalConfig.DaoConfig - .MaxRowByRowSize - ? BulkCopyType.Default - : BulkCopyType.MultipleRows, - UseParameters = _journalConfig.DaoConfig.PreferParametersOnMultiRowInsert, - MaxBatchSize = _journalConfig.DaoConfig.DbRoundTripBatchSize - }, xs); + await BulkInsertNoTagTableTags(db, xs); await db.CommitTransactionAsync(); } catch (Exception e) @@ -195,9 +257,58 @@ await db.GetTable() } } + private async Task HandleTagTableInsert(Seq xs) + { + using (var db = _connectionFactory.GetConnection()) + { + try + { + await db.BeginTransactionAsync(IsolationLevel + .ReadCommitted); + await consumeSequenceForTagInsert(xs, db); + await db.CommitTransactionAsync(); + } + catch (Exception ex) + { + try + { + await db.RollbackTransactionAsync(); + } + catch (Exception exception) + { + throw ex; + } + + throw; + } + } + } + + private async Task consumeSequenceForTagInsert(Seq xs, DataConnection db) + { + Seq tail = xs; + while (tail.Count > 0) + { + Seq noTags; + Seq hasTags; + (noTags, tail) = + tail.Span(r => r.tagArr.Length == 0); + if (noTags.Count > 0) + { + await BulkInsertNoTagTableTags(db, noTags); + } + + (hasTags, tail) = + tail.Span(r => r.tagArr.Length > 0); + if (hasTags.Count > 0) + { + await BulkInsertWithTagTableTags(db, hasTags); + } + } + } //By using a custom flatten here, we avoid an Enumerable/LINQ allocation //And are able to have a little more control over default capacity of array. - static List FlattenListOfListsToList(List>> source) { + static List FlattenListOfListsToList(List> source) { //List ResultSet( // Akka.Util.Try> item) @@ -236,7 +347,7 @@ public async Task> AsyncWriteMessages( } protected static ImmutableList BuildWriteRejections( - List>> serializedTries) + List> serializedTries) { Exception[] builderEx = new Exception[serializedTries.Count]; diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseJournalDaoWithReadMessages.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseJournalDaoWithReadMessages.cs index b201b7ef..0f6f8310 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseJournalDaoWithReadMessages.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseJournalDaoWithReadMessages.cs @@ -40,21 +40,30 @@ protected BaseJournalDaoWithReadMessages(IAdvancedScheduler ec, FlowControlEnum.Continue), async opt => { - async Task>)>> - RetrieveNextBatch() + async Task>> BatchFromDb(string s, long l, int i, + long fromSeqNo) { - Seq< - Util.Try> msg; + Seq> msg; using (var conn = _connectionFactory.GetConnection()) { - msg = await Messages(conn, persistenceId, - opt.Item1, - toSequenceNr, batchSize) + msg = await Messages(conn, s, + fromSeqNo, + l, i) .RunWith( - ExtSeq.Seq>(), mat); + ExtSeq.Seq>(), mat); } + return msg; + } + + async Task>)>> + RetrieveNextBatch(long fromSeq) + { + Seq< + Util.Try> msg; + msg = await BatchFromDb(persistenceId, toSequenceNr, batchSize, fromSeq); + var hasMoreEvents = msg.Count == batchSize; //var lastMsg = msg.IsEmpty.LastOrDefault(); Util.Option lastSeq = Util.Option.None; @@ -99,9 +108,9 @@ protected BaseJournalDaoWithReadMessages(IAdvancedScheduler ec, case FlowControlEnum.Stop: return Util.Option<((long, FlowControlEnum), Seq>)>.None; case FlowControlEnum.Continue: - return await RetrieveNextBatch(); + return await RetrieveNextBatch(opt.Item1); case FlowControlEnum.ContinueDelayed when refreshInterval.HasValue: - return await FutureTimeoutSupport.After(refreshInterval.Value.Item1,refreshInterval.Value.Item2, RetrieveNextBatch); + return await FutureTimeoutSupport.After(refreshInterval.Value.Item1,refreshInterval.Value.Item2,()=> RetrieveNextBatch(opt.Item1)); default: return InvalidFlowThrowHelper(opt); } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs index 21ec4de9..3c476932 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs @@ -20,6 +20,7 @@ public class ByteArrayJournalSerializer : FlowPersistentReprSerializer _journalConfig; private readonly string[] _separatorArray; + private readonly TagWriteMode _tagWriteMode; public ByteArrayJournalSerializer(IProviderConfig journalConfig, Akka.Serialization.Serialization serializer, string separator) { @@ -27,6 +28,7 @@ public ByteArrayJournalSerializer(IProviderConfig journalCon _serializer = serializer; _separator = separator; _separatorArray = new[] {_separator}; + _tagWriteMode = journalConfig.TableConfig.TagWriteMode; } /// @@ -47,6 +49,43 @@ private static string StringSep(IImmutableSet tags, tl + separator + tr); } + private JournalRow CreateJournalRow( + IImmutableSet tags, IPersistentRepresentation _persistentRepr, long ts) + { + switch (_tagWriteMode) + { + case TagWriteMode.CommaSeparatedArray: + return new JournalRow() + { + tags = StringSep(tags, _separator), + Timestamp = _persistentRepr.Timestamp == 0 + ? ts + : _persistentRepr.Timestamp + }; + case TagWriteMode.TagTable: + return new JournalRow() + { + tags = "", + tagArr = tags.ToArray(), + Timestamp = _persistentRepr.Timestamp == 0 + ? ts + : _persistentRepr.Timestamp + }; + case TagWriteMode.CommaSeparatedArrayAndTagTable: + return new JournalRow() + { + tags = StringSep(tags, _separator), + tagArr = tags.ToArray(), + Timestamp = _persistentRepr.Timestamp == 0 + ? ts + : _persistentRepr.Timestamp + }; + default: + throw new Exception("Invalid Tag Write Mode!"); + } + } + + protected override Try Serialize(IPersistentRepresentation persistentRepr, IImmutableSet tTags, long timeStamp = 0) { try @@ -55,39 +94,37 @@ protected override Try Serialize(IPersistentRepresentation persisten return Akka.Serialization.Serialization.WithTransport( _serializer.System, (persistentRepr , _serializer.FindSerializerForType(persistentRepr.Payload.GetType(),_journalConfig.DefaultSerializer), - StringSep(tTags,_separator), - timeStamp + CreateJournalRow(tTags,persistentRepr,timeStamp) ), state => { - var (_persistentRepr, serializer,tags,ts) = state; - string thisManifest = ""; + var (_persistentRepr, serializer,row) = state; + if (serializer is SerializerWithStringManifest withStringManifest) { - thisManifest = + row.manifest = withStringManifest.Manifest(_persistentRepr.Payload); } else { if (serializer.IncludeManifest) { - thisManifest = _persistentRepr.Payload + row.manifest = _persistentRepr.Payload .GetType().TypeQualifiedName(); } } - return new Try(new JournalRow() + { - message = - serializer.ToBinary(_persistentRepr.Payload), - manifest = thisManifest, - persistenceId = _persistentRepr.PersistenceId, - tags = tags, - Identifier = serializer.Identifier, - sequenceNumber = _persistentRepr.SequenceNr, - Timestamp = _persistentRepr.Timestamp == 0 - ? ts - : _persistentRepr.Timestamp - }); + row.message = + serializer.ToBinary(_persistentRepr + .Payload); + row.persistenceId = + _persistentRepr.PersistenceId; + row.Identifier = serializer.Identifier; + row.sequenceNumber = _persistentRepr.SequenceNr; + } + return new Try(row + ); }); } catch (Exception e) @@ -121,7 +158,7 @@ protected override Try Serialize(IPersistentRepresentation persisten t.manifest, t.deleted, ActorRefs.NoSender, null, t.Timestamp), t.tags?.Split(_separatorArray, StringSplitOptions.RemoveEmptyEntries) - .ToImmutableHashSet() ?? ImmutableHashSet.Empty, + .ToImmutableHashSet() ?? t.tagArr?.ToImmutableHashSet()?? ImmutableHashSet.Empty, t.ordering)); } else @@ -134,7 +171,7 @@ protected override Try Serialize(IPersistentRepresentation persisten t.manifest, t.deleted, ActorRefs.NoSender, null, t.Timestamp), t.tags?.Split(_separatorArray, StringSplitOptions.RemoveEmptyEntries) - .ToImmutableHashSet() ?? ImmutableHashSet.Empty, + .ToImmutableHashSet() ??t.tagArr?.ToImmutableHashSet()?? ImmutableHashSet.Empty, t.ordering)); // TODO: hack. Replace when https://github.com/akkadotnet/akka.net/issues/3811 } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs index 4574e9d6..e4311d83 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs @@ -3,6 +3,12 @@ namespace Akka.Persistence.Sql.Linq2Db.Journal.Types { + + public sealed class JournalTagRow + { + public long JournalOrderingId { get; set; } + public string TagValue { get; set; } + } public sealed class JournalRow { public JournalRow() @@ -22,5 +28,6 @@ public JournalRow() public string tags { get; set; } public string manifest { get; set; } public int? Identifier { get; set; } + public string[] tagArr { get; set; } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index 9d1ff11f..afdbe0e5 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -15,6 +15,7 @@ using Akka.Util; using LinqToDB; using LinqToDB.Data; +using LinqToDB.Tools; namespace Akka.Persistence.Sql.Linq2Db.Query.Dao { @@ -104,19 +105,43 @@ public Source< { using (var conn = input._connectionFactory.GetConnection()) { - return await conn.GetTable() + var evts = await conn.GetTable() .OrderBy(r => r.ordering) .Where(r => r.ordering > input.offset && r.ordering <= input.maxOffset) .Take(input.maxTake).ToListAsync(); + return await AddTagDataIfNeeded(evts, conn); } } ).Via(deserializeFlow); } - + + public async Task> AddTagDataIfNeeded(List toAdd, DataConnection context) + { + if (_readJournalConfig.PluginConfig.TagReadMode == + TagReadMode.TagTableOnly) + { + var tagRows = await context.GetTable() + .Where(r => + r.JournalOrderingId.In(toAdd.Select(r => r.ordering))) + .OrderBy(r=>r.JournalOrderingId) + .ToListAsync(); + foreach (var journalRow in toAdd) + { + journalRow.tagArr = + tagRows.Where(r => + r.JournalOrderingId == + journalRow.ordering) + .Select(r => r.TagValue) + .ToArray(); + } + } + + return toAdd; + } public Source< Akka.Util.Try<(IPersistentRepresentation, IImmutableSet, long)>, NotUsed> EventsByTag(string tag, long offset, long maxOffset, @@ -124,29 +149,159 @@ public Source< { var separator = _readJournalConfig.PluginConfig.TagSeparator; var maxTake = MaxTake(max); - return AsyncSource.FromEnumerable(new{separator,tag,offset,maxOffset,maxTake,_connectionFactory}, - async(input)=> - { - using (var conn = input._connectionFactory.GetConnection()) + switch (_readJournalConfig.PluginConfig.TagReadMode) + { + case TagReadMode.DefaultConcatVarchar: + return AsyncSource.FromEnumerable(new{separator,tag,offset,maxOffset,maxTake,_connectionFactory}, + async(input)=> + { + using (var conn = input._connectionFactory.GetConnection()) + { + return await conn.GetTable() + .Where(r => r.tags.Contains(input.tag)) + .OrderBy(r => r.ordering) + .Where(r => + r.ordering > input.offset && r.ordering <= input.maxOffset) + .Take(input.maxTake).ToListAsync(); + } + }).Via(perfectlyMatchTag(tag, separator)) + .Via(deserializeFlow); + case TagReadMode.MigrateToTagTable: + return eventByTagMigration(tag, offset, maxOffset, separator, maxTake); + case TagReadMode.TagTableOnly: + return eventByTagTableOnly(tag, offset, maxOffset, separator, maxTake); + default: + throw new ArgumentOutOfRangeException(); + } + + + + } + + private Source< + Try<(IPersistentRepresentation, IImmutableSet, long)>, + NotUsed> eventByTagTableOnly(string tag, long offset, + long maxOffset, + string separator, int maxTake) + { + return AsyncSource.FromEnumerable( + new { - return await conn.GetTable() - .Where(r => r.tags.Contains(input.tag)) - .OrderBy(r => r.ordering) - .Where(r => - r.ordering > input.offset && r.ordering <= input.maxOffset) - .Take(input.maxTake).ToListAsync(); - } - }).Via(perfectlyMatchTag(tag, separator)) + separator, tag, offset, maxOffset, maxTake, _connectionFactory + }, + async (input) => + { + //TODO: Optimize Flow + using (var conn = input._connectionFactory.GetConnection()) + { + //First, Get eligible rows. + var mainRows = await + conn.GetTable() + .LeftJoin( + conn.GetTable< + JournalTagRow>(), + (jr, jtr) => + jr.ordering == + jtr.JournalOrderingId, + (jr, jtr) => + new { jr, jtr }) + .Where(r => + r.jtr.TagValue == input.tag) + .Select(r=>r.jr) + .Where(r => + r.ordering > input.offset && + r.ordering <= input.maxOffset) + .Take(input.maxTake).ToListAsync(); + //Then, Get the tags for the rows. + var tagRows = await conn.GetTable() + .Where(r => + r.JournalOrderingId.In( + mainRows.Select(r => + r.ordering))) + .ToListAsync(); + foreach (var journalRow in mainRows) + { + journalRow.tagArr = + tagRows.Where(r => + r.JournalOrderingId == + journalRow.ordering) + .Select(r => r.TagValue) + .ToArray(); + } + + return mainRows; + } + }).Via(perfectlyMatchTag(tag, separator)) .Via(deserializeFlow); + } + private Source, long)>, NotUsed> eventByTagMigration(string tag, long offset, long maxOffset, + string separator, int maxTake) + { + return AsyncSource.FromEnumerable( + new + { + separator, tag, offset, maxOffset, maxTake, _connectionFactory + }, + async (input) => + { + //NOTE: This flow is probably not performant, + //It is meant to allow for safe migration + //And is not necessarily intended for long term use + using (var conn = input._connectionFactory.GetConnection()) + { + //First, fin + var mainRows = await conn.GetTable() + .Where(r => r.ordering.In( + conn.GetTable() + .LeftJoin( + conn.GetTable< + JournalTagRow>(), + (jr, jtr) => + jr.ordering == + jtr.JournalOrderingId, + (jr, jtr) => + new { jr, jtr }) + .Where(r => + r.jr.tags.Contains( + input.tag) || + r.jtr.TagValue == input.tag) + .Select(r => r.jr.ordering) + .OrderBy(r => r) + .Where(r => + r > input.offset && + r <= input.maxOffset) + .Take(input.maxTake))).ToListAsync(); + var tagRows = await conn.GetTable() + .Where(r => + r.JournalOrderingId.In( + mainRows.Select(r => + r.ordering))) + .ToListAsync(); + foreach (var journalRow in mainRows) + { + journalRow.tagArr = + tagRows.Where(r => + r.JournalOrderingId == + journalRow.ordering) + .Select(r => r.TagValue) + .ToArray(); + } + + return mainRows; + } + }).Via(perfectlyMatchTag(tag, separator)) + .Via(deserializeFlow); } private Flow perfectlyMatchTag( string tag, string separator) { - + //Do the tagArr check first here + //Since the logic is simpler. return Flow.Create().Where(r => + r.tagArr?.Contains(tag)?? (r.tags ?? "") .Split(new[] {separator}, StringSplitOptions.RemoveEmptyEntries) .Any(t => t.Contains(tag))); diff --git a/src/Akka.Persistence.Sql.Linq2Db/Serialization/PersistentReprSerializer.cs b/src/Akka.Persistence.Sql.Linq2Db/Serialization/PersistentReprSerializer.cs index c2b1e347..fb3f9cac 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Serialization/PersistentReprSerializer.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Serialization/PersistentReprSerializer.cs @@ -11,7 +11,7 @@ namespace Akka.Persistence.Sql.Linq2Db.Serialization { public abstract class PersistentReprSerializer { - public List>> Serialize( + public List> Serialize( IEnumerable messages, long timeStamp = 0) { return messages.Select(aw => @@ -23,58 +23,106 @@ public abstract class PersistentReprSerializer //Also, if we are only persisting a single event //We will only enumerate if we have more than one element. var payloads = - (aw.Payload as IImmutableList - ); + (aw.Payload as IImmutableList + ); if (payloads is null) { - return new Util.Try>( + return new Util.Try( new ArgumentNullException( $"{aw.PersistenceId} received empty payload for sequenceNr range " + $"{aw.LowestSequenceNr} - {aw.HighestSequenceNr}")); } + //Preallocate our list; In the common case //This saves a tiny bit of garbage - var retList = new List(payloads.Count); + var retList = new T[payloads.Count]; if (payloads.Count == 1) + { + // If there's only one payload + // Don't allocate the enumerable. + var ser = Serialize(payloads[0], timeStamp); + var opt = ser.Success; + if (opt.HasValue) { - // If there's only one payload - // Don't allocate the enumerable. - var ser = Serialize(payloads[0], timeStamp); - var opt = ser.Success; - if (opt.HasValue) - { - retList.Add(opt.Value); - return new Util.Try>(retList); - } - else - { - return new Util.Try>(ser.Failure.Value); - } + retList[0] = opt.Value; + return new Util.Try(retList); } + else + { + return new Util.Try(ser.Failure.Value); + } + } else { + int idx = 0; foreach (var p in payloads) { var ser = Serialize(p, timeStamp); var opt = ser.Success; if (opt.HasValue) { - retList.Add(opt.Value); + retList[idx] = opt.Value; + idx = idx + 1; } else { - return new Util.Try>(ser.Failure.Value); + return new Util.Try(ser.Failure.Value); } } - return new Util.Try>(retList); + return new Util.Try(retList); } //return new Util.Try>(retList); - }).ToList(); } + private List> HandleSerializeList(long timeStamp, AtomicWrite[] msgArr) + { + List> fullSet = + new List>(msgArr.Length); + for (int i = 0; i < msgArr.Length; i++) + { + var payloads = + (msgArr[i].Payload as + IImmutableList + ); + if (payloads is null) + { + fullSet.Add(new Util.Try( + new ArgumentNullException( + $"{msgArr[i].PersistenceId} received empty payload for sequenceNr range " + + $"{msgArr[i].LowestSequenceNr} - {msgArr[i].HighestSequenceNr}"))); + } + else + { + fullSet.Add(serializerItem(timeStamp, payloads)); + } + } + + return fullSet; + } + + private Util.Try serializerItem(long timeStamp, IImmutableList payloads) + { + var retList = new T[payloads.Count]; + for (int j = 0; j < payloads.Count; j++) + { + var ser = Serialize(payloads[j], timeStamp); + var opt = ser.Success; + if (opt.HasValue) + { + retList[j] = opt.Value; + } + else + { + return new Util.Try(ser.Failure.Value); + } + } + + return new Util.Try(retList); + } + public Akka.Util.Try Serialize(IPersistentRepresentation persistentRepr, long timeStamp = 0) { From 8b657246ca156e5381dd9ae96b0b67da002d0178 Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 19 Mar 2022 17:47:35 -0400 Subject: [PATCH 03/20] fix incomplete rename refactor, re-add case to guarantee manifest is written even if empty --- .../Config/ReadJournalPluginConfig.cs | 12 ++++++------ .../Db/AkkaPersistenceDataConnectionFactory.cs | 1 + .../Journal/DAO/ByteArrayJournalSerializer.cs | 11 ++++++----- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 10 +++++----- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs index 10394f2c..bcd8da16 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs @@ -9,18 +9,18 @@ public ReadJournalPluginConfig(Configuration.Config config) TagSeparator = config.GetString("tag-separator", ","); Dao = config.GetString("dao", "akka.persistence.sql.linq2db.dao.bytea.readjournal.bytearrayreadjournaldao"); - var tagReadStr = config.GetString("tag-read-mode", "defaultconcatvarchar"); + var tagReadStr = config.GetString("tag-read-mode", "default"); if (Enum.TryParse(tagReadStr,true,out TagReadMode tgr)) { } else if (tagReadStr.Equals("default", StringComparison.InvariantCultureIgnoreCase)) { - tgr = TagReadMode.DefaultConcatVarchar; + tgr = TagReadMode.CommaSeparatedArray; } else if (tagReadStr.Equals("migrate", StringComparison.InvariantCultureIgnoreCase)) { - tgr = TagReadMode.MigrateToTagTable; + tgr = TagReadMode.CommaSeparatedArrayAndTagTable; } TagReadMode = tgr; @@ -35,8 +35,8 @@ public ReadJournalPluginConfig(Configuration.Config config) [Flags] public enum TagReadMode { - DefaultConcatVarchar = 1, - MigrateToTagTable = 3, - TagTableOnly = 2 + CommaSeparatedArray = 1, + TagTable = 2, + CommaSeparatedArrayAndTagTable = 3 } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index c29a33c3..74d44b86 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -136,6 +136,7 @@ private static void MapJournalRow(IProviderConfig config, .Member(r => r.Timestamp) .HasColumnName(tableConfig.ColumnNames.Created); //We can skip writing tags the old way by ignoring the column in mapping. + journalRowBuilder.Member(r => r.tagArr).IsNotColumn(); if ((tableConfig.TagWriteMode & TagWriteMode.CommaSeparatedArray) == 0) { diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs index 3c476932..d9a7a7f3 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs @@ -105,13 +105,14 @@ protected override Try Serialize(IPersistentRepresentation persisten row.manifest = withStringManifest.Manifest(_persistentRepr.Payload); } - else + else if (serializer.IncludeManifest) { - if (serializer.IncludeManifest) - { - row.manifest = _persistentRepr.Payload + row.manifest = _persistentRepr.Payload .GetType().TypeQualifiedName(); - } + } + else + { + row.manifest = ""; } { diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index afdbe0e5..04703218 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -121,8 +121,8 @@ public Source< public async Task> AddTagDataIfNeeded(List toAdd, DataConnection context) { - if (_readJournalConfig.PluginConfig.TagReadMode == - TagReadMode.TagTableOnly) + if ((_readJournalConfig.PluginConfig.TagReadMode & + TagReadMode.TagTable) != 0) { var tagRows = await context.GetTable() .Where(r => @@ -151,7 +151,7 @@ public Source< var maxTake = MaxTake(max); switch (_readJournalConfig.PluginConfig.TagReadMode) { - case TagReadMode.DefaultConcatVarchar: + case TagReadMode.CommaSeparatedArray: return AsyncSource.FromEnumerable(new{separator,tag,offset,maxOffset,maxTake,_connectionFactory}, async(input)=> { @@ -166,9 +166,9 @@ public Source< } }).Via(perfectlyMatchTag(tag, separator)) .Via(deserializeFlow); - case TagReadMode.MigrateToTagTable: + case TagReadMode.CommaSeparatedArrayAndTagTable: return eventByTagMigration(tag, offset, maxOffset, separator, maxTake); - case TagReadMode.TagTableOnly: + case TagReadMode.TagTable: return eventByTagTableOnly(tag, offset, maxOffset, separator, maxTake); default: throw new ArgumentOutOfRangeException(); From adc8ce49b0cfc67d10e550b1e3212b73a4da184d Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sun, 20 Mar 2022 15:38:47 -0400 Subject: [PATCH 04/20] fix: code for journal queries was not always respecting isdeleted --- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index 04703218..c7616a3c 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -101,11 +101,11 @@ public Source< var maxTake = MaxTake(max); - return AsyncSource.FromEnumerable(new {_connectionFactory,maxTake,maxOffset,offset},async(input)=> + return AsyncSource.FromEnumerable(new {t=this,maxTake,maxOffset,offset},async(input)=> { - using (var conn = input._connectionFactory.GetConnection()) + using (var conn = input.t._connectionFactory.GetConnection()) { - var evts = await conn.GetTable() + var evts = await input.t.baseQuery(conn) .OrderBy(r => r.ordering) .Where(r => r.ordering > input.offset && @@ -152,12 +152,12 @@ public Source< switch (_readJournalConfig.PluginConfig.TagReadMode) { case TagReadMode.CommaSeparatedArray: - return AsyncSource.FromEnumerable(new{separator,tag,offset,maxOffset,maxTake,_connectionFactory}, + return AsyncSource.FromEnumerable(new{separator,tag,offset,maxOffset,maxTake,t=this}, async(input)=> { - using (var conn = input._connectionFactory.GetConnection()) + using (var conn = input.t._connectionFactory.GetConnection()) { - return await conn.GetTable() + return await input.t.baseQuery(conn) .Where(r => r.tags.Contains(input.tag)) .OrderBy(r => r.ordering) .Where(r => @@ -187,16 +187,16 @@ private Source< return AsyncSource.FromEnumerable( new { - separator, tag, offset, maxOffset, maxTake, _connectionFactory + separator, tag, offset, maxOffset, maxTake, t=this }, async (input) => { //TODO: Optimize Flow - using (var conn = input._connectionFactory.GetConnection()) + using (var conn = input.t._connectionFactory.GetConnection()) { //First, Get eligible rows. var mainRows = await - conn.GetTable() + input.t.baseQuery(conn) .LeftJoin( conn.GetTable< JournalTagRow>(), @@ -241,37 +241,26 @@ private Source< return AsyncSource.FromEnumerable( new { - separator, tag, offset, maxOffset, maxTake, _connectionFactory + separator, tag, offset, maxOffset, maxTake, t =this }, async (input) => { //NOTE: This flow is probably not performant, //It is meant to allow for safe migration //And is not necessarily intended for long term use - using (var conn = input._connectionFactory.GetConnection()) + using (var conn = input.t._connectionFactory.GetConnection()) { //First, fin - var mainRows = await conn.GetTable() + var mainRows = await input.t.baseQuery(conn) .Where(r => r.ordering.In( - conn.GetTable() - .LeftJoin( - conn.GetTable< - JournalTagRow>(), - (jr, jtr) => - jr.ordering == - jtr.JournalOrderingId, - (jr, jtr) => - new { jr, jtr }) - .Where(r => - r.jr.tags.Contains( - input.tag) || - r.jtr.TagValue == input.tag) - .Select(r => r.jr.ordering) - .OrderBy(r => r) + conn.GetTable().Where(r=>r.TagValue==input.tag).Select(r=>r.JournalOrderingId)) + || r.tags.Contains(input.tag) + ) + .OrderBy(r => r.ordering) .Where(r => - r > input.offset && - r <= input.maxOffset) - .Take(input.maxTake))).ToListAsync(); + r.ordering > input.offset && + r.ordering <= input.maxOffset) + .Take(input.maxTake).ToListAsync(); var tagRows = await conn.GetTable() .Where(r => r.JournalOrderingId.In( @@ -355,6 +344,7 @@ public Source JournalSequence(long offset, long limit) using (var conn = input._connectionFactory.GetConnection()) { + //persistence-jdbc does not filter deleted here. return await conn.GetTable() .Where(r => r.ordering > input.offset) .Select(r => r.ordering) @@ -367,6 +357,7 @@ public async Task MaxJournalSequenceAsync() { using (var db = _connectionFactory.GetConnection()) { + //persistence-jdbc does not filter deleted here. return await db.GetTable() .Select(r => r.ordering) .FirstOrDefaultAsync(); From 6936c1e861b84e6cfa6b94c4ef689358868b44ce Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sun, 17 Apr 2022 13:38:34 -0400 Subject: [PATCH 05/20] Added WIP Tag-join-via UUID Support for users who may want faster writes with tags at expense of size and read speed. Cleaned up ReadJournal Queries --- .../Config/JournalTableConfig.cs | 9 + .../Config/ReadJournalPluginConfig.cs | 1 + .../AkkaPersistenceDataConnectionFactory.cs | 37 +++- .../Journal/DAO/BaseByteArrayJournalDao.cs | 116 ++++++++-- .../Journal/Types/JournalRow.cs | 5 +- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 198 ++++++++++++------ .../Query/JournalSequenceActor.cs | 6 +- src/Akka.Persistence.Sql.Linq2Db/Readme.MD | 21 +- 8 files changed, 303 insertions(+), 90 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs index f5b532d8..c4c6c046 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs @@ -10,6 +10,12 @@ public enum TagWriteMode TagTable = 2, CommaSeparatedArrayAndTagTable = 3, } + + public enum TagTableMode + { + OrderingId, + SequentialUUID + } public class JournalTableConfig { @@ -22,6 +28,9 @@ public class JournalTableConfig public bool WarnOnAutoInitializeFail { get; } public TagWriteMode TagWriteMode { get; } + public TagTableMode TagTableMode { get; } + public string? TagTableName { get; } + public JournalTableConfig(Configuration.Config config) { diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs index bcd8da16..c27d5806 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs @@ -30,6 +30,7 @@ public ReadJournalPluginConfig(Configuration.Config config) public string TagSeparator { get; set; } public TagReadMode TagReadMode { get; set; } + public TagTableMode TagTableMode { get; } } [Flags] diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index 74d44b86..c82c1c09 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -6,6 +6,7 @@ using Akka.Persistence.Sql.Linq2Db.Journal; using Akka.Persistence.Sql.Linq2Db.Journal.Types; using Akka.Persistence.Sql.Linq2Db.Snapshot; +using Akka.Streams.Dsl; using Akka.Util; using LinqToDB; using LinqToDB.Configuration; @@ -41,7 +42,6 @@ public AkkaPersistenceDataConnectionFactory(IProviderConfig opts = new LinqToDbConnectionOptionsBuilder() .UseConnectionString(providerName, connString) .UseMappingSchema(mappingSchema).Build(); - if (providerName.ToLower().StartsWith("sqlserver")) { policy = new SqlServerRetryPolicy(); @@ -154,6 +154,41 @@ private static void MapJournalRow(IProviderConfig config, .Member(r=>r.sequenceNumber).IsPrimaryKey(); } + void SetJoinCol(PropertyMappingBuilder builder, + PropertyMappingBuilder propertyMappingBuilder) + { + if (config.TableConfig.TagTableMode == + TagTableMode.SequentialUUID) + { + builder.Member(r => r.JournalOrderingId) + .IsNotColumn() + .Member(r => r.WriteUUID) + .IsColumn().IsPrimaryKey(); + journalRowBuilder.Member(r => r.WriteUUID) + .IsColumn(); + } + else + { + builder.Member(r => r.WriteUUID) + .IsNotColumn() + .Member(r => r.JournalOrderingId) + .IsColumn().IsPrimaryKey(); + journalRowBuilder.Member(r => r.WriteUUID) + .IsNotColumn(); + } + } + if ((config.TableConfig.TagWriteMode & TagWriteMode.TagTable) != 0) + { + var tagTableBuilder = fmb.Entity() + .HasTableName(tableConfig.TagTableName) + .HasSchemaName(tableConfig.SchemaName) + .Member(r => r.TagValue) + .IsColumn().IsNullable(false) + .HasLength(64) + .IsPrimaryKey(); + SetJoinCol(tagTableBuilder, journalRowBuilder); + } + //Probably overkill, but we only set Metadata Mapping if specified //That we are in delete compatibility mode. if (config.IDaoConfig.SqlCommonCompatibilityMode) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs index 1cc8a141..ed0f14e7 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs @@ -24,16 +24,35 @@ namespace Akka.Persistence.Sql.Linq2Db.Journal.DAO { - public static class EnumerableExtensions + public class SequentialUUIDGenerator { - public static IEnumerable SelectStateful( - this IEnumerable enumerable, TState state, - Func selector) + private long _counter = DateTime.UtcNow.Ticks; + + /// + /// Gets a value to be assigned to a property. + /// + /// The change tracking entry of the entity for which the value is being generated. + /// The value to be assigned to a property. + public Guid Next() { - foreach (var item in enumerable) + var guidBytes = Guid.NewGuid().ToByteArray(); + var counterBytes = BitConverter.GetBytes(Interlocked.Increment(ref _counter)); + + if (!BitConverter.IsLittleEndian) { - yield return selector(item,state); + System.Array.Reverse(counterBytes); } + + guidBytes[08] = counterBytes[1]; + guidBytes[09] = counterBytes[0]; + guidBytes[10] = counterBytes[7]; + guidBytes[11] = counterBytes[6]; + guidBytes[12] = counterBytes[5]; + guidBytes[13] = counterBytes[4]; + guidBytes[14] = counterBytes[3]; + guidBytes[15] = counterBytes[2]; + + return new Guid(guidBytes); } } public abstract class BaseByteArrayJournalDao : @@ -66,6 +85,7 @@ protected BaseByteArrayJournalDao(IAdvancedScheduler sched, Serializer = serializer; deserializeFlow = Serializer.DeserializeFlow(); deserializeFlowMapped = Serializer.DeserializeFlow().Select(MessageWithBatchMapper()); + _uuidGen = new SequentialUUIDGenerator(); //Due to C# rules we have to initialize WriteQueue here //Keeping it here vs init function prevents accidental moving of init //to where variables aren't set yet. @@ -119,6 +139,7 @@ private async Task QueueWriteJournalRows(Seq xs) new TaskCompletionSource( TaskCreationOptions.RunContinuationsAsynchronously ); + //Send promise and rows into queue. If the Queue takes it, //It will write the Promise state when finished writing (or failing) var result = @@ -150,6 +171,7 @@ private async Task QueueWriteJournalRows(Seq xs) private async Task WriteJournalRows(Seq xs) { { + //hot path: //If we only have one row, penalty for BulkCopy //Isn't worth it due to insert caching/transaction/etc. @@ -180,18 +202,18 @@ private async Task InsertSingle(Seq xs) } - private async Task BulkInsertWithTagTableTags(DataConnection dc, + private async Task InsertWithOrderingAndBulkInsertTags(DataConnection dc, Seq xs) { var tagsToInsert = new List(xs.Count); foreach (var journalRow in xs) { var dbid = await dc.InsertWithInt64IdentityAsync(journalRow); - tagsToInsert.AddRange(journalRow.tagArr.SelectStateful(dbid, - (tag, id) => new JournalTagRow() - { JournalOrderingId = id, TagValue = tag })); + foreach (var s1 in journalRow.tagArr) + { + tagsToInsert.Add(new JournalTagRow(){JournalOrderingId = dbid, TagValue = s1}); + } } - await dc.GetTable() .BulkCopyAsync(new BulkCopyOptions() { @@ -216,12 +238,19 @@ await dc.GetTable() MaxBatchSize = _journalConfig.DaoConfig.DbRoundTripBatchSize }, xs); } - private async Task InsertMultiple(Seq xs) { if ((_journalConfig.TableConfig.TagWriteMode & TagWriteMode.TagTable) !=0) { - await HandleTagTableInsert(xs); + if (_journalConfig.TableConfig.TagTableMode == + TagTableMode.OrderingId) + { + await HandleTagTableInsert(xs); + } + else + { + await HandleTagTableUUIDInsert(xs); + } } else { @@ -230,6 +259,61 @@ private async Task InsertMultiple(Seq xs) } + private async Task HandleTagTableUUIDInsert(Seq xs) + { + var tagWrites = new List(); + foreach (var journalRow in xs) + { + if (journalRow.tagArr?.Length > 0) + { + var uid = NextUUID(); + journalRow.WriteUUID = uid; + foreach (var s1 in journalRow.tagArr) + { + tagWrites.Add(new JournalTagRow(){WriteUUID=uid, TagValue = s1}); + } + } + } + + using (var ctx = _connectionFactory.GetConnection()) + { + using (var tx = await ctx.BeginTransactionAsync()) + { + try + { + await ctx.BulkCopyAsync(new BulkCopyOptions() + { + TableName = _journalConfig.TableConfig.TableName, + MaxBatchSize = _journalConfig.DaoConfig + .DbRoundTripBatchSize + },xs); + if (tagWrites.Count > 0) + { + await ctx.BulkCopyAsync(new BulkCopyOptions() + { + TableName = _journalConfig.TableConfig.TagTableName, + MaxBatchSize = _journalConfig.DaoConfig + .DbRoundTripTagBatchSize, + UseParameters = _journalConfig.DaoConfig + .PreferParametersOnMultiRowInsert + }, tagWrites); + } + await ctx.CommitTransactionAsync(); + } + catch (Exception e) + { + await ctx.RollbackTransactionAsync(); + throw; + } + } + } + } + + private Guid NextUUID() + { + return _uuidGen.Next(); + } + private async Task HandleDefaultInsert(Seq xs) { using (var db = _connectionFactory.GetConnection()) @@ -302,7 +386,7 @@ private async Task consumeSequenceForTagInsert(Seq xs, DataConnectio tail.Span(r => r.tagArr.Length > 0); if (hasTags.Count > 0) { - await BulkInsertWithTagTableTags(db, hasTags); + await InsertWithOrderingAndBulkInsertTags(db, hasTags); } } } @@ -560,7 +644,9 @@ private static readonly Expression> sequenceNumberSelector = r => r.SequenceNumber; - + + private readonly SequentialUUIDGenerator _uuidGen; + public async Task Update(string persistenceId, long sequenceNr, object payload) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs index e4311d83..a7ee3e9d 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs @@ -1,4 +1,5 @@ -using LinqToDB; +using System; +using LinqToDB; using LinqToDB.Mapping; namespace Akka.Persistence.Sql.Linq2Db.Journal.Types @@ -8,6 +9,7 @@ public sealed class JournalTagRow { public long JournalOrderingId { get; set; } public string TagValue { get; set; } + public Guid WriteUUID { get; set; } } public sealed class JournalRow { @@ -29,5 +31,6 @@ public JournalRow() public string manifest { get; set; } public int? Identifier { get; set; } public string[] tagArr { get; set; } + public Guid? WriteUUID { get; set; } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index c7616a3c..4298a034 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using Akka.Actor; using Akka.Persistence.Sql.Linq2Db.Config; @@ -45,6 +46,7 @@ protected BaseByteReadArrayJournalDAO(IAdvancedScheduler ec, protected IQueryable baseQuery(DataConnection connection) { + return connection.GetTable() .Where(jr => includeDeleted == false || (jr.deleted == false)); @@ -124,23 +126,83 @@ public async Task> AddTagDataIfNeeded(List toAdd, D if ((_readJournalConfig.PluginConfig.TagReadMode & TagReadMode.TagTable) != 0) { - var tagRows = await context.GetTable() - .Where(r => - r.JournalOrderingId.In(toAdd.Select(r => r.ordering))) - .OrderBy(r=>r.JournalOrderingId) - .ToListAsync(); + await addTagDataFromTagTable(toAdd, context); + } + return toAdd; + } + + private async Task addTagDataFromTagTable(List toAdd, DataConnection context) + { + var pred = TagCheckPredicate(toAdd); + var tagRows = pred.HasValue + ? await context.GetTable() + .Where(pred.Value) + .ToListAsync() + : new List(); + if (_readJournalConfig.TableConfig.TagTableMode == + TagTableMode.OrderingId) + { + foreach (var journalRow in toAdd) + { + journalRow.tagArr = + tagRows.Where(r => + r.JournalOrderingId == + journalRow.ordering) + .Select(r => r.TagValue) + .ToArray(); + } + } + else + { foreach (var journalRow in toAdd) { journalRow.tagArr = tagRows.Where(r => - r.JournalOrderingId == - journalRow.ordering) - .Select(r => r.TagValue) - .ToArray(); + r.WriteUUID == + journalRow.WriteUUID) + .Select(r => r.TagValue) + .ToArray(); } } + } - return toAdd; + public Option>> TagCheckPredicate( + List toAdd) + { + if (_readJournalConfig.PluginConfig.TagTableMode == + TagTableMode.SequentialUUID) + { + //Check whether we have anything to query for two reasons: + //1: Linq2Db may choke on an empty 'in' set. + //2: Don't wanna make a useless round trip to the DB, + // if we know nothing is tagged. + var set = toAdd.Where(r => r.WriteUUID.HasValue) + .Select(r => r.WriteUUID.Value).ToList(); + if (set.Count == 0) + { + return Option>>.None; + } + else + { + return new Option>>(r => + r.WriteUUID.In(set)); + } + } + else + { + //We can just check the count here. + //Alas, we won't know if there are tags + //Until we actually query on this one. + if (toAdd.Count == 0) + { + return Option>>.None; + } + else + { + return new Option>>( r => + r.JournalOrderingId.In(toAdd.Select(r => r.ordering))); + } + } } public Source< Akka.Util.Try<(IPersistentRepresentation, IImmutableSet, long)>, @@ -195,43 +257,35 @@ private Source< using (var conn = input.t._connectionFactory.GetConnection()) { //First, Get eligible rows. - var mainRows = await - input.t.baseQuery(conn) - .LeftJoin( - conn.GetTable< - JournalTagRow>(), - (jr, jtr) => + var mainRows = await + input.t.baseQuery(conn) + .LeftJoin( + conn.GetTable< + JournalTagRow>(), + _readJournalConfig.TableConfig + .TagTableMode == + TagTableMode.OrderingId + ? (jr, jtr) => jr.ordering == - jtr.JournalOrderingId, - (jr, jtr) => - new { jr, jtr }) - .Where(r => - r.jtr.TagValue == input.tag) - .Select(r=>r.jr) - .Where(r => - r.ordering > input.offset && - r.ordering <= input.maxOffset) - .Take(input.maxTake).ToListAsync(); - //Then, Get the tags for the rows. - var tagRows = await conn.GetTable() - .Where(r => - r.JournalOrderingId.In( - mainRows.Select(r => - r.ordering))) - .ToListAsync(); - foreach (var journalRow in mainRows) - { - journalRow.tagArr = - tagRows.Where(r => - r.JournalOrderingId == - journalRow.ordering) - .Select(r => r.TagValue) - .ToArray(); - } - + jtr.JournalOrderingId + : (jr, jtr) => + jr.WriteUUID == jtr.WriteUUID, + (jr, jtr) => + new { jr, jtr }) + .Where(r => + r.jtr.TagValue == input.tag) + .Select(r => r.jr) + .Where(r => + r.ordering > input.offset && + r.ordering <= input.maxOffset) + .Take(input.maxTake).ToListAsync(); + await addTagDataFromTagTable(mainRows, conn); return mainRows; } - }).Via(perfectlyMatchTag(tag, separator)) + }) + //We still PerfectlyMatchTag here + //Because DB Collation :) + .Via(perfectlyMatchTag(tag, separator)) .Via(deserializeFlow); } @@ -250,33 +304,40 @@ private Source< //And is not necessarily intended for long term use using (var conn = input.t._connectionFactory.GetConnection()) { - //First, fin + //First, find the rows. + //We use IN here instead of leftjoin + //because it's safer from a + //'avoid duplicate rows tripping things up later' + //standpoint. var mainRows = await input.t.baseQuery(conn) - .Where(r => r.ordering.In( - conn.GetTable().Where(r=>r.TagValue==input.tag).Select(r=>r.JournalOrderingId)) - || r.tags.Contains(input.tag) - ) + .Where( + _readJournalConfig.TableConfig + .TagTableMode == TagTableMode.OrderingId + ? r => r.ordering.In( + conn.GetTable< + JournalTagRow>() + .Where(r => + r.TagValue == + input.tag) + .Select(r => + r.JournalOrderingId)) + || r.tags.Contains(input.tag) + : r => r.WriteUUID.Value.In( + conn.GetTable< + JournalTagRow>() + .Where(r => + r.TagValue == + input.tag) + .Select(r => + r.WriteUUID)) + || r.tags.Contains(input.tag) + ) .OrderBy(r => r.ordering) - .Where(r => - r.ordering > input.offset && - r.ordering <= input.maxOffset) - .Take(input.maxTake).ToListAsync(); - var tagRows = await conn.GetTable() .Where(r => - r.JournalOrderingId.In( - mainRows.Select(r => - r.ordering))) - .ToListAsync(); - foreach (var journalRow in mainRows) - { - journalRow.tagArr = - tagRows.Where(r => - r.JournalOrderingId == - journalRow.ordering) - .Select(r => r.TagValue) - .ToArray(); - } - + r.ordering > input.offset && + r.ordering <= input.maxOffset) + .Take(input.maxTake).ToListAsync(); + await addTagDataFromTagTable(mainRows, conn); return mainRows; } }).Via(perfectlyMatchTag(tag, separator)) @@ -360,6 +421,7 @@ public async Task MaxJournalSequenceAsync() //persistence-jdbc does not filter deleted here. return await db.GetTable() .Select(r => r.ordering) + .OrderByDescending(r=>r) .FirstOrDefaultAsync(); } } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/JournalSequenceActor.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/JournalSequenceActor.cs index b71cd6f0..efab6533 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/JournalSequenceActor.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/JournalSequenceActor.cs @@ -72,7 +72,7 @@ protected bool _receive(object message, long currentMaxOrdering, .RunWith(Sink.Seq(), _mat) .PipeTo(Self, sender: Self, success: res => - new NewOrderingIds(currentMaxOrdering, res)); + new NewOrderingIds(currentMaxOrdering, res),f=> new Status.Failure(f)); } else if (message is NewOrderingIds nids) { @@ -186,9 +186,7 @@ protected override void PreStart() self.Tell(new QueryOrderingIds()); try { - - - _readJournalDao.MaxJournalSequenceAsync().ContinueWith(t => + _readJournalDao.MaxJournalSequenceAsync().ContinueWith(t => { if (t.IsFaulted) { diff --git a/src/Akka.Persistence.Sql.Linq2Db/Readme.MD b/src/Akka.Persistence.Sql.Linq2Db/Readme.MD index fe826cff..9a29c031 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Readme.MD +++ b/src/Akka.Persistence.Sql.Linq2Db/Readme.MD @@ -57,7 +57,26 @@ Please read the documentation carefully. Some features may be specific to use ca - Classes used in place of ValueTuples in certain areas - We don't have separate Query classes at this time. This can definitely be improved in future - A couple of places around `WriteMessagesAsync` have had their logic moved to facilitate performance (i.e. use of `await` instead of `ContinueWith`) - - Backwards Compatibility mode is implemented, to interoperate with existing journals and snapsho stores. + - Backwards Compatibility mode is implemented, to interoperate with existing journals and snapsho stores. + + - Tag Table Support (Alpha): + - Allows the writing of tags to a separate table to allow for different performance strategies when working with tags. + - Supports Two Tag Table Modes: + - WriteUUID: The tag table and join uses a 'sequential-uuid' type field that will have lower page splits while allowing for good row locality on insert. + - This option is intended for those who want maximum write performance, at the expense of database storage and load. + - OrderingId: Uses the Journal Row's 'ordering' sequential Int64 for the tag table and join. + - This option is intended for those who want more efficient use of the DB's space + - This will result in slower writes, but faster/more efficient reads. + - Provides multiple modes of operation for reads and writes, note that there are separate switches for both read and write! + - CommaSeparatedOnly: The old behavior, where the comma separated tags are held in a column + - CommaSeparatedAndTagTable: Will Read/Write from both the Comma Separated column as well as the Tag Table + - TagTableOnly: will only use the tag table for Read/Write + - 'Live' Migration should be possible via the following flow: + 1. Run Migration scripts to create new columns/tables. + 2. Rolling Deploy your system with Reads and Writes in 'CommaSeparatedAndTagTable' mode. + 3. Rolling deploy your system (again), with Writes now in 'TagTableOnly' mode. + 4. Run Migration App/Script to move existing tags into tag table. + 5. Rolling deploy your system (last one!) with Reads now in 'TagTableOnly' mode. ## Currently Implemented: From 627d159f42b0d9aff5679888c5a69461108ff40e Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sun, 17 Apr 2022 13:54:51 -0400 Subject: [PATCH 06/20] Try refactoring to make older compiler happy about expressions. --- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 62 ++++++++++--------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index 4298a034..742a12f8 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -262,14 +262,7 @@ private Source< .LeftJoin( conn.GetTable< JournalTagRow>(), - _readJournalConfig.TableConfig - .TagTableMode == - TagTableMode.OrderingId - ? (jr, jtr) => - jr.ordering == - jtr.JournalOrderingId - : (jr, jtr) => - jr.WriteUUID == jtr.WriteUUID, + EventsByTagOnlyJoinPredicate, (jr, jtr) => new { jr, jtr }) .Where(r => @@ -289,6 +282,16 @@ private Source< .Via(deserializeFlow); } + private Expression> EventsByTagOnlyJoinPredicate => + _readJournalConfig.TableConfig + .TagTableMode == + TagTableMode.OrderingId + ? (jr, jtr) => + jr.ordering == + jtr.JournalOrderingId + : (jr, jtr) => + jr.WriteUUID == jtr.WriteUUID; + private Source, long)>, NotUsed> eventByTagMigration(string tag, long offset, long maxOffset, string separator, int maxTake) { @@ -311,26 +314,7 @@ private Source< //standpoint. var mainRows = await input.t.baseQuery(conn) .Where( - _readJournalConfig.TableConfig - .TagTableMode == TagTableMode.OrderingId - ? r => r.ordering.In( - conn.GetTable< - JournalTagRow>() - .Where(r => - r.TagValue == - input.tag) - .Select(r => - r.JournalOrderingId)) - || r.tags.Contains(input.tag) - : r => r.WriteUUID.Value.In( - conn.GetTable< - JournalTagRow>() - .Where(r => - r.TagValue == - input.tag) - .Select(r => - r.WriteUUID)) - || r.tags.Contains(input.tag) + eventsByTagMigrationPredicate(conn, input.tag) ) .OrderBy(r => r.ordering) .Where(r => @@ -344,6 +328,28 @@ private Source< .Via(deserializeFlow); } + private Expression> eventsByTagMigrationPredicate(DataConnection conn, string tagVal) + { + return _readJournalConfig.TableConfig + .TagTableMode == TagTableMode.OrderingId + ? r => r.ordering.In( + Queryable.Where(conn.GetTable< + JournalTagRow>(), r => + r.TagValue == + tagVal) + .Select(r => + r.JournalOrderingId)) + || r.tags.Contains(tagVal) + : r => r.WriteUUID.Value.In( + Queryable.Where(conn.GetTable< + JournalTagRow>(), r => + r.TagValue == + tagVal) + .Select(r => + r.WriteUUID)) + || r.tags.Contains(tagVal); + } + private Flow perfectlyMatchTag( string tag, string separator) From bc5257925c518e97841cf3942185394e1eb3d028 Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sun, 17 Apr 2022 14:01:58 -0400 Subject: [PATCH 07/20] Try Unwinding ternarys to make compiler happy. --- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index 742a12f8..eae422ab 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -282,15 +282,21 @@ private Source< .Via(deserializeFlow); } - private Expression> EventsByTagOnlyJoinPredicate => - _readJournalConfig.TableConfig - .TagTableMode == - TagTableMode.OrderingId - ? (jr, jtr) => - jr.ordering == - jtr.JournalOrderingId - : (jr, jtr) => - jr.WriteUUID == jtr.WriteUUID; + private Expression> EventsByTagOnlyJoinPredicate + { + get + { + if (_readJournalConfig.TableConfig + .TagTableMode == + TagTableMode.OrderingId) + return (jr, jtr) => + jr.ordering == + jtr.JournalOrderingId; + else + return (jr, jtr) => + jr.WriteUUID == jtr.WriteUUID; + } + } private Source, long)>, NotUsed> eventByTagMigration(string tag, long offset, long maxOffset, string separator, int maxTake) @@ -330,24 +336,28 @@ private Source< private Expression> eventsByTagMigrationPredicate(DataConnection conn, string tagVal) { - return _readJournalConfig.TableConfig - .TagTableMode == TagTableMode.OrderingId - ? r => r.ordering.In( - Queryable.Where(conn.GetTable< - JournalTagRow>(), r => - r.TagValue == - tagVal) - .Select(r => - r.JournalOrderingId)) - || r.tags.Contains(tagVal) - : r => r.WriteUUID.Value.In( - Queryable.Where(conn.GetTable< - JournalTagRow>(), r => - r.TagValue == - tagVal) - .Select(r => - r.WriteUUID)) - || r.tags.Contains(tagVal); + if (_readJournalConfig.TableConfig.TagTableMode == TagTableMode.OrderingId) + { + return (JournalRow r) => r.ordering.In( + conn.GetTable< + JournalTagRow>().Where(r => + r.TagValue == + tagVal) + .Select(r => + r.JournalOrderingId)) + || r.tags.Contains(tagVal); + } + else + { + return (JournalRow r) => r.WriteUUID.Value.In( + conn.GetTable< + JournalTagRow>().Where(r => + r.TagValue == + tagVal) + .Select(r => + r.WriteUUID)) + || r.tags.Contains(tagVal); + } } private Flow perfectlyMatchTag( From 37780993f9b4ce6a716516b173c1a22f9c41477d Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sun, 17 Apr 2022 14:21:47 -0400 Subject: [PATCH 08/20] WIP Allow proper handling of EventManifest --- .../Config/JournalTableConfig.cs | 1 + .../Db/AkkaPersistenceDataConnectionFactory.cs | 11 +++++++++++ .../Journal/DAO/ByteArrayJournalSerializer.cs | 5 +++-- .../Journal/Types/JournalRow.cs | 1 + 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs index c4c6c046..bdd78d96 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs @@ -30,6 +30,7 @@ public class JournalTableConfig public TagWriteMode TagWriteMode { get; } public TagTableMode TagTableMode { get; } public string? TagTableName { get; } + public bool UseEventManifestColumn { get; } public JournalTableConfig(Configuration.Config config) { diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index c82c1c09..589a8580 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -177,6 +177,17 @@ void SetJoinCol(PropertyMappingBuilder builder, .IsNotColumn(); } } + + if (config.TableConfig.UseEventManifestColumn) + { + journalRowBuilder.Member(r => r.eventManifest) + .IsColumn().HasLength(64); + } + else + { + journalRowBuilder.Member(r => r.eventManifest) + .IsNotColumn(); + } if ((config.TableConfig.TagWriteMode & TagWriteMode.TagTable) != 0) { var tagTableBuilder = fmb.Entity() diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs index d9a7a7f3..b070bd9e 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs @@ -123,6 +123,7 @@ protected override Try Serialize(IPersistentRepresentation persisten _persistentRepr.PersistenceId; row.Identifier = serializer.Identifier; row.sequenceNumber = _persistentRepr.SequenceNr; + row.eventManifest = _persistentRepr.Manifest; } return new Try(row ); @@ -156,7 +157,7 @@ protected override Try Serialize(IPersistentRepresentation persisten state.message, state.type); }), t.sequenceNumber, t.persistenceId, - t.manifest, t.deleted, ActorRefs.NoSender, null, t.Timestamp), + t.eventManifest??t.manifest, t.deleted, ActorRefs.NoSender, null, t.Timestamp), t.tags?.Split(_separatorArray, StringSplitOptions.RemoveEmptyEntries) .ToImmutableHashSet() ?? t.tagArr?.ToImmutableHashSet()?? ImmutableHashSet.Empty, @@ -169,7 +170,7 @@ protected override Try Serialize(IPersistentRepresentation persisten new Persistent(_serializer.Deserialize(t.message, identifierMaybe.Value,t.manifest), t.sequenceNumber, t.persistenceId, - t.manifest, t.deleted, ActorRefs.NoSender, null, t.Timestamp), + t.eventManifest?? t.manifest, t.deleted, ActorRefs.NoSender, null, t.Timestamp), t.tags?.Split(_separatorArray, StringSplitOptions.RemoveEmptyEntries) .ToImmutableHashSet() ??t.tagArr?.ToImmutableHashSet()?? ImmutableHashSet.Empty, diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs index a7ee3e9d..9573af07 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs @@ -32,5 +32,6 @@ public JournalRow() public int? Identifier { get; set; } public string[] tagArr { get; set; } public Guid? WriteUUID { get; set; } + public string eventManifest { get; set; } } } \ No newline at end of file From 34570b937e7d72b29b1eb35f65d3d7c4ce18db98 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Mon, 18 Jul 2022 08:37:55 -0500 Subject: [PATCH 09/20] Rename Class1.cs to JournalIndexHelper.cs --- .../{Class1.cs => JournalIndexHelper.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Akka.Persistence.Linq2Db.IndexHelperLib/{Class1.cs => JournalIndexHelper.cs} (99%) diff --git a/Akka.Persistence.Linq2Db.IndexHelperLib/Class1.cs b/Akka.Persistence.Linq2Db.IndexHelperLib/JournalIndexHelper.cs similarity index 99% rename from Akka.Persistence.Linq2Db.IndexHelperLib/Class1.cs rename to Akka.Persistence.Linq2Db.IndexHelperLib/JournalIndexHelper.cs index 65f5b08e..3acf33c6 100644 --- a/Akka.Persistence.Linq2Db.IndexHelperLib/Class1.cs +++ b/Akka.Persistence.Linq2Db.IndexHelperLib/JournalIndexHelper.cs @@ -47,4 +47,4 @@ private static IndexDefinition beginCreateIndex(string tableName, string schemaN return idx; } } -} \ No newline at end of file +} From f5e59b3abfd426e6eda30d42ec1556496d2f2d57 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Wed, 23 Nov 2022 20:53:06 +0700 Subject: [PATCH 10/20] Move dangling projects into src folder --- Akka.Persistence.Linq2Db.sln | 30 +++++++++---------- .../Akka.Linq2Db.Sandbox.csproj | 0 ....Persistence.Linq2Db.IndexHelperApp.csproj | 3 +- .../Options.cs | 0 .../Program.cs | 0 .../example.hocon | 0 ....Persistence.Linq2Db.IndexHelperLib.csproj | 0 .../JournalIndexHelper.cs | 0 8 files changed, 17 insertions(+), 16 deletions(-) rename {Akka.Linq2Db.Sandbox => src/Akka.Linq2Db.Sandbox}/Akka.Linq2Db.Sandbox.csproj (100%) rename {Akka.Persistence.Linq2Db.IndexHelperApp => src/Akka.Persistence.Linq2Db.IndexHelperApp}/Akka.Persistence.Linq2Db.IndexHelperApp.csproj (76%) rename {Akka.Persistence.Linq2Db.IndexHelperApp => src/Akka.Persistence.Linq2Db.IndexHelperApp}/Options.cs (100%) rename {Akka.Persistence.Linq2Db.IndexHelperApp => src/Akka.Persistence.Linq2Db.IndexHelperApp}/Program.cs (100%) rename {Akka.Persistence.Linq2Db.IndexHelperApp => src/Akka.Persistence.Linq2Db.IndexHelperApp}/example.hocon (100%) rename {Akka.Persistence.Linq2Db.IndexHelperLib => src/Akka.Persistence.Linq2Db.IndexHelperLib}/Akka.Persistence.Linq2Db.IndexHelperLib.csproj (100%) rename {Akka.Persistence.Linq2Db.IndexHelperLib => src/Akka.Persistence.Linq2Db.IndexHelperLib}/JournalIndexHelper.cs (100%) diff --git a/Akka.Persistence.Linq2Db.sln b/Akka.Persistence.Linq2Db.sln index 6ff268fa..dde72ac0 100644 --- a/Akka.Persistence.Linq2Db.sln +++ b/Akka.Persistence.Linq2Db.sln @@ -43,11 +43,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build-system", "build-syste build-system\windows-release.yaml = build-system\windows-release.yaml EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Linq2Db.IndexHelperLib", "Akka.Persistence.Linq2Db.IndexHelperLib\Akka.Persistence.Linq2Db.IndexHelperLib.csproj", "{AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Linq2Db.Sandbox", "src\Akka.Linq2Db.Sandbox\Akka.Linq2Db.Sandbox.csproj", "{C9D2CCA5-D431-44F7-B8FC-5424655291B6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Linq2Db.IndexHelperApp", "Akka.Persistence.Linq2Db.IndexHelperApp\Akka.Persistence.Linq2Db.IndexHelperApp.csproj", "{D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Linq2Db.IndexHelperApp", "src\Akka.Persistence.Linq2Db.IndexHelperApp\Akka.Persistence.Linq2Db.IndexHelperApp.csproj", "{7B03FDD2-45CC-4F83-B23F-66B4EE82437A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Linq2Db.Sandbox", "Akka.Linq2Db.Sandbox\Akka.Linq2Db.Sandbox.csproj", "{697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Linq2Db.IndexHelperLib", "src\Akka.Persistence.Linq2Db.IndexHelperLib\Akka.Persistence.Linq2Db.IndexHelperLib.csproj", "{FD78AE77-C95F-4D5D-90EC-923935BAC580}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -91,18 +91,18 @@ Global {170698FA-DA1E-40BC-896D-AFA67976C0EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {170698FA-DA1E-40BC-896D-AFA67976C0EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {170698FA-DA1E-40BC-896D-AFA67976C0EB}.Release|Any CPU.Build.0 = Release|Any CPU - {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AACE3FBC-51FE-4A9B-B6C4-4CCA750DB22E}.Release|Any CPU.Build.0 = Release|Any CPU - {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5C851AA-DB80-4E9F-BD2E-03E63DC3082E}.Release|Any CPU.Build.0 = Release|Any CPU - {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {697B9FC8-29E2-4F7D-B63B-9E4B873F6AA1}.Release|Any CPU.Build.0 = Release|Any CPU + {C9D2CCA5-D431-44F7-B8FC-5424655291B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9D2CCA5-D431-44F7-B8FC-5424655291B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9D2CCA5-D431-44F7-B8FC-5424655291B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9D2CCA5-D431-44F7-B8FC-5424655291B6}.Release|Any CPU.Build.0 = Release|Any CPU + {7B03FDD2-45CC-4F83-B23F-66B4EE82437A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B03FDD2-45CC-4F83-B23F-66B4EE82437A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B03FDD2-45CC-4F83-B23F-66B4EE82437A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B03FDD2-45CC-4F83-B23F-66B4EE82437A}.Release|Any CPU.Build.0 = Release|Any CPU + {FD78AE77-C95F-4D5D-90EC-923935BAC580}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD78AE77-C95F-4D5D-90EC-923935BAC580}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD78AE77-C95F-4D5D-90EC-923935BAC580}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD78AE77-C95F-4D5D-90EC-923935BAC580}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj b/src/Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj similarity index 100% rename from Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj rename to src/Akka.Linq2Db.Sandbox/Akka.Linq2Db.Sandbox.csproj diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj b/src/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj similarity index 76% rename from Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj rename to src/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj index caf07276..4c2a6e43 100644 --- a/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj +++ b/src/Akka.Persistence.Linq2Db.IndexHelperApp/Akka.Persistence.Linq2Db.IndexHelperApp.csproj @@ -7,7 +7,8 @@ - + + diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs b/src/Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs similarity index 100% rename from Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs rename to src/Akka.Persistence.Linq2Db.IndexHelperApp/Options.cs diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs b/src/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs similarity index 100% rename from Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs rename to src/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs diff --git a/Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon b/src/Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon similarity index 100% rename from Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon rename to src/Akka.Persistence.Linq2Db.IndexHelperApp/example.hocon diff --git a/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj b/src/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj similarity index 100% rename from Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj rename to src/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj diff --git a/Akka.Persistence.Linq2Db.IndexHelperLib/JournalIndexHelper.cs b/src/Akka.Persistence.Linq2Db.IndexHelperLib/JournalIndexHelper.cs similarity index 100% rename from Akka.Persistence.Linq2Db.IndexHelperLib/JournalIndexHelper.cs rename to src/Akka.Persistence.Linq2Db.IndexHelperLib/JournalIndexHelper.cs From 56777fa070cedd10a672ae0a9335716ad6a76284 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Wed, 23 Nov 2022 21:46:16 +0700 Subject: [PATCH 11/20] Cleanup property names --- .../Db/AkkaPersistenceDataConnectionFactory.cs | 12 ++++++------ .../Journal/DAO/BaseByteArrayJournalDao.cs | 12 ++++++------ .../Journal/DAO/ByteArrayJournalSerializer.cs | 6 +++--- .../Journal/Types/JournalRow.cs | 8 +++----- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 14 +++++++------- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index 92383207..e4740510 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -167,30 +167,30 @@ void SetJoinCol(PropertyMappingBuilder builder, { builder.Member(r => r.JournalOrderingId) .IsNotColumn() - .Member(r => r.WriteUUID) + .Member(r => r.WriteUuid) .IsColumn().IsPrimaryKey(); - journalRowBuilder.Member(r => r.WriteUUID) + journalRowBuilder.Member(r => r.WriteUuid) .IsColumn(); } else { - builder.Member(r => r.WriteUUID) + builder.Member(r => r.WriteUuid) .IsNotColumn() .Member(r => r.JournalOrderingId) .IsColumn().IsPrimaryKey(); - journalRowBuilder.Member(r => r.WriteUUID) + journalRowBuilder.Member(r => r.WriteUuid) .IsNotColumn(); } } if (config.TableConfig.UseEventManifestColumn) { - journalRowBuilder.Member(r => r.eventManifest) + journalRowBuilder.Member(r => r.EventManifest) .IsColumn().HasLength(64); } else { - journalRowBuilder.Member(r => r.eventManifest) + journalRowBuilder.Member(r => r.EventManifest) .IsNotColumn(); } if ((config.TableConfig.TagWriteMode & TagWriteMode.TagTable) != 0) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs index cbd5e188..ec67f572 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs @@ -15,13 +15,13 @@ using Akka.Streams; using Akka.Streams.Dsl; using LanguageExt; -using LinqToDB; +using LinqToDB;using LinqToDB.Common; using LinqToDB.Data; using static LanguageExt.Prelude; namespace Akka.Persistence.Sql.Linq2Db.Journal.Dao { - public class SequentialUUIDGenerator + public class SequentialUuidGenerator { private long _counter = DateTime.UtcNow.Ticks; @@ -68,7 +68,7 @@ public abstract class BaseByteArrayJournalDao : protected readonly ILoggingAdapter Logger; private readonly Flow, long)>, NotUsed> _deserializeFlow; private readonly Flow, NotUsed> _deserializeFlowMapped; - private readonly SequentialUUIDGenerator _uuidGen; + private readonly SequentialUuidGenerator _uuidGen; protected BaseByteArrayJournalDao( IAdvancedScheduler scheduler, @@ -85,7 +85,7 @@ protected BaseByteArrayJournalDao( Serializer = serializer; _deserializeFlow = Serializer.DeserializeFlow(); _deserializeFlowMapped = Serializer.DeserializeFlow().Select(MessageWithBatchMapper()); - _uuidGen = new SequentialUUIDGenerator(); + _uuidGen = new SequentialUuidGenerator(); //Due to C# rules we have to initialize WriteQueue here //Keeping it here vs init function prevents accidental moving of init @@ -246,10 +246,10 @@ private async Task HandleTagTableUuidInsert(Seq xs) if (journalRow.TagArr?.Length > 0) { var uid = NextUuid(); - journalRow.WriteUUID = uid; + journalRow.WriteUuid = uid; foreach (var s1 in journalRow.TagArr) { - tagWrites.Add(new JournalTagRow { WriteUUID = uid, TagValue = s1 }); + tagWrites.Add(new JournalTagRow { WriteUuid = uid, TagValue = s1 }); } } } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs index 93b96362..f218f4a1 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs @@ -105,7 +105,7 @@ protected override Try Serialize( row.PersistenceId = representation.PersistenceId; row.Identifier = serializer.Identifier; row.SequenceNumber = representation.SequenceNr; - row.eventManifest = representation.Manifest; + row.EventManifest = representation.Manifest; return new Try(row); }); @@ -135,7 +135,7 @@ protected override Try Serialize( action: state => state.Item1.FromBinary(state.message, state.type)), sequenceNr: t.SequenceNumber, persistenceId: t.PersistenceId, - manifest: t.eventManifest ?? t.Manifest, + manifest: t.EventManifest ?? t.Manifest, isDeleted: t.Deleted, sender: ActorRefs.NoSender, writerGuid: null, @@ -152,7 +152,7 @@ protected override Try Serialize( payload: _serializer.Deserialize(t.Message, identifierMaybe.Value, t.Manifest), sequenceNr: t.SequenceNumber, persistenceId: t.PersistenceId, - manifest: t.eventManifest ?? t.Manifest, + manifest: t.EventManifest ?? t.Manifest, isDeleted: t.Deleted, sender: ActorRefs.NoSender, writerGuid: null, diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs index de3f82fa..69508efd 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/Types/JournalRow.cs @@ -1,6 +1,4 @@ using System; -using LinqToDB; -using LinqToDB.Mapping; namespace Akka.Persistence.Sql.Linq2Db.Journal.Types { @@ -10,7 +8,7 @@ public sealed class JournalTagRow public string TagValue { get; set; } - public Guid WriteUUID { get; set; } + public Guid WriteUuid { get; set; } } public sealed class JournalRow @@ -35,8 +33,8 @@ public sealed class JournalRow public string[] TagArr { get; set; } - public Guid? WriteUUID { get; set; } + public Guid? WriteUuid { get; set; } - public string eventManifest { get; set; } + public string EventManifest { get; set; } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index 5ab2b6a0..c5e52f27 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -128,7 +128,7 @@ private async Task AddTagDataFromTagTable(List toAdd, DataConnection foreach (var journalRow in toAdd) { journalRow.TagArr = tagRows - .Where(r => r.WriteUUID == journalRow.WriteUUID) + .Where(r => r.WriteUuid == journalRow.WriteUuid) .Select(r => r.TagValue).ToArray(); } } @@ -144,11 +144,11 @@ public Option>> TagCheckPredicate( //2: Don't wanna make a useless round trip to the DB, // if we know nothing is tagged. var set = toAdd - .Where(r => r.WriteUUID.HasValue) - .Select(r => r.WriteUUID.Value).ToList(); + .Where(r => r.WriteUuid.HasValue) + .Select(r => r.WriteUuid.Value).ToList(); return set.Count == 0 ? Option>>.None - : new Option>>(r => r.WriteUUID.In(set)); + : new Option>>(r => r.WriteUuid.In(set)); } //We can just check the count here. @@ -233,7 +233,7 @@ private Expression> EventsByTagOnlyJoinPre if (_readJournalConfig.TableConfig.TagTableMode == TagTableMode.OrderingId) return (jr, jtr) => jr.Ordering == jtr.JournalOrderingId; - return (jr, jtr) => jr.WriteUUID == jtr.WriteUUID; + return (jr, jtr) => jr.WriteUuid == jtr.WriteUuid; } } @@ -280,10 +280,10 @@ private Expression> EventsByTagMigrationPredicate(DataCon .Select(j => j.JournalOrderingId)) || r.Tags.Contains(tagVal); } - return r => r.WriteUUID.Value + return r => r.WriteUuid.Value .In(conn.GetTable() .Where(j => j.TagValue == tagVal) - .Select(j => j.WriteUUID)) + .Select(j => j.WriteUuid)) || r.Tags.Contains(tagVal); } From d4e2984abc1effcdd86b23bfed12976c37e0e704 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Wed, 23 Nov 2022 21:54:17 +0700 Subject: [PATCH 12/20] Fix merge issues --- .../Akka.Persistence.Linq2Db.IndexHelperLib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj b/src/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj index 49c19454..54627a03 100644 --- a/src/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj +++ b/src/Akka.Persistence.Linq2Db.IndexHelperLib/Akka.Persistence.Linq2Db.IndexHelperLib.csproj @@ -5,7 +5,7 @@ - + From 060da94c21cc8db2ca4d3a7ec06aa0ed8b56ba6a Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Wed, 23 Nov 2022 22:47:53 +0700 Subject: [PATCH 13/20] Fix table column name compatibility problem --- .../Config/JournalTableConfig.cs | 34 ++++++------------ .../AkkaPersistenceDataConnectionFactory.cs | 35 ++++++++++++------- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 2 +- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs index 25dda55b..861fe254 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs @@ -14,7 +14,7 @@ public enum TagWriteMode public enum TagTableMode { OrderingId, - SequentialUUID + SequentialUuid } public class JournalTableConfig @@ -29,7 +29,7 @@ public class JournalTableConfig public TagWriteMode TagWriteMode { get; } public TagTableMode TagTableMode { get; } - public string? TagTableName { get; } + public string TagTableName { get; } public bool UseEventManifestColumn { get; } public JournalTableConfig(Configuration.Config config) @@ -46,28 +46,16 @@ public JournalTableConfig(Configuration.Config config) AutoInitialize = localCfg.GetBoolean("auto-init", false); WarnOnAutoInitializeFail = localCfg.GetBoolean("warn-on-auto-init-fail", true); - var s = config.GetString("tag-write-mode", "default"); - if (Enum.TryParse(s, true, out TagWriteMode res)) + var s = config.GetString("tag-write-mode", "default").ToLowerInvariant(); + if (!Enum.TryParse(s, true, out TagWriteMode res)) { - - } - else if (s.Equals("default", StringComparison.InvariantCultureIgnoreCase)) - { - res = TagWriteMode.CommaSeparatedArray; - } - else if (s.Equals("migration", - StringComparison.InvariantCultureIgnoreCase)) - { - res = TagWriteMode.CommaSeparatedArrayAndTagTable; - } - else if (s.Equals("tagtableonly", - StringComparison.InvariantCultureIgnoreCase)) - { - res = TagWriteMode.TagTable; - } - else - { - res = TagWriteMode.CommaSeparatedArray; + res = s switch + { + "default" => TagWriteMode.CommaSeparatedArray, + "migration" => TagWriteMode.CommaSeparatedArrayAndTagTable, + "tagtableonly" => TagWriteMode.TagTable, + _ => TagWriteMode.CommaSeparatedArray + }; } TagWriteMode = res; } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index e4740510..54f38627 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -159,27 +159,21 @@ private static void MapJournalRow(IProviderConfig config, .Member(r=>r.SequenceNumber).IsPrimaryKey(); } - void SetJoinCol(PropertyMappingBuilder builder, - PropertyMappingBuilder propertyMappingBuilder) + void SetJoinCol(PropertyMappingBuilder builder) { - if (config.TableConfig.TagTableMode == - TagTableMode.SequentialUUID) + if (config.TableConfig.TagTableMode == TagTableMode.SequentialUuid) { builder.Member(r => r.JournalOrderingId) - .IsNotColumn() - .Member(r => r.WriteUuid) + .IsNotColumn(); + builder.Member(r => r.WriteUuid) .IsColumn().IsPrimaryKey(); - journalRowBuilder.Member(r => r.WriteUuid) - .IsColumn(); } else { builder.Member(r => r.WriteUuid) - .IsNotColumn() - .Member(r => r.JournalOrderingId) - .IsColumn().IsPrimaryKey(); - journalRowBuilder.Member(r => r.WriteUuid) .IsNotColumn(); + builder.Member(r => r.JournalOrderingId) + .IsColumn().IsPrimaryKey(); } } @@ -193,6 +187,7 @@ void SetJoinCol(PropertyMappingBuilder builder, journalRowBuilder.Member(r => r.EventManifest) .IsNotColumn(); } + if ((config.TableConfig.TagWriteMode & TagWriteMode.TagTable) != 0) { var tagTableBuilder = fmb.Entity() @@ -202,9 +197,23 @@ void SetJoinCol(PropertyMappingBuilder builder, .IsColumn().IsNullable(false) .HasLength(64) .IsPrimaryKey(); - SetJoinCol(tagTableBuilder, journalRowBuilder); + + SetJoinCol(tagTableBuilder); } + // column compatibility + if (config.TableConfig.TagTableMode == TagTableMode.SequentialUuid) + { + journalRowBuilder.Member(r => r.WriteUuid) + .IsColumn(); + } + else + { + journalRowBuilder.Member(r => r.WriteUuid) + .IsNotColumn(); + } + + //Probably overkill, but we only set Metadata Mapping if specified //That we are in delete compatibility mode. if (config.IDaoConfig.SqlCommonCompatibilityMode) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index c5e52f27..73ed18a1 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -137,7 +137,7 @@ private async Task AddTagDataFromTagTable(List toAdd, DataConnection public Option>> TagCheckPredicate( List toAdd) { - if (_readJournalConfig.PluginConfig.TagTableMode == TagTableMode.SequentialUUID) + if (_readJournalConfig.PluginConfig.TagTableMode == TagTableMode.SequentialUuid) { //Check whether we have anything to query for two reasons: //1: Linq2Db may choke on an empty 'in' set. From 5a4595b012f32b8da34fb782c2c23f3fec39b230 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Wed, 23 Nov 2022 22:58:44 +0700 Subject: [PATCH 14/20] Fix logical delete compatibility problem --- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index 73ed18a1..89b3ff6f 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -44,14 +44,16 @@ protected BaseByteReadArrayJournalDao( protected IQueryable BaseQuery(DataConnection connection) { - return connection.GetTable() - .Where(jr => _includeDeleted == false || jr.Deleted == false); + return _includeDeleted + ? connection.GetTable() + : connection.GetTable().Where(jr => jr.Deleted == false); } protected static IQueryable BaseQueryStatic(DataConnection connection, bool includeDeleted) { - return connection.GetTable() - .Where(jr => includeDeleted == false || jr.Deleted == false); + return includeDeleted + ? connection.GetTable() + : connection.GetTable().Where(jr => jr.Deleted == false); } public Source AllPersistenceIdsSource(long max) @@ -293,10 +295,10 @@ private Flow PerfectlyMatchTag( { //Do the tagArr check first here //Since the logic is simpler. - return Flow.Create().Where(r => - r.TagArr?.Contains(tag) ?? (r.Tags ?? "") - .Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries) - .Any(t => t.Contains(tag))); + return Flow.Create() + .Where(r => r.TagArr?.Contains(tag) ?? (r.Tags ?? "") + .Split( new[] { separator }, StringSplitOptions.RemoveEmptyEntries ) + .Any(t => t.Contains(tag))); } public override Source, NotUsed> Messages( From 095b0dd7e27ca3553e308355ccc3b4c77c8e4584 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 1 Dec 2022 07:09:58 +0700 Subject: [PATCH 15/20] post-merge cleanup --- .../Config/JournalTableConfig.cs | 14 ++--- .../Config/ReadJournalPluginConfig.cs | 13 ++--- .../AkkaPersistenceDataConnectionFactory.cs | 2 +- .../Journal/DAO/ByteArrayJournalSerializer.cs | 9 +--- .../Query/Dao/BaseByteReadArrayJournalDAO.cs | 51 ++++++++++--------- .../persistence.conf | 2 + 6 files changed, 37 insertions(+), 54 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs index 861fe254..ad2bd5c5 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs @@ -3,12 +3,10 @@ namespace Akka.Persistence.Sql.Linq2Db.Config { - [Flags] public enum TagWriteMode { - CommaSeparatedArray = 1, + Csv = 1, TagTable = 2, - CommaSeparatedArrayAndTagTable = 3, } public enum TagTableMode @@ -46,16 +44,10 @@ public JournalTableConfig(Configuration.Config config) AutoInitialize = localCfg.GetBoolean("auto-init", false); WarnOnAutoInitializeFail = localCfg.GetBoolean("warn-on-auto-init-fail", true); - var s = config.GetString("tag-write-mode", "default").ToLowerInvariant(); + var s = config.GetString("tag-write-mode", "csv").ToLowerInvariant(); if (!Enum.TryParse(s, true, out TagWriteMode res)) { - res = s switch - { - "default" => TagWriteMode.CommaSeparatedArray, - "migration" => TagWriteMode.CommaSeparatedArrayAndTagTable, - "tagtableonly" => TagWriteMode.TagTable, - _ => TagWriteMode.CommaSeparatedArray - }; + res = TagWriteMode.Csv; } TagWriteMode = res; } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs index cbb7ba5e..69726ffc 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/ReadJournalPluginConfig.cs @@ -9,15 +9,10 @@ public ReadJournalPluginConfig(Configuration.Config config) TagSeparator = config.GetString("tag-separator", ";"); Dao = config.GetString("dao", "Akka.Persistence.Sql.Linq2Db.Journal.Dao.ByteArrayJournalDao, Akka.Persistence.Sql.Linq2Db"); - var tagReadStr = config.GetString("tag-read-mode", "default").ToLowerInvariant(); + var tagReadStr = config.GetString("tag-read-mode", "csv").ToLowerInvariant(); if (!Enum.TryParse(tagReadStr,true,out var tgr)) { - tgr = tagReadStr switch - { - "default" => TagReadMode.CommaSeparatedArray, - "migrate" => TagReadMode.CommaSeparatedArrayAndTagTable, - _ => TagReadMode.CommaSeparatedArray - }; + tgr = TagReadMode.Csv; } TagReadMode = tgr; @@ -30,11 +25,9 @@ public ReadJournalPluginConfig(Configuration.Config config) public TagTableMode TagTableMode { get; } } - [Flags] public enum TagReadMode { - CommaSeparatedArray = 1, + Csv = 1, TagTable = 2, - CommaSeparatedArrayAndTagTable = 3 } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index 54f38627..9f64ac8b 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -139,7 +139,7 @@ private static void MapJournalRow(IProviderConfig config, //We can skip writing tags the old way by ignoring the column in mapping. journalRowBuilder.Member(r => r.TagArr).IsNotColumn(); - if ((tableConfig.TagWriteMode & TagWriteMode.CommaSeparatedArray) == 0) + if ((tableConfig.TagWriteMode & TagWriteMode.Csv) == 0) { journalRowBuilder.Member(r => r.Tags).IsNotColumn(); } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs index f218f4a1..f38a6957 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/ByteArrayJournalSerializer.cs @@ -45,7 +45,7 @@ private JournalRow CreateJournalRow(IImmutableSet tags, IPersistentRepre { return _tagWriteMode switch { - TagWriteMode.CommaSeparatedArray => new JournalRow + TagWriteMode.Csv => new JournalRow { Tags = StringSep(tags, _separator), Timestamp = representation.Timestamp == 0 ? ts : representation.Timestamp @@ -58,13 +58,6 @@ private JournalRow CreateJournalRow(IImmutableSet tags, IPersistentRepre Timestamp = representation.Timestamp == 0 ? ts : representation.Timestamp }, - TagWriteMode.CommaSeparatedArrayAndTagTable => new JournalRow() - { - Tags = StringSep(tags, _separator), - TagArr = tags.ToArray(), - Timestamp = representation.Timestamp == 0 ? ts : representation.Timestamp - }, - _ => throw new Exception($"Invalid Tag Write Mode! Was: {_tagWriteMode}") }; } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs index 707c3ba9..d9411df1 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Query/Dao/BaseByteReadArrayJournalDAO.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using System.Linq.Expressions; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Akka.Actor; using Akka.Persistence.Sql.Linq2Db.Config; @@ -58,6 +59,7 @@ public Source AllPersistenceIdsSource(long max) }); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int MaxTake(long max) { return max > int.MaxValue ? int.MaxValue : (int)max; @@ -76,11 +78,11 @@ private static int MaxTake(long max) { await using var conn = input._connectionFactory.GetConnection(); var events = await conn.GetTable() - .OrderBy(r => r.Ordering) .Where(r => r.Ordering > input.offset && r.Ordering <= input.maxOffset && r.Deleted == false) + .OrderBy(r => r.Ordering) .Take(input.maxTake).ToListAsync(); return await AddTagDataIfNeeded(events, conn); } @@ -89,7 +91,7 @@ private static int MaxTake(long max) public async Task> AddTagDataIfNeeded(List toAdd, DataConnection context) { - if ((_readJournalConfig.PluginConfig.TagReadMode & TagReadMode.TagTable) != 0) + if (_readJournalConfig.PluginConfig.TagReadMode == TagReadMode.TagTable) { await AddTagDataFromTagTable(toAdd, context); } @@ -122,8 +124,7 @@ private async Task AddTagDataFromTagTable(List toAdd, DataConnection } } - public Option>> TagCheckPredicate( - List toAdd) + public Option>> TagCheckPredicate(List toAdd) { if (_readJournalConfig.PluginConfig.TagTableMode == TagTableMode.SequentialUuid) { @@ -157,7 +158,7 @@ public Option>> TagCheckPredicate( var maxTake = MaxTake(max); switch (_readJournalConfig.PluginConfig.TagReadMode) { - case TagReadMode.CommaSeparatedArray: + case TagReadMode.Csv: return AsyncSource.FromEnumerable( new { separator, tag, offset, maxOffset, maxTake, ConnectionFactory }, async input => @@ -166,14 +167,15 @@ public Option>> TagCheckPredicate( await using var conn = input.ConnectionFactory.GetConnection(); return await conn.GetTable() - .Where(r => r.Tags.Contains(tagValue)) + .Where(r => + r.Tags.Contains(tagValue) + && !r.Deleted + && r.Ordering > input.offset + && r.Ordering <= input.maxOffset) .OrderBy(r => r.Ordering) - .Where(r => r.Ordering > input.offset && r.Ordering <= input.maxOffset) .Take(input.maxTake).ToListAsync(); }) .Via(_deserializeFlow); - case TagReadMode.CommaSeparatedArrayAndTagTable: - return EventByTagMigration(tag, offset, maxOffset, separator, maxTake); case TagReadMode.TagTable: return EventByTagTableOnly(tag, offset, maxOffset, separator, maxTake); default: @@ -203,7 +205,7 @@ public Option>> TagCheckPredicate( (jr, jtr) => new { jr, jtr }) .Where(r => r.jtr.TagValue == input.tag) .Select(r => r.jr) - .Where(r => r.Ordering > input.offset && r.Ordering <= input.maxOffset) + .Where(r => r.Ordering > input.offset && r.Ordering <= input.maxOffset && !r.Deleted) .Take(input.maxTake).ToListAsync(); await AddTagDataFromTagTable(mainRows, conn); return mainRows; @@ -244,9 +246,8 @@ private Expression> EventsByTagOnlyJoinPre // First, find the rows. // We use IN here instead of left join because it's safer from a // 'avoid duplicate rows tripping things up later' standpoint. - var tagValue = $"{separator}{input.tag}{separator}"; var mainRows = await conn.GetTable() - .Where(EventsByTagMigrationPredicate(conn, input.tag)) + .Where(EventsByTagMigrationPredicate(conn, input.tag, separator)) .OrderBy(r => r.Ordering) .Where(r => r.Ordering > input.offset && r.Ordering <= input.maxOffset && r.Deleted == false) .Take(input.maxTake).ToListAsync(); @@ -254,25 +255,27 @@ private Expression> EventsByTagOnlyJoinPre await AddTagDataFromTagTable(mainRows, conn); return mainRows; }) - .Via(PerfectlyMatchTag(tag, separator)) .Via(_deserializeFlow); } - private Expression> EventsByTagMigrationPredicate(DataConnection conn, string tagVal) + private Expression> EventsByTagMigrationPredicate(DataConnection conn, string tag, string separator) { + var tagValue = $"{separator}{tag}{separator}"; if (_readJournalConfig.TableConfig.TagTableMode == TagTableMode.OrderingId) { - return r => r.Ordering - .In(conn.GetTable() - .Where(j => j.TagValue == tagVal) - .Select(j => j.JournalOrderingId)) - || r.Tags.Contains(tagVal); + return r => + r.Tags.Contains(tagValue) || + r.Ordering.In( + conn.GetTable() + .Where(j => j.TagValue == tag) + .Select(j => j.JournalOrderingId)); } - return r => r.WriteUuid.Value - .In(conn.GetTable() - .Where(j => j.TagValue == tagVal) - .Select(j => j.WriteUuid)) - || r.Tags.Contains(tagVal); + return r => + r.Tags.Contains(tagValue) || + r.WriteUuid.Value.In( + conn.GetTable() + .Where(j => j.TagValue == tag) + .Select(j => j.WriteUuid)); } private Flow PerfectlyMatchTag( diff --git a/src/Akka.Persistence.Sql.Linq2Db/persistence.conf b/src/Akka.Persistence.Sql.Linq2Db/persistence.conf index 5fd71f74..070efbdd 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/persistence.conf +++ b/src/Akka.Persistence.Sql.Linq2Db/persistence.conf @@ -80,6 +80,8 @@ # You may wish to provide a dedicated dispatcher instead materializer-dispatcher = "akka.actor.default-dispatcher" + tag-write-mode = csv + tag-read-mode = csv tag-separator = ";" dao = "Akka.Persistence.Sql.Linq2Db.Journal.Dao.ByteArrayJournalDao, Akka.Persistence.Sql.Linq2Db" From 969116b6823932967e762484c7d4a3380fc7b751 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 1 Dec 2022 10:51:01 +0700 Subject: [PATCH 16/20] Convert unit tests to use TestKit --- .../DockerBatchingSqlServerJournalPerfSpec.cs | 4 +- .../DockerSqlServerJournalPerfSpec.cs | 4 +- .../DockerLinq2DbPostgreSQLJournalPerfSpec.cs | 2 +- .../DockerLinq2DbSqlServerJournalPerfSpec.cs | 8 +- ...e.Linq2Db.Compatibility.DockerTests.csproj | 16 --- ...greSqlSqlCommonJournalCompatibilitySpec.cs | 105 ---------------- ...reSqlSqlCommonSnapshotCompatibilitySpec.cs | 112 ------------------ .../Postgres/PostgreSQLSpecsFixture.cs | 11 -- ...ostgreSqlCommonJournalCompatibilitySpec.cs | 27 +++++ ...stgreSqlCommonSnapshotCompatibilitySpec.cs | 38 ++++++ .../PostgreSqlCompatibilitySpecConfig.cs | 88 ++++++++++++++ .../Postgres/PostgreSqlSpecsFixture.cs | 16 +++ .../SqlServerCompatibilitySpecConfig.cs | 12 +- .../SqlServer/SqlServerSpecsFixture.cs | 12 +- ...ServerSqlCommonJournalCompatibilitySpec.cs | 2 +- ...erverSqlCommonSnapshotCompatibilitySpec.cs | 5 +- ...istence.Linq2Db.Compatibility.Tests.csproj | 1 + .../JournalCompatActor.cs | 18 +-- .../SqlCommonJournalCompatibilitySpec.cs | 112 ++++++++---------- .../SqlCommonSnapshotCompatibilitySpec.cs | 92 +++++++------- .../Docker/PostgreDbUtils.cs | 74 ++++++++++++ .../Docker/PostgreSQLFixture.cs | 23 ++-- .../Docker/PostgreSQLSpecsFixture.cs | 9 -- .../Docker/PostgreSqlSpecsFixture.cs | 15 +++ .../{DockerDbUtils.cs => SqlServerDbUtils.cs} | 3 +- .../Docker/SqlServerSpecsFixture.cs | 8 +- .../Postgres/PostgreSQLJournalSpec.cs | 2 +- .../SQLServerJournalCustomConfigSpec.cs | 4 +- .../SQLServerJournalDefaultConfigSpec.cs | 4 +- .../SqlServer/SQLServerJournalSpec.cs | 4 +- .../SqlServer/SQLServerSnapshotSpec.cs | 4 +- .../DbUtils.cs | 64 ---------- src/common.props | 6 +- 33 files changed, 418 insertions(+), 487 deletions(-) delete mode 100644 src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonJournalCompatibilitySpec.cs delete mode 100644 src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonSnapshotCompatibilitySpec.cs delete mode 100644 src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSQLSpecsFixture.cs create mode 100644 src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonJournalCompatibilitySpec.cs create mode 100644 src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonSnapshotCompatibilitySpec.cs create mode 100644 src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCompatibilitySpecConfig.cs create mode 100644 src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlSpecsFixture.cs create mode 100644 src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreDbUtils.cs delete mode 100644 src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLSpecsFixture.cs create mode 100644 src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSqlSpecsFixture.cs rename src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/{DockerDbUtils.cs => SqlServerDbUtils.cs} (97%) delete mode 100644 src/Akka.Persistence.Sql.Linq2Db.Tests/DbUtils.cs diff --git a/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerBatchingSqlServerJournalPerfSpec.cs b/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerBatchingSqlServerJournalPerfSpec.cs index c463c695..6a704284 100644 --- a/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerBatchingSqlServerJournalPerfSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerBatchingSqlServerJournalPerfSpec.cs @@ -17,7 +17,7 @@ public DockerBatchingSqlServerJournalPerfSpec(ITestOutputHelper output, SqlServe public static Config InitConfig(SqlServerFixture fixture) { //need to make sure db is created before the tests start - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); var specString = $@" akka.persistence {{ publish-plugin-commands = on @@ -29,7 +29,7 @@ class = ""Akka.Persistence.SqlServer.Journal.BatchingSqlServerJournal, Akka.Pers table-name = EventJournal schema-name = dbo auto-initialize = on - connection-string = ""{DockerDbUtils.ConnectionString}"" + connection-string = ""{SqlServerDbUtils.ConnectionString}"" }} }} }}"; diff --git a/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerSqlServerJournalPerfSpec.cs b/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerSqlServerJournalPerfSpec.cs index 1f2bff44..aa054f33 100644 --- a/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerSqlServerJournalPerfSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Benchmark.DockerComparisonTests/SqlCommon/DockerSqlServerJournalPerfSpec.cs @@ -16,7 +16,7 @@ public DockerSqlServerJournalPerfSpec(ITestOutputHelper output, SqlServerFixture public static Config InitConfig(SqlServerFixture fixture) { //need to make sure db is created before the tests start - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); var specString = $@" akka.persistence {{ publish-plugin-commands = on @@ -28,7 +28,7 @@ class = ""Akka.Persistence.SqlServer.Journal.SqlServerJournal, Akka.Persistence. table-name = EventJournal schema-name = dbo auto-initialize = on - connection-string = ""{DockerDbUtils.ConnectionString}"" + connection-string = ""{SqlServerDbUtils.ConnectionString}"" }} }} }}"; diff --git a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs index 157db565..3587d5db 100644 --- a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs @@ -45,7 +45,7 @@ public DockerLinq2DbPostgreSqlJournalPerfSpec(ITestOutputHelper output, "postgresperf", output,40, eventsCount: TestConstants.DockerNumMessages) { var extension = Linq2DbPersistence.Get(Sys); - var config = Create(DockerDbUtils.ConnectionString) + var config = Create(SqlServerDbUtils.ConnectionString) .WithFallback(extension.DefaultConfig) .GetConfig("akka.persistence.journal.linq2db"); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(config)); diff --git a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs index 6318a7fa..34e48e00 100644 --- a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs @@ -48,7 +48,7 @@ public DockerLinq2DbSqlServerJournalPerfSpec(ITestOutputHelper output, "sqlserverperf", output,40, eventsCount: TestConstants.DockerNumMessages) { var extension = Linq2DbPersistence.Get(Sys); - var config = Create(DockerDbUtils.ConnectionString) + var config = Create(SqlServerDbUtils.ConnectionString) .WithFallback(extension.DefaultConfig) .GetConfig("akka.persistence.journal.linq2db"); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(config)); @@ -66,14 +66,14 @@ public DockerLinq2DbSqlServerJournalPerfSpec(ITestOutputHelper output, public static Config InitConfig(SqlServerFixture fixture) { //need to make sure db is created before the tests start - DbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); - return Create(DbUtils.ConnectionString); + return Create(SqlServerDbUtils.ConnectionString); } protected override void Dispose(bool disposing) { base.Dispose(disposing); - DbUtils.Clean(); + SqlServerDbUtils.Clean(); } [Fact] diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Akka.Persistence.Linq2Db.Compatibility.DockerTests.csproj b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Akka.Persistence.Linq2Db.Compatibility.DockerTests.csproj index c3e21ca1..1d0ec8ff 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Akka.Persistence.Linq2Db.Compatibility.DockerTests.csproj +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Akka.Persistence.Linq2Db.Compatibility.DockerTests.csproj @@ -2,13 +2,9 @@ - false - Akka.Persistence.Linq2Db.CompatibilityTests.Docker - netcoreapp3.1 - @@ -32,16 +28,4 @@ - - - - - - - - - - - - diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonJournalCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonJournalCompatibilitySpec.cs deleted file mode 100644 index c028fe6a..00000000 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonJournalCompatibilitySpec.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using Akka.Configuration; -using Akka.Persistence.Sql.Linq2Db.Config; -using Akka.Persistence.Sql.Linq2Db.Db; -using Akka.Persistence.Sql.Linq2Db.Journal; -using Akka.Persistence.Sql.Linq2Db.Journal.Types; -using Akka.Persistence.Sql.Linq2Db.Tests.Docker; -using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; -using LinqToDB; -using Xunit; -using Xunit.Abstractions; - -namespace Akka.Persistence.Linq2Db.CompatibilityTests.Docker.Postgres -{ - [Collection("PostgreSQLSpec")] - public class DockerLinq2DbPostgreSqlSqlCommonJournalCompatibilitySpec : SqlCommonJournalCompatibilitySpec - { - public static string _journalBaseConfig = @" - akka.persistence {{ - publish-plugin-commands = on - journal {{ - plugin = ""akka.persistence.journal.testspec"" - testspec {{ - class = ""{0}"" - #plugin-dispatcher = ""akka.actor.default-dispatcher"" - plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" - - connection-string = ""{1}"" -#connection-string = ""FullUri=file:test.db&cache=shared"" - provider-name = """ + LinqToDB.ProviderName.PostgreSQL95 + @""" - use-clone-connection = true - table-compatibility-mode = ""postgres"" - tables.journal {{ - auto-init = true - table-name = ""{2}"" - schema-name = ""public"" - metadata-table-name = ""{3}"" - }} - }} - postgresql {{ - class = ""Akka.Persistence.PostgreSql.Journal.PostgreSqlJournal, Akka.Persistence.PostgreSql"" - #plugin-dispatcher = ""akka.actor.default-dispatcher"" - plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" - table-name = ""{2}"" - metadata-table-name = ""{3}"" - schema-name = public - auto-initialize = on - connection-string = ""{1}"" - }} - }} - }} - "; - - public static Configuration.Config Create(string connString) - { - return ConfigurationFactory.ParseString( - string.Format(_journalBaseConfig, - typeof(Linq2DbWriteJournal).AssemblyQualifiedName, - connString,"event_journal","metadata")); - } - - protected override Configuration.Config Config { get; } - - protected override string OldJournal => - "akka.persistence.journal.postgresql"; - - protected override string NewJournal => - "akka.persistence.journal.testspec"; - - - public DockerLinq2DbPostgreSqlSqlCommonJournalCompatibilitySpec(ITestOutputHelper output, - PostgreSQLFixture fixture) : base( output) - { - //DebuggingHelpers.SetupTraceDump(output); - Config = InitConfig(fixture); - var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(Create(DockerDbUtils.ConnectionString).GetConfig("akka.persistence.journal.testspec"))); - using (var conn = connFactory.GetConnection()) - { - try - { - conn.GetTable().Delete(); - } - catch (Exception e) - { - } - - } - } - - public static Configuration.Config InitConfig(PostgreSQLFixture fixture) - { - //need to make sure db is created before the tests start - //DbUtils.Initialize(fixture.ConnectionString); - - - return Create(fixture.ConnectionString); - } - protected void Dispose(bool disposing) - { - //base.Dispose(disposing); -// DbUtils.Clean(); - } - - } -} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonSnapshotCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonSnapshotCompatibilitySpec.cs deleted file mode 100644 index ab1cda4e..00000000 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/DockerLinq2DbPostgreSqlSqlCommonSnapshotCompatibilitySpec.cs +++ /dev/null @@ -1,112 +0,0 @@ -// //----------------------------------------------------------------------- -// // -// // Copyright (C) 2009-2020 Lightbend Inc. -// // Copyright (C) 2013-2020 .NET Foundation -// // -// //----------------------------------------------------------------------- - -using System; -using Akka.Configuration; -using Akka.Persistence.PostgreSql.Snapshot; -using Akka.Persistence.Sql.Linq2Db.Config; -using Akka.Persistence.Sql.Linq2Db.Db; -using Akka.Persistence.Sql.Linq2Db.Snapshot; -using Akka.Persistence.Sql.Linq2Db.Tests.Docker; -using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; -using LinqToDB; -using Xunit; -using Xunit.Abstractions; - -namespace Akka.Persistence.Linq2Db.CompatibilityTests.Docker.Postgres -{ - [Collection("PostgreSQLSpec")] - public class DockerLinq2DbPostgreSqlSqlCommonSnapshotCompatibilitySpec : SqlCommonSnapshotCompatibilitySpec - { - public static string _snapshotBaseConfig = @" - akka.persistence {{ - publish-plugin-commands = on - snapshot-store {{ - plugin = ""akka.persistence.snapshot-store.testspec"" - testspec {{ - class = ""{0}"" - #plugin-dispatcher = ""akka.actor.default-dispatcher"" - plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" - - connection-string = ""{1}"" -#connection-string = ""FullUri=file:test.db&cache=shared"" - provider-name = """ + LinqToDB.ProviderName.PostgreSQL95 + @""" - use-clone-connection = true - table-compatibility-mode = ""postgres"" - tables.snapshot {{ - auto-init = true - table-name = ""{2}"" - schema-name = ""public"" - metadata-table-name = ""{3}"" - }} - }} - postgresql {{ - class = """+typeof(PostgreSqlSnapshotStore).AssemblyQualifiedName +@""" - #plugin-dispatcher = ""akka.actor.default-dispatcher"" - plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" - table-name = ""{2}"" - metadata-table-name = ""{3}"" - schema-name = public - auto-initialize = on - connection-string = ""{1}"" - }} - }} - }} - "; - - public static Configuration.Config Create(string connString) - { - return ConfigurationFactory.ParseString( - string.Format(_snapshotBaseConfig, - typeof(Linq2DbSnapshotStore).AssemblyQualifiedName, - connString,"snapshot_compat","metadata")); - } - - protected override Configuration.Config Config { get; } - - protected override string OldSnapshot => - "akka.persistence.snapshot-store.postgresql"; - - protected override string NewSnapshot => - "akka.persistence.snapshot-store.testspec"; - - - public DockerLinq2DbPostgreSqlSqlCommonSnapshotCompatibilitySpec(ITestOutputHelper output, - PostgreSQLFixture fixture) : base( output) - { - //DebuggingHelpers.SetupTraceDump(output); - Config = InitConfig(fixture); - var connFactory = new AkkaPersistenceDataConnectionFactory(new SnapshotConfig(Create(DockerDbUtils.ConnectionString).GetConfig("akka.persistence.snapshot-store.testspec"))); - using (var conn = connFactory.GetConnection()) - { - try - { - conn.GetTable().Delete(); - } - catch (Exception e) - { - } - - } - } - - public static Configuration.Config InitConfig(PostgreSQLFixture fixture) - { - //need to make sure db is created before the tests start - //DbUtils.Initialize(fixture.ConnectionString); - - - return Create(fixture.ConnectionString); - } - protected void Dispose(bool disposing) - { - //base.Dispose(disposing); -// DbUtils.Clean(); - } - - } -} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSQLSpecsFixture.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSQLSpecsFixture.cs deleted file mode 100644 index 480958ba..00000000 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSQLSpecsFixture.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Akka.Persistence.Sql.Linq2Db.Tests.Docker; -using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; -using Xunit; - -namespace Akka.Persistence.Linq2Db.CompatibilityTests.Docker.Postgres -{ - [CollectionDefinition("PostgreSQLSpec")] - public sealed class PostgreSQLSpecsFixture : ICollectionFixture - { - } -} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonJournalCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonJournalCompatibilitySpec.cs new file mode 100644 index 00000000..872a32dc --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonJournalCompatibilitySpec.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using Akka.Configuration; +using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Persistence.Linq2Db.CompatibilityTests.Docker.Postgres +{ + [Collection("PostgreSqlSpec")] + public class PostgreSqlCommonJournalCompatibilitySpec : SqlCommonJournalCompatibilitySpec + { + protected override Config Config { get; } + + protected override string OldJournal => + "akka.persistence.journal.postgresql"; + + protected override string NewJournal => + "akka.persistence.journal.linq2db"; + + public PostgreSqlCommonJournalCompatibilitySpec(ITestOutputHelper output, PostgreSqlFixture fixture) + : base( output) + { + PostgreDbUtils.Initialize(fixture); + Config = PostgreSqlCompatibilitySpecConfig.InitJournalConfig("event_journal", "metadata"); + } + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonSnapshotCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonSnapshotCompatibilitySpec.cs new file mode 100644 index 00000000..d57ad440 --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCommonSnapshotCompatibilitySpec.cs @@ -0,0 +1,38 @@ +// //----------------------------------------------------------------------- +// // +// // Copyright (C) 2009-2020 Lightbend Inc. +// // Copyright (C) 2013-2020 .NET Foundation +// // +// //----------------------------------------------------------------------- + +using System; +using Akka.Configuration; +using Akka.Persistence.PostgreSql.Snapshot; +using Akka.Persistence.Sql.Linq2Db.Config; +using Akka.Persistence.Sql.Linq2Db.Db; +using Akka.Persistence.Sql.Linq2Db.Snapshot; +using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; +using LinqToDB; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Persistence.Linq2Db.CompatibilityTests.Docker.Postgres +{ + [Collection("PostgreSqlSpec")] + public class PostgreSqlCommonSnapshotCompatibilitySpec : SqlCommonSnapshotCompatibilitySpec + { + protected override Config Config => PostgreSqlCompatibilitySpecConfig.InitSnapshotConfig("snapshot_store"); + + protected override string OldSnapshot => + "akka.persistence.snapshot-store.postgresql"; + + protected override string NewSnapshot => + "akka.persistence.snapshot-store.linq2db"; + + public PostgreSqlCommonSnapshotCompatibilitySpec(ITestOutputHelper output, PostgreSqlFixture fixture) + : base( output) + { + PostgreDbUtils.Initialize(fixture); + } + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCompatibilitySpecConfig.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCompatibilitySpecConfig.cs new file mode 100644 index 00000000..afad9b3d --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlCompatibilitySpecConfig.cs @@ -0,0 +1,88 @@ +using Akka.Configuration; +using Akka.Persistence.PostgreSql; +using Akka.Persistence.PostgreSql.Journal; +using Akka.Persistence.PostgreSql.Snapshot; +using Akka.Persistence.Sql.Linq2Db; +using Akka.Persistence.Sql.Linq2Db.Journal; +using Akka.Persistence.Sql.Linq2Db.Snapshot; +using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; + +namespace Akka.Persistence.Linq2Db.CompatibilityTests.Docker.Postgres +{ + public class PostgreSqlCompatibilitySpecConfig + { + public static Config InitSnapshotConfig(string tableName) + { + var specString = $@" +akka.persistence {{ + publish-plugin-commands = on + snapshot-store {{ + postgresql {{ + class = ""{typeof(PostgreSqlSnapshotStore).AssemblyQualifiedName}"" + plugin-dispatcher = ""akka.actor.default-dispatcher"" + connection-string = ""{PostgreDbUtils.ConnectionString}"" + connection-timeout = 30s + schema-name = public + table-name = {tableName} + auto-initialize = on + sequential-access = off + }} + + linq2db {{ + class = ""{typeof(Linq2DbSnapshotStore).AssemblyQualifiedName}"" + plugin-dispatcher = ""akka.actor.default-dispatcher"" + connection-string = ""{PostgreDbUtils.ConnectionString}"" + provider-name = {LinqToDB.ProviderName.PostgreSQL95} + table-compatibility-mode = postgres + tables {{ + snapshot {{ + auto-init = true + warn-on-auto-init-fail = false + table-name = {tableName} + }} + }} + }} + }} +}}"; + + return ConfigurationFactory.ParseString(specString); + } + + public static Config InitJournalConfig(string tableName, string metadataTableName) + { + var specString = $@" +akka.persistence {{ + publish-plugin-commands = on + journal {{ + postgresql {{ + class = ""Akka.Persistence.PostgreSql.Journal.PostgreSqlJournal, Akka.Persistence.PostgreSql"" + plugin-dispatcher = ""akka.actor.default-dispatcher"" + connection-string = ""{PostgreDbUtils.ConnectionString}"" + connection-timeout = 30s + schema-name = public + table-name = ""{tableName}"" + metadata-table-name = ""{metadataTableName}"" + auto-initialize = on + }} + + linq2db {{ + class = ""{typeof(Linq2DbWriteJournal).AssemblyQualifiedName}"" + plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" + connection-string = ""{PostgreDbUtils.ConnectionString}"" + provider-name = ""{LinqToDB.ProviderName.PostgreSQL95}"" + parallelism = 3 + table-compatibility-mode = postgres + tables.journal {{ + auto-init = true + warn-on-auto-init-fail = false + table-name = ""{tableName}"" + metadata-table-name = ""{metadataTableName}"" + }} + }} + }} +}}"; + + return ConfigurationFactory.ParseString(specString); + } + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlSpecsFixture.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlSpecsFixture.cs new file mode 100644 index 00000000..faab8dbd --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/Postgres/PostgreSqlSpecsFixture.cs @@ -0,0 +1,16 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; +using Xunit; + +namespace Akka.Persistence.Linq2Db.CompatibilityTests.Docker.Postgres +{ + [CollectionDefinition("PostgreSqlSpec")] + public sealed class PostgreSqlSpecsFixture : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerCompatibilitySpecConfig.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerCompatibilitySpecConfig.cs index 5e77fdbf..f15bc7af 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerCompatibilitySpecConfig.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerCompatibilitySpecConfig.cs @@ -11,7 +11,6 @@ public class SqlServerCompatibilitySpecConfig { public static Config InitSnapshotConfig(string tableName) { - DbUtils.ConnectionString = DockerDbUtils.ConnectionString; var specString = $@" akka.persistence {{ publish-plugin-commands = on @@ -19,7 +18,7 @@ public static Config InitSnapshotConfig(string tableName) sql-server {{ class = ""Akka.Persistence.SqlServer.Snapshot.SqlServerSnapshotStore, Akka.Persistence.SqlServer"" plugin-dispatcher = ""akka.actor.default-dispatcher"" - connection-string = ""{DbUtils.ConnectionString}"" + connection-string = ""{SqlServerDbUtils.ConnectionString}"" connection-timeout = 30s schema-name = dbo table-name = ""{tableName}"" @@ -32,7 +31,7 @@ class = ""Akka.Persistence.SqlServer.Snapshot.SqlServerSnapshotStore, Akka.Persi class = ""{typeof(Linq2DbSnapshotStore).AssemblyQualifiedName}"" plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" #plugin-dispatcher = ""akka.actor.default-dispatcher"" - connection-string = ""{DbUtils.ConnectionString}"" + connection-string = ""{SqlServerDbUtils.ConnectionString}"" provider-name = """ + LinqToDB.ProviderName.SqlServer2017 + $@""" table-compatibility-mode = sqlserver tables {{ @@ -51,13 +50,10 @@ class = ""{typeof(Linq2DbSnapshotStore).AssemblyQualifiedName}"" public static Config InitJournalConfig(string tableName, string metadataTableName) { - DbUtils.ConnectionString = DockerDbUtils.ConnectionString; var specString = $@" akka.persistence {{ publish-plugin-commands = on journal {{ - plugin = ""akka.persistence.journal.sql-server"" - sql-server {{ class = ""Akka.Persistence.SqlServer.Journal.SqlServerJournal, Akka.Persistence.SqlServer"" plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" @@ -65,13 +61,13 @@ class = ""Akka.Persistence.SqlServer.Journal.SqlServerJournal, Akka.Persistence. metadata-table-name = ""{metadataTableName}"" schema-name = dbo auto-initialize = on - connection-string = ""{DbUtils.ConnectionString}"" + connection-string = ""{SqlServerDbUtils.ConnectionString}"" }} linq2db {{ class = ""{typeof(Linq2DbWriteJournal).AssemblyQualifiedName}"" plugin-dispatcher = ""akka.persistence.dispatchers.default-plugin-dispatcher"" - connection-string = ""{DbUtils.ConnectionString}"" + connection-string = ""{SqlServerDbUtils.ConnectionString}"" provider-name = ""{LinqToDB.ProviderName.SqlServer2017}"" parallelism = 3 table-compatibility-mode = sqlserver diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSpecsFixture.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSpecsFixture.cs index 18d31a3a..ae7f1165 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSpecsFixture.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSpecsFixture.cs @@ -1,11 +1,9 @@ -// //----------------------------------------------------------------------- -// // -// // Copyright (C) 2009-2020 Lightbend Inc. -// // Copyright (C) 2013-2020 .NET Foundation -// // -// //----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- -using Akka.Persistence.Sql.Linq2Db.Tests.Docker; using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; using Xunit; diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonJournalCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonJournalCompatibilitySpec.cs index 53b7b7c5..6e2fec77 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonJournalCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonJournalCompatibilitySpec.cs @@ -11,7 +11,7 @@ public class SqlServerSqlCommonJournalCompatibilitySpec : SqlCommonJournalCompat public SqlServerSqlCommonJournalCompatibilitySpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(outputHelper) { - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); } protected override string OldJournal => diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonSnapshotCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonSnapshotCompatibilitySpec.cs index 81fe1c87..1afcaf2a 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonSnapshotCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.DockerTests/SqlServer/SqlServerSqlCommonSnapshotCompatibilitySpec.cs @@ -1,5 +1,4 @@ -using Akka.Persistence.Sql.Linq2Db.Tests.Docker; -using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; +using Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker; using Xunit; using Xunit.Abstractions; @@ -11,7 +10,7 @@ public class SqlServerSqlCommonSnapshotCompatibilitySpec : SqlCommonSnapshotComp public SqlServerSqlCommonSnapshotCompatibilitySpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(outputHelper) { - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); } protected override string OldSnapshot => diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Akka.Persistence.Linq2Db.Compatibility.Tests.csproj b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Akka.Persistence.Linq2Db.Compatibility.Tests.csproj index c5a06536..f212deb6 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Akka.Persistence.Linq2Db.Compatibility.Tests.csproj +++ b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Akka.Persistence.Linq2Db.Compatibility.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/JournalCompatActor.cs b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/JournalCompatActor.cs index 43e8ecae..e18058fa 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/JournalCompatActor.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/JournalCompatActor.cs @@ -20,8 +20,8 @@ public DeleteUpToSequenceNumber(long nr) } public class JournalCompatActor : ReceivePersistentActor { - private List events = new List(); - private IActorRef deleteSubscriber; + private readonly List _events = new List(); + private IActorRef _deleteSubscriber; public JournalCompatActor(string journal, string persistenceId) { JournalPluginId = journal; @@ -31,22 +31,22 @@ public JournalCompatActor(string journal, string persistenceId) var sender = Sender; Persist(se, p => { - events.Add(p); + _events.Add(p); sender.Tell(se); }); }); - Command(ce=>Context.Sender.Tell(events.Any(e=>e.Guid==ce.Guid))); + Command(ce=>Context.Sender.Tell(_events.Any(e=>e.Guid==ce.Guid))); Command(gsn => Context.Sender.Tell( - new CurrentSequenceNr(this.LastSequenceNr))); + new CurrentSequenceNr(LastSequenceNr))); Command(dc => { - deleteSubscriber = Context.Sender; + _deleteSubscriber = Context.Sender; DeleteMessages(dc.Number); }); - Command(dms => deleteSubscriber?.Tell(dms)); - Command(dmf=>deleteSubscriber?.Tell(dmf)); - Recover(se => events.Add(se)); + Command(dms => _deleteSubscriber?.Tell(dms)); + Command(dmf=>_deleteSubscriber?.Tell(dmf)); + Recover(se => _events.Add(se)); } public override string PersistenceId { get; } } diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs index 1ff26fff..133c8e00 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Akka.Actor; using Akka.Event; -using Akka.TestKit.Xunit2.Internals; +using Akka.TestKit; using FluentAssertions; using LanguageExt.UnitsOfMeasure; using Xunit; @@ -10,23 +10,17 @@ namespace Akka.Persistence.Linq2Db.CompatibilityTests { - public abstract class SqlCommonJournalCompatibilitySpec + public abstract class SqlCommonJournalCompatibilitySpec: IAsyncLifetime { + private Akka.TestKit.Xunit2.TestKit _testKit; + private ActorSystem _sys; + private TestProbe _probe; + private ILoggingAdapter _log; + private readonly ITestOutputHelper _helper; + public SqlCommonJournalCompatibilitySpec(ITestOutputHelper outputHelper) { - Output = outputHelper; - } - - public ITestOutputHelper Output { get; } - - protected void InitializeLogger(ActorSystem system) - { - if (Output != null) - { - var extSystem = (ExtendedActorSystem)system; - var logger = extSystem.SystemActorOf(Props.Create(() => new TestOutputLogger(Output)), "log-test"); - logger.Tell(new InitializeLogger(system.EventStream)); - } + _helper = outputHelper; } protected abstract Configuration.Config Config { get; } @@ -34,36 +28,43 @@ protected void InitializeLogger(ActorSystem system) protected abstract string OldJournal { get; } protected abstract string NewJournal { get; } + public Task InitializeAsync() + { + _testKit = new Akka.TestKit.Xunit2.TestKit(Config, nameof(SqlCommonJournalCompatibilitySpec), _helper); + _sys = _testKit.Sys; + _probe = _testKit.CreateTestProbe(); + _log = _testKit.Log; + return Task.CompletedTask; + } + + public Task DisposeAsync() + { + return Task.CompletedTask; + } + [Fact] public async Task Can_Recover_SqlCommon_Journal() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-1")), "test-recover-1"); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; (await persistRef.Ask(someEvent, 5.Seconds())).Should().Be(someEvent); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); + + EnsureTerminated(persistRef); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-1")), "test-recover-1"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); } + [Fact] public async Task Can_Persist_SqlCommon_Journal() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-2")), "test-persist-1"); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; @@ -71,11 +72,9 @@ public async Task Can_Persist_SqlCommon_Journal() (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-2")), "test-persist-1"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -90,10 +89,7 @@ public async Task Can_Persist_SqlCommon_Journal() [Fact] public async Task SqlCommon_Journal_Can_Recover_L2Db_Journal() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-3")), "test-recover-2"); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; @@ -101,11 +97,9 @@ public async Task SqlCommon_Journal_Can_Recover_L2Db_Journal() (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-3")), "test-recover-2"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -113,10 +107,7 @@ public async Task SqlCommon_Journal_Can_Recover_L2Db_Journal() [Fact] public async Task SqlCommon_Journal_Can_Persist_L2db_Journal() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-4")), "test-persist-2"); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; @@ -124,11 +115,9 @@ public async Task SqlCommon_Journal_Can_Persist_L2db_Journal() (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-4")), "test-persist-2"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -143,11 +132,8 @@ public async Task SqlCommon_Journal_Can_Persist_L2db_Journal() [Fact] public async Task L2db_Journal_Delete_Compat_mode_Preserves_proper_SequenceNr() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - const string persistenceId = "d-1"; - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, persistenceId)), "test-compat-delete-seqno"); var ourGuid1 = Guid.NewGuid(); @@ -173,23 +159,29 @@ public async Task L2db_Journal_Delete_Compat_mode_Preserves_proper_SequenceNr() var delResult = await persistRef.Ask(new DeleteUpToSequenceNumber(currentSequenceNr.SequenceNumber)); delResult.Should().BeOfType(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, persistenceId)), "test-compat-delete-seqno"); var reincaranatedSequenceNrNewJournal = await persistRef.Ask(new GetSequenceNr(),TimeSpan.FromSeconds(5)); - Output.WriteLine($"oldSeq : {currentSequenceNr.SequenceNumber} - newSeq : {reincaranatedSequenceNrNewJournal.SequenceNumber}"); + _log.Info($"oldSeq : {currentSequenceNr.SequenceNumber} - newSeq : {reincaranatedSequenceNrNewJournal.SequenceNumber}"); reincaranatedSequenceNrNewJournal.SequenceNumber.Should().Be(currentSequenceNr.SequenceNumber); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, persistenceId)), "test-compat-delete-seqno"); var reincaranatedSequenceNr = await persistRef.Ask(new GetSequenceNr(),TimeSpan.FromSeconds(5)); - Output.WriteLine($"oldSeq : {currentSequenceNr.SequenceNumber} - newSeq : {reincaranatedSequenceNr.SequenceNumber}"); + _log.Info($"oldSeq : {currentSequenceNr.SequenceNumber} - newSeq : {reincaranatedSequenceNr.SequenceNumber}"); reincaranatedSequenceNr.SequenceNumber.Should().Be(currentSequenceNr.SequenceNumber); } + + private void EnsureTerminated(IActorRef actorRef) + { + _probe.Watch(actorRef); + actorRef.Tell(PoisonPill.Instance); + _probe.ExpectTerminated(actorRef); + _probe.Unwatch(actorRef); + } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs index 42235fc6..66b805e8 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Akka.Actor; using Akka.Event; -using Akka.TestKit.Xunit2.Internals; +using Akka.TestKit; using FluentAssertions; using LanguageExt.UnitsOfMeasure; using Xunit; @@ -10,35 +10,41 @@ namespace Akka.Persistence.Linq2Db.CompatibilityTests { - public abstract class SqlCommonSnapshotCompatibilitySpec + public abstract class SqlCommonSnapshotCompatibilitySpec: IAsyncLifetime { + private Akka.TestKit.Xunit2.TestKit _testKit; + private ActorSystem _sys; + private TestProbe _probe; + private ILoggingAdapter _log; + private readonly ITestOutputHelper _helper; + protected abstract Configuration.Config Config { get; } - public SqlCommonSnapshotCompatibilitySpec(ITestOutputHelper outputHelper) + public SqlCommonSnapshotCompatibilitySpec(ITestOutputHelper helper) { - Output = outputHelper; + _helper = helper; } - protected void InitializeLogger(ActorSystem system) - { - if (Output != null) - { - var extSystem = (ExtendedActorSystem)system; - var logger = extSystem.SystemActorOf(Props.Create(() => new TestOutputLogger(Output)), "log-test"); - logger.Tell(new InitializeLogger(system.EventStream)); - } - } - - public ITestOutputHelper Output { get; } protected abstract string OldSnapshot { get; } protected abstract string NewSnapshot { get; } + public Task InitializeAsync() + { + _testKit = new Akka.TestKit.Xunit2.TestKit(Config, nameof(SqlCommonJournalCompatibilitySpec), _helper); + _sys = _testKit.Sys; + _probe = _testKit.CreateTestProbe(); + _log = _testKit.Log; + return Task.CompletedTask; + } + + public Task DisposeAsync() + { + return Task.CompletedTask; + } + [Fact] public async Task Can_Recover_SqlCommon_Snapshot() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-1")), "test-snap-recover-1"); var ourGuid = Guid.NewGuid(); @@ -47,22 +53,18 @@ public async Task Can_Recover_SqlCommon_Snapshot() (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-1")), "test-snap-recover-1"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); } + [Fact] public async Task Can_Persist_SqlCommon_Snapshot() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-2")), "test-snap-persist-1"); var ourGuid = Guid.NewGuid(); @@ -71,11 +73,9 @@ public async Task Can_Persist_SqlCommon_Snapshot() (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-2")), "test-snap-persist-1"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -90,10 +90,7 @@ public async Task Can_Persist_SqlCommon_Snapshot() [Fact] public async Task SqlCommon_Snapshot_Can_Recover_L2Db_Snapshot() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-3")), "test-snap-recover-2"); var ourGuid = Guid.NewGuid(); @@ -102,11 +99,9 @@ public async Task SqlCommon_Snapshot_Can_Recover_L2Db_Snapshot() (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-3")), "test-snap-recover-2"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -115,10 +110,7 @@ public async Task SqlCommon_Snapshot_Can_Recover_L2Db_Snapshot() [Fact] public async Task SqlCommon_Snapshot_Can_Persist_L2db_Snapshot() { - var sys1 = ActorSystem.Create("first", Config); - InitializeLogger(sys1); - - var persistRef = sys1.ActorOf(Props.Create(() => + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-4")), "test-snap-persist-2"); var ourGuid = Guid.NewGuid(); @@ -127,11 +119,9 @@ public async Task SqlCommon_Snapshot_Can_Persist_L2db_Snapshot() (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); - // Intentionally being called twice to make sure that the actor state inside the user guardian has been cleared - (await persistRef.GracefulStop(10.Seconds())).Should().BeTrue(); + EnsureTerminated(persistRef); - persistRef = sys1.ActorOf(Props.Create(() => + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-4")), "test-snap-persist-2"); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(10))) .Should().BeTrue(); @@ -142,5 +132,13 @@ public async Task SqlCommon_Snapshot_Can_Persist_L2db_Snapshot() (await persistRef.Ask(new ContainsEvent { Guid = ourSecondGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); } + + private void EnsureTerminated(IActorRef actorRef) + { + _probe.Watch(actorRef); + actorRef.Tell(PoisonPill.Instance); + _probe.ExpectTerminated(actorRef); + _probe.Unwatch(actorRef); + } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreDbUtils.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreDbUtils.cs new file mode 100644 index 00000000..ef15df03 --- /dev/null +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreDbUtils.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2016 Lightbend Inc. +// Copyright (C) 2013-2016 Akka.NET project +// +//----------------------------------------------------------------------- + +using System; +using Npgsql; + +namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker +{ + public static class PostgreDbUtils + { + public static string ConnectionString { get; private set; } + + public static void Initialize(PostgreSqlFixture fixture) + { + ConnectionString = fixture.ConnectionString; + var connectionBuilder = new NpgsqlConnectionStringBuilder(ConnectionString); + + //connect to postgres database to create a new database + var databaseName = connectionBuilder.Database; + + using var conn = new NpgsqlConnection(ConnectionString); + conn.Open(); + + bool dbExists; + using (var cmd = new NpgsqlCommand()) + { + cmd.CommandText = $@"SELECT TRUE FROM pg_database WHERE datname='{databaseName}'"; + cmd.Connection = conn; + + var result = cmd.ExecuteScalar(); + dbExists = result != null && Convert.ToBoolean(result); + } + + if (dbExists) + { + DoClean(conn); + } + else + { + DoCreate(conn, databaseName); + } + } + + public static void Clean() + { + using var conn = new NpgsqlConnection(ConnectionString); + conn.Open(); + DoClean(conn); + } + + private static void DoCreate(NpgsqlConnection conn, string databaseName) + { + using var cmd = new NpgsqlCommand(); + cmd.CommandText = $@"CREATE DATABASE {databaseName}"; + cmd.Connection = conn; + cmd.ExecuteNonQuery(); + } + + private static void DoClean(NpgsqlConnection conn) + { + using var cmd = new NpgsqlCommand(); + cmd.CommandText = @" + DROP TABLE IF EXISTS public.event_journal; + DROP TABLE IF EXISTS public.snapshot_store; + DROP TABLE IF EXISTS public.metadata;"; + cmd.Connection = conn; + cmd.ExecuteNonQuery(); + } + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLFixture.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLFixture.cs index 124c1cf5..c455c95b 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLFixture.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLFixture.cs @@ -5,6 +5,7 @@ using Akka.Util; using Docker.DotNet; using Docker.DotNet.Models; +using Npgsql; using Xunit; namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker @@ -15,7 +16,6 @@ namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker public class PostgreSqlFixture : IAsyncLifetime { protected readonly string PostgresContainerName = $"postgresSqlServer-{Guid.NewGuid():N}"; - //protected readonly string PostgreSqlImageName = $"PostgreSQL-{Guid.NewGuid():N}"; protected readonly DockerClient Client; public PostgreSqlFixture() @@ -88,17 +88,18 @@ await Client.Containers.CreateContainerAsync(new CreateContainerParameters // Provide a 10 second startup delay await Task.Delay(TimeSpan.FromSeconds(10)); - ConnectionString = $"Server=127.0.0.1;Port={sqlServerHostPort};" + - "Database=postgres;User Id=postgres;Password=postgres"; + //ConnectionString = $"Server=127.0.0.1;Port={sqlServerHostPort};Database=postgres;User Id=postgres;Password=postgres"; - //var connectionString = new NpgsqlConnectionStringBuilder() - //{ - // Host = "localhost", Password = "l0lTh1sIsOpenSource", - // Username = "postgres", Database = "postgres", - // Port = sqlServerHostPort - //}; - // - //ConnectionString = connectionString.ToString(); + var connectionString = new NpgsqlConnectionStringBuilder() + { + Host = "localhost", + Password = "postgres", + Username = "postgres", + Database = "postgres", + Port = sqlServerHostPort + }; + + ConnectionString = connectionString.ToString(); } public async Task DisposeAsync() diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLSpecsFixture.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLSpecsFixture.cs deleted file mode 100644 index 7812712a..00000000 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSQLSpecsFixture.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Xunit; - -namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker -{ - [CollectionDefinition("PostgreSQLSpec")] - public sealed class PostgreSqlSpecsFixture : ICollectionFixture - { - } -} \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSqlSpecsFixture.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSqlSpecsFixture.cs new file mode 100644 index 00000000..7fadfab9 --- /dev/null +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/PostgreSqlSpecsFixture.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using Xunit; + +namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker +{ + [CollectionDefinition("PostgreSqlSpec")] + public sealed class PostgreSqlSpecsFixture : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/DockerDbUtils.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/SqlServerDbUtils.cs similarity index 97% rename from src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/DockerDbUtils.cs rename to src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/SqlServerDbUtils.cs index d302be3b..829ae5cb 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/DockerDbUtils.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/SqlServerDbUtils.cs @@ -2,7 +2,7 @@ namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker { - public static class DockerDbUtils + public static class SqlServerDbUtils { public static string ConnectionString { get; private set; } @@ -25,7 +25,6 @@ IF db_id('{0}') IS NULL BEGIN CREATE DATABASE {0} END - ", databaseName); cmd.Connection = conn; diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/SqlServerSpecsFixture.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/SqlServerSpecsFixture.cs index 9a11b7a1..90a327bd 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/SqlServerSpecsFixture.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Docker/SqlServerSpecsFixture.cs @@ -1,4 +1,10 @@ -using Xunit; +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using Xunit; namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Docker { diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs index 5d05c1cf..775f7a24 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs @@ -95,7 +95,7 @@ public DockerLinq2DbPostgreSqlJournalSpec(ITestOutputHelper output, PostgreSqlFi : base(InitConfig(fixture), "postgresperf", output) { var extension = Linq2DbPersistence.Get(Sys); - var config = Create(DockerDbUtils.ConnectionString) + var config = Create(SqlServerDbUtils.ConnectionString) .WithFallback(extension.DefaultConfig) .GetConfig("akka.persistence.journal.linq2db"); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(config)); diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs index 37bb9afb..e7ce68ba 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs @@ -15,7 +15,7 @@ public class SqlServerJournalCustomConfigSpec : JournalSpec { public static Configuration.Config Initialize(SqlServerFixture fixture) { - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); return Configuration; } @@ -24,7 +24,7 @@ public static Configuration.Config Initialize(SqlServerFixture fixture) "customSpec", "customJournalSpec", "customJournalMetadata", - ProviderName.SqlServer2017, DockerDbUtils.ConnectionString, true); + ProviderName.SqlServer2017, SqlServerDbUtils.ConnectionString, true); public SqlServerJournalCustomConfigSpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(Initialize(fixture), "SQLServer-custom", outputHelper) diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs index b52aef80..c4f1cc07 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs @@ -15,7 +15,7 @@ public class SqlServerJournalDefaultConfigSpec : JournalSpec { public static Configuration.Config Initialize(SqlServerFixture fixture) { - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); return Configuration; } @@ -24,7 +24,7 @@ public static Configuration.Config Initialize(SqlServerFixture fixture) "defaultJournalSpec", "defaultJournalMetadata", ProviderName.SqlServer2017, - DockerDbUtils.ConnectionString); + SqlServerDbUtils.ConnectionString); public SqlServerJournalDefaultConfigSpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(Initialize(fixture), "SQLServer-default", outputHelper) diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs index 57c7905f..8c174d8e 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs @@ -15,12 +15,12 @@ public class SqlServerJournalSpec : JournalSpec { public static Configuration.Config Initialize(SqlServerFixture fixture) { - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); return Configuration; } private static Configuration.Config Configuration => - SqlServerJournalSpecConfig.Create(DockerDbUtils.ConnectionString, "journalSpec"); + SqlServerJournalSpecConfig.Create(SqlServerDbUtils.ConnectionString, "journalSpec"); public SqlServerJournalSpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(Initialize(fixture), "SQLServer", outputHelper) diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs index 28853a8d..9de2321b 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs @@ -15,10 +15,10 @@ public class SqlServerSnapshotSpec : SnapshotStoreSpec { public static Configuration.Config Initialize(SqlServerFixture fixture) { - DockerDbUtils.Initialize(fixture.ConnectionString); + SqlServerDbUtils.Initialize(fixture.ConnectionString); return Configuration; } - private static Configuration.Config Configuration => SqlServerSnapshotSpecConfig.Create(DockerDbUtils.ConnectionString,"snapshotSpec"); + private static Configuration.Config Configuration => SqlServerSnapshotSpecConfig.Create(SqlServerDbUtils.ConnectionString,"snapshotSpec"); public SqlServerSnapshotSpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(Initialize(fixture)) diff --git a/src/Akka.Persistence.Sql.Linq2Db.Tests/DbUtils.cs b/src/Akka.Persistence.Sql.Linq2Db.Tests/DbUtils.cs deleted file mode 100644 index 422bdf2f..00000000 --- a/src/Akka.Persistence.Sql.Linq2Db.Tests/DbUtils.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Data.SqlClient; - -namespace Akka.Persistence.Sql.Linq2Db.Tests -{ - public static class DbUtils - { - public static string ConnectionString { get; set; } - - public static void Initialize(string connectionString) - { - var connectionBuilder = new SqlConnectionStringBuilder(connectionString); - - //connect to postgres database to create a new database - var databaseName = connectionBuilder.InitialCatalog; - connectionBuilder.InitialCatalog = "master"; - ConnectionString = connectionBuilder.ToString(); - - using var conn = new SqlConnection(ConnectionString); - - conn.Open(); - - using (var cmd = new SqlCommand()) - { - cmd.CommandText = string.Format(@" - IF db_id('{0}') IS NULL - BEGIN - CREATE DATABASE {0} - END - - ", databaseName); - cmd.Connection = conn; - - cmd.ExecuteScalar(); - } - - DropTables(conn, databaseName); - - // set this back to the journal/snapshot database - connectionBuilder.InitialCatalog = databaseName; - ConnectionString = connectionBuilder.ToString(); - } - - public static void Clean() - { - var connectionBuilder = new SqlConnectionStringBuilder(ConnectionString); - var databaseName = connectionBuilder.InitialCatalog; - using var conn = new SqlConnection(ConnectionString); - conn.Open(); - DropTables(conn, databaseName); - } - - private static void DropTables(SqlConnection conn, string databaseName) - { - using var cmd = new SqlCommand(); - cmd.CommandText = $@" - USE {databaseName}; - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'EventJournal') BEGIN DROP TABLE dbo.EventJournal END; - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'Metadata') BEGIN DROP TABLE dbo.Metadata END; - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'SnapshotStore') BEGIN DROP TABLE dbo.SnapshotStore END;"; - cmd.Connection = conn; - cmd.ExecuteNonQuery(); - } - } -} \ No newline at end of file diff --git a/src/common.props b/src/common.props index bdfc54df..e1601298 100644 --- a/src/common.props +++ b/src/common.props @@ -19,9 +19,9 @@ 3.2.0 3.3.0 - 1.4.45 - 0.5.1 - 1.4.45 + 1.4.46 + 0.5.2-beta1 + 1.4.46 1.4.35 1.4.35 From bfe4f8671ad1942384f8f882c417a9565e81a8af Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 1 Dec 2022 11:41:43 +0700 Subject: [PATCH 17/20] Fix unit tests --- .../SqlCommonJournalCompatibilitySpec.cs | 33 +++++++------------ .../SqlCommonSnapshotCompatibilitySpec.cs | 24 +++++--------- .../Postgres/PostgreSQLJournalSpec.cs | 4 +-- 3 files changed, 21 insertions(+), 40 deletions(-) diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs index 133c8e00..e1e4fae4 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonJournalCompatibilitySpec.cs @@ -45,8 +45,7 @@ public Task DisposeAsync() [Fact] public async Task Can_Recover_SqlCommon_Journal() { - var persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(OldJournal, "p-1")), "test-recover-1"); + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-1"))); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; (await persistRef.Ask(someEvent, 5.Seconds())).Should().Be(someEvent); @@ -55,8 +54,7 @@ public async Task Can_Recover_SqlCommon_Journal() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(NewJournal, "p-1")), "test-recover-1"); + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-1"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); } @@ -64,8 +62,7 @@ public async Task Can_Recover_SqlCommon_Journal() [Fact] public async Task Can_Persist_SqlCommon_Journal() { - var persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(OldJournal, "p-2")), "test-persist-1"); + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-2"))); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; (await persistRef.Ask(someEvent, 5.Seconds())).Should().Be(someEvent); @@ -74,8 +71,7 @@ public async Task Can_Persist_SqlCommon_Journal() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(NewJournal, "p-2")), "test-persist-1"); + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-2"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -89,8 +85,7 @@ public async Task Can_Persist_SqlCommon_Journal() [Fact] public async Task SqlCommon_Journal_Can_Recover_L2Db_Journal() { - var persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(NewJournal, "p-3")), "test-recover-2"); + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-3"))); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; (await persistRef.Ask(someEvent, 5.Seconds())).Should().Be(someEvent); @@ -99,16 +94,14 @@ public async Task SqlCommon_Journal_Can_Recover_L2Db_Journal() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(OldJournal, "p-3")), "test-recover-2"); + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-3"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); } [Fact] public async Task SqlCommon_Journal_Can_Persist_L2db_Journal() { - var persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(NewJournal, "p-4")), "test-persist-2"); + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, "p-4"))); var ourGuid = Guid.NewGuid(); var someEvent = new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 }; (await persistRef.Ask(someEvent, 5.Seconds())).Should().Be(someEvent); @@ -117,8 +110,7 @@ public async Task SqlCommon_Journal_Can_Persist_L2db_Journal() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(OldJournal, "p-4")), "test-persist-2"); + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, "p-4"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -133,8 +125,7 @@ public async Task SqlCommon_Journal_Can_Persist_L2db_Journal() public async Task L2db_Journal_Delete_Compat_mode_Preserves_proper_SequenceNr() { const string persistenceId = "d-1"; - var persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(NewJournal, persistenceId)), "test-compat-delete-seqno"); + var persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, persistenceId))); var ourGuid1 = Guid.NewGuid(); var ourGuid2 = Guid.NewGuid(); @@ -161,16 +152,14 @@ public async Task L2db_Journal_Delete_Compat_mode_Preserves_proper_SequenceNr() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(NewJournal, persistenceId)), "test-compat-delete-seqno"); + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(NewJournal, persistenceId))); var reincaranatedSequenceNrNewJournal = await persistRef.Ask(new GetSequenceNr(),TimeSpan.FromSeconds(5)); _log.Info($"oldSeq : {currentSequenceNr.SequenceNumber} - newSeq : {reincaranatedSequenceNrNewJournal.SequenceNumber}"); reincaranatedSequenceNrNewJournal.SequenceNumber.Should().Be(currentSequenceNr.SequenceNumber); EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new JournalCompatActor(OldJournal, persistenceId)), "test-compat-delete-seqno"); + persistRef = _sys.ActorOf(Props.Create(() => new JournalCompatActor(OldJournal, persistenceId))); var reincaranatedSequenceNr = await persistRef.Ask(new GetSequenceNr(),TimeSpan.FromSeconds(5)); _log.Info($"oldSeq : {currentSequenceNr.SequenceNumber} - newSeq : {reincaranatedSequenceNr.SequenceNumber}"); reincaranatedSequenceNr.SequenceNumber.Should().Be(currentSequenceNr.SequenceNumber); diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs index 66b805e8..db740133 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/SqlCommonSnapshotCompatibilitySpec.cs @@ -44,8 +44,7 @@ public Task DisposeAsync() [Fact] public async Task Can_Recover_SqlCommon_Snapshot() { - var persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(OldSnapshot, "p-1")), "test-snap-recover-1"); + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-1"))); var ourGuid = Guid.NewGuid(); (await persistRef.Ask(new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 })) @@ -55,8 +54,7 @@ public async Task Can_Recover_SqlCommon_Snapshot() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(NewSnapshot, "p-1")), "test-snap-recover-1"); + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-1"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); } @@ -64,8 +62,7 @@ public async Task Can_Recover_SqlCommon_Snapshot() [Fact] public async Task Can_Persist_SqlCommon_Snapshot() { - var persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(OldSnapshot, "p-2")), "test-snap-persist-1"); + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-2"))); var ourGuid = Guid.NewGuid(); (await persistRef.Ask(new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 })) @@ -75,8 +72,7 @@ public async Task Can_Persist_SqlCommon_Snapshot() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(NewSnapshot, "p-2")), "test-snap-persist-1"); + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-2"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); @@ -90,8 +86,7 @@ public async Task Can_Persist_SqlCommon_Snapshot() [Fact] public async Task SqlCommon_Snapshot_Can_Recover_L2Db_Snapshot() { - var persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(NewSnapshot, "p-3")), "test-snap-recover-2"); + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-3"))); var ourGuid = Guid.NewGuid(); (await persistRef.Ask(new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 })) @@ -101,8 +96,7 @@ public async Task SqlCommon_Snapshot_Can_Recover_L2Db_Snapshot() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(OldSnapshot, "p-3")), "test-snap-recover-2"); + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-3"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(5))) .Should().BeTrue(); } @@ -110,8 +104,7 @@ public async Task SqlCommon_Snapshot_Can_Recover_L2Db_Snapshot() [Fact] public async Task SqlCommon_Snapshot_Can_Persist_L2db_Snapshot() { - var persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(NewSnapshot, "p-4")), "test-snap-persist-2"); + var persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(NewSnapshot, "p-4"))); var ourGuid = Guid.NewGuid(); (await persistRef.Ask(new SomeEvent { EventName = "rec-test", Guid = ourGuid, Number = 1 })) @@ -121,8 +114,7 @@ public async Task SqlCommon_Snapshot_Can_Persist_L2db_Snapshot() EnsureTerminated(persistRef); - persistRef = _sys.ActorOf(Props.Create(() => - new SnapshotCompatActor(OldSnapshot, "p-4")), "test-snap-persist-2"); + persistRef = _sys.ActorOf(Props.Create(() => new SnapshotCompatActor(OldSnapshot, "p-4"))); (await persistRef.Ask(new ContainsEvent { Guid = ourGuid }, TimeSpan.FromSeconds(10))) .Should().BeTrue(); diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs index 775f7a24..03625683 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs @@ -14,7 +14,7 @@ namespace Akka.Persistence.Sql.Linq2Db.Tests.Docker.Postgres { - [Collection("PostgreSQLSpec")] + [Collection("PostgreSqlSpec")] public class PostgreSqlSnapshotSpec : SnapshotStoreSpec { @@ -65,7 +65,7 @@ public PostgreSqlSnapshotSpec(ITestOutputHelper outputHelper, PostgreSqlFixture } } - [Collection("PostgreSQLSpec")] + [Collection("PostgreSqlSpec")] public class DockerLinq2DbPostgreSqlJournalSpec : JournalSpec { public static Configuration.Config Create(string connString) From d7a283d5ad4f821cc59b27853c7c9950a64cdf04 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 2 Feb 2023 22:29:22 +0700 Subject: [PATCH 18/20] Add SqlServer SQL script migration spec --- scripts/MySql/1_Migration_Setup.sql | 4 +- scripts/PostgreSql/1_Migration_Setup.sql | 4 +- scripts/SqlServer/1_Migration_Setup.sql | 4 +- scripts/SqlServer/2_Migration.sql | 4 +- ...ce.Linq2Db.Data.Compatibility.Tests.csproj | 38 +++++++++++++++ .../DataCompatibilitySpec.cs | 2 +- .../SqlScriptCompatibilitySpec.cs | 41 ++++++++++++++++ .../SqlServerScriptCompatibilitySpec.cs | 33 +++++++++++++ .../SqlServer/SqlServerSpecsFixture.cs | 3 +- .../Config/JournalTableColumnNames.cs | 7 ++- .../Config/JournalTableConfig.cs | 12 +++-- .../Config/TagTableColumnNames.cs | 43 +++++++++++++++++ .../Config/TagTableConfig.cs | 41 ++++++++++++++++ .../AkkaPersistenceDataConnectionFactory.cs | 47 +++++++++---------- .../Journal/DAO/BaseByteArrayJournalDao.cs | 2 +- .../persistence.conf | 12 ++++- 16 files changed, 252 insertions(+), 45 deletions(-) create mode 100644 src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs create mode 100644 src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs create mode 100644 src/Akka.Persistence.Sql.Linq2Db/Config/TagTableColumnNames.cs create mode 100644 src/Akka.Persistence.Sql.Linq2Db/Config/TagTableConfig.cs diff --git a/scripts/MySql/1_Migration_Setup.sql b/scripts/MySql/1_Migration_Setup.sql index df160900..34a235dc 100644 --- a/scripts/MySql/1_Migration_Setup.sql +++ b/scripts/MySql/1_Migration_Setup.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS TagTable( +CREATE TABLE IF NOT EXISTS tags( ordering_id BIGINT NOT NULL, tag NVARCHAR(64) NOT NULL, PRIMARY KEY (ordering_id, tag) @@ -38,7 +38,7 @@ proc_main: BEGIN END IF; IF LENGTH(slice) > 0 THEN - INSERT IGNORE INTO TagTable (ordering_id, tag) VALUES (Id, slice); + INSERT IGNORE INTO tags (ordering_id, tag) VALUES (Id, slice); END IF; SET String = RIGHT(String, LENGTH(String) - idx); diff --git a/scripts/PostgreSql/1_Migration_Setup.sql b/scripts/PostgreSql/1_Migration_Setup.sql index d0579804..e05b3d45 100644 --- a/scripts/PostgreSql/1_Migration_Setup.sql +++ b/scripts/PostgreSql/1_Migration_Setup.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS "public"."TagTable"( +CREATE TABLE IF NOT EXISTS "public"."tags"( ordering_id BIGINT NOT NULL, tag VARCHAR(64) NOT NULL, PRIMARY KEY (ordering_id, tag) @@ -10,7 +10,7 @@ BEGIN FOR var_t IN(SELECT unnest(string_to_array(tags, ';')) AS t) LOOP CONTINUE WHEN var_t.t IS NULL OR var_t.t = ''; - INSERT INTO "public"."TagTable" (ordering_id, tag) + INSERT INTO "public"."tags" (ordering_id, tag) VALUES (id, var_t.t) ON CONFLICT DO NOTHING; END LOOP; diff --git a/scripts/SqlServer/1_Migration_Setup.sql b/scripts/SqlServer/1_Migration_Setup.sql index 6c21f4a0..df6227e2 100644 --- a/scripts/SqlServer/1_Migration_Setup.sql +++ b/scripts/SqlServer/1_Migration_Setup.sql @@ -1,7 +1,7 @@ IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = N'dbo' AND TABLE_NAME = N'TagTable') + WHERE TABLE_SCHEMA = N'dbo' AND TABLE_NAME = N'tags') BEGIN - CREATE TABLE [dbo].[TagTable]( + CREATE TABLE [dbo].[tags]( ordering_id BIGINT NOT NULL, tag NVARCHAR(64) NOT NULL, PRIMARY KEY (ordering_id, tag) diff --git a/scripts/SqlServer/2_Migration.sql b/scripts/SqlServer/2_Migration.sql index be316d4c..56f9fcd1 100644 --- a/scripts/SqlServer/2_Migration.sql +++ b/scripts/SqlServer/2_Migration.sql @@ -1,10 +1,10 @@ -INSERT INTO [dbo].[TagTable]([ordering_id], [tag]) +INSERT INTO [dbo].[tags]([ordering_id], [tag]) SELECT * FROM ( SELECT a.[Ordering], b.[items] FROM [dbo].[EventJournal] AS a CROSS APPLY [dbo].[Split](a.Tags, ';') b ) AS s([ordering_id], [tag]) WHERE NOT EXISTS ( - SELECT * FROM [dbo].[TagTable] t WITH (updlock) + SELECT * FROM [dbo].[tags] t WITH (updlock) WHERE s.[ordering_id] = t.[ordering_id] AND s.[tag] = t.[tag] ); \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/Akka.Persistence.Linq2Db.Data.Compatibility.Tests.csproj b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/Akka.Persistence.Linq2Db.Data.Compatibility.Tests.csproj index 189347df..03e9e213 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/Akka.Persistence.Linq2Db.Data.Compatibility.Tests.csproj +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/Akka.Persistence.Linq2Db.Data.Compatibility.Tests.csproj @@ -18,6 +18,8 @@ + + @@ -39,6 +41,42 @@ + + SqlServer\1_Migration_Setup.sql + Always + + + SqlServer\2_Migration.sql + Always + + + SqlServer\3_Post_Migration_Cleanup.sql + Always + + + PostgreSql\1_Migration_Setup.sql + Always + + + PostgreSql\2_Migration.sql + Always + + + PostgreSql\3_Post_Migration_Cleanup.sql + Always + + + MySql\1_Migration_Setup.sql + Always + + + MySql\2_Migration.sql + Always + + + MySql\3_Post_Migration_Cleanup.sql + Always + Always diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpec.cs index a4b845c8..e27f6978 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpec.cs @@ -28,7 +28,7 @@ protected DataCompatibilitySpec(ITestOutputHelper output): base(output) { } - protected sealed override void Setup(AkkaConfigurationBuilder builder, IServiceProvider provider) + protected override void Setup(AkkaConfigurationBuilder builder, IServiceProvider provider) { } diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs new file mode 100644 index 00000000..fcd8d169 --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs @@ -0,0 +1,41 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2023 Lightbend Inc. +// Copyright (C) 2013-2023 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.IO; +using Akka.Hosting; +using Akka.Persistence.Linq2Db.Data.Compatibility.Tests.Internal; +using Xunit.Abstractions; + +namespace Akka.Persistence.Linq2Db.Data.Compatibility.Tests +{ + public abstract class SqlScriptCompatibilitySpec: DataCompatibilitySpec where T: ITestContainer, new() + { + protected abstract string ScriptFolder { get; } + + public SqlScriptCompatibilitySpec(ITestOutputHelper output) : base(output) + { + } + + protected override void Setup(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + var workingDir = Path.GetDirectoryName(GetType().Assembly.Location); + var migrationSetup = File.ReadAllText(Path.Combine(workingDir!, ScriptFolder, "1_Migration_Setup.sql")); + var migration = File.ReadAllText(Path.Combine(workingDir!, ScriptFolder, "2_Migration.sql")); + var migrationCleanup = File.ReadAllText(Path.Combine(workingDir!, ScriptFolder, "3_Post_Migration_Cleanup.sql")); + + ExecuteSqlScripts(migrationSetup, migration, migrationCleanup); + + base.Setup(builder, provider); + builder.AddHocon(@" +akka.persistence.journal.linq2db.tag-write-mode = TagTable +akka.persistence.query.journal.linq2db.tag-read-mode = TagTable", HoconAddMode.Prepend); + } + + protected abstract void ExecuteSqlScripts(string setup, string migration, string cleanup); + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs new file mode 100644 index 00000000..0722038c --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs @@ -0,0 +1,33 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2023 .NET Foundation +// +// ----------------------------------------------------------------------- + +using Microsoft.Data.SqlClient; +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlServer.Management.Smo; +using Xunit.Abstractions; + +namespace Akka.Persistence.Linq2Db.Data.Compatibility.Tests.SqlServer +{ + public class SqlServerScriptCompatibilitySpec: SqlScriptCompatibilitySpec + { + public SqlServerScriptCompatibilitySpec(ITestOutputHelper output) : base(output) + { + } + + protected override TestSettings Settings => SqlServerSpecSettings.Instance; + protected override string ScriptFolder => "SqlServer"; + + protected override void ExecuteSqlScripts(string setup, string migration, string cleanup) + { + using var conn = new SqlConnection(Fixture.ConnectionString); + var server = new Server(new ServerConnection(conn)); + + server.ConnectionContext.ExecuteNonQuery(setup); + server.ConnectionContext.ExecuteNonQuery(migration); + server.ConnectionContext.ExecuteNonQuery(cleanup); + } + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecsFixture.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecsFixture.cs index 2b663884..763fec29 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecsFixture.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecsFixture.cs @@ -63,7 +63,8 @@ protected override Task AfterContainerStartedAsync() ["Server"] = $"localhost,{Port}", ["Database"] = DatabaseName, ["User Id"] = User, - ["Password"] = Password + ["Password"] = Password, + ["TrustServerCertificate"] = "true" }; _connectionString = builder.ToString(); diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableColumnNames.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableColumnNames.cs index dd51fc89..5c8cc1e2 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableColumnNames.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableColumnNames.cs @@ -3,7 +3,7 @@ namespace Akka.Persistence.Sql.Linq2Db.Config { - public class JournalTableColumnNames + public class JournalTableColumnNames : IEquatable { public JournalTableColumnNames(Configuration.Config config) { @@ -28,9 +28,9 @@ public JournalTableColumnNames(Configuration.Config config) public string Identifier { get; } public string Manifest { get; } - private bool Equals(JournalTableColumnNames other) + public bool Equals(JournalTableColumnNames other) { - return + return other is { } && Ordering == other.Ordering && Deleted == other.Deleted && PersistenceId == other.PersistenceId && @@ -44,7 +44,6 @@ private bool Equals(JournalTableColumnNames other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; return obj is JournalTableColumnNames j && Equals(j); } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs index 93c3a771..47db31ac 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs @@ -19,11 +19,11 @@ public class JournalTableConfig : IEquatable { public string SchemaName { get; } public TagWriteMode TagWriteMode { get; } - public TagTableMode TagTableMode { get; } - public string TagTableName { get; } - public bool UseEventManifestColumn { get; } + public TagTableMode TagTableMode { get; } = TagTableMode.OrderingId; + public bool UseEventManifestColumn { get; } = false; public EventJournalTableConfig EventJournalTable { get; } public MetadataTableConfig MetadataTable { get; } + public TagTableConfig TagTable { get; } public JournalTableConfig(Configuration.Config config) { var mappingPath = config.GetString("table-mapping"); @@ -47,12 +47,16 @@ public JournalTableConfig(Configuration.Config config) throw new ConfigurationException($"The configuration path akka.persistence.journal.linq2db.{mappingPath} does not exist"); if (mappingPath != "default") - mappingConfig.WithFallback(config.GetConfig("default")); + { + var defaultConfig = config.GetConfig("default"); + mappingConfig = mappingConfig.WithFallback(defaultConfig); + } SchemaName = mappingConfig.GetString("schema-name"); EventJournalTable = new EventJournalTableConfig(mappingConfig); MetadataTable = new MetadataTableConfig(mappingConfig); + TagTable = new TagTableConfig(mappingConfig); } public bool Equals(JournalTableConfig other) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/TagTableColumnNames.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/TagTableColumnNames.cs new file mode 100644 index 00000000..272e69f4 --- /dev/null +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/TagTableColumnNames.cs @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2023 Lightbend Inc. +// Copyright (C) 2013-2023 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; + +namespace Akka.Persistence.Sql.Linq2Db.Config; + +public class TagTableColumnNames: IEquatable +{ + public TagTableColumnNames(Configuration.Config config) + { + var cfg = config.GetConfig("columns"); + OrderingId = cfg.GetString("ordering-id", "ordering_id"); + Tag = cfg.GetString("tag-value", "tag"); + WriteUuid = cfg.GetString("writer-uuid", "writer_uuid"); + } + + public string OrderingId { get; } + public string Tag { get; } + public string WriteUuid { get; } + + public bool Equals(TagTableColumnNames other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return OrderingId == other.OrderingId && Tag == other.Tag && WriteUuid == other.WriteUuid; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) return true; + return obj is TagTableColumnNames tag && Equals(tag); + } + + public override int GetHashCode() + { + return HashCode.Combine(OrderingId, Tag, WriteUuid); + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/TagTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/TagTableConfig.cs new file mode 100644 index 00000000..90c6f4bf --- /dev/null +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/TagTableConfig.cs @@ -0,0 +1,41 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2023 Lightbend Inc. +// Copyright (C) 2013-2023 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; + +namespace Akka.Persistence.Sql.Linq2Db.Config; + +public class TagTableConfig : IEquatable +{ + public TagTableConfig(Configuration.Config config) + { + var journalConfig = config.GetConfig("tag"); + Name = journalConfig.GetString("table-name"); + ColumnNames = new TagTableColumnNames(journalConfig); + } + + public string Name { get; } + public TagTableColumnNames ColumnNames { get; } + + public bool Equals(TagTableConfig other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Name == other.Name && Equals(ColumnNames, other.ColumnNames); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) return true; + return obj is TagTableConfig cfg && Equals(cfg); + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, ColumnNames); + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index d3b469bd..8930a4ba 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -142,7 +142,7 @@ private static void MapJournalRow(IProviderConfig config, //We can skip writing tags the old way by ignoring the column in mapping. journalRowBuilder.Member(r => r.TagArr).IsNotColumn(); - if ((tableConfig.TagWriteMode & TagWriteMode.Csv) == 0) + if (tableConfig.TagWriteMode == TagWriteMode.Csv) { journalRowBuilder.Member(r => r.Tags).IsNotColumn(); } @@ -162,24 +162,6 @@ private static void MapJournalRow(IProviderConfig config, .Member(r=>r.SequenceNumber).IsPrimaryKey(); } - void SetJoinCol(PropertyMappingBuilder builder) - { - if (config.TableConfig.TagTableMode == TagTableMode.SequentialUuid) - { - builder.Member(r => r.JournalOrderingId) - .IsNotColumn(); - builder.Member(r => r.WriteUuid) - .IsColumn().IsPrimaryKey(); - } - else - { - builder.Member(r => r.WriteUuid) - .IsNotColumn(); - builder.Member(r => r.JournalOrderingId) - .IsColumn().IsPrimaryKey(); - } - } - if (config.TableConfig.UseEventManifestColumn) { journalRowBuilder.Member(r => r.EventManifest) @@ -191,17 +173,33 @@ void SetJoinCol(PropertyMappingBuilder builder) .IsNotColumn(); } - if ((config.TableConfig.TagWriteMode & TagWriteMode.TagTable) != 0) + if (config.TableConfig.TagWriteMode == TagWriteMode.TagTable) { + var tagConfig = tableConfig.TagTable; + var tagColumns = tagConfig.ColumnNames; + var tagTableBuilder = fmb.Entity() - .HasTableName(tableConfig.TagTableName) .HasSchemaName(tableConfig.SchemaName) - .Member(r => r.TagValue) + .HasTableName(tagConfig.Name) + .Member(r => r.TagValue).HasColumnName(tagColumns.Tag) .IsColumn().IsNullable(false) .HasLength(64) - .IsPrimaryKey(); + .IsPrimaryKey() + .Member(r => r.JournalOrderingId).HasColumnName(tagColumns.OrderingId) + .IsColumn().IsPrimaryKey(); - SetJoinCol(tagTableBuilder); + if (config.TableConfig.TagTableMode == TagTableMode.SequentialUuid) + { + tagTableBuilder.Member(r => r.JournalOrderingId) + .IsNotColumn(); + tagTableBuilder.Member(r => r.WriteUuid).HasColumnName(tagColumns.WriteUuid) + .IsColumn().IsPrimaryKey(); + } + else + { + tagTableBuilder.Member(r => r.WriteUuid) + .IsNotColumn(); + } } // column compatibility @@ -216,7 +214,6 @@ void SetJoinCol(PropertyMappingBuilder builder) .IsNotColumn(); } - //Probably overkill, but we only set Metadata Mapping if specified //That we are in delete compatibility mode. if (config.IDaoConfig.SqlCommonCompatibilityMode) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs index e27a2ed5..b59f2ddc 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs @@ -266,7 +266,7 @@ await ctx.BulkCopyAsync(new BulkCopyOptions { await ctx.BulkCopyAsync(new BulkCopyOptions { - TableName = JournalConfig.TableConfig.TagTableName, + TableName = JournalConfig.TableConfig.TagTable.Name, MaxBatchSize = JournalConfig.DaoConfig.DbRoundTripTagBatchSize, UseParameters = JournalConfig.DaoConfig.PreferParametersOnMultiRowInsert }, tagWrites); diff --git a/src/Akka.Persistence.Sql.Linq2Db/persistence.conf b/src/Akka.Persistence.Sql.Linq2Db/persistence.conf index 961196a2..3d2d988f 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/persistence.conf +++ b/src/Akka.Persistence.Sql.Linq2Db/persistence.conf @@ -82,7 +82,6 @@ materializer-dispatcher = "akka.actor.default-dispatcher" tag-write-mode = csv - tag-read-mode = csv # should corresponding journal table be initialized automatically #if delete-compatibility-mode is true, both tables are created @@ -132,6 +131,15 @@ sequence-number = sequence_number } } + + tag { + table-name = "tags" + columns { + ordering-id = ordering_id + tag-value = tag + writer-uuid = writer_uuid + } + } } # Akka.Persistence.SqlServer compatibility table name and column name mapping @@ -261,6 +269,8 @@ refresh-interval = 1s # interval for refreshing connection-string = "" # Connection String is Required! + + tag-read-mode = csv journal-sequence-retrieval{ batch-size = 10000 From b2a332943418c4e0fa979781b80ef8550cca9a31 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 3 Feb 2023 01:59:48 +0700 Subject: [PATCH 19/20] Add PostgreSql and MySql SQL script spec --- scripts/MySql/1_Migration_Setup.sql | 7 +-- .../DockerLinq2DbPostgreSQLJournalPerfSpec.cs | 3 +- .../DockerLinq2DbSqlServerJournalPerfSpec.cs | 3 +- .../Linq2Db/MSSQLiteLinq2DbJournalPerfSpec.cs | 4 +- .../Sqlite/SQLiteCompatibilitySpecConfig.cs | 2 +- .../DataCompatibilitySpecBase.cs | 13 +++--- .../{MySqlSpecsFixture.cs => MySqlFixture.cs} | 0 .../MySql/MySqlScriptCompatibilitySpec.cs | 38 ++++++++++++++++ ...qlSpecsFixture.cs => PostgreSqlFixture.cs} | 0 .../PostgreSqlScriptCompatibilitySpec.cs | 39 +++++++++++++++++ .../PostgreSql/PostgreSqlSpecSettings.cs | 1 - .../SqlScriptCompatibilitySpec.cs | 1 - .../SqlServer/SqlServerCompatibilitySpec.cs | 6 --- .../SqlServerScriptCompatibilitySpec.cs | 2 + .../SqlServer/SqlServerSpecSettings.cs | 3 -- .../Program.cs | 3 +- .../SqliteAllEventsSpec.cs | 2 +- .../SqliteCurrentAllEventsSpec.cs | 2 +- .../SqliteCurrentEventsByPersistenceIdSpec.cs | 2 +- .../SqliteCurrentEventsByTagSpec.cs | 2 +- .../SqliteCurrentPersistenceIdsSpec.cs | 2 +- .../SqliteEventsByPersistenceIdSpec.cs | 2 +- .../SqliteEventsByTagSpec.cs | 2 +- .../SqlitePersistenceIdsSpec.cs | 2 +- .../Postgres/PostgreSQLJournalSpec.cs | 6 +-- .../SQLServerJournalCustomConfigSpec.cs | 3 +- .../SQLServerJournalDefaultConfigSpec.cs | 3 +- .../SqlServer/SQLServerJournalSpec.cs | 3 +- .../SqlServer/SQLServerSnapshotSpec.cs | 3 +- .../Settings/JournalConfigSpec.cs | 2 +- .../Settings/ReadJournalConfigSpec.cs | 2 +- .../Settings/SnapshotConfigSpec.cs | 2 +- .../Config/JournalTableConfig.cs | 9 ++-- .../Config/SnapshotTableConfiguration.cs | 2 +- .../AkkaPersistenceDataConnectionFactory.cs | 2 +- src/Akka.Persistence.Sql.Linq2Db/Extension.cs | 43 ++++++++++--------- .../Journal/Linq2DbWriteJournal.cs | 9 ++-- .../Snapshot/Linq2DbSnapshotStore.cs | 4 +- .../persistence.conf | 2 +- 39 files changed, 145 insertions(+), 91 deletions(-) rename src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/{MySqlSpecsFixture.cs => MySqlFixture.cs} (100%) create mode 100644 src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlScriptCompatibilitySpec.cs rename src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/{PostgresSqlSpecsFixture.cs => PostgreSqlFixture.cs} (100%) create mode 100644 src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlScriptCompatibilitySpec.cs diff --git a/scripts/MySql/1_Migration_Setup.sql b/scripts/MySql/1_Migration_Setup.sql index 34a235dc..51a17a0a 100644 --- a/scripts/MySql/1_Migration_Setup.sql +++ b/scripts/MySql/1_Migration_Setup.sql @@ -5,9 +5,10 @@ CREATE TABLE IF NOT EXISTS tags( ); DROP PROCEDURE IF EXISTS Split; -DELIMITER # + +DELIMITER ?? CREATE PROCEDURE Split() -proc_main: BEGIN +BEGIN DECLARE v_cursor_done TINYINT UNSIGNED DEFAULT 0; DECLARE Id INT UNSIGNED; @@ -51,5 +52,5 @@ proc_main: BEGIN CLOSE v_cursor; -END proc_main # +END?? DELIMITER ; \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs index 46323169..4d4471e3 100644 --- a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbPostgreSQLJournalPerfSpec.cs @@ -46,9 +46,8 @@ public DockerLinq2DbPostgreSqlJournalPerfSpec(ITestOutputHelper output, PostgreSqlFixture fixture) : base(InitConfig(fixture), "postgresperf", output,40, eventsCount: TestConstants.DockerNumMessages) { - var extension = Linq2DbPersistence.Get(Sys); var config = Create(SqlServerDbUtils.ConnectionString) - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.journal.linq2db"); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(config)); using var conn = connFactory.GetConnection(); diff --git a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs index a9693c5d..882c0fea 100644 --- a/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Benchmark.DockerTests/Docker/Linq2Db/DockerLinq2DbSqlServerJournalPerfSpec.cs @@ -49,9 +49,8 @@ public DockerLinq2DbSqlServerJournalPerfSpec(ITestOutputHelper output, SqlServerFixture fixture) : base(InitConfig(fixture), "sqlserverperf", output,40, eventsCount: TestConstants.DockerNumMessages) { - var extension = Linq2DbPersistence.Get(Sys); var config = Create(SqlServerDbUtils.ConnectionString) - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.journal.linq2db"); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(config)); using var conn = connFactory.GetConnection(); diff --git a/src/Akka.Persistence.Linq2Db.Benchmark.Tests/Linq2Db/MSSQLiteLinq2DbJournalPerfSpec.cs b/src/Akka.Persistence.Linq2Db.Benchmark.Tests/Linq2Db/MSSQLiteLinq2DbJournalPerfSpec.cs index 3acce59c..752b01c0 100644 --- a/src/Akka.Persistence.Linq2Db.Benchmark.Tests/Linq2Db/MSSQLiteLinq2DbJournalPerfSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Benchmark.Tests/Linq2Db/MSSQLiteLinq2DbJournalPerfSpec.cs @@ -34,13 +34,11 @@ public static void InitWalForFileDb() public MsSqliteLinq2DbJournalPerfSpec(ITestOutputHelper output) : base(SqLiteJournalSpecConfig.Create(ConnString, ProviderName.SQLiteMS), "SqliteJournalSpec", output,eventsCount: TestConstants.NumMessages) { - var extension = Linq2DbPersistence.Get(Sys); - HeldSqliteConnection.Open(); //InitWALForFileDb(); var conf = new JournalConfig( SqLiteJournalSpecConfig.Create(ConnString, ProviderName.SQLiteMS) - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.journal.linq2db")); var connFactory = new AkkaPersistenceDataConnectionFactory(conf); diff --git a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Sqlite/SQLiteCompatibilitySpecConfig.cs b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Sqlite/SQLiteCompatibilitySpecConfig.cs index 56013a02..83afedd4 100644 --- a/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Sqlite/SQLiteCompatibilitySpecConfig.cs +++ b/src/Akka.Persistence.Linq2Db.Compatibility.Tests/Sqlite/SQLiteCompatibilitySpecConfig.cs @@ -49,7 +49,7 @@ class = ""{typeof(Linq2DbSnapshotStore).AssemblyQualifiedName}"" }}"; return ConfigurationFactory.ParseString(specString) - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public static Config InitJournalConfig(string tableName, string metadataTableName, string connectionString) diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpecBase.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpecBase.cs index 36436186..209e5a88 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpecBase.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/DataCompatibilitySpecBase.cs @@ -65,10 +65,10 @@ private Config Config() batch-size = 3 db-round-trip-max-batch-size = 6 replay-batch-size = 6 - +{(Settings.SchemaName is { } ? @$" {Settings.TableMapping} {{ - schema-name = {Settings.SchemaName ?? "null"} - }} + schema-name = {Settings.SchemaName} + }}" : "")} }} }} @@ -92,9 +92,10 @@ private Config Config() table-mapping = {Settings.TableMapping} auto-initialize = off +{(Settings.SchemaName is { } ? @$" {Settings.TableMapping} {{ - schema-name = {Settings.SchemaName ?? "null"} - }} + schema-name = {Settings.SchemaName} + }}" : "")} }} }} }}"; @@ -109,7 +110,7 @@ protected virtual void CustomSetup(AkkaConfigurationBuilder builder, IServicePro private void InternalSetup(AkkaConfigurationBuilder builder, IServiceProvider provider) { builder.AddHocon( - Config().WithFallback(Linq2DbPersistence.DefaultConfiguration()), + Config().WithFallback(Linq2DbPersistence.DefaultConfiguration), HoconAddMode.Prepend); Setup(builder, provider); } diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlSpecsFixture.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlFixture.cs similarity index 100% rename from src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlSpecsFixture.cs rename to src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlFixture.cs diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlScriptCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlScriptCompatibilitySpec.cs new file mode 100644 index 00000000..cd8be48c --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/MySql/MySqlScriptCompatibilitySpec.cs @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2023 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System.Text; +using MySql.Data.MySqlClient; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Persistence.Linq2Db.Data.Compatibility.Tests.MySql +{ + [Collection("SqlCompatSpec")] + public class MySqlScriptCompatibilitySpec : SqlScriptCompatibilitySpec + { + public MySqlScriptCompatibilitySpec(ITestOutputHelper output) : base(output) + { + } + + protected override TestSettings Settings => MySqlSpecSettings.Instance; + protected override string ScriptFolder => "MySql"; + protected override void ExecuteSqlScripts(string setup, string migration, string cleanup) + { + using var conn = new MySqlConnection(Fixture.ConnectionString); + var script = new MySqlScript(conn); + + script.Query = setup; + script.Execute(); + + script.Query = migration; + script.Execute(); + + script.Query = cleanup; + script.Execute(); + } + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgresSqlSpecsFixture.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlFixture.cs similarity index 100% rename from src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgresSqlSpecsFixture.cs rename to src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlFixture.cs diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlScriptCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlScriptCompatibilitySpec.cs new file mode 100644 index 00000000..5306d197 --- /dev/null +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlScriptCompatibilitySpec.cs @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2023 .NET Foundation +// +// ----------------------------------------------------------------------- + +using Npgsql; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Persistence.Linq2Db.Data.Compatibility.Tests.PostgreSql +{ + [Collection("SqlCompatSpec")] + public class PostgreSqlScriptCompatibilitySpec : SqlScriptCompatibilitySpec + { + public PostgreSqlScriptCompatibilitySpec(ITestOutputHelper output) : base(output) + { + } + + protected override TestSettings Settings => PostgreSqlSpecSettings.Instance; + protected override string ScriptFolder => "PostgreSql"; + protected override void ExecuteSqlScripts(string setup, string migration, string cleanup) + { + using var conn = new NpgsqlConnection(Fixture.ConnectionString); + conn.Open(); + + var cmd = new NpgsqlCommand { + Connection = conn + }; + + cmd.CommandText = setup; + cmd.ExecuteNonQuery(); + cmd.CommandText = migration; + cmd.ExecuteNonQuery(); + cmd.CommandText = cleanup; + cmd.ExecuteNonQuery(); + } + } +} \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlSpecSettings.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlSpecSettings.cs index fa475cea..8b6d7174 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlSpecSettings.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/PostgreSql/PostgreSqlSpecSettings.cs @@ -17,6 +17,5 @@ private PostgreSqlSpecSettings() public override string ProviderName => LinqToDB.ProviderName.PostgreSQL; public override string TableMapping => "postgresql"; - } } \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs index fcd8d169..e3a5912a 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlScriptCompatibilitySpec.cs @@ -1,6 +1,5 @@ // ----------------------------------------------------------------------- // -// Copyright (C) 2009-2023 Lightbend Inc. // Copyright (C) 2013-2023 .NET Foundation // // ----------------------------------------------------------------------- diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerCompatibilitySpec.cs index 675e4637..ed473f01 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerCompatibilitySpec.cs @@ -4,12 +4,6 @@ // // ----------------------------------------------------------------------- -using System; -using Akka.Configuration; -using Akka.Hosting; -using Akka.Persistence.Sql.Linq2Db; -using Akka.Persistence.Sql.Linq2Db.Journal; -using Akka.Persistence.Sql.Linq2Db.Snapshot; using Xunit; using Xunit.Abstractions; diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs index 0722038c..ee3b0156 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerScriptCompatibilitySpec.cs @@ -7,10 +7,12 @@ using Microsoft.Data.SqlClient; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Smo; +using Xunit; using Xunit.Abstractions; namespace Akka.Persistence.Linq2Db.Data.Compatibility.Tests.SqlServer { + [Collection("SqlCompatSpec")] public class SqlServerScriptCompatibilitySpec: SqlScriptCompatibilitySpec { public SqlServerScriptCompatibilitySpec(ITestOutputHelper output) : base(output) diff --git a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecSettings.cs b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecSettings.cs index 11bd078d..a496fec7 100644 --- a/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecSettings.cs +++ b/src/Akka.Persistence.Linq2Db.Data.Compatibility.Tests/SqlServer/SqlServerSpecSettings.cs @@ -17,8 +17,5 @@ private SqlServerSpecSettings() public override string ProviderName => LinqToDB.ProviderName.SqlServer2017; public override string TableMapping => "sql-server"; - - public override string SchemaName => "dbo"; - } } \ No newline at end of file diff --git a/src/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs b/src/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs index 7fc54b49..117ce111 100644 --- a/src/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs +++ b/src/Akka.Persistence.Linq2Db.IndexHelperApp/Program.cs @@ -2,6 +2,7 @@ using System.IO; using Akka.Configuration; using Akka.Persistence.Linq2Db.IndexHelperLib; +using Akka.Persistence.Sql.Linq2Db; using Akka.Persistence.Sql.Linq2Db.Config; using Akka.Persistence.Sql.Linq2Db.Tests; using CommandLine; @@ -36,7 +37,7 @@ public static void Main(string[] args) var journalConf = new JournalConfig(conf .GetConfig(opts.HoconPath) //.GetConfig("akka.persistence.journal.linq2db.testGen") - .WithFallback(Sql.Linq2Db.Journal.Linq2DbWriteJournal.DefaultConfiguration)); + .WithFallback(Linq2DbPersistence.DefaultConfiguration)); var generator = GetGenerator(journalConf.ProviderName); var helper = new JournalIndexHelper(); GeneratePerOptions(opts, helper, journalConf, generator); diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteAllEventsSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteAllEventsSpec.cs index 2fd858a4..c069d797 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteAllEventsSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteAllEventsSpec.cs @@ -42,7 +42,7 @@ public static Config Config(int id) auto-initialize = on }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public SqliteAllEventsSpec(ITestOutputHelper output) : base(Config(Counter.GetAndIncrement()), nameof(SqliteAllEventsSpec), output) diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentAllEventsSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentAllEventsSpec.cs index de297de7..99323d19 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentAllEventsSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentAllEventsSpec.cs @@ -43,7 +43,7 @@ public static Config Config(int id) auto-initialize = on }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public SqliteCurrentAllEventsSpec(ITestOutputHelper output) : base(Config(Counter.GetAndIncrement()), nameof(SqliteCurrentAllEventsSpec), output) { diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByPersistenceIdSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByPersistenceIdSpec.cs index d3e72a94..cf04067c 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByPersistenceIdSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByPersistenceIdSpec.cs @@ -54,7 +54,7 @@ class = ""{typeof(Linq2DbWriteJournal).AssemblyQualifiedName}"" }} }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public SqliteCurrentEventsByPersistenceIdSpec(ITestOutputHelper output) : base(Config(Counter.GetAndIncrement()), nameof(SqliteCurrentEventsByPersistenceIdSpec), output) diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByTagSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByTagSpec.cs index ecef9d75..3b2368a7 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByTagSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentEventsByTagSpec.cs @@ -55,7 +55,7 @@ class = ""{typeof(Linq2DbWriteJournal).AssemblyQualifiedName}"" auto-initialize = on }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public SqliteCurrentEventsByTagSpec(ITestOutputHelper output) : base(Config(Counter.GetAndIncrement()), nameof(SqliteCurrentEventsByTagSpec), output) diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentPersistenceIdsSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentPersistenceIdsSpec.cs index 151bb511..e87e764e 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentPersistenceIdsSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteCurrentPersistenceIdsSpec.cs @@ -46,7 +46,7 @@ public static Config Config(int id) auto-initialize = on }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public SqliteCurrentPersistenceIdsSpec(ITestOutputHelper output) : base(Config(Counter.GetAndIncrement()), nameof(SqliteCurrentPersistenceIdsSpec), output) diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByPersistenceIdSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByPersistenceIdSpec.cs index a125a2a8..d9163814 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByPersistenceIdSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByPersistenceIdSpec.cs @@ -44,7 +44,7 @@ public static Config Config(int id) auto-initialize = on }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public SqliteEventsByPersistenceIdSpec(ITestOutputHelper output) : base(Config(Counter.GetAndIncrement()), nameof(SqliteEventsByPersistenceIdSpec), output) diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByTagSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByTagSpec.cs index c323f355..9a056792 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByTagSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqliteEventsByTagSpec.cs @@ -52,7 +52,7 @@ public static Config Config(int id) auto-initialize = on }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } public SqliteEventsByTagSpec(ITestOutputHelper output) : base(Config(Counter.GetAndIncrement()), nameof(SqliteEventsByTagSpec), output) diff --git a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqlitePersistenceIdsSpec.cs b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqlitePersistenceIdsSpec.cs index f9ddfea6..2b94a927 100644 --- a/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqlitePersistenceIdsSpec.cs +++ b/src/Akka.Persistence.Linq2Db.Journal.Query.Tests/SqlitePersistenceIdsSpec.cs @@ -71,7 +71,7 @@ class = ""Akka.Persistence.Sqlite.Snapshot.SqliteSnapshotStore, Akka.Persistence write-plugin = ""akka.persistence.journal.linq2db"" }} akka.test.single-expect-default = 10s") - .WithFallback(Linq2DbPersistence.DefaultConfiguration()); + .WithFallback(Linq2DbPersistence.DefaultConfiguration); } } diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs index c6895c55..f24ca121 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/Postgres/PostgreSQLJournalSpec.cs @@ -44,12 +44,11 @@ class = ""{typeof(Linq2DbSnapshotStore).AssemblyQualifiedName}"" public PostgreSqlSnapshotSpec(ITestOutputHelper outputHelper, PostgreSqlFixture fixture) : base(Configuration(fixture)) { - var extension = Linq2DbPersistence.Get(Sys); DebuggingHelpers.SetupTraceDump(outputHelper); var connFactory = new AkkaPersistenceDataConnectionFactory( new SnapshotConfig( Configuration(fixture) - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.snapshot-store.linq2db"))); using (var conn = connFactory.GetConnection()) { @@ -92,9 +91,8 @@ class = ""{typeof(Linq2DbWriteJournal).AssemblyQualifiedName}"" public DockerLinq2DbPostgreSqlJournalSpec(ITestOutputHelper output, PostgreSqlFixture fixture) : base(InitConfig(fixture), "postgresperf", output) { - var extension = Linq2DbPersistence.Get(Sys); var config = Create(SqlServerDbUtils.ConnectionString) - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.journal.linq2db"); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(config)); using (var conn = connFactory.GetConnection()) diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs index 8455b4ad..912b862c 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalCustomConfigSpec.cs @@ -29,11 +29,10 @@ public static Configuration.Config Initialize(SqlServerFixture fixture) public SqlServerJournalCustomConfigSpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(Initialize(fixture), "SQLServer-custom", outputHelper) { - var extension = Linq2DbPersistence.Get(Sys); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig( Configuration .GetConfig("akka.persistence.journal.customSpec") - .WithFallback(extension.DefaultJournalConfig))); + .WithFallback(Linq2DbPersistence.DefaultJournalConfiguration))); using (var conn = connFactory.GetConnection()) { try diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs index c4f1cc07..ae758ef9 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalDefaultConfigSpec.cs @@ -29,10 +29,9 @@ public static Configuration.Config Initialize(SqlServerFixture fixture) public SqlServerJournalDefaultConfigSpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(Initialize(fixture), "SQLServer-default", outputHelper) { - var extension = Linq2DbPersistence.Get(Sys); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig( Configuration - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.journal.linq2db"))); using (var conn = connFactory.GetConnection()) { diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs index 8c174d8e..0073ec73 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerJournalSpec.cs @@ -25,9 +25,8 @@ public static Configuration.Config Initialize(SqlServerFixture fixture) public SqlServerJournalSpec(ITestOutputHelper outputHelper, SqlServerFixture fixture) : base(Initialize(fixture), "SQLServer", outputHelper) { - var extension = Linq2DbPersistence.Get(Sys); var config = Configuration - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.journal.linq2db"); var connFactory = new AkkaPersistenceDataConnectionFactory(new JournalConfig(config)); using (var conn = connFactory.GetConnection()) diff --git a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs index 9de2321b..fd6f4e39 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.DockerTests/SqlServer/SQLServerSnapshotSpec.cs @@ -24,11 +24,10 @@ public SqlServerSnapshotSpec(ITestOutputHelper outputHelper, SqlServerFixture fi : base(Initialize(fixture)) { DebuggingHelpers.SetupTraceDump(outputHelper); - var extension = Linq2DbPersistence.Get(Sys); var connFactory = new AkkaPersistenceDataConnectionFactory( new SnapshotConfig( Configuration - .WithFallback(extension.DefaultConfig) + .WithFallback(Linq2DbPersistence.DefaultConfiguration) .GetConfig("akka.persistence.snapshot-store.linq2db"))); using (var conn = connFactory.GetConnection()) diff --git a/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/JournalConfigSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/JournalConfigSpec.cs index da4bf43a..ee343a0e 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/JournalConfigSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/JournalConfigSpec.cs @@ -20,7 +20,7 @@ public class JournalConfigSpec public JournalConfigSpec() { - _defaultConfig = Linq2DbPersistence.DefaultConfiguration(); + _defaultConfig = Linq2DbPersistence.DefaultConfiguration; } [Fact(DisplayName = "Default journal HOCON config should contain default values")] diff --git a/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/ReadJournalConfigSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/ReadJournalConfigSpec.cs index f84f5316..19623816 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/ReadJournalConfigSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/ReadJournalConfigSpec.cs @@ -21,7 +21,7 @@ public class ReadJournalConfigSpec public ReadJournalConfigSpec() { - _defaultConfig = Linq2DbPersistence.DefaultConfiguration(); + _defaultConfig = Linq2DbPersistence.DefaultConfiguration; } [Fact(DisplayName = "Default journal query HOCON config should contain default values")] diff --git a/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/SnapshotConfigSpec.cs b/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/SnapshotConfigSpec.cs index d2b50d96..ed8dc2da 100644 --- a/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/SnapshotConfigSpec.cs +++ b/src/Akka.Persistence.Sql.Linq2Db.Tests/Settings/SnapshotConfigSpec.cs @@ -20,7 +20,7 @@ public class SnapshotConfigSpec public SnapshotConfigSpec() { - _defaultConfig = Linq2DbPersistence.DefaultConfiguration(); + _defaultConfig = Linq2DbPersistence.DefaultConfiguration; } [Fact(DisplayName = "Default snapshot HOCON config should contain default values")] diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs index 47db31ac..feaaf877 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/JournalTableConfig.cs @@ -5,8 +5,8 @@ namespace Akka.Persistence.Sql.Linq2Db.Config { public enum TagWriteMode { - Csv = 1, - TagTable = 2, + Csv, + TagTable, } public enum TagTableMode @@ -47,10 +47,7 @@ public JournalTableConfig(Configuration.Config config) throw new ConfigurationException($"The configuration path akka.persistence.journal.linq2db.{mappingPath} does not exist"); if (mappingPath != "default") - { - var defaultConfig = config.GetConfig("default"); - mappingConfig = mappingConfig.WithFallback(defaultConfig); - } + mappingConfig = mappingConfig.WithFallback(Linq2DbPersistence.DefaultJournalMappingConfiguration); SchemaName = mappingConfig.GetString("schema-name"); diff --git a/src/Akka.Persistence.Sql.Linq2Db/Config/SnapshotTableConfiguration.cs b/src/Akka.Persistence.Sql.Linq2Db/Config/SnapshotTableConfiguration.cs index 314ce759..11b3c712 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Config/SnapshotTableConfiguration.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Config/SnapshotTableConfiguration.cs @@ -18,7 +18,7 @@ public SnapshotTableConfiguration(Configuration.Config config) throw new ConfigurationException($"The configuration path akka.persistence.journal.linq2db.{mappingPath} does not exist"); if (mappingPath != "default") - mappingConfig.WithFallback(config.GetConfig("default")); + mappingConfig.WithFallback(Linq2DbPersistence.DefaultSnapshotMappingConfiguration); SchemaName = mappingConfig.GetString("schema-name"); diff --git a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs index 8930a4ba..90acac96 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Db/AkkaPersistenceDataConnectionFactory.cs @@ -142,7 +142,7 @@ private static void MapJournalRow(IProviderConfig config, //We can skip writing tags the old way by ignoring the column in mapping. journalRowBuilder.Member(r => r.TagArr).IsNotColumn(); - if (tableConfig.TagWriteMode == TagWriteMode.Csv) + if (tableConfig.TagWriteMode == TagWriteMode.TagTable) { journalRowBuilder.Member(r => r.Tags).IsNotColumn(); } diff --git a/src/Akka.Persistence.Sql.Linq2Db/Extension.cs b/src/Akka.Persistence.Sql.Linq2Db/Extension.cs index 4a63f532..87645bf1 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Extension.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Extension.cs @@ -16,34 +16,35 @@ public sealed class Linq2DbPersistence : IExtension public const string JournalConfigPath = "akka.persistence.journal.linq2db"; public const string SnapshotStoreConfigPath = "akka.persistence.snapshot-store.linq2db"; - /// - /// Returns a default configuration for akka persistence Linq2Db-based journals and snapshot stores. - /// - /// - public static Configuration.Config DefaultConfiguration() + public static readonly Configuration.Config DefaultJournalConfiguration; + public static readonly Configuration.Config DefaultSnapshotConfiguration; + public static readonly Configuration.Config DefaultConfiguration; + public static readonly Configuration.Config DefaultJournalMappingConfiguration; + public static readonly Configuration.Config DefaultSnapshotMappingConfiguration; + + public readonly Configuration.Config DefaultJournalConfig = DefaultJournalConfiguration; + public readonly Configuration.Config DefaultSnapshotConfig = DefaultSnapshotConfiguration; + public readonly Configuration.Config DefaultConfig = DefaultConfiguration; + public readonly Configuration.Config DefaultJournalMappingConfig = DefaultJournalMappingConfiguration; + public readonly Configuration.Config DefaultSnapshotMappingConfig = DefaultSnapshotMappingConfiguration; + + static Linq2DbPersistence() { var journalConfig = ConfigurationFactory.FromResource("Akka.Persistence.Sql.Linq2Db.persistence.conf"); var snapshotConfig = ConfigurationFactory.FromResource("Akka.Persistence.Sql.Linq2Db.snapshot.conf"); - - return journalConfig.WithFallback(snapshotConfig); - } - - public readonly Configuration.Config DefaultJournalConfig; - public readonly Configuration.Config DefaultSnapshotConfig; - public readonly Configuration.Config DefaultConfig; - public readonly Configuration.Config DefaultJournalMappingConfig; - public readonly Configuration.Config DefaultSnapshotMappingConfig; + + DefaultConfiguration = journalConfig.WithFallback(snapshotConfig); + + DefaultJournalConfiguration = DefaultConfiguration.GetConfig(JournalConfigPath); + DefaultSnapshotConfiguration = DefaultConfiguration.GetConfig(SnapshotStoreConfigPath); + + DefaultJournalMappingConfiguration = DefaultJournalConfiguration.GetConfig("default"); + DefaultSnapshotMappingConfiguration = DefaultSnapshotConfiguration.GetConfig("default"); + } public Linq2DbPersistence(ExtendedActorSystem system) { - DefaultConfig = DefaultConfiguration(); system.Settings.InjectTopLevelFallback(DefaultConfig); - - DefaultJournalConfig = DefaultConfig.GetConfig(JournalConfigPath); - DefaultSnapshotConfig = DefaultConfig.GetConfig(SnapshotStoreConfigPath); - - DefaultJournalMappingConfig = DefaultJournalConfig.GetConfig("default"); - DefaultSnapshotMappingConfig = DefaultSnapshotConfig.GetConfig("default"); } public static Linq2DbPersistence Get(ActorSystem system) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/Linq2DbWriteJournal.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/Linq2DbWriteJournal.cs index 8cc49236..5bf84ec0 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/Linq2DbWriteJournal.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/Linq2DbWriteJournal.cs @@ -44,11 +44,8 @@ public static DateTime FromUnixEpochMillis(in long unixEpochMillis) public class Linq2DbWriteJournal : AsyncWriteJournal { - [Obsolete(message: "Use Linq2DbPersistence.Get(ActorSystem).DefaultConfig instead")] - public static readonly Configuration.Config DefaultConfiguration = - ConfigurationFactory.FromResource("Akka.Persistence.Sql.Linq2Db.persistence.conf"); - - public readonly Linq2DbPersistence Extension = Linq2DbPersistence.Get(Context.System); + [Obsolete(message: "Use Linq2DbPersistence.DefaultConfiguration or Linq2DbPersistence.Get(ActorSystem).DefaultConfig instead")] + public static readonly Configuration.Config DefaultConfiguration = Linq2DbPersistence.DefaultConfiguration; private readonly ActorMaterializer _mat; private readonly JournalConfig _journalConfig; @@ -61,7 +58,7 @@ public Linq2DbWriteJournal(Configuration.Config journalConfig) try { - var config = journalConfig.WithFallback(Extension.DefaultJournalConfig); + var config = journalConfig.WithFallback(Linq2DbPersistence.DefaultJournalConfiguration); _journalConfig = new JournalConfig(config); _mat = Materializer.CreateSystemMaterializer( context: (ExtendedActorSystem)Context.System, diff --git a/src/Akka.Persistence.Sql.Linq2Db/Snapshot/Linq2DbSnapshotStore.cs b/src/Akka.Persistence.Sql.Linq2Db/Snapshot/Linq2DbSnapshotStore.cs index 5416b207..4904a86d 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Snapshot/Linq2DbSnapshotStore.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Snapshot/Linq2DbSnapshotStore.cs @@ -19,15 +19,13 @@ public class Linq2DbSnapshotStore : SnapshotStore public static readonly Configuration.Config DefaultConfiguration = ConfigurationFactory.FromResource("Akka.Persistence.Sql.Linq2Db.snapshot.conf"); - public readonly Linq2DbPersistence Extension = Linq2DbPersistence.Get(Context.System); - private readonly SnapshotConfig _snapshotConfig; private readonly ByteArraySnapshotDao _dao; public Linq2DbSnapshotStore(Configuration.Config snapshotConfig) { - var config = snapshotConfig.WithFallback(Extension.DefaultSnapshotConfig); + var config = snapshotConfig.WithFallback(Linq2DbPersistence.DefaultSnapshotConfiguration); _snapshotConfig = new SnapshotConfig(config); diff --git a/src/Akka.Persistence.Sql.Linq2Db/persistence.conf b/src/Akka.Persistence.Sql.Linq2Db/persistence.conf index 3d2d988f..55a9be1e 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/persistence.conf +++ b/src/Akka.Persistence.Sql.Linq2Db/persistence.conf @@ -226,7 +226,7 @@ } } - postgres = ${akka.persistence.journal.linq2db.postgresql} # backward compat naming + # postgres = ${akka.persistence.journal.linq2db.postgresql} # backward compat naming # Akka.Persistence.MySql compatibility table name and column name mapping mysql { From c59c9802df9e4a880dc65c3f9f08aaf3c936b1d1 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 3 Feb 2023 03:51:53 +0700 Subject: [PATCH 20/20] Remove bit flag --- .../Journal/DAO/BaseByteArrayJournalDao.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs index b59f2ddc..1b2c072c 100644 --- a/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs +++ b/src/Akka.Persistence.Sql.Linq2Db/Journal/DAO/BaseByteArrayJournalDao.cs @@ -174,7 +174,7 @@ private async Task WriteJournalRows(Seq xs) private async Task InsertSingle(Seq xs) { - if ((JournalConfig.TableConfig.TagWriteMode & TagWriteMode.TagTable)!=0 && xs.Head.TagArr.Length>0) + if (JournalConfig.TableConfig.TagWriteMode == TagWriteMode.TagTable && xs.Head.TagArr.Length>0) { //Lazy fallback; do the InsertMultiple call here and leave it at that. await InsertMultiple(xs); @@ -219,7 +219,7 @@ await dc.GetTable().BulkCopyAsync(new BulkCopyOptions private async Task InsertMultiple(Seq xs) { - if ((JournalConfig.TableConfig.TagWriteMode & TagWriteMode.TagTable) !=0) + if (JournalConfig.TableConfig.TagWriteMode == TagWriteMode.TagTable) { if (JournalConfig.TableConfig.TagTableMode == TagTableMode.OrderingId) {