Index: branches/lucene-search-2.1/src/org/wikimedia/lsearch/frontend/SearchDaemon.java |
— | — | @@ -359,6 +359,12 @@ |
360 | 360 | } |
361 | 361 | } |
362 | 362 | } |
| 363 | + ArrayList<String> outOfRotation = cache.indexesTakenOutOfRotation(); |
| 364 | + if( !outOfRotation.isEmpty() ){ |
| 365 | + sendOutputLine("Out of rotation:"); |
| 366 | + for(String s : outOfRotation) |
| 367 | + sendOutputLine(s); |
| 368 | + } |
363 | 369 | } |
364 | 370 | |
365 | 371 | // never use keepalive |
Index: branches/lucene-search-2.1/src/org/wikimedia/lsearch/interoperability/RMIMessengerClient.java |
— | — | @@ -429,5 +429,28 @@ |
430 | 430 | log.error("Messenger not bound: "+e.getMessage(),e); |
431 | 431 | } |
432 | 432 | } |
| 433 | + |
| 434 | + public void takeOutOfRotation(String host, String myHost, String dbrole) throws RemoteException { |
| 435 | + RMIMessenger r; |
| 436 | + try { |
| 437 | + r = messengerFromCache(host); |
| 438 | + r.takeOutOfRotation(myHost, dbrole); |
| 439 | + } catch(NotBoundException e){ |
| 440 | + e.printStackTrace(); |
| 441 | + log.error("Messenger not bound: "+e.getMessage(),e); |
| 442 | + } |
| 443 | + |
| 444 | + } |
433 | 445 | |
| 446 | + public void returnToRotation(String host, String myHost, String dbrole) throws RemoteException { |
| 447 | + RMIMessenger r; |
| 448 | + try { |
| 449 | + r = messengerFromCache(host); |
| 450 | + r.returnToRotation(myHost, dbrole); |
| 451 | + } catch(NotBoundException e){ |
| 452 | + e.printStackTrace(); |
| 453 | + log.error("Messenger not bound: "+e.getMessage(),e); |
| 454 | + } |
| 455 | + } |
| 456 | + |
434 | 457 | } |
Index: branches/lucene-search-2.1/src/org/wikimedia/lsearch/interoperability/RMIMessenger.java |
— | — | @@ -266,4 +266,22 @@ |
267 | 267 | |
268 | 268 | /** Remote host has been deployed */ |
269 | 269 | public void hostDeployed(String host) throws RemoteException; |
| 270 | + |
| 271 | + /** |
| 272 | + * Take index out of rotation temporarely |
| 273 | + * |
| 274 | + * @param host |
| 275 | + * @param dbrole |
| 276 | + * @throws RemoteException |
| 277 | + */ |
| 278 | + public void takeOutOfRotation(String host, String dbrole) throws RemoteException; |
| 279 | + |
| 280 | + /** |
| 281 | + * Put index back to rotation |
| 282 | + * |
| 283 | + * @param host |
| 284 | + * @param dbrole |
| 285 | + * @throws RemoteException |
| 286 | + */ |
| 287 | + public void returnToRotation(String host, String dbrole) throws RemoteException; |
270 | 288 | } |
Index: branches/lucene-search-2.1/src/org/wikimedia/lsearch/interoperability/RMIMessengerImpl.java |
— | — | @@ -254,7 +254,20 @@ |
255 | 255 | cache = SearcherCache.getInstance(); |
256 | 256 | cache.hostDeploying(host); |
257 | 257 | } |
| 258 | + |
| 259 | + public void takeOutOfRotation(String host, String dbrole) throws RemoteException { |
| 260 | + if(cache == null) |
| 261 | + cache = SearcherCache.getInstance(); |
| 262 | + cache.takeOutOfRotation(host, dbrole); |
| 263 | + |
| 264 | + } |
258 | 265 | |
| 266 | + public void returnToRotation(String host, String dbrole) throws RemoteException { |
| 267 | + if(cache == null) |
| 268 | + cache = SearcherCache.getInstance(); |
| 269 | + cache.returnToRotation(host, dbrole); |
| 270 | + } |
| 271 | + |
259 | 272 | protected RMIMessengerImpl(){ |
260 | 273 | networkStatus = null; |
261 | 274 | indexRegistry = null; |
Index: branches/lucene-search-2.1/src/org/wikimedia/lsearch/search/SearchEngine.java |
— | — | @@ -592,6 +592,13 @@ |
593 | 593 | Hashtable<String,NamespaceFilter> cachedFilters = GlobalConfiguration.getInstance().getNamespacePrefixes(); |
594 | 594 | boolean searchAll = false; |
595 | 595 | |
| 596 | + // check for empty queries |
| 597 | + if(searchterm.equals("")){ |
| 598 | + res = new SearchResults(); |
| 599 | + res.setErrorMsg("Empty search"); |
| 600 | + return res; |
| 601 | + } |
| 602 | + |
596 | 603 | // if search is over one field, try to use filters |
597 | 604 | if(fields.size()==1){ |
598 | 605 | if(fields.contains(new NamespaceFilter())){ |
— | — | @@ -702,7 +709,7 @@ |
703 | 710 | e.printStackTrace(); |
704 | 711 | res = new SearchResults(); |
705 | 712 | res.retry(); |
706 | | - log.warn("Retry, temportal error for query: ["+q+"] on "+iid+" : "+e.getMessage(),e); |
| 713 | + log.warn("Retry, temporal error for query: ["+searchterm+"] on "+iid+" : "+e.getMessage(),e); |
707 | 714 | return res; |
708 | 715 | } |
709 | 716 | } catch(ParseException e){ |
Index: branches/lucene-search-2.1/src/org/wikimedia/lsearch/search/UpdateThread.java |
— | — | @@ -106,6 +106,8 @@ |
107 | 107 | protected boolean forceLocalDeployment = false; |
108 | 108 | /** If old update/ dirs should be deleted once the new index is deployed */ |
109 | 109 | protected boolean deleteOldUpdates = false; |
| 110 | + /** when indexes are updated, which indexes to take out of rotation */ |
| 111 | + protected Set<String> forceRedirect = Collections.synchronizedSet(new HashSet<String>()); |
110 | 112 | |
111 | 113 | protected static Object threadSerialization = new Object(); |
112 | 114 | |
— | — | @@ -236,6 +238,19 @@ |
237 | 239 | log.warn("Error notifying host "+host+" of index deployment: "+e.getMessage(),e); |
238 | 240 | } |
239 | 241 | } |
| 242 | + } else if( !forceRedirect.isEmpty() ){ |
| 243 | + for(String dbrole : forceRedirect){ |
| 244 | + cache.takeOutOfRotation("localhost", dbrole); |
| 245 | + String myHost = global.getLocalhost(); |
| 246 | + for(String host : global.getAllSearchHosts()){ |
| 247 | + try{ |
| 248 | + if(!host.equals(myHost)) |
| 249 | + messenger.takeOutOfRotation(host,myHost,dbrole); |
| 250 | + } catch(Exception e){ |
| 251 | + log.warn("Error trying to take index "+dbrole+" out of rotation on "+host+" : "+e.getMessage(),e); |
| 252 | + } |
| 253 | + } |
| 254 | + } |
240 | 255 | } |
241 | 256 | // if local, use cp -lr instead of rsync |
242 | 257 | if(global.isLocalhost(iid.getIndexHost())){ |
— | — | @@ -302,6 +317,20 @@ |
303 | 318 | log.warn("Error notifying host "+host+" of end of deployment: "+e.getMessage(),e); |
304 | 319 | } |
305 | 320 | } |
| 321 | + } else if( !forceRedirect.isEmpty() ){ |
| 322 | + for(String dbrole : forceRedirect){ |
| 323 | + cache.returnToRotation("localhost", dbrole); |
| 324 | + String myHost = global.getLocalhost(); |
| 325 | + for(String host : global.getAllSearchHosts()){ |
| 326 | + try{ |
| 327 | + if(!host.equals(myHost)) |
| 328 | + messenger.returnToRotation(host,myHost,dbrole); |
| 329 | + } catch(Exception e){ |
| 330 | + log.warn("Error trying to return index "+dbrole+" in rotation on "+host+" : "+e.getMessage(),e); |
| 331 | + } |
| 332 | + } |
| 333 | + } |
| 334 | + |
306 | 335 | } |
307 | 336 | } |
308 | 337 | } |
— | — | @@ -418,6 +447,15 @@ |
419 | 448 | rsyncPath = config.getString("Rsync","path","/usr/bin/rsync"); |
420 | 449 | rsyncParams = config.getString("Rsync","params",""); |
421 | 450 | forceLocalDeployment = config.getBoolean("Search","forceLocalDeployment"); |
| 451 | + for(String indexpart : config.getArray("Search","forceRedirect")){ |
| 452 | + try{ |
| 453 | + // make sure these indexes are actually valid index names |
| 454 | + IndexId iid = IndexId.get(indexpart); |
| 455 | + forceRedirect.add(iid.toString()); |
| 456 | + } catch(RuntimeException e){ |
| 457 | + log.warn("Ignoring force redirect index "+indexpart+" since it doesn't exist."); |
| 458 | + } |
| 459 | + } |
422 | 460 | deleteOldUpdates = config.getBoolean("Search","deleteOldUpdates"); |
423 | 461 | } |
424 | 462 | |
Index: branches/lucene-search-2.1/src/org/wikimedia/lsearch/search/SearcherCache.java |
— | — | @@ -212,6 +212,9 @@ |
213 | 213 | |
214 | 214 | /** Remote hosts being deployed, never use their searchers, unless necessary! (host->deployment level) */ |
215 | 215 | protected Hashtable<String,Integer> hostsDeploying = new Hashtable<String,Integer>(); |
| 216 | + |
| 217 | + /** dbrole -> hosts - indexes taken out of rotation */ |
| 218 | + protected Hashtable<String,Set<String>> outOfRotation = new Hashtable<String,Set<String>>(); |
216 | 219 | |
217 | 220 | /** deployment has been tried at least once for these */ |
218 | 221 | protected static Set<String> initialWarmup = Collections.synchronizedSet(new HashSet<String>()); |
— | — | @@ -227,6 +230,40 @@ |
228 | 231 | return localCache.containsKey(iid.toString()); |
229 | 232 | } |
230 | 233 | |
| 234 | + /** Take a certain index on a remote or localhost out of rotation */ |
| 235 | + public void takeOutOfRotation(String host, String dbrole){ |
| 236 | + synchronized(outOfRotation){ |
| 237 | + Set<String> hosts = outOfRotation.get(dbrole); |
| 238 | + if(hosts == null) |
| 239 | + outOfRotation.put(dbrole, hosts = Collections.synchronizedSet(new HashSet<String>())); |
| 240 | + hosts.add(host); |
| 241 | + } |
| 242 | + } |
| 243 | + |
| 244 | + /** Put certain index back into rotation */ |
| 245 | + public void returnToRotation(String host, String dbrole){ |
| 246 | + synchronized(outOfRotation){ |
| 247 | + Set<String> hosts = outOfRotation.get(dbrole); |
| 248 | + if(hosts == null){ |
| 249 | + log.warn("Tried to put host="+host+", dbrole="+dbrole+" back into rotation, but hasn't been out of rotation."); |
| 250 | + return; |
| 251 | + } |
| 252 | + hosts.remove(host); |
| 253 | + if(hosts.isEmpty()) |
| 254 | + outOfRotation.remove(dbrole); |
| 255 | + } |
| 256 | + } |
| 257 | + |
| 258 | + /** Check if this index is out of rotation */ |
| 259 | + public boolean isOutOfRotation(String host, IndexId iid){ |
| 260 | + synchronized(outOfRotation){ |
| 261 | + Set<String> hosts = outOfRotation.get(iid.toString()); |
| 262 | + if(hosts != null && hosts.contains(host)) |
| 263 | + return true; |
| 264 | + return false; |
| 265 | + } |
| 266 | + } |
| 267 | + |
231 | 268 | /** Signalize that host is begining it's index update, and that we shouldn't touch it */ |
232 | 269 | public void hostDeploying(String host){ |
233 | 270 | synchronized(hostsDeploying){ |
— | — | @@ -253,6 +290,19 @@ |
254 | 291 | } |
255 | 292 | } |
256 | 293 | |
| 294 | + /** Produce nice human-readable list of indexes out of rotation */ |
| 295 | + public ArrayList<String> indexesTakenOutOfRotation(){ |
| 296 | + ArrayList<String> out = new ArrayList<String>(); |
| 297 | + synchronized(hostsDeploying){ |
| 298 | + for(Entry<String,Set<String>> e : outOfRotation.entrySet()){ |
| 299 | + for(String host : e.getValue()){ |
| 300 | + out.add(e.getKey()+" at "+host); |
| 301 | + } |
| 302 | + } |
| 303 | + } |
| 304 | + return out; |
| 305 | + } |
| 306 | + |
257 | 307 | public boolean thisHostIsDeploying(){ |
258 | 308 | return hostsDeploying.containsKey("localhost"); |
259 | 309 | } |
— | — | @@ -265,7 +315,8 @@ |
266 | 316 | * @return |
267 | 317 | */ |
268 | 318 | public String getRandomHost(IndexId iid){ |
269 | | - if(iid.isMySearch() && hasLocalSearcher(iid) && !hostsDeploying.containsKey("localhost")) |
| 319 | + if(iid.isMySearch() && hasLocalSearcher(iid) |
| 320 | + && !hostsDeploying.containsKey("localhost") && !isOutOfRotation("localhost",iid)) |
270 | 321 | return "localhost"; |
271 | 322 | if(!initialized.contains(iid.toString())) |
272 | 323 | initializeRemote(iid); |
— | — | @@ -277,6 +328,11 @@ |
278 | 329 | HashSet<String> hosts = new HashSet<String>(); |
279 | 330 | hosts.addAll(pools.keySet()); |
280 | 331 | hosts.removeAll(hostsDeploying.keySet()); |
| 332 | + // get hosts for which this index is out of rotation |
| 333 | + Set<String> takenOut = outOfRotation.get(iid.toString()); |
| 334 | + if(takenOut != null) |
| 335 | + hosts.removeAll(takenOut); |
| 336 | + // no hosts left |
281 | 337 | if(hosts.size() == 0) |
282 | 338 | return null; |
283 | 339 | int num = (int)(Math.random()*hosts.size()); |