diff --git a/src/Umbraco.Core/DelegateExtensions.cs b/src/Umbraco.Core/DelegateExtensions.cs new file mode 100644 index 0000000000..78450448e2 --- /dev/null +++ b/src/Umbraco.Core/DelegateExtensions.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace Umbraco.Core +{ + public static class DelegateExtensions + { + public static Attempt RetryUntilSuccessOrTimeout(this Func> task, TimeSpan timeout, TimeSpan pause) + { + if (pause.TotalMilliseconds < 0) + { + throw new ArgumentException("pause must be >= 0 milliseconds"); + } + var stopwatch = Stopwatch.StartNew(); + do + { + var result = task(); + if (result) { return result; } + Thread.Sleep((int)pause.TotalMilliseconds); + } + while (stopwatch.Elapsed < timeout); + return Attempt.Fail(); + } + + public static Attempt RetryUntilSuccessOrMaxAttempts(this Func> task, int totalAttempts, TimeSpan pause) + { + if (pause.TotalMilliseconds < 0) + { + throw new ArgumentException("pause must be >= 0 milliseconds"); + } + int attempts = 0; + do + { + attempts++; + var result = task(attempts); + if (result) { return result; } + Thread.Sleep((int)pause.TotalMilliseconds); + } + while (attempts < totalAttempts); + return Attempt.Fail(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 15fe2da84b..8275c768d0 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -302,6 +302,7 @@ + diff --git a/src/Umbraco.Tests/DelegateExtensionsTests.cs b/src/Umbraco.Tests/DelegateExtensionsTests.cs new file mode 100644 index 0000000000..511440c2c7 --- /dev/null +++ b/src/Umbraco.Tests/DelegateExtensionsTests.cs @@ -0,0 +1,46 @@ +using System; +using Lucene.Net.Index; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests +{ + [TestFixture] + public class DelegateExtensionsTests + { + + [Test] + public void Only_Executes_Specific_Count() + { + var maxTries = 5; + var totalTries = 0; + DelegateExtensions.RetryUntilSuccessOrMaxAttempts((currentTry) => + { + totalTries = currentTry; + return Attempt.Fail(); + }, 5, TimeSpan.FromMilliseconds(10)); + + Assert.AreEqual(maxTries, totalTries); + } + + [Test] + public void Quits_On_Success_Count() + { + var maxTries = 5; + var totalTries = 0; + DelegateExtensions.RetryUntilSuccessOrMaxAttempts((currentTry) => + { + totalTries = currentTry; + if (totalTries == 2) + { + return Attempt.Succeed(); + } + return Attempt.Fail(); + }, 5, TimeSpan.FromMilliseconds(10)); + + Assert.AreEqual(2, totalTries); + } + + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index d47e7a21b2..07ac002a48 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -24,6 +24,18 @@ namespace Umbraco.Tests.Strings ShortStringHelperResolver.Reset(); } + [TestCase("hello", "world", false)] + [TestCase("hello", "hello", true)] + [TestCase("hellohellohellohellohellohellohello", "hellohellohellohellohellohellohelloo", false)] + [TestCase("hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohelloo", false)] + [TestCase("hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", true)] + public void String_To_Guid(string first, string second, bool result) + { + Console.WriteLine("First: " + first.ToGuid()); + Console.WriteLine("Second: " + second.ToGuid()); + Assert.AreEqual(result, first.ToGuid() == second.ToGuid()); + } + [TestCase("alert('hello');", false)] [TestCase("~/Test.js", true)] [TestCase("../Test.js", true)] diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ef3ce50682..bc0e0058b2 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -176,6 +176,7 @@ + diff --git a/src/UmbracoExamine/LocalStorage/LocalTempStorageIndexer.cs b/src/UmbracoExamine/LocalStorage/LocalTempStorageIndexer.cs index 3e548c52a1..dc48fdc917 100644 --- a/src/UmbracoExamine/LocalStorage/LocalTempStorageIndexer.cs +++ b/src/UmbracoExamine/LocalStorage/LocalTempStorageIndexer.cs @@ -18,6 +18,7 @@ namespace UmbracoExamine.LocalStorage internal enum InitializeDirectoryFlags { Success = 0, + SuccessNoIndexExists = 1, FailedCorrupt = 100, FailedLocked = 101, @@ -56,24 +57,41 @@ namespace UmbracoExamine.LocalStorage baseLuceneDirectory, //flag to disable the mirrored folder if not successful (int)success >= 100); - + + //If the master index simply doesn't exist, we don't continue to try to open anything since there will + // actually be nothing there. + if (success == InitializeDirectoryFlags.SuccessNoIndexExists) + { + return; + } //Try to open the reader, this will fail if the index is corrupt and we'll need to handle that - - try + var result = DelegateExtensions.RetryUntilSuccessOrMaxAttempts(i => { - using (IndexReader.Open( - LuceneDirectory, - DeletePolicyTracker.Current.GetPolicy(LuceneDirectory), - true)) + try { + using (IndexReader.Open( + LuceneDirectory, + DeletePolicyTracker.Current.GetPolicy(LuceneDirectory), + true)) + { + } + + return Attempt.Succeed(true); } - } - catch (Exception ex) + catch (Exception ex) + { + LogHelper.WarnWithException( + string.Format("Could not open an index reader, local temp storage index is empty or corrupt... retrying... {0}", configuredPath), + ex); + } + return Attempt.Fail(false); + }, 5, TimeSpan.FromSeconds(1)); + + if (result.Success == false) { - LogHelper.WarnWithException( - string.Format("Could not open an index reader, local temp storage index is empty or corrupt... attempting to clear index files in local temp storage, will operate from main storage only {0}", configuredPath), - ex); + LogHelper.Warn( + string.Format("Could not open an index reader, local temp storage index is empty or corrupt... attempting to clear index files in local temp storage, will operate from main storage only {0}", configuredPath)); ClearFilesInPath(TempPath); @@ -83,7 +101,7 @@ namespace UmbracoExamine.LocalStorage baseLuceneDirectory, //Disable mirrored index, we're kind of screwed here only use master index true); - } + } break; case LocalStorageType.LocalOnly: @@ -181,69 +199,47 @@ namespace UmbracoExamine.LocalStorage /// private Attempt TryCreateWriterWithRetry(Lucene.Net.Store.Directory baseLuceneDirectory, Analyzer analyzer) { - var maxTries = 50; + var maxTries = 5; - //try now - var writerAttempt = TryCreateWriter(baseLuceneDirectory, analyzer); - if (writerAttempt) return writerAttempt; - - var currentTry = 0; - while (currentTry < maxTries) + var result = DelegateExtensions.RetryUntilSuccessOrMaxAttempts((currentTry) => { - LogHelper.Info("Could not create writer on {0}, retrying ....", baseLuceneDirectory.ToString); - - //first wait then retry - Thread.Sleep(1000); - //last try... - if (currentTry == (maxTries - 1)) + if (currentTry == maxTries) { LogHelper.Info("Could not acquire index lock, attempting to force unlock it..."); //unlock it! IndexWriter.Unlock(baseLuceneDirectory); } - writerAttempt = TryCreateWriter(baseLuceneDirectory, analyzer); + var writerAttempt = TryCreateWriter(baseLuceneDirectory, analyzer); + if (writerAttempt) return writerAttempt; + LogHelper.Info("Could not create writer on {0}, retrying ....", baseLuceneDirectory.ToString); + return Attempt.Fail(); + }, 5, TimeSpan.FromSeconds(1)); - //if successful, exit loop - if (writerAttempt) break; - - currentTry++; - } - - return writerAttempt; + return result; } private bool TryWaitForDirectoryUnlock(Lucene.Net.Store.Directory dir) { - var maxTries = 50; + var maxTries = 5; - //try now - if (IndexWriter.IsLocked(dir) == false) return true; - - var currentTry = 0; - while (currentTry < maxTries) + var result = DelegateExtensions.RetryUntilSuccessOrMaxAttempts((currentTry) => { - LogHelper.Info("Could not acquire directory lock for {0} writer, retrying ....", dir.ToString); - - //first wait then retry - Thread.Sleep(1000); - //last try... - if (currentTry == (maxTries-1)) + if (currentTry == maxTries) { LogHelper.Info("Could not acquire directory lock, attempting to force unlock it..."); //unlock it! IndexWriter.Unlock(dir); } - //try again - if (IndexWriter.IsLocked(dir) == false) return true; + if (IndexWriter.IsLocked(dir) == false) return Attempt.Succeed(true); + LogHelper.Info("Could not acquire directory lock for {0} writer, retrying ....", dir.ToString); + return Attempt.Fail(); + }, 5, TimeSpan.FromSeconds(1)); - currentTry++; - } - - return false; + return result; } private InitializeDirectoryFlags InitializeLocalIndexAndDirectory(Lucene.Net.Store.Directory baseLuceneDirectory, Analyzer analyzer, string configuredPath) @@ -256,7 +252,7 @@ namespace UmbracoExamine.LocalStorage } //copy index if it exists, don't do anything if it's not there - if (IndexReader.IndexExists(baseLuceneDirectory) == false) return InitializeDirectoryFlags.Success; + if (IndexReader.IndexExists(baseLuceneDirectory) == false) return InitializeDirectoryFlags.SuccessNoIndexExists; var writerAttempt = TryCreateWriterWithRetry(baseLuceneDirectory, analyzer); @@ -269,6 +265,7 @@ namespace UmbracoExamine.LocalStorage //Try to open the reader from the source, this will fail if the index is corrupt and we'll need to handle that try { + //NOTE: To date I've not seen this error occur using (writerAttempt.Result.GetReader()) { }