Skip to content

Commit

Permalink
Add timeout for ambient transactions (#295)
Browse files Browse the repository at this point in the history
  • Loading branch information
lecaillon committed Jan 27, 2023
1 parent 0cda29d commit e53c539
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 37 deletions.
1 change: 1 addition & 0 deletions src/Evolve.Cli/EvolveFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static Evolve Build(Program options, Action<string> logInfoDelegate = nul
PlaceholderSuffix = options.PlaceholderSuffix,
Encoding = ParseEncoding(options.Encoding),
CommandTimeout = options.CommandTimeout,
AmbientTransactionTimeout = options.AmbientTransactionTimeout,
OutOfOrder = options.OutOfOrder,
IsEraseDisabled = options.EraseDisabled,
MustEraseOnValidationError = options.EraseOnValidationError,
Expand Down
4 changes: 3 additions & 1 deletion src/Evolve.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class Program

static int Main(string[] args) => CommandLineApplication.Execute<Program>(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)
{
Expand Down Expand Up @@ -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;

Expand Down
16 changes: 1 addition & 15 deletions src/Evolve.Tool/Evolve.Tool.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,7 @@ Every time you build your project, it will automatically ensure that your databa
<RepositoryUrl>https://github.com/lecaillon/Evolve.git</RepositoryUrl>
<PackageTags>evolve flyway dotnet tool sql database migration mysql sqlserver cassandra mariadb sqlite postgresql cockroachdb</PackageTags>
<PackageReleaseNotes>## 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</PackageReleaseNotes>
- #293 Add timeout for ambient transactions</PackageReleaseNotes>
</PropertyGroup>

<ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions src/Evolve/Configuration/IEvolveConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ public interface IEvolveConfiguration
/// </summary>
int? CommandTimeout { get; }

/// <summary>
/// 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.)
/// </summary>
int? AmbientTransactionTimeout { get; }

/// <summary>
/// When set, Evolve will scan the given list of assembly to load embedded migration scripts.
/// </summary>
Expand Down
51 changes: 44 additions & 7 deletions src/Evolve/Evolve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Assembly> EmbeddedResourceAssemblies { get; set; } = new List<Assembly>();
public IEnumerable<string> EmbeddedResourceFilters { get; set; } = new List<string>();
public bool RetryRepeatableMigrationsUntilNoError { get; set; }
Expand Down Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -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);
}
}

/// <summary>
/// Execute OutOfOrder migration when allowed and needed
/// </summary>
Expand Down
15 changes: 1 addition & 14 deletions src/Evolve/Evolve.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,7 @@ Every time you build your project, it will automatically ensure that your databa
<iconUrl>https://raw.githubusercontent.com/lecaillon/Evolve/master/images/logo128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes>## 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</releaseNotes>
- #293 Add timeout for ambient transactions</releaseNotes>
<tags>evolve flyway sql database migration mysql sqlserver cassandra mariadb sqlite postgresql cockroachdb</tags>
<dependencies>
<group targetFramework="netstandard2.0" />
Expand Down
1 change: 1 addition & 0 deletions test/Evolve.Tests/EvolveConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Assembly> EmbeddedResourceAssemblies { get; set; } = new List<Assembly>();
public IEnumerable<string> EmbeddedResourceFilters { get; set; } = new List<string>();
public bool RetryRepeatableMigrationsUntilNoError { get; set; }
Expand Down

0 comments on commit e53c539

Please sign in to comment.