From e53c539de65e724da03aa6b2516ad9e7fc41c432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20L=C3=A9caillon?= Date: Fri, 27 Jan 2023 21:23:04 +0100 Subject: [PATCH] Add timeout for ambient transactions (#295) --- src/Evolve.Cli/EvolveFactory.cs | 1 + src/Evolve.Cli/Program.cs | 4 +- src/Evolve.Tool/Evolve.Tool.csproj | 16 +----- .../Configuration/IEvolveConfiguration.cs | 6 +++ src/Evolve/Evolve.cs | 51 ++++++++++++++++--- src/Evolve/Evolve.nuspec | 15 +----- test/Evolve.Tests/EvolveConfiguration.cs | 1 + 7 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/Evolve.Cli/EvolveFactory.cs b/src/Evolve.Cli/EvolveFactory.cs index 58ae21a1..5264a405 100644 --- a/src/Evolve.Cli/EvolveFactory.cs +++ b/src/Evolve.Cli/EvolveFactory.cs @@ -37,6 +37,7 @@ public static Evolve Build(Program options, Action logInfoDelegate = nul PlaceholderSuffix = options.PlaceholderSuffix, Encoding = ParseEncoding(options.Encoding), CommandTimeout = options.CommandTimeout, + AmbientTransactionTimeout = options.AmbientTransactionTimeout, OutOfOrder = options.OutOfOrder, IsEraseDisabled = options.EraseDisabled, MustEraseOnValidationError = options.EraseOnValidationError, diff --git a/src/Evolve.Cli/Program.cs b/src/Evolve.Cli/Program.cs index 09ea69e7..e9e89b7a 100644 --- a/src/Evolve.Cli/Program.cs +++ b/src/Evolve.Cli/Program.cs @@ -15,7 +15,6 @@ class Program static int Main(string[] args) => CommandLineApplication.Execute(args); - [SuppressMessage("Design", "CA1031: Do not catch general exception types")] [SuppressMessage("Qualité du code", "IDE0051: Supprimer les membres privés non utilisés")] private int OnExecute(CommandLineApplication app, IConsole console) { @@ -91,6 +90,9 @@ private int OnExecute(CommandLineApplication app, IConsole console) [Option("--command-timeout", "The wait time in seconds before terminating the attempt to execute a migration and generating an error. Default: 30", CommandOptionType.SingleValue)] public int? CommandTimeout { get; } = Default.CommandTimeout; + [Option("--ambient-tx-timeout", "The wait time in seconds before terminating the attempt to execute a migration and generating an error in the ambient transaction. Default: 60", CommandOptionType.SingleValue)] + public int? AmbientTransactionTimeout { get; } = Default.AmbientTransactionTimeout; + [Option("--out-of-order", "Allows migration scripts to be run “out of order”. Default: false", CommandOptionType.SingleValue)] public bool OutOfOrder { get; } = Default.OutOfOrder; diff --git a/src/Evolve.Tool/Evolve.Tool.csproj b/src/Evolve.Tool/Evolve.Tool.csproj index f3330525..8b5dbb4d 100644 --- a/src/Evolve.Tool/Evolve.Tool.csproj +++ b/src/Evolve.Tool/Evolve.Tool.csproj @@ -23,21 +23,7 @@ Every time you build your project, it will automatically ensure that your databa https://github.com/lecaillon/Evolve.git evolve flyway dotnet tool sql database migration mysql sqlserver cassandra mariadb sqlite postgresql cockroachdb ## Features -- #218 Add new option -- evolve-repeat-always at the beginning of the script to always execute a repeatable migration -- #220 Add new option MigrationLoader to help you customize the Evolve migration collect process and enable your own specific logic -- #222 Enable quotes in migration name -- #224 Add SQL Server GO delimiter support in SQL comment -- #228 Add new command Validate -- #249 Add support of secrets in the connection string -- #267 Add support for using Azure-issued access tokens with SQL Server connections -- #274 Skip Hidden and System migration files by default - -## Breaking changes -- #250 Drop support of .NET Core 3.1, add support of .NET6 - -## Bug fixes -- #252 Fix transaction not completely rolled back with option OutOfOrder -- #253 Fix drop Postgresql objects with dependencies +- #293 Add timeout for ambient transactions diff --git a/src/Evolve/Configuration/IEvolveConfiguration.cs b/src/Evolve/Configuration/IEvolveConfiguration.cs index 16e663e2..aad44a61 100644 --- a/src/Evolve/Configuration/IEvolveConfiguration.cs +++ b/src/Evolve/Configuration/IEvolveConfiguration.cs @@ -172,6 +172,12 @@ public interface IEvolveConfiguration /// int? CommandTimeout { get; } + /// + /// Defines the wait time before terminating the attempt to execute + /// a migration and generating an error in the ambient transaction. (The default is 60 seconds.) + /// + int? AmbientTransactionTimeout { get; } + /// /// When set, Evolve will scan the given list of assembly to load embedded migration scripts. /// diff --git a/src/Evolve/Evolve.cs b/src/Evolve/Evolve.cs index f80a227f..64510adb 100644 --- a/src/Evolve/Evolve.cs +++ b/src/Evolve/Evolve.cs @@ -71,6 +71,7 @@ public string MetadataTableSchema public bool EnableClusterMode { get; set; } = true; public bool OutOfOrder { get; set; } = false; public int? CommandTimeout { get; set; } + public int? AmbientTransactionTimeout { get; set; } public IEnumerable EmbeddedResourceAssemblies { get; set; } = new List(); public IEnumerable EmbeddedResourceFilters { get; set; } = new List(); public bool RetryRepeatableMigrationsUntilNoError { get; set; } @@ -397,17 +398,40 @@ private void InternalMigrate(DatabaseHelper db) } else { - using var scope = new TransactionScope(); - db.WrappedConnection.UseAmbientTransaction(); - lastAppliedVersion = Migrate(); - - if (TransactionMode == TransactionKind.CommitAll) + TransactionScope scope; + var defaultAmbientTransactionTimeout = TransactionManager.DefaultTimeout; + if (AmbientTransactionTimeout != null) { - scope.Complete(); + var newAmbientTransactionTimeout = new TimeSpan(0, 0, AmbientTransactionTimeout.Value); + ConfigureTransactionTimeoutCore(newAmbientTransactionTimeout); + scope = new TransactionScope(TransactionScopeOption.Required, newAmbientTransactionTimeout); } else { - LogRollbackAppliedMigration(); + scope = new TransactionScope(); + } + + try + { + db.WrappedConnection.UseAmbientTransaction(); + lastAppliedVersion = Migrate(); + + if (TransactionMode == TransactionKind.CommitAll) + { + scope.Complete(); + } + else + { + LogRollbackAppliedMigration(); + } + } + finally + { + scope.Dispose(); + if (AmbientTransactionTimeout != null) + { + ConfigureTransactionTimeoutCore(defaultAmbientTransactionTimeout); + } } } @@ -436,6 +460,19 @@ MigrationVersion Migrate() } } + private static void ConfigureTransactionTimeoutCore(TimeSpan timeout) + { + SetTransactionManagerField("s_cachedMaxTimeout", true); + SetTransactionManagerField("s_maximumTimeout", timeout); + + static void SetTransactionManagerField(string fieldName, object value) + { + typeof(TransactionManager) + .GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static)! + .SetValue(null, value); + } + } + /// /// Execute OutOfOrder migration when allowed and needed /// diff --git a/src/Evolve/Evolve.nuspec b/src/Evolve/Evolve.nuspec index ccecf084..6d2a77ea 100644 --- a/src/Evolve/Evolve.nuspec +++ b/src/Evolve/Evolve.nuspec @@ -14,20 +14,7 @@ Every time you build your project, it will automatically ensure that your databa https://raw.githubusercontent.com/lecaillon/Evolve/master/images/logo128.png false ## Features -- #218 Add new option -- evolve-repeat-always at the beginning of the script to always execute a repeatable migration -- #220 Add new option MigrationLoader to help you customize the Evolve migration collect process and enable your own specific logic -- #222 Enable quotes in migration name -- #224 Add SQL Server GO delimiter support in SQL comment -- #228 Add new command Validate -- #274 Skip Hidden and System migration files by default -- #262 Allow to set default DBMS in Evolve constructor to skip auto-detection - -## Breaking changes -- #245 Rename the namespace Evolve to `EvolveDb` to avoid name collision when using the class Evolve - -## Bug fixes -- #252 Fix transaction not completely rolled back with option OutOfOrder -- #253 Fix drop Postgresql objects with dependencies +- #293 Add timeout for ambient transactions evolve flyway sql database migration mysql sqlserver cassandra mariadb sqlite postgresql cockroachdb diff --git a/test/Evolve.Tests/EvolveConfiguration.cs b/test/Evolve.Tests/EvolveConfiguration.cs index 07561009..afcce44f 100644 --- a/test/Evolve.Tests/EvolveConfiguration.cs +++ b/test/Evolve.Tests/EvolveConfiguration.cs @@ -35,6 +35,7 @@ public string MetadataTableSchema public bool EnableClusterMode { get; set; } = true; public bool OutOfOrder { get; set; } = false; public int? CommandTimeout { get; set; } + public int? AmbientTransactionTimeout { get; set; } public IEnumerable EmbeddedResourceAssemblies { get; set; } = new List(); public IEnumerable EmbeddedResourceFilters { get; set; } = new List(); public bool RetryRepeatableMigrationsUntilNoError { get; set; }