Index: trunk/lucene-search-2.0/src/org/wikimedia/lsearch/search/SearcherCache.java |
— | — | @@ -5,6 +5,8 @@ |
6 | 6 | import java.rmi.RemoteException; |
7 | 7 | import java.rmi.registry.LocateRegistry; |
8 | 8 | import java.rmi.registry.Registry; |
| 9 | +import java.util.ArrayList; |
| 10 | +import java.util.Arrays; |
9 | 11 | import java.util.Collections; |
10 | 12 | import java.util.HashSet; |
11 | 13 | import java.util.Hashtable; |
— | — | @@ -52,19 +54,54 @@ |
53 | 55 | log.debug("Closing searchable "+s); |
54 | 56 | s.close(); |
55 | 57 | } catch (IOException e) { |
56 | | - log.warn("I/O error closing searchable "+s); |
| 58 | + log.warn("I/O error closing searchables "+s); |
57 | 59 | } |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + public static final int OPEN_SEARCHERS = 4; |
| 64 | + |
| 65 | + /** Holds OPEN_SEARCHERS num of index searchers, for multiprocessor workstations */ |
| 66 | + public static class SearcherPool { |
| 67 | + IndexSearcherMul searchers[] = new IndexSearcherMul[OPEN_SEARCHERS]; |
| 68 | + |
| 69 | + SearcherPool(IndexId iid, String path) throws IOException { |
| 70 | + for(int i=0;i<OPEN_SEARCHERS;i++){ |
| 71 | + searchers[i] = open(iid, path); |
| 72 | + } |
58 | 73 | } |
59 | 74 | |
| 75 | + private IndexSearcherMul open(IndexId iid, String path) throws IOException { |
| 76 | + IndexSearcherMul searcher = null; |
| 77 | + log.debug("Openning local index for "+iid); |
| 78 | + if(!iid.isMySearch()) |
| 79 | + throw new IOException(iid+" is not searched by this host."); |
| 80 | + if(iid.isLogical()) |
| 81 | + throw new IOException(iid+" will not open logical index."); |
| 82 | + try { |
| 83 | + searcher = new IndexSearcherMul(path); |
| 84 | + searcher.setSimilarity(new WikiSimilarity()); |
| 85 | + } catch (IOException e) { |
| 86 | + // tell registry this is not a good index |
| 87 | + IndexRegistry.getInstance().invalidateCurrent(iid); |
| 88 | + log.error("I/O Error opening index at path "+iid.getCanonicalSearchPath()+" : "+e.getMessage()); |
| 89 | + throw e; |
| 90 | + } |
| 91 | + return searcher; |
| 92 | + } |
60 | 93 | |
61 | | - } |
| 94 | + IndexSearcherMul get(){ |
| 95 | + return searchers[(int)(Math.random()*searchers.length)]; |
| 96 | + } |
| 97 | + |
| 98 | + } |
62 | 99 | static org.apache.log4j.Logger log = Logger.getLogger(SearcherCache.class); |
63 | 100 | /** dbrole@host -> RemoteSearchable */ |
64 | 101 | protected Hashtable<String,CachedSearchable> remoteCache; |
65 | 102 | /** dbrole -> set(dbrole@host) */ |
66 | 103 | protected Hashtable<String,Set<String>> remoteKeys; |
67 | 104 | /** dbrole -> IndexSearcher */ |
68 | | - protected Hashtable<String,IndexSearcherMul> localCache; |
| 105 | + protected Hashtable<String,SearcherPool> localCache; |
69 | 106 | /** searchable -> host */ |
70 | 107 | protected Hashtable<Searchable,String> searchableHost; |
71 | 108 | |
— | — | @@ -125,6 +162,14 @@ |
126 | 163 | } |
127 | 164 | } |
128 | 165 | |
| 166 | + /** Get searcher from local cache, or if doesn't exist null */ |
| 167 | + protected IndexSearcherMul fromLocalCache(String key){ |
| 168 | + SearcherPool pool = localCache.get(key); |
| 169 | + if(pool != null) |
| 170 | + return pool.get(); |
| 171 | + return null; |
| 172 | + } |
| 173 | + |
129 | 174 | /** |
130 | 175 | * Returns a searchable from cache. If the searchable is not |
131 | 176 | * in cache, the method will create it (via local call or RMI) |
— | — | @@ -135,7 +180,7 @@ |
136 | 181 | public Searchable getSearchable(IndexId iid, String host) throws NotBoundException, IOException{ |
137 | 182 | Searchable s = null; |
138 | 183 | if(global.isLocalhost(host)) |
139 | | - s = localCache.get(makeKey(iid,host)); |
| 184 | + s = fromLocalCache(iid.toString()); |
140 | 185 | else |
141 | 186 | s = remoteCache.get(makeKey(iid,host)); |
142 | 187 | |
— | — | @@ -152,7 +197,7 @@ |
153 | 198 | * @throws IOException |
154 | 199 | */ |
155 | 200 | public IndexSearcherMul getLocalSearcher(IndexId iid) throws IOException{ |
156 | | - IndexSearcherMul s = localCache.get(iid.toString()); |
| 201 | + IndexSearcherMul s = fromLocalCache(iid.toString()); |
157 | 202 | |
158 | 203 | if(s == null) |
159 | 204 | s = addLocalSearcherToCache(iid); |
— | — | @@ -209,12 +254,13 @@ |
210 | 255 | synchronized(iid){ |
211 | 256 | // make sure some other thread has not opened the searcher |
212 | 257 | if(localCache.get(iid.toString()) == null){ |
213 | | - IndexSearcherMul searcher = getLocal(iid); |
214 | | - localCache.put(iid.toString(),searcher); |
215 | | - searchableHost.put(searcher,""); |
216 | | - return searcher; |
| 258 | + SearcherPool pool = new SearcherPool(iid,iid.getCanonicalSearchPath()); |
| 259 | + localCache.put(iid.toString(),pool); |
| 260 | + for(IndexSearcherMul s : pool.searchers) |
| 261 | + searchableHost.put(s,""); |
| 262 | + return pool.get(); |
217 | 263 | } else |
218 | | - return localCache.get(iid.toString()); |
| 264 | + return fromLocalCache(iid.toString()); |
219 | 265 | } |
220 | 266 | } |
221 | 267 | |
— | — | @@ -255,29 +301,6 @@ |
256 | 302 | } |
257 | 303 | } |
258 | 304 | |
259 | | - /** Get a local {@link Searchable} object. This function is called only when |
260 | | - * index is openned for the first time (e.g. at startup). |
261 | | - * The preferred way of openning/updating indexes is via {@link UpdateThread}. |
262 | | - */ |
263 | | - protected IndexSearcherMul getLocal(IndexId iid) throws IOException{ |
264 | | - IndexSearcherMul searcher = null; |
265 | | - log.debug("Openning local index for "+iid); |
266 | | - if(!iid.isMySearch()) |
267 | | - throw new IOException(iid+" is not searched by this host."); |
268 | | - if(iid.isLogical()) |
269 | | - throw new IOException(iid+" will not open logical index."); |
270 | | - try { |
271 | | - searcher = new IndexSearcherMul(iid.getCanonicalSearchPath()); |
272 | | - searcher.setSimilarity(new WikiSimilarity()); |
273 | | - } catch (IOException e) { |
274 | | - // tell registry this is not a good index |
275 | | - IndexRegistry.getInstance().invalidateCurrent(iid); |
276 | | - log.error("I/O Error opening index at path "+iid.getCanonicalSearchPath()+" : "+e.getMessage()); |
277 | | - throw e; |
278 | | - } |
279 | | - return searcher; |
280 | | - } |
281 | | - |
282 | 305 | /** make a key for cache hashtables */ |
283 | 306 | protected String makeKey(IndexId iid, String host){ |
284 | 307 | return iid+"@"+host; |
— | — | @@ -320,11 +343,7 @@ |
321 | 344 | */ |
322 | 345 | public void invalidateSearchable(IndexId iid, String host, SearchableMul rs){ |
323 | 346 | if(global.isLocalhost(host)){ |
324 | | - try { |
325 | | - invalidateLocalSearcher(iid,getLocal(iid)); |
326 | | - } catch (IOException e) { |
327 | | - // error logged elsewhere |
328 | | - } |
| 347 | + log.error("Should use function invalidateLocalSearcher for local searcher invalidation"); |
329 | 348 | return; |
330 | 349 | } |
331 | 350 | String key = makeKey(iid,host); |
— | — | @@ -372,31 +391,36 @@ |
373 | 392 | * @param iid |
374 | 393 | * @param searcher |
375 | 394 | */ |
376 | | - public void invalidateLocalSearcher(IndexId iid, IndexSearcherMul searcher){ |
| 395 | + public IndexSearcherMul[] invalidateLocalSearcher(IndexId iid, SearcherPool newpool) { |
377 | 396 | IndexSearcherMul olds; |
378 | | - boolean close = false; |
379 | | - log.debug("Invalidating local searcher for "+iid); |
| 397 | + log.debug("Invalidating local searcher for "+iid); |
| 398 | + ArrayList<SearchableMul> close = new ArrayList<SearchableMul>(); |
380 | 399 | synchronized(lock){ |
381 | | - olds = localCache.get(iid.toString()); |
| 400 | + SearcherPool oldpool = localCache.get(iid.toString()); |
382 | 401 | // put in the new value |
383 | | - localCache.put(iid.toString(),searcher); |
384 | | - searchableHost.put(searcher,""); |
385 | | - if(olds == null) |
386 | | - return; // no old searcher |
387 | | - searchableHost.remove(olds); |
388 | | - // close the old index searcher (imediatelly, or queue if in use) |
389 | | - SimpleInt useCount = inUse.get(olds); |
390 | | - if(useCount == null || useCount.count == 0) |
391 | | - close = true; |
392 | | - else{ |
393 | | - log.debug("Searcher for "+iid+" will be closed after local searches are done."); |
| 402 | + localCache.put(iid.toString(),newpool); |
| 403 | + for(IndexSearcherMul s : newpool.searchers) |
| 404 | + searchableHost.put(s,""); |
| 405 | + if(oldpool == null) |
| 406 | + return newpool.searchers; // no old searcher |
| 407 | + for(IndexSearcherMul s : oldpool.searchers){ |
| 408 | + searchableHost.remove(s); |
| 409 | + // close the old index searcher (imediatelly, or queue if in use) |
| 410 | + SimpleInt useCount = inUse.get(s); |
| 411 | + if(useCount == null || useCount.count == 0) |
| 412 | + close.add(s); |
| 413 | + else{ |
| 414 | + log.debug("Searcher for "+iid+" will be closed after local searches are done."); |
| 415 | + } |
| 416 | + |
| 417 | + closeQueue.add(s); |
394 | 418 | } |
395 | | - |
396 | | - closeQueue.add(olds); |
397 | 419 | } |
398 | 420 | // close outside of sync block |
399 | | - if(close) |
400 | | - closeSearcher(olds); |
| 421 | + for(SearchableMul s : close) |
| 422 | + closeSearcher(s); |
| 423 | + |
| 424 | + return newpool.searchers; |
401 | 425 | } |
402 | 426 | |
403 | 427 | /** Tell the cache that the searcher is in use */ |
— | — | @@ -461,7 +485,7 @@ |
462 | 486 | |
463 | 487 | protected SearcherCache(){ |
464 | 488 | remoteCache = new Hashtable<String,CachedSearchable>(); |
465 | | - localCache = new Hashtable<String,IndexSearcherMul>(); |
| 489 | + localCache = new Hashtable<String,SearcherPool>(); |
466 | 490 | deadHosts = Collections.synchronizedSet(new HashSet<SearchHost>()); |
467 | 491 | global = GlobalConfiguration.getInstance(); |
468 | 492 | inUse = new Hashtable<Searchable,SimpleInt>(); |
Index: trunk/lucene-search-2.0/src/org/wikimedia/lsearch/search/UpdateThread.java |
— | — | @@ -208,8 +208,7 @@ |
209 | 209 | searchpath.mkdir(); |
210 | 210 | |
211 | 211 | // check if updated index is a valid one (throws an exception on error) |
212 | | - IndexSearcherMul is = new IndexSearcherMul(li.path); |
213 | | - is.setSimilarity(new WikiSimilarity()); |
| 212 | + SearcherCache.SearcherPool pool = new SearcherCache.SearcherPool(iid,li.path); |
214 | 213 | |
215 | 214 | // refresh the symlink |
216 | 215 | command = "/bin/rm -rf "+iid.getSearchPath(); |
— | — | @@ -221,7 +220,7 @@ |
222 | 221 | |
223 | 222 | // update registry, cache, rmi object |
224 | 223 | registry.refreshUpdates(iid); |
225 | | - updateCache(is,li); |
| 224 | + updateCache(pool,li); |
226 | 225 | RMIServer.rebind(iid); |
227 | 226 | registry.refreshCurrent(li); |
228 | 227 | |
— | — | @@ -236,11 +235,12 @@ |
237 | 236 | } |
238 | 237 | |
239 | 238 | /** Update search cache after successful rsync of update version of index */ |
240 | | - protected void updateCache(IndexSearcherMul is, LocalIndex li){ |
| 239 | + protected void updateCache(SearcherCache.SearcherPool pool, LocalIndex li){ |
241 | 240 | // do some typical queries to preload some lucene caches, pages into memory, etc.. |
242 | | - Warmup.warmupIndexSearcher(is,li.iid,true); |
| 241 | + for(IndexSearcherMul is : pool.searchers) |
| 242 | + Warmup.warmupIndexSearcher(is,li.iid,true); |
243 | 243 | // add to cache |
244 | | - cache.invalidateLocalSearcher(li.iid,is); |
| 244 | + cache.invalidateLocalSearcher(li.iid,pool); |
245 | 245 | } |
246 | 246 | |
247 | 247 | protected UpdateThread(){ |