PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Database/Indexing/IndexSearcherHolder.cs

https://github.com/nwendel/ravendb
C# | 263 lines | 224 code | 37 blank | 2 comment | 20 complexity | 2d579083a0fc00078ac63aad1311d196 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Diagnostics;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Runtime.CompilerServices;
  7. using System.Threading;
  8. using Lucene.Net.Index;
  9. using Raven.Abstractions;
  10. using Lucene.Net.Search;
  11. using Raven.Database.Config;
  12. using Task = System.Threading.Tasks.Task;
  13. using Raven.Abstractions.Logging;
  14. using Raven.Json.Linq;
  15. namespace Raven.Database.Indexing
  16. {
  17. public class IndexSearcherHolder
  18. {
  19. private readonly int indexId;
  20. private readonly WorkContext context;
  21. private static readonly ILog Log = LogManager.GetCurrentClassLogger();
  22. private volatile IndexSearcherHoldingState current;
  23. public IndexSearcherHolder(int indexId, WorkContext context)
  24. {
  25. this.indexId = indexId;
  26. this.context = context;
  27. }
  28. public ManualResetEvent SetIndexSearcher(IndexSearcher searcher, bool wait)
  29. {
  30. var old = current;
  31. current = new IndexSearcherHoldingState(searcher);
  32. if (old == null)
  33. return null;
  34. // here we try to make sure that the actual facet cache is up to do when we update the index searcher.
  35. // we use this to ensure that any facets that has been recently queried is warmed up and in the cache
  36. if (context.Configuration.PrewarmFacetsOnIndexingMaxAge != TimeSpan.Zero)
  37. {
  38. var usedFacets = old.GetUsedFacets(context.Configuration.PrewarmFacetsOnIndexingMaxAge).ToArray();
  39. if (usedFacets.Length > 0)
  40. {
  41. var preFillCache = Task.Factory.StartNew(() =>
  42. {
  43. var sp = Stopwatch.StartNew();
  44. try
  45. {
  46. IndexedTerms.PreFillCache(current, usedFacets, searcher.IndexReader);
  47. }
  48. catch (Exception e)
  49. {
  50. Log.WarnException(
  51. string.Format("Failed to properly pre-warm the facets cache ({1}) for index {0}", indexId,
  52. string.Join(",", usedFacets)), e);
  53. }
  54. finally
  55. {
  56. Log.Debug("Pre-warming the facet cache for {0} took {2}. Facets: {1}", indexId, string.Join(",", usedFacets), sp.Elapsed);
  57. }
  58. });
  59. preFillCache.Wait(context.Configuration.PrewarmFacetsSyncronousWaitTime);
  60. }
  61. }
  62. Interlocked.Increment(ref old.Usage);
  63. using (old)
  64. {
  65. if (wait)
  66. return old.MarkForDisposalWithWait();
  67. old.MarkForDisposal();
  68. return null;
  69. }
  70. }
  71. public IDisposable GetSearcher(out IndexSearcher searcher)
  72. {
  73. var indexSearcherHoldingState = GetCurrentStateHolder();
  74. try
  75. {
  76. searcher = indexSearcherHoldingState.IndexSearcher;
  77. return indexSearcherHoldingState;
  78. }
  79. catch (Exception e)
  80. {
  81. Log.ErrorException("Failed to get the index searcher.", e);
  82. indexSearcherHoldingState.Dispose();
  83. throw;
  84. }
  85. }
  86. public IDisposable GetSearcherAndTermDocs(out IndexSearcher searcher, out RavenJObject[] termDocs)
  87. {
  88. var indexSearcherHoldingState = GetCurrentStateHolder();
  89. try
  90. {
  91. searcher = indexSearcherHoldingState.IndexSearcher;
  92. termDocs = indexSearcherHoldingState.GetOrCreateTerms();
  93. return indexSearcherHoldingState;
  94. }
  95. catch (Exception)
  96. {
  97. indexSearcherHoldingState.Dispose();
  98. throw;
  99. }
  100. }
  101. internal IndexSearcherHoldingState GetCurrentStateHolder()
  102. {
  103. while (true)
  104. {
  105. var state = current;
  106. Interlocked.Increment(ref state.Usage);
  107. if (state.ShouldDispose)
  108. {
  109. state.Dispose();
  110. continue;
  111. }
  112. return state;
  113. }
  114. }
  115. public class IndexSearcherHoldingState : IDisposable, ILowMemoryHandler
  116. {
  117. public readonly IndexSearcher IndexSearcher;
  118. public volatile bool ShouldDispose;
  119. public int Usage;
  120. private RavenJObject[] readEntriesFromIndex;
  121. private readonly Lazy<ManualResetEvent> disposed = new Lazy<ManualResetEvent>(() => new ManualResetEvent(false));
  122. private readonly ConcurrentDictionary<string, DateTime> lastFacetQuery = new ConcurrentDictionary<string, DateTime>();
  123. private readonly ReaderWriterLockSlim rwls = new ReaderWriterLockSlim();
  124. private readonly Dictionary<string, LinkedList<CacheVal>[]> cache = new Dictionary<string, LinkedList<CacheVal>[]>();
  125. public ReaderWriterLockSlim Lock
  126. {
  127. get { return rwls; }
  128. }
  129. public class CacheVal
  130. {
  131. public Term Term;
  132. public double? Val;
  133. public override string ToString()
  134. {
  135. return string.Format("Term: {0}, Val: {1}", Term, Val);
  136. }
  137. }
  138. public IEnumerable<CacheVal> GetFromCache(string field, int doc)
  139. {
  140. LinkedList<CacheVal>[] vals;
  141. if (cache.TryGetValue(field, out vals) == false)
  142. yield break;
  143. if (vals[doc] == null)
  144. yield break;
  145. foreach (var cacheVal in vals[doc])
  146. {
  147. yield return cacheVal;
  148. }
  149. }
  150. public IEnumerable<Term> GetTermsFromCache(string field, int doc)
  151. {
  152. return GetFromCache(field, doc).Select(cacheVal => cacheVal.Term);
  153. }
  154. public IEnumerable<string> GetUsedFacets(TimeSpan tooOld)
  155. {
  156. var now = SystemTime.UtcNow;
  157. return lastFacetQuery.Where(x => (now - x.Value) < tooOld).Select(x => x.Key);
  158. }
  159. public bool IsInCache(string field)
  160. {
  161. var now = SystemTime.UtcNow;
  162. lastFacetQuery.AddOrUpdate(field, now, (s, time) => time > now ? time : now);
  163. return cache.ContainsKey(field);
  164. }
  165. public IndexSearcherHoldingState(IndexSearcher indexSearcher)
  166. {
  167. IndexSearcher = indexSearcher;
  168. MemoryStatistics.RegisterLowMemoryHandler(this);
  169. }
  170. public void HandleLowMemory()
  171. {
  172. rwls.EnterWriteLock();
  173. try
  174. {
  175. lastFacetQuery.Clear();
  176. cache.Clear();
  177. }
  178. finally
  179. {
  180. rwls.ExitWriteLock();
  181. }
  182. }
  183. public void MarkForDisposal()
  184. {
  185. ShouldDispose = true;
  186. }
  187. public ManualResetEvent MarkForDisposalWithWait()
  188. {
  189. var x = disposed.Value;// first create the value
  190. ShouldDispose = true;
  191. return x;
  192. }
  193. public void Dispose()
  194. {
  195. if (Interlocked.Decrement(ref Usage) > 0)
  196. return;
  197. if (ShouldDispose == false)
  198. return;
  199. DisposeRudely();
  200. }
  201. private void DisposeRudely()
  202. {
  203. if (IndexSearcher != null)
  204. {
  205. using (IndexSearcher)
  206. using (IndexSearcher.IndexReader) { }
  207. }
  208. if (disposed.IsValueCreated)
  209. disposed.Value.Set();
  210. }
  211. [MethodImpl(MethodImplOptions.Synchronized)]
  212. public RavenJObject[] GetOrCreateTerms()
  213. {
  214. if (readEntriesFromIndex != null)
  215. return readEntriesFromIndex;
  216. var indexReader = IndexSearcher.IndexReader;
  217. readEntriesFromIndex = IndexedTerms.ReadAllEntriesFromIndex(indexReader);
  218. return readEntriesFromIndex;
  219. }
  220. public void SetInCache(string field, LinkedList<CacheVal>[] items)
  221. {
  222. cache[field] = items;
  223. }
  224. }
  225. }
  226. }