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