Compare commits

...

9 Commits

9 changed files with 123 additions and 23 deletions

View File

@@ -18,10 +18,10 @@
<components>
<!--
Uncomment to use ReadUncommitted as the default isolation level. Please not that
Sql Server Ce doesn't support ReadUncommitted.
Uncomment to use ReadUncommitted as the default isolation level.
Please note that Sql Server Ce doesn't support ReadUncommitted.
Isolation level for all database transaction.
Isolation level for all database transactions.
See http://msdn.microsoft.com/en-us/library/system.transactions.isolationlevel.aspx
-->
<!--
@@ -33,6 +33,40 @@
</properties>
</component>
-->
<!--
Uncomment to use ReadUncommitted as the isolation level for data migrations.
Please note that Sql Server Ce doesn't support ReadUncommitted.
Isolation level for data migration database transactions.
See http://msdn.microsoft.com/en-us/library/system.transactions.isolationlevel.aspx
-->
<component instance-scope="per-lifetime-scope"
type="Orchard.Data.Migration.DataMigrationManager, Orchard.Framework"
service="Orchard.Data.Migration.IDataMigrationManager">
<properties>
<property name="IsolationLevel" value="ReadUncommitted" />
</properties>
</component>
<!--
Uncomment to use ReadUncommitted as the isolation level for background tasks.
Please note that Sql Server Ce doesn't support ReadUncommitted.
Isolation level for background task database transactions.
See http://msdn.microsoft.com/en-us/library/system.transactions.isolationlevel.aspx
-->
<!--
<component instance-scope="per-lifetime-scope"
type="Orchard.Tasks.BackgroundService, Orchard.Framework"
service="Orchard.Tasks.IBackgroundService">
<properties>
<property name="IsolationLevel" value="ReadUncommitted" />
</properties>
</component>
-->
<!--
Delay between background services executions

View File

@@ -11,5 +11,9 @@ namespace Orchard.Commands {
public void Sweep() {
// Don't run any background service in command line
}
public void Terminate() {
// No need to terminate anything here
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using Orchard.ContentManagement.MetaData;
@@ -21,7 +22,6 @@ namespace Orchard.Data.Migration {
private readonly IDataMigrationInterpreter _interpreter;
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly ITransactionManager _transactionManager;
private readonly List<string> _processedFeatures;
public DataMigrationManager(
@@ -31,6 +31,7 @@ namespace Orchard.Data.Migration {
IDataMigrationInterpreter interpreter,
IContentDefinitionManager contentDefinitionManager,
ITransactionManager transactionManager) {
_dataMigrations = dataMigrations;
_dataMigrationRepository = dataMigrationRepository;
_extensionManager = extensionManager;
@@ -40,10 +41,11 @@ namespace Orchard.Data.Migration {
_processedFeatures = new List<string>();
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
IsolationLevel = IsolationLevel.ReadCommitted;
}
public Localizer T { get; set; }
public ILogger Logger { get; set; }
public IsolationLevel IsolationLevel { get; set; }
public IEnumerable<string> GetFeaturesThatNeedUpdate() {
var currentVersions = _dataMigrationRepository.Table.ToDictionary(r => r.DataMigrationClass);
@@ -91,7 +93,7 @@ namespace Orchard.Data.Migration {
// apply update methods to each migration class for the module
foreach (var migration in migrations) {
_transactionManager.RequireNew();
_transactionManager.RequireNew(IsolationLevel);
// copy the object for the Linq query
var tempMigration = migration;
@@ -119,11 +121,11 @@ namespace Orchard.Data.Migration {
while (lookupTable.ContainsKey(current)) {
try {
Logger.Information("Applying migration for {0} from version {1}.", feature, current);
Logger.Information("Applying migration for {0} from version {1}", feature, current);
current = (int)lookupTable[current].Invoke(migration, new object[0]);
}
catch (Exception ex) {
Logger.Error(ex, "An unexpected error occurred while applying migration on {0} from version {1}.", feature, current);
Logger.Error(ex, "An unexpected error occurred while applying migration on {0} from version {1}", feature, current);
throw;
}
}
@@ -140,16 +142,15 @@ namespace Orchard.Data.Migration {
}
}
catch (Exception e) {
Logger.Error(e, "Error while running migration version {0} for {1}.", current, feature);
Logger.Error(e, "Error while running migration version {0} for {1}", current, feature);
_transactionManager.Cancel();
throw new OrchardException(T("Error while running migration version {0} for {1}.", current, feature), e);
}
}
}
public void Uninstall(string feature) {
Logger.Information("Uninstalling feature: {0}.", feature);
Logger.Information("Uninstalling feature: {0}", feature);
var migrations = GetDataMigrations(feature);

View File

@@ -244,6 +244,10 @@ namespace Orchard.Environment {
StartUpdatedShells();
}
public void Stop(bool immediate) {
DisposeShellContext();
}
void IShellSettingsManagerEventHandler.Saved(ShellSettings settings) {
Logger.Debug("Shell saved: " + settings.Name);

View File

@@ -1,9 +1,9 @@
using System.Web.Hosting;
using Orchard.Environment.Configuration;
using Orchard.Environment.ShellBuilders;
using Orchard.Localization;
namespace Orchard.Environment {
public interface IOrchardHost {
public interface IOrchardHost : IRegisteredObject {
/// <summary>
/// Called once on startup to configure app domain, and load/apply existing shell configuration
/// </summary>

View File

@@ -335,6 +335,7 @@
<Compile Include="Services\IJsonConverter.cs" />
<Compile Include="Settings\CurrentSiteWorkContext.cs" />
<Compile Include="Settings\ResourceDebugMode.cs" />
<Compile Include="Tasks\ITerminatable.cs" />
<Compile Include="Themes\CurrentThemeWorkContext.cs" />
<Compile Include="Themes\ThemeManager.cs" />
<Compile Include="Time\CurrentTimeZoneWorkContext.cs" />

View File

@@ -1,15 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using JetBrains.Annotations;
using Orchard.Data;
using Orchard.Environment.Configuration;
using Orchard.Logging;
using Orchard.ContentManagement;
namespace Orchard.Tasks {
public interface IBackgroundService : IDependency {
void Sweep();
void Terminate();
}
[UsedImplicitly]
@@ -17,32 +22,64 @@ namespace Orchard.Tasks {
private readonly IEnumerable<IBackgroundTask> _tasks;
private readonly ITransactionManager _transactionManager;
private readonly string _shellName;
private readonly IContentManager _contentManager;
private bool _shuttingDown;
private AutoResetEvent _finishedEvent = new AutoResetEvent(true);
public BackgroundService(
IEnumerable<IBackgroundTask> tasks,
ITransactionManager transactionManager,
ShellSettings shellSettings,
IContentManager contentManager) {
ShellSettings shellSettings) {
_tasks = tasks;
_transactionManager = transactionManager;
_shellName = shellSettings.Name;
_contentManager = contentManager;
Logger = NullLogger.Instance;
IsolationLevel = IsolationLevel.ReadCommitted;
}
public ILogger Logger { get; set; }
public IsolationLevel IsolationLevel { get; set; }
public void Sweep() {
foreach(var task in _tasks) {
if (_shuttingDown) {
return;
}
try {
_transactionManager.RequireNew();
_finishedEvent.Reset();
_transactionManager.RequireNew(IsolationLevel);
task.Sweep();
}
catch (Exception e) {
_transactionManager.Cancel();
Logger.Error(e, "Error while processing background task on tenant '{0}'.", _shellName);
}
finally
{
_finishedEvent.Set();
}
}
}
public void Terminate() {
Logger.Debug("Background service terminating...");
_shuttingDown = true;
foreach (var task in _tasks.Where(t => t is ITerminatable)) {
try {
((ITerminatable)task).Terminate();
}
catch (Exception ex) {
Logger.Error(ex, "Error while terminating background task {0}", task.GetType().Name);
}
}
if (_finishedEvent != null) {
_finishedEvent.WaitOne(TimeSpan.FromSeconds(90));
_finishedEvent.Dispose();
_finishedEvent = null;
}
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Tasks {
public interface ITerminatable {
void Terminate();
}
}

View File

@@ -1,10 +1,12 @@
using System;
using System.Runtime.InteropServices;
using System.Timers;
using System.Web.Hosting;
using Orchard.Logging;
namespace Orchard.Tasks {
public interface ISweepGenerator : ISingletonDependency {
public interface ISweepGenerator : IRegisteredObject, ISingletonDependency {
void Activate();
void Terminate();
}
@@ -12,6 +14,9 @@ namespace Orchard.Tasks {
public class SweepGenerator : ISweepGenerator, IDisposable {
private readonly IWorkContextAccessor _workContextAccessor;
private readonly Timer _timer;
private bool _shuttingDown;
private IBackgroundService _manager;
public SweepGenerator(IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
@@ -19,6 +24,8 @@ namespace Orchard.Tasks {
_timer.Elapsed += Elapsed;
Logger = NullLogger.Instance;
Interval = TimeSpan.FromMinutes(1);
HostingEnvironment.RegisterObject(this);
}
public ILogger Logger { get; set; }
@@ -46,7 +53,7 @@ namespace Orchard.Tasks {
return;
try {
if (_timer.Enabled) {
if (_timer.Enabled && !_shuttingDown) {
DoWork();
}
}
@@ -61,13 +68,20 @@ namespace Orchard.Tasks {
public void DoWork() {
using (var scope = _workContextAccessor.CreateWorkContextScope()) {
// resolve the manager and invoke it
var manager = scope.Resolve<IBackgroundService>();
manager.Sweep();
_manager = scope.Resolve<IBackgroundService>();
_manager.Sweep();
}
}
public void Dispose() {
_timer.Dispose();
}
public void Stop(bool immediate) {
_shuttingDown = true;
_manager.Terminate();
HostingEnvironment.UnregisterObject(this);
}
}
}