Using lock files instead of IndexSynLock

--HG--
branch : indexing
This commit is contained in:
Sebastien Ros
2011-03-03 14:19:02 -08:00
parent f50e47bfdd
commit 32efbc19cc
5 changed files with 59 additions and 52 deletions

View File

@@ -19,12 +19,14 @@ using Orchard.Environment;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
using Orchard.FileSystems.AppData; using Orchard.FileSystems.AppData;
using Orchard.FileSystems.LockFile;
using Orchard.Indexing; using Orchard.Indexing;
using Orchard.Indexing.Handlers; using Orchard.Indexing.Handlers;
using Orchard.Indexing.Models; using Orchard.Indexing.Models;
using Orchard.Indexing.Services; using Orchard.Indexing.Services;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Security; using Orchard.Security;
using Orchard.Services;
using Orchard.Tasks.Indexing; using Orchard.Tasks.Indexing;
using Orchard.Tests.FileSystems.AppData; using Orchard.Tests.FileSystems.AppData;
using Orchard.Tests.Stubs; using Orchard.Tests.Stubs;
@@ -38,10 +40,12 @@ namespace Orchard.Tests.Modules.Indexing {
private IContentManager _contentManager; private IContentManager _contentManager;
private Mock<IContentDefinitionManager> _contentDefinitionManager; private Mock<IContentDefinitionManager> _contentDefinitionManager;
private StubLogger _logger; private StubLogger _logger;
private const string IndexName = "Search"; private ILockFileManager _lockFileManager;
private const string IndexName = "Search";
private readonly string _basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); private readonly string _basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
[TestFixtureTearDown] [TestFixtureTearDown]
public void Clean() { public void Clean() {
if (Directory.Exists(_basePath)) { if (Directory.Exists(_basePath)) {
@@ -62,7 +66,6 @@ namespace Orchard.Tests.Modules.Indexing {
builder.RegisterType<IndexingTaskExecutor>().As<IIndexNotifierHandler>(); builder.RegisterType<IndexingTaskExecutor>().As<IIndexNotifierHandler>();
builder.RegisterType<DefaultIndexManager>().As<IIndexManager>(); builder.RegisterType<DefaultIndexManager>().As<IIndexManager>();
builder.RegisterType<IndexingTaskManager>().As<IIndexingTaskManager>(); builder.RegisterType<IndexingTaskManager>().As<IIndexingTaskManager>();
builder.RegisterType<IndexSynLock>().As<IIndexSynLock>();
builder.RegisterType<DefaultContentManager>().As<IContentManager>(); builder.RegisterType<DefaultContentManager>().As<IContentManager>();
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>(); builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
builder.RegisterInstance(_contentDefinitionManager.Object); builder.RegisterInstance(_contentDefinitionManager.Object);
@@ -80,6 +83,9 @@ namespace Orchard.Tests.Modules.Indexing {
builder.RegisterType<BodyPartHandler>().As<IContentHandler>(); builder.RegisterType<BodyPartHandler>().As<IContentHandler>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>(); builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
builder.RegisterType<DefaultLockFileManager>().As<ILockFileManager>();
builder.RegisterInstance<IClock>(_clock = new StubClock());
// setting up a ShellSettings instance // setting up a ShellSettings instance
_shellSettings = new ShellSettings { Name = "My Site" }; _shellSettings = new ShellSettings { Name = "My Site" };
builder.RegisterInstance(_shellSettings).As<ShellSettings>(); builder.RegisterInstance(_shellSettings).As<ShellSettings>();
@@ -100,7 +106,7 @@ namespace Orchard.Tests.Modules.Indexing {
public override void Init() { public override void Init() {
base.Init(); base.Init();
_lockFileManager = _container.Resolve<ILockFileManager>();
_provider = _container.Resolve<IIndexProvider>(); _provider = _container.Resolve<IIndexProvider>();
_indexNotifier = _container.Resolve<IIndexNotifierHandler>(); _indexNotifier = _container.Resolve<IIndexNotifierHandler>();
_contentManager = _container.Resolve<IContentManager>(); _contentManager = _container.Resolve<IContentManager>();
@@ -116,10 +122,6 @@ namespace Orchard.Tests.Modules.Indexing {
.Returns(thingType); .Returns(thingType);
} }
private string[] Indexes() {
return new DirectoryInfo(Path.Combine(_basePath, "Sites", "My Site", "Indexes")).GetDirectories().Select(d => d.Name).ToArray();
}
[Test] [Test]
public void IndexShouldBeEmptyWhenThereIsNoContent() { public void IndexShouldBeEmptyWhenThereIsNoContent() {
_indexNotifier.UpdateIndex(IndexName); _indexNotifier.UpdateIndex(IndexName);
@@ -130,7 +132,7 @@ namespace Orchard.Tests.Modules.Indexing {
} }
[Test] [Test]
public void ShouldIngoreNonIndexableContentWhenRebuildingTheIndex() { public void ShouldIgnoreNonIndexableContentWhenRebuildingTheIndex() {
var alphaType = new ContentTypeDefinitionBuilder() var alphaType = new ContentTypeDefinitionBuilder()
.Named("alpha") .Named("alpha")
.Build(); .Build();
@@ -195,6 +197,21 @@ namespace Orchard.Tests.Modules.Indexing {
Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started")); Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Rebuild index started"));
} }
[Test]
public void IndexingTaskExecutorShouldBeReEntrant() {
ILockFile lockFile = null;
_lockFileManager.TryAcquireLock("Sites/My Site/Search.settings.xml.lock", ref lockFile);
using (lockFile) {
_indexNotifier.UpdateIndex(IndexName);
Assert.That(_logger.LogEntries.Count, Is.EqualTo(1));
Assert.That(_logger.LogEntries, Has.Some.Matches<LogEntry>(entry => entry.LogFormat == "Index was requested but was already running"));
}
_logger.LogEntries.Clear();
_indexNotifier.UpdateIndex(IndexName);
Assert.That(_logger.LogEntries, Has.None.Matches<LogEntry>(entry => entry.LogFormat == "Index was requested but was already running"));
}
[Test] [Test]
public void ShouldUpdateTheIndexWhenContentIsUnPublished() { public void ShouldUpdateTheIndexWhenContentIsUnPublished() {
_contentManager.Create<Thing>(ThingDriver.ContentTypeName).Text = "Lorem ipsum"; _contentManager.Create<Thing>(ThingDriver.ContentTypeName).Text = "Lorem ipsum";
@@ -293,7 +310,7 @@ namespace Orchard.Tests.Modules.Indexing {
} }
public void Log(LogLevel level, Exception exception, string format, params object[] args) { public void Log(LogLevel level, Exception exception, string format, params object[] args) {
LogEntries.Add(new LogEntry() { LogEntries.Add(new LogEntry {
LogArgs = args, LogArgs = args,
LogException = exception, LogException = exception,
LogFormat = format, LogFormat = format,

View File

@@ -206,10 +206,6 @@ namespace Lucene.Services {
return new LuceneSearchBuilder(GetDirectory(indexName)) { Logger = Logger }; return new LuceneSearchBuilder(GetDirectory(indexName)) { Logger = Logger };
} }
private string GetSettingsFileName(string indexName) {
return _appDataFolder.MapPath(_appDataFolder.Combine(_basePath, indexName + ".settings.xml"));
}
public DateTime? GetLastIndexUtc(string indexName) { public DateTime? GetLastIndexUtc(string indexName) {
var settingsFileName = GetSettingsFileName(indexName); var settingsFileName = GetSettingsFileName(indexName);
@@ -254,5 +250,9 @@ namespace Lucene.Services {
reader.Close(); reader.Close();
} }
} }
private string GetSettingsFileName(string indexName) {
return _appDataFolder.MapPath(_appDataFolder.Combine(_basePath, indexName + ".settings.xml"));
}
} }
} }

View File

@@ -60,7 +60,6 @@
<Compile Include="Services\IndexingTaskExecutor.cs" /> <Compile Include="Services\IndexingTaskExecutor.cs" />
<Compile Include="Services\IndexingTaskManager.cs" /> <Compile Include="Services\IndexingTaskManager.cs" />
<Compile Include="Services\IIndexService.cs" /> <Compile Include="Services\IIndexService.cs" />
<Compile Include="Services\IndexSynLock.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\IndexService.cs" /> <Compile Include="Services\IndexService.cs" />
<Compile Include="Settings\EditorEvents.cs" /> <Compile Include="Settings\EditorEvents.cs" />

View File

@@ -1,25 +0,0 @@
using System.Collections.Generic;
namespace Orchard.Indexing.Services {
public interface IIndexSynLock : ISingletonDependency {
object GetSynLock(string indexName);
}
public class IndexSynLock : IIndexSynLock {
private readonly Dictionary<string, object> _synLocks;
private readonly object _synLock = new object();
public IndexSynLock() {
_synLocks =new Dictionary<string, object>();
}
public object GetSynLock(string indexName) {
lock(_synLock) {
if(!_synLocks.ContainsKey(indexName)) {
_synLocks[indexName] = new object();
}
return _synLocks[indexName];
}
}
}
}

View File

@@ -4,6 +4,9 @@ using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Data; using Orchard.Data;
using Orchard.Environment.Configuration;
using Orchard.FileSystems.AppData;
using Orchard.FileSystems.LockFile;
using Orchard.Indexing.Models; using Orchard.Indexing.Models;
using Orchard.Indexing.Settings; using Orchard.Indexing.Settings;
using Orchard.Logging; using Orchard.Logging;
@@ -14,6 +17,10 @@ namespace Orchard.Indexing.Services {
/// <summary> /// <summary>
/// Contains the logic which is regularly executed to retrieve index information from multiple content handlers. /// Contains the logic which is regularly executed to retrieve index information from multiple content handlers.
/// </summary> /// </summary>
/// <remarks>
/// This class is synchronized using a lock file as both command line and web workers can potentially use it,
/// and singleton locks would not be shared accross those two.
/// </remarks>
[UsedImplicitly] [UsedImplicitly]
public class IndexingTaskExecutor : IIndexNotifierHandler { public class IndexingTaskExecutor : IIndexNotifierHandler {
private readonly IClock _clock; private readonly IClock _clock;
@@ -22,7 +29,9 @@ namespace Orchard.Indexing.Services {
private readonly IIndexManager _indexManager; private readonly IIndexManager _indexManager;
private readonly IIndexingTaskManager _indexingTaskManager; private readonly IIndexingTaskManager _indexingTaskManager;
private readonly IContentManager _contentManager; private readonly IContentManager _contentManager;
private readonly IIndexSynLock _indexSynLock; private readonly IAppDataFolder _appDataFolder;
private readonly ShellSettings _shellSettings;
private readonly ILockFileManager _lockFileManager;
public IndexingTaskExecutor( public IndexingTaskExecutor(
IClock clock, IClock clock,
@@ -30,28 +39,34 @@ namespace Orchard.Indexing.Services {
IIndexManager indexManager, IIndexManager indexManager,
IIndexingTaskManager indexingTaskManager, IIndexingTaskManager indexingTaskManager,
IContentManager contentManager, IContentManager contentManager,
IIndexSynLock indexSynLock) { IAppDataFolder appDataFolder,
ShellSettings shellSettings,
ILockFileManager lockFileManager) {
_clock = clock; _clock = clock;
_repository = repository; _repository = repository;
_indexManager = indexManager; _indexManager = indexManager;
_indexingTaskManager = indexingTaskManager; _indexingTaskManager = indexingTaskManager;
_contentManager = contentManager; _contentManager = contentManager;
_indexSynLock = indexSynLock; _appDataFolder = appDataFolder;
_shellSettings = shellSettings;
_lockFileManager = lockFileManager;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
public ILogger Logger { get; set; } public ILogger Logger { get; set; }
public void UpdateIndex(string indexName) { public void UpdateIndex(string indexName) {
var synLock = _indexSynLock.GetSynLock(indexName); ILockFile lockFile = null;
var settingsFilename = GetSettingsFileName(indexName);
var lockFilename = settingsFilename + ".lock";
if (!System.Threading.Monitor.TryEnter(synLock)) { if (!_lockFileManager.TryAcquireLock(lockFilename, ref lockFile)) {
Logger.Information("Index was requested but was already running"); Logger.Information("Index was requested but was already running");
return; return;
} }
try { using (lockFile) {
if (!_indexManager.HasIndexProvider()) { if (!_indexManager.HasIndexProvider()) {
return; return;
} }
@@ -99,8 +114,8 @@ namespace Orchard.Indexing.Services {
// retrieve not yet processed tasks // retrieve not yet processed tasks
var taskRecords = lastIndexUtc == null var taskRecords = lastIndexUtc == null
? _repository.Fetch(x => true).ToArray() ? _repository.Fetch(x => true).ToArray()
: _repository.Fetch(x => x.CreatedUtc >= lastIndexUtc).ToArray(); // CreatedUtc and lastIndexUtc might be equal if a content item is created in a background task : _repository.Fetch(x => x.CreatedUtc >= lastIndexUtc).ToArray(); // CreatedUtc and lastIndexUtc might be equal if a content item is created in a background task
// nothing to do ?))) // nothing to do ?)))
if (taskRecords.Length + updateIndexDocuments.Count == 0) { if (taskRecords.Length + updateIndexDocuments.Count == 0) {
@@ -158,9 +173,6 @@ namespace Orchard.Indexing.Services {
} }
} }
} }
finally {
System.Threading.Monitor.Exit(synLock);
}
} }
static TypeIndexing GetTypeIndexingSettings(ContentItem contentItem) { static TypeIndexing GetTypeIndexingSettings(ContentItem contentItem) {
@@ -171,5 +183,9 @@ namespace Orchard.Indexing.Services {
} }
return contentItem.TypeDefinition.Settings.GetModel<TypeIndexing>(); return contentItem.TypeDefinition.Settings.GetModel<TypeIndexing>();
} }
private string GetSettingsFileName(string indexName) {
return _appDataFolder.Combine("Sites", _shellSettings.Name, indexName + ".settings.xml");
}
} }
} }