Skip to content

Commit

Permalink
Implement SQL query transaction isolation level from Akka.NET 1.5.3 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Arkatufus authored Apr 21, 2023
1 parent 391b679 commit f1754fb
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 70 deletions.
87 changes: 46 additions & 41 deletions src/Akka.Persistence.SqlServer.Tests/DbUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,42 @@
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.IO;
using Microsoft.Data.SqlClient;

namespace Akka.Persistence.SqlServer.Tests
{
public static class DbUtils
{
public static string ConnectionString { get; private set; }
private static SqlConnectionStringBuilder _builder;
public static string ConnectionString => _builder.ToString();

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();
_builder = new SqlConnectionStringBuilder(connectionString);
var databaseName = $"akka_persistence_tests_{Guid.NewGuid()}";
_builder.InitialCatalog = databaseName;

var connectionBuilder = new SqlConnectionStringBuilder(connectionString)
{
InitialCatalog = "master"
};

using (var conn = new SqlConnection(ConnectionString))
using (var conn = new SqlConnection(connectionBuilder.ToString()))
{
conn.Open();

using (var cmd = new SqlCommand())
{
cmd.CommandText = string.Format(@"
IF db_id('{0}') IS NULL
BEGIN
CREATE DATABASE {0}
END
", databaseName);
cmd.CommandText = $@"
IF db_id('{databaseName}') IS NULL
BEGIN
CREATE DATABASE [{databaseName}];
END";
cmd.Connection = conn;

var result = cmd.ExecuteScalar();
cmd.ExecuteScalar();
}

DropTables(conn, databaseName);

// set this back to the journal/snapshot database
connectionBuilder.InitialCatalog = databaseName;
ConnectionString = connectionBuilder.ToString();
}

// Delete local snapshot flat file database
Expand All @@ -55,32 +50,42 @@ CREATE DATABASE {0}

public static void Clean()
{
var connectionBuilder = new SqlConnectionStringBuilder(ConnectionString);
var databaseName = connectionBuilder.InitialCatalog;
using (var conn = new SqlConnection(ConnectionString))
var oldDatabaseName = _builder.InitialCatalog;
var databaseName = $"akka_persistence_tests_{Guid.NewGuid()}";
_builder.InitialCatalog = databaseName;

var connectionBuilder = new SqlConnectionStringBuilder(ConnectionString)
{
InitialCatalog = "master"
};

using (var conn = new SqlConnection(connectionBuilder.ToString()))
{
conn.Open();
DropTables(conn, databaseName);

using (var cmd = new SqlCommand())
{
cmd.CommandText = $@"
IF db_id('{oldDatabaseName}') IS NOT NULL
BEGIN
ALTER DATABASE [{oldDatabaseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [{oldDatabaseName}];
END
IF db_id('{databaseName}') IS NULL
BEGIN
CREATE DATABASE [{databaseName}];
END
";
cmd.Connection = conn;
cmd.ExecuteScalar();
}
}

// Delete local snapshot flat file database
var path = "./snapshots";
if (Directory.Exists(path))
Directory.Delete(path, true);
}

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();
}
}
}
}
214 changes: 214 additions & 0 deletions src/Akka.Persistence.SqlServer.Tests/SqlServerSettingsSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Data;
using Akka.Configuration;
using Akka.Dispatch;
using Akka.Persistence.Sql.Common;
using Akka.Persistence.Sql.Common.Extensions;
using Akka.Persistence.SqlServer.Journal;
using Akka.Persistence.SqlServer.Snapshot;
using FluentAssertions;
using FluentAssertions.Extensions;
using Xunit;
Expand Down Expand Up @@ -32,6 +39,108 @@ public void Should_SqlServer_journal_has_default_config()
config.GetBoolean("auto-initialize").Should().BeFalse();
config.GetString("timestamp-provider").Should()
.Be("Akka.Persistence.Sql.Common.Journal.DefaultTimestampProvider, Akka.Persistence.Sql.Common");
config.GetString("read-isolation-level").Should().Be("unspecified");
config.GetString("write-isolation-level").Should().Be("unspecified");
}

[Fact]
public void SqlServer_JournalSettings_default_should_contain_default_config()
{
var config = SqlServerPersistence.Get(Sys).DefaultJournalConfig;
var settings = new JournalSettings(config);

// values should be correct
settings.ConnectionString.Should().Be(string.Empty);
settings.ConnectionStringName.Should().BeNullOrEmpty();
settings.ConnectionTimeout.Should().Be(TimeSpan.FromSeconds(30));
settings.JournalTableName.Should().Be("EventJournal");
settings.SchemaName.Should().Be("dbo");
settings.MetaTableName.Should().Be("Metadata");
settings.TimestampProvider.Should().Be("Akka.Persistence.Sql.Common.Journal.DefaultTimestampProvider, Akka.Persistence.Sql.Common");
settings.ReadIsolationLevel.Should().Be(IsolationLevel.Unspecified);
settings.WriteIsolationLevel.Should().Be(IsolationLevel.Unspecified);
settings.AutoInitialize.Should().BeFalse();

// values should reflect configuration
settings.ConnectionString.Should().Be(config.GetString("connection-string"));
settings.ConnectionStringName.Should().Be(config.GetString("connection-string-name"));
settings.ConnectionTimeout.Should().Be(config.GetTimeSpan("connection-timeout"));
settings.JournalTableName.Should().Be(config.GetString("table-name"));
settings.SchemaName.Should().Be(config.GetString("schema-name"));
settings.MetaTableName.Should().Be(config.GetString("metadata-table-name"));
settings.TimestampProvider.Should().Be(config.GetString("timestamp-provider"));
settings.ReadIsolationLevel.Should().Be(config.GetIsolationLevel("read-isolation-level"));
settings.WriteIsolationLevel.Should().Be(config.GetIsolationLevel("write-isolation-level"));
settings.AutoInitialize.Should().Be(config.GetBoolean("auto-initialize"));
}

[Fact]
public void Modified_SqlServer_JournalSettings_should_contain_proper_config()
{
var fullConfig = ConfigurationFactory.ParseString(@"
akka.persistence.journal {
sql-server {
connection-string = ""a""
connection-string-name = ""b""
connection-timeout = 3s
table-name = ""c""
auto-initialize = on
metadata-table-name = ""d""
schema-name = ""e""
serializer = ""f""
read-isolation-level = snapshot
write-isolation-level = snapshot
sequential-access = on
}
}").WithFallback(SqlServerPersistence.DefaultConfiguration());

var config = fullConfig.GetConfig("akka.persistence.journal.sql-server");
var settings = new JournalSettings(config);
var executorConfig = SqlServerJournal.CreateQueryConfiguration(config, settings);

// values should be correct
settings.ConnectionString.Should().Be("a");
settings.ConnectionStringName.Should().Be("b");
settings.JournalTableName.Should().Be("c");
settings.MetaTableName.Should().Be("d");
settings.SchemaName.Should().Be("e");
settings.ConnectionTimeout.Should().Be(TimeSpan.FromSeconds(3));
settings.ReadIsolationLevel.Should().Be(IsolationLevel.Snapshot);
settings.WriteIsolationLevel.Should().Be(IsolationLevel.Snapshot);
settings.AutoInitialize.Should().BeTrue();

executorConfig.JournalEventsTableName.Should().Be("c");
executorConfig.MetaTableName.Should().Be("d");
executorConfig.SchemaName.Should().Be("e");
#pragma warning disable CS0618
executorConfig.DefaultSerializer.Should().Be("f");
#pragma warning restore CS0618
executorConfig.Timeout.Should().Be(TimeSpan.FromSeconds(3));
executorConfig.ReadIsolationLevel.Should().Be(IsolationLevel.Snapshot);
executorConfig.WriteIsolationLevel.Should().Be(IsolationLevel.Snapshot);
executorConfig.UseSequentialAccess.Should().BeTrue();

// values should reflect configuration
settings.ConnectionString.Should().Be(config.GetString("connection-string"));
settings.ConnectionStringName.Should().Be(config.GetString("connection-string-name"));
settings.ConnectionTimeout.Should().Be(config.GetTimeSpan("connection-timeout"));
settings.JournalTableName.Should().Be(config.GetString("table-name"));
settings.SchemaName.Should().Be(config.GetString("schema-name"));
settings.MetaTableName.Should().Be(config.GetString("metadata-table-name"));
settings.ReadIsolationLevel.Should().Be(config.GetIsolationLevel("read-isolation-level"));
settings.WriteIsolationLevel.Should().Be(config.GetIsolationLevel("write-isolation-level"));
settings.AutoInitialize.Should().Be(config.GetBoolean("auto-initialize"));

executorConfig.JournalEventsTableName.Should().Be(config.GetString("table-name"));
executorConfig.MetaTableName.Should().Be(config.GetString("metadata-table-name"));
executorConfig.SchemaName.Should().Be(config.GetString("schema-name"));
#pragma warning disable CS0618
executorConfig.DefaultSerializer.Should().Be(config.GetString("serializer"));
#pragma warning restore CS0618
executorConfig.Timeout.Should().Be(config.GetTimeSpan("connection-timeout"));
executorConfig.ReadIsolationLevel.Should().Be(config.GetIsolationLevel("read-isolation-level"));
executorConfig.WriteIsolationLevel.Should().Be(config.GetIsolationLevel("write-isolation-level"));
executorConfig.UseSequentialAccess.Should().Be(config.GetBoolean("auto-initialize"));
}

[Fact]
Expand All @@ -50,6 +159,111 @@ public void Should_SqlServer_snapshot_has_default_config()
config.GetString("schema-name").Should().Be("dbo");
config.GetString("table-name").Should().Be("SnapshotStore");
config.GetBoolean("auto-initialize").Should().BeFalse();
config.GetString("read-isolation-level").Should().Be("unspecified");
config.GetString("write-isolation-level").Should().Be("unspecified");
}

[Fact]
public void SqlServer_SnapshotStoreSettings_default_should_contain_default_config()
{
var config = SqlServerPersistence.Get(Sys).DefaultSnapshotConfig;
var settings = new SnapshotStoreSettings(config);

// values should be correct
settings.ConnectionString.Should().Be(string.Empty);
settings.ConnectionStringName.Should().BeNullOrEmpty();
settings.ConnectionTimeout.Should().Be(TimeSpan.FromSeconds(30));
settings.SchemaName.Should().Be("dbo");
settings.TableName.Should().Be("SnapshotStore");
settings.AutoInitialize.Should().BeFalse();
#pragma warning disable CS0618
settings.DefaultSerializer.Should().BeNullOrEmpty();
#pragma warning restore CS0618
settings.ReadIsolationLevel.Should().Be(IsolationLevel.Unspecified);
settings.WriteIsolationLevel.Should().Be(IsolationLevel.Unspecified);
settings.FullTableName.Should().Be($"{settings.SchemaName}.{settings.TableName}");

// values should reflect configuration
settings.ConnectionString.Should().Be(config.GetString("connection-string"));
settings.ConnectionStringName.Should().Be(config.GetString("connection-string-name"));
settings.ConnectionTimeout.Should().Be(config.GetTimeSpan("connection-timeout"));
settings.SchemaName.Should().Be(config.GetString("schema-name"));
settings.TableName.Should().Be(config.GetString("table-name"));
settings.ReadIsolationLevel.Should().Be(config.GetIsolationLevel("read-isolation-level"));
settings.WriteIsolationLevel.Should().Be(config.GetIsolationLevel("write-isolation-level"));
settings.AutoInitialize.Should().Be(config.GetBoolean("auto-initialize"));
#pragma warning disable CS0618
settings.DefaultSerializer.Should().Be(config.GetString("serializer"));
#pragma warning restore CS0618
}

[Fact]
public void Modified_SqlServer_SnapshotStoreSettings_should_contain_proper_config()
{
var fullConfig = ConfigurationFactory.ParseString(@"
akka.persistence.snapshot-store.sql-server
{
connection-string = ""a""
connection-string-name = ""b""
connection-timeout = 3s
table-name = ""c""
auto-initialize = on
serializer = ""d""
schema-name = ""e""
sequential-access = on
read-isolation-level = snapshot
write-isolation-level = snapshot
}").WithFallback(SqlServerPersistence.DefaultConfiguration());

var config = fullConfig.GetConfig("akka.persistence.snapshot-store.sql-server");
var settings = new SnapshotStoreSettings(config);
var executorConfig = SqlServerSnapshotStore.CreateQueryConfiguration(config, settings);

// values should be correct
settings.ConnectionString.Should().Be("a");
settings.ConnectionStringName.Should().Be("b");
settings.ConnectionTimeout.Should().Be(TimeSpan.FromSeconds(3));
settings.TableName.Should().Be("c");
#pragma warning disable CS0618
settings.DefaultSerializer.Should().Be("d");
#pragma warning restore CS0618
settings.SchemaName.Should().Be("e");
settings.AutoInitialize.Should().BeTrue();
settings.ReadIsolationLevel.Should().Be(IsolationLevel.Snapshot);
settings.WriteIsolationLevel.Should().Be(IsolationLevel.Snapshot);

executorConfig.SnapshotTableName.Should().Be("c");
#pragma warning disable CS0618
executorConfig.DefaultSerializer.Should().Be("d");
#pragma warning restore CS0618
executorConfig.SchemaName.Should().Be("e");
executorConfig.Timeout.Should().Be(TimeSpan.FromSeconds(3));
executorConfig.ReadIsolationLevel.Should().Be(IsolationLevel.Snapshot);
executorConfig.WriteIsolationLevel.Should().Be(IsolationLevel.Snapshot);
executorConfig.UseSequentialAccess.Should().BeTrue();

// values should reflect configuration
settings.ConnectionString.Should().Be(config.GetString("connection-string"));
settings.ConnectionStringName.Should().Be(config.GetString("connection-string-name"));
settings.ConnectionTimeout.Should().Be(config.GetTimeSpan("connection-timeout"));
settings.TableName.Should().Be(config.GetString("table-name"));
#pragma warning disable CS0618
settings.DefaultSerializer.Should().Be(config.GetString("serializer"));
#pragma warning restore CS0618
settings.SchemaName.Should().Be(config.GetString("schema-name"));
settings.AutoInitialize.Should().Be(config.GetBoolean("auto-initialize"));
settings.ReadIsolationLevel.Should().Be(config.GetIsolationLevel("read-isolation-level"));
settings.WriteIsolationLevel.Should().Be(config.GetIsolationLevel("write-isolation-level"));

executorConfig.SnapshotTableName.Should().Be(config.GetString("table-name"));
#pragma warning disable CS0618
executorConfig.DefaultSerializer.Should().Be(config.GetString("serializer"));
#pragma warning restore CS0618
executorConfig.SchemaName.Should().Be(config.GetString("schema-name"));
executorConfig.Timeout.Should().Be(config.GetTimeSpan("connection-timeout"));
executorConfig.ReadIsolationLevel.Should().Be(config.GetIsolationLevel("read-isolation-level"));
executorConfig.WriteIsolationLevel.Should().Be(config.GetIsolationLevel("write-isolation-level"));
executorConfig.UseSequentialAccess.Should().Be(config.GetBoolean("sequential-access"));
}
}
}
Loading

0 comments on commit f1754fb

Please sign in to comment.