Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SQL query transaction isolation level from Akka.NET 1.5.3 #301

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a new database for each unit tests to prevent cross-polination between unit tests


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