Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
FilterManager.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Linq;
21 using Lucene.Net.Support;
22 
23 namespace Lucene.Net.Search
24 {
25 
26  /// <summary> Filter caching singleton. It can be used
27  /// to save filters locally for reuse.
28  /// This class makes it possble to cache Filters even when using RMI, as it
29  /// keeps the cache on the seaercher side of the RMI connection.
30  ///
31  /// Also could be used as a persistent storage for any filter as long as the
32  /// filter provides a proper hashCode(), as that is used as the key in the cache.
33  ///
34  /// The cache is periodically cleaned up from a separate thread to ensure the
35  /// cache doesn't exceed the maximum size.
36  /// </summary>
37  public class FilterManager
38  {
39 
40  protected internal static FilterManager manager;
41 
42  /// <summary>The default maximum number of Filters in the cache </summary>
43  protected internal const int DEFAULT_CACHE_CLEAN_SIZE = 100;
44  /// <summary>The default frequency of cache clenup </summary>
45  protected internal const long DEFAULT_CACHE_SLEEP_TIME = 1000 * 60 * 10;
46 
47  /// <summary>The cache itself </summary>
48  protected internal IDictionary<int, FilterItem> cache;
49  /// <summary>Maximum allowed cache size </summary>
50  protected internal int cacheCleanSize;
51  /// <summary>Cache cleaning frequency </summary>
52  protected internal long cleanSleepTime;
53  /// <summary>Cache cleaner that runs in a separate thread </summary>
54  protected internal FilterCleaner internalFilterCleaner;
55 
56  private static readonly object _staticSyncObj = new object();
57  public static FilterManager Instance
58  {
59  get
60  {
61  lock (_staticSyncObj)
62  {
63  return manager ?? (manager = new FilterManager());
64  }
65  }
66  }
67 
68  /// <summary> Sets up the FilterManager singleton.</summary>
69  protected internal FilterManager()
70  {
71  cache = new HashMap<int, FilterItem>();
72  cacheCleanSize = DEFAULT_CACHE_CLEAN_SIZE; // Let the cache get to 100 items
73  cleanSleepTime = DEFAULT_CACHE_SLEEP_TIME; // 10 minutes between cleanings
74 
75  internalFilterCleaner = new FilterCleaner(this);
76  ThreadClass fcThread = new ThreadClass(new System.Threading.ThreadStart(internalFilterCleaner.Run));
77  // setto be a Daemon so it doesn't have to be stopped
78  fcThread.IsBackground = true;
79  fcThread.Start();
80  }
81 
82  /// <summary> Sets the max size that cache should reach before it is cleaned up</summary>
83  /// <param name="value"> maximum allowed cache size </param>
84  public virtual void SetCacheSize(int value)
85  {
86  this.cacheCleanSize = value;
87  }
88 
89  /// <summary> Sets the cache cleaning frequency in milliseconds.</summary>
90  /// <param name="value"> cleaning frequency in millioseconds </param>
91  public virtual void SetCleanThreadSleepTime(long value)
92  {
93  this.cleanSleepTime = value;
94  }
95 
96  /// <summary> Returns the cached version of the filter. Allows the caller to pass up
97  /// a small filter but this will keep a persistent version around and allow
98  /// the caching filter to do its job.
99  ///
100  /// </summary>
101  /// <param name="filter">The input filter
102  /// </param>
103  /// <returns> The cached version of the filter
104  /// </returns>
105  public virtual Filter GetFilter(Filter filter)
106  {
107  lock (cache)
108  {
109  FilterItem fi = null;
110  fi = cache[filter.GetHashCode()];
111  if (fi != null)
112  {
113  fi.timestamp = System.DateTime.UtcNow.Ticks;
114  return fi.filter;
115  }
116  cache[filter.GetHashCode()] = new FilterItem(filter);
117  return filter;
118  }
119  }
120 
121  /// <summary> Holds the filter and the last time the filter was used, to make LRU-based
122  /// cache cleaning possible.
123  /// TODO: Clean this up when we switch to Java 1.5
124  /// </summary>
125  protected internal class FilterItem
126  {
127  public Filter filter;
128  public long timestamp;
129 
130  public FilterItem(Filter filter)
131  {
132  this.filter = filter;
133  this.timestamp = System.DateTime.UtcNow.Ticks;
134  }
135  }
136 
137 
138  /// <summary> Keeps the cache from getting too big.
139  /// If we were using Java 1.5, we could use LinkedHashMap and we would not need this thread
140  /// to clean out the cache.
141  ///
142  /// The SortedSet sortedFilterItems is used only to sort the items from the cache,
143  /// so when it's time to clean up we have the TreeSet sort the FilterItems by
144  /// timestamp.
145  ///
146  /// Removes 1.5 * the numbers of items to make the cache smaller.
147  /// For example:
148  /// If cache clean size is 10, and the cache is at 15, we would remove (15 - 10) * 1.5 = 7.5 round up to 8.
149  /// This way we clean the cache a bit more, and avoid having the cache cleaner having to do it frequently.
150  /// </summary>
151  protected internal class FilterCleaner : IThreadRunnable
152  {
153  private class FilterItemComparer : IComparer<KeyValuePair<int, FilterItem>>
154  {
155  #region IComparer<FilterItem> Members
156 
157  public int Compare(KeyValuePair<int, FilterItem> x, KeyValuePair<int, FilterItem> y)
158  {
159  return x.Value.timestamp.CompareTo(y.Value.timestamp);
160  }
161 
162  #endregion
163  }
164 
165  private bool running = true;
166  private FilterManager manager;
167  private ISet<KeyValuePair<int, FilterItem>> sortedFilterItems;
168 
169  public FilterCleaner(FilterManager enclosingInstance)
170  {
171  this.manager = enclosingInstance;
172  sortedFilterItems = new SortedSet<KeyValuePair<int, FilterItem>>(new FilterItemComparer());
173  }
174 
175  public virtual void Run()
176  {
177  while (running)
178  {
179  // sort items from oldest to newest
180  // we delete the oldest filters
181  if (this.manager.cache.Count > this.manager.cacheCleanSize)
182  {
183  // empty the temporary set
184  sortedFilterItems.Clear();
185  lock (this.manager.cache)
186  {
187  sortedFilterItems.UnionWith(this.manager.cache);
188  int numToDelete = (int)((this.manager.cache.Count - this.manager.cacheCleanSize) * 1.5);
189 
190  //delete all of the cache entries not used in a while
191  sortedFilterItems.ExceptWith(sortedFilterItems.Take(numToDelete).ToArray());
192  }
193  // empty the set so we don't tie up the memory
194  sortedFilterItems.Clear();
195  }
196  // take a nap
197  System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64)10000 * this.manager.cleanSleepTime));
198 
199  }
200  }
201  }
202  }
203 }