From 3efc38dc65cfd3d0814c97a52e41bbd853a6113b Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 4 Sep 2015 14:06:08 +0100 Subject: [PATCH 1/3] Refactored distributed locks to support existing installations. --- .../Services/SetupService.cs | 5 ++ .../Orchard.Setup/Services/SetupService.cs | 5 ++ .../Data/Migration/AutomaticDataMigrations.cs | 22 ++++++++- src/Orchard/Orchard.Framework.csproj | 2 +- .../Locking/Migrations/FrameworkMigrations.cs | 22 --------- .../Services/DistributedLockSchemaBuilder.cs | 47 +++++++++++++++++++ 6 files changed, 79 insertions(+), 24 deletions(-) delete mode 100644 src/Orchard/Tasks/Locking/Migrations/FrameworkMigrations.cs create mode 100644 src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs index c125a21c9..ffb149108 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs @@ -20,6 +20,7 @@ using Orchard.Logging; using Orchard.Recipes.Services; using Orchard.Security; using Orchard.Settings; +using Orchard.Tasks.Locking.Services; using Orchard.Utility.Extensions; namespace Orchard.ImportExport.Services @@ -101,6 +102,10 @@ namespace Orchard.ImportExport.Services schemaBuilder.AlterTable("Orchard_Framework_DataMigrationRecord", table => table.AddUniqueConstraint("UC_DMR_DataMigrationClass_Version", "DataMigrationClass", "Version")); + // Create the distributed lock record schema. + var distributedLockSchemaBuilder = new DistributedLockSchemaBuilder(_shellSettings, schemaBuilder); + distributedLockSchemaBuilder.CreateSchema(); + var dataMigrationManager = environment.Resolve(); dataMigrationManager.Update("Settings"); diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index 16b3e465b..dfff5b008 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -23,6 +23,7 @@ using Orchard.Recipes.Models; using Orchard.Recipes.Services; using Orchard.Security; using Orchard.Settings; +using Orchard.Tasks.Locking.Services; using Orchard.Utility.Extensions; namespace Orchard.Setup.Services { @@ -154,6 +155,10 @@ namespace Orchard.Setup.Services { schemaBuilder.AlterTable("Orchard_Framework_DataMigrationRecord", table => table.AddUniqueConstraint("UC_DMR_DataMigrationClass_Version", "DataMigrationClass", "Version")); + // Create the distributed lock record schema. + var distributedLockSchemaBuilder = new DistributedLockSchemaBuilder(_shellSettings, schemaBuilder); + distributedLockSchemaBuilder.CreateSchema(); + var dataMigrationManager = environment.Resolve(); dataMigrationManager.Update("Settings"); diff --git a/src/Orchard/Data/Migration/AutomaticDataMigrations.cs b/src/Orchard/Data/Migration/AutomaticDataMigrations.cs index 7c0693540..887e0a941 100644 --- a/src/Orchard/Data/Migration/AutomaticDataMigrations.cs +++ b/src/Orchard/Data/Migration/AutomaticDataMigrations.cs @@ -1,6 +1,9 @@ using System; using System.Linq; +using Orchard.Data.Migration.Interpreters; +using Orchard.Data.Migration.Schema; using Orchard.Environment; +using Orchard.Environment.Configuration; using Orchard.Environment.Features; using Orchard.Logging; using Orchard.Tasks.Locking.Services; @@ -13,15 +16,21 @@ namespace Orchard.Data.Migration { private readonly IDataMigrationManager _dataMigrationManager; private readonly IFeatureManager _featureManager; private readonly IDistributedLockService _distributedLockService; + private readonly IDataMigrationInterpreter _dataMigrationInterpreter; + private readonly ShellSettings _shellSettings; public AutomaticDataMigrations( IDataMigrationManager dataMigrationManager, + IDataMigrationInterpreter dataMigrationInterpreter, IFeatureManager featureManager, - IDistributedLockService distributedLockService) { + IDistributedLockService distributedLockService, + ShellSettings shellSettings) { _dataMigrationManager = dataMigrationManager; _featureManager = featureManager; _distributedLockService = distributedLockService; + _shellSettings = shellSettings; + _dataMigrationInterpreter = dataMigrationInterpreter; Logger = NullLogger.Instance; } @@ -30,6 +39,7 @@ namespace Orchard.Data.Migration { public void Activated() { IDistributedLock @lock; + EnsureDistributedLockSchema(); if(_distributedLockService.TryAcquireLock(GetType().FullName, TimeSpan.FromMinutes(30), TimeSpan.FromMilliseconds(250), out @lock)) { using (@lock) { // Let's make sure that the basic set of features is enabled. If there are any that are not enabled, then let's enable them first. @@ -58,5 +68,15 @@ namespace Orchard.Data.Migration { public void Terminating() { // No-op. } + + /// + /// This ensures that the framework migrations have run for the distributed locking feature, as existing Orchard installations will not have the required tables when upgrading. + /// + private void EnsureDistributedLockSchema() { + // Ensure the distributed lock record schema exists. + var schemaBuilder = new SchemaBuilder(_dataMigrationInterpreter); + var distributedLockSchemaBuilder = new DistributedLockSchemaBuilder(_shellSettings, schemaBuilder); + distributedLockSchemaBuilder.EnsureSchema(); + } } } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 6e2f60292..11adc4cf9 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -399,7 +399,7 @@ - + diff --git a/src/Orchard/Tasks/Locking/Migrations/FrameworkMigrations.cs b/src/Orchard/Tasks/Locking/Migrations/FrameworkMigrations.cs deleted file mode 100644 index 2a43a2e19..000000000 --- a/src/Orchard/Tasks/Locking/Migrations/FrameworkMigrations.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Orchard.Data.Migration; - -namespace Orchard.Tasks.Locking.Migrations { - public class FrameworkMigrations : DataMigrationImpl { - - public int Create() { - SchemaBuilder.CreateTable("DistributedLockRecord", table => table - .Column("Id", column => column.PrimaryKey().Identity()) - .Column("Name", column => column.NotNull().WithLength(512).Unique()) - .Column("MachineName", column => column.WithLength(256)) - .Column("CreatedUtc") - .Column("ValidUntilUtc", column => column.Nullable())); - - SchemaBuilder.AlterTable("DistributedLockRecord", table => { - table.CreateIndex("IDX_DistributedLockRecord_Name", "Name"); - }); - - return 1; - } - } -} \ No newline at end of file diff --git a/src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs b/src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs new file mode 100644 index 000000000..8a577d8a1 --- /dev/null +++ b/src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs @@ -0,0 +1,47 @@ +using System; +using Orchard.Data.Migration.Schema; +using Orchard.Environment.Configuration; + +namespace Orchard.Tasks.Locking.Services { + public class DistributedLockSchemaBuilder { + private readonly ShellSettings _shellSettings; + private readonly SchemaBuilder _schemaBuilder; + private const string TableName = "Orchard_Framework_DistributedLockRecord"; + + public DistributedLockSchemaBuilder(ShellSettings shellSettings, SchemaBuilder schemaBuilder) { + _shellSettings = shellSettings; + _schemaBuilder = schemaBuilder; + } + + public void EnsureSchema() { + if (SchemaExists()) + return; + + CreateSchema(); + } + + public void CreateSchema() { + _schemaBuilder.CreateTable(TableName, table => table + .Column("Id", column => column.PrimaryKey().Identity()) + .Column("Name", column => column.NotNull().WithLength(512).Unique()) + .Column("MachineName", column => column.WithLength(256)) + .Column("CreatedUtc") + .Column("ValidUntilUtc", column => column.Nullable())); + + _schemaBuilder.AlterTable(TableName, table => { + table.CreateIndex("IDX_DistributedLockRecord_Name", "Name"); + }); + } + + public bool SchemaExists() { + try { + var tablePrefix = String.IsNullOrEmpty(_shellSettings.DataTablePrefix) ? "" : _shellSettings.DataTablePrefix + "_"; + _schemaBuilder.ExecuteSql(String.Format("select * from {0}{1}", tablePrefix, TableName)); + return true; + } + catch { + return false; + } + } + } +} \ No newline at end of file From 94db186b37fff9fc60a796bff6bc9648a0b7aec3 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 4 Sep 2015 14:43:30 +0100 Subject: [PATCH 2/3] Minor polishing. --- src/Orchard/Data/Migration/AutomaticDataMigrations.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Orchard/Data/Migration/AutomaticDataMigrations.cs b/src/Orchard/Data/Migration/AutomaticDataMigrations.cs index 887e0a941..088903a06 100644 --- a/src/Orchard/Data/Migration/AutomaticDataMigrations.cs +++ b/src/Orchard/Data/Migration/AutomaticDataMigrations.cs @@ -38,8 +38,9 @@ namespace Orchard.Data.Migration { public ILogger Logger { get; set; } public void Activated() { + EnsureDistributedLockSchemaExists(); + IDistributedLock @lock; - EnsureDistributedLockSchema(); if(_distributedLockService.TryAcquireLock(GetType().FullName, TimeSpan.FromMinutes(30), TimeSpan.FromMilliseconds(250), out @lock)) { using (@lock) { // Let's make sure that the basic set of features is enabled. If there are any that are not enabled, then let's enable them first. @@ -72,7 +73,7 @@ namespace Orchard.Data.Migration { /// /// This ensures that the framework migrations have run for the distributed locking feature, as existing Orchard installations will not have the required tables when upgrading. /// - private void EnsureDistributedLockSchema() { + private void EnsureDistributedLockSchemaExists() { // Ensure the distributed lock record schema exists. var schemaBuilder = new SchemaBuilder(_dataMigrationInterpreter); var distributedLockSchemaBuilder = new DistributedLockSchemaBuilder(_shellSettings, schemaBuilder); From e9f883c296ba9734210827844b9bcd466e3a0430 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 8 Sep 2015 20:56:28 +0100 Subject: [PATCH 3/3] Removed unnecessary call to DistributedLockSchemaBuilder. Also added code to commit the transaction if the schema was created so that it can be used in the same request (DataMigrationManager is doing the same after each migration). --- .../Modules/Orchard.Setup/Services/SetupService.cs | 5 ----- src/Orchard/Data/Migration/AutomaticDataMigrations.cs | 6 +++++- .../Tasks/Locking/Services/DistributedLockSchemaBuilder.cs | 5 +++-- .../Tasks/Locking/Services/DistributedLockService.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index dfff5b008..16b3e465b 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -23,7 +23,6 @@ using Orchard.Recipes.Models; using Orchard.Recipes.Services; using Orchard.Security; using Orchard.Settings; -using Orchard.Tasks.Locking.Services; using Orchard.Utility.Extensions; namespace Orchard.Setup.Services { @@ -155,10 +154,6 @@ namespace Orchard.Setup.Services { schemaBuilder.AlterTable("Orchard_Framework_DataMigrationRecord", table => table.AddUniqueConstraint("UC_DMR_DataMigrationClass_Version", "DataMigrationClass", "Version")); - // Create the distributed lock record schema. - var distributedLockSchemaBuilder = new DistributedLockSchemaBuilder(_shellSettings, schemaBuilder); - distributedLockSchemaBuilder.CreateSchema(); - var dataMigrationManager = environment.Resolve(); dataMigrationManager.Update("Settings"); diff --git a/src/Orchard/Data/Migration/AutomaticDataMigrations.cs b/src/Orchard/Data/Migration/AutomaticDataMigrations.cs index 088903a06..ba6462cd1 100644 --- a/src/Orchard/Data/Migration/AutomaticDataMigrations.cs +++ b/src/Orchard/Data/Migration/AutomaticDataMigrations.cs @@ -18,18 +18,21 @@ namespace Orchard.Data.Migration { private readonly IDistributedLockService _distributedLockService; private readonly IDataMigrationInterpreter _dataMigrationInterpreter; private readonly ShellSettings _shellSettings; + private readonly ITransactionManager _transactionManager; public AutomaticDataMigrations( IDataMigrationManager dataMigrationManager, IDataMigrationInterpreter dataMigrationInterpreter, IFeatureManager featureManager, IDistributedLockService distributedLockService, + ITransactionManager transactionManager, ShellSettings shellSettings) { _dataMigrationManager = dataMigrationManager; _featureManager = featureManager; _distributedLockService = distributedLockService; _shellSettings = shellSettings; + _transactionManager = transactionManager; _dataMigrationInterpreter = dataMigrationInterpreter; Logger = NullLogger.Instance; @@ -77,7 +80,8 @@ namespace Orchard.Data.Migration { // Ensure the distributed lock record schema exists. var schemaBuilder = new SchemaBuilder(_dataMigrationInterpreter); var distributedLockSchemaBuilder = new DistributedLockSchemaBuilder(_shellSettings, schemaBuilder); - distributedLockSchemaBuilder.EnsureSchema(); + if (distributedLockSchemaBuilder.EnsureSchema()) + _transactionManager.RequireNew(); } } } diff --git a/src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs b/src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs index 8a577d8a1..2f22f1220 100644 --- a/src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs +++ b/src/Orchard/Tasks/Locking/Services/DistributedLockSchemaBuilder.cs @@ -13,11 +13,12 @@ namespace Orchard.Tasks.Locking.Services { _schemaBuilder = schemaBuilder; } - public void EnsureSchema() { + public bool EnsureSchema() { if (SchemaExists()) - return; + return false; CreateSchema(); + return true; } public void CreateSchema() { diff --git a/src/Orchard/Tasks/Locking/Services/DistributedLockService.cs b/src/Orchard/Tasks/Locking/Services/DistributedLockService.cs index 34ab4332f..05c87b41f 100644 --- a/src/Orchard/Tasks/Locking/Services/DistributedLockService.cs +++ b/src/Orchard/Tasks/Locking/Services/DistributedLockService.cs @@ -110,7 +110,7 @@ namespace Orchard.Tasks.Locking.Services { ); }); - return result; + return result; } private DistributedLock AcquireLockInternal(string name, TimeSpan? maxValidFor) {