Auf der Suche nach dem heiligen Gral

Was passiert aber, wenn wir nun Aufrufe aus mehreren Threads ausführen, um Parallelitätsprobleme zu simulieren? Lässt man einen solchen Testfall mit einer hohen Anzahl von Threads von der Kommandozeile aus laufen, erhält man möglicherweise folgende Fehlermeldung:

org.apache.solr.common.SolrException: Error opening new searcher. exceeded limit of maxWarmingSearchers=2, try again lat er.

Werfen wir daraufhin einen Blick in solrconfig.xml, finden wir dort folgenden Eintrag:

2

Wenn wir diese Zahl erhöhen, sollte unser Problem verschwinden. Was aber passiert hier? Das Solr Wiki erklärt hierzu: Jedes Mal, wenn ein neuer Index-Searcher geöffnet wird, wird der Cache des Searcher aufgewärmt, bevor Solr Anfragen an diesen Searcher weiterleitet. Es ist eminent wichtig für die Antwortzeiten bei Abfragen, dass die Abfragen auf aufgewärmte Caches treffen. Die Abwägung ist hierbei einerseits, wie aufgewärmt der Cache sein soll, und andererseits einen möglichst zeitnahen Zugriff auf die jüngsten Daten im Suchserver zu bekommen. In leselastigen, schreibarmen Umgebungen stellt das in aller Regel keine Einschränkung dar, aber bei hochgradig parallelen Schreibprozessen ist es ein Problem. Eine Möglichkeit wäre die Nutzung eines Master-Slave-Setups, bei dem alle Schreiboperationen gegen den Master durchgeführt werden, während die Leseoperationen auf den replizierten Slave-Knoten durchgeführt werden. Allerdings erhöht das die Komplexität des generellen Aufbaus. Eine andere Alternative ist das Abschalten des Auto-Commits der Änderungen im Solr-Server.

Ein weiteres potenzielles Problem in hochgradig parallelen schreiblastigen Umgebungen kann der index lock sein. Manchmal schlägt hier das Standard-ulimit zu, das mit 1024 eher konservativ ausgelegt ist. Der Fehler „Too many open files“ zeigt dann an, dass kein weiteres java.io.File-Objekt mehr angelegt werden kann. Es macht also Sinn, das ulimit in solchen Situationen zu vervielfachen. Weitere Konfigurationsparameter für Optimierungen sind im Solr Wiki [10] zu finden.

Die gute Nachricht ist, dass Lucene bereits seit Version 2.9 die Near-Real-Time-(NRT-)Suche unterstützt und die entsprechende Solr-Funktionalität im allerdings noch nicht freigegebenen Trunk zu finden ist. Lucene ergänzt quasi die Möglichkeit für die lesende Seite, auf die noch nicht committeten Änderungen auf der schreibenden Seite zuzugreifen und damit Änderungen in nahezu Echtzeit in der Suche bereitzustellen [11]. Im Solr-Server gibt es unter Nutzung der Lucene-NRT-Funktionalität dann so genannte soft commits, die Indexänderungen nahezu direkt sichtbar machen [12].

ElasticSearch-Nutzer andererseits müssen nicht auf einen Release warten, da dort Lucene-NRT bereits integriert ist. Führt man ähnliche parallele Tests mit ElasticSearch durch, sehen wir, dass sich die lesende und schreibende Seite nicht ins Gehege kommen.

Listing 4
public class ElasticSearchUtil { 

  public enum FolderSchemaField { 
    ID, FOLDER_ID, PARENT_ID, NAME, USER, TOTAL, UNREAD; 

    public String getSName() { 
      return toString().toLowerCase(); 
    } 
  } 

  public static void deleteByUser(Client client, String indexname, String username, 
    int maxDeletions) { 
    SearchResponse s = client.prepareSearch(indexname).setSearchType(
      SearchType.DFS_QUERY_THEN_FETCH).setQuery(termQuery(
      FolderSchemaField.USER.getSName(),username)).setFrom(0).setSize(
      maxDeletions).setExplain(true).execute().actionGet(); 

      for (int i = 0; i 
Listing 5
  @Test 
  public void testInsertFetchDelete() throws ElasticSearchException, IOException, 
    InterruptedException { 
    Client client = new TransportClient().addTransportAddress(new
      InetSocketTransportAddress( LOCALHOST, PORT)); 
    ElasticSearchUtil.deleteByUser(client, INDEX, "me", 1000); 

    IndexResponse i = client.prepareIndex(INDEX, TYPE, "1").setSource(
      jsonBuilder().startObject().field(FolderSchemaField.USER.getSName(),
      "me").field( FolderSchemaField.TOTAL.getSName(),
      1).field(FolderSchemaField.NAME.getSName(),
      "folderName").endObject()).execute() .actionGet(); 

    assertTrue("1".equals(i.getId())); 
   GetResponse g = client.prepareGet(INDEX, TYPE, "1").execute().actionGet(); 
   Map sourceElements = g.getSource(); 
    assertTrue(sourceElements.containsKey(FolderSchemaField.USER.getSName())); 
    assertTrue(sourceElements.containsKey(FolderSchemaField.TOTAL.getSName())); 
    assertTrue(sourceElements.containsKey(FolderSchemaField.NAME.getSName())); 

    SearchRequestBuilder builder =
      client.prepareSearch(INDEX).setSearchType(SearchType.COUNT) .setQuery(
      termQuery(FolderSchemaField.USER.getSName(), "me")).setFrom(0).setSize(
      500).setExplain(true); 
    builder.setOperationThreading(SearchOperationThreading.NO_THREADS); 
    SearchResponse s = builder.execute().actionGet(); 

    while (1 != s.getHits().getTotalHits()) { 
      s = builder.execute().actionGet(); 
    } 

    assertTrue(1 == s.getHits().getTotalHits()); 

    DeleteResponse d = client.prepareDelete(INDEX, TYPE, "1").execute().actionGet(); 
    assertTrue(null != d); 
    g = client.prepareGet(INDEX, TYPE, "1").execute().actionGet(); 
    assertTrue(!g.exists()); 

    client.close(); 
  }

Eine weitere angenehme Eigenschaft von ElasticSearch ist, dass es die Verwendung mehrerer Indizes annähernd transparent für den Anwender macht. Wir können einen neuen Index on the Fly hinzufügen und mehrere Indizes ohne komplexe Konfiguration in einer einzigen Abfrage ansprechen. Solr erlaubt mit etwas mehr (initialer) Konfiguration auch das so genannte Multi-core Setup für die Arbeit mit mehreren Indizes. ElasticSearch bietet eine Lucene-basierte Suche ohne viel administrativen Overhead, Solr andererseits besitzt eine hervorragende Administrationsoberfläche für das Experimentieren mit Abfragen, Debugging und Analysemöglichkeiten.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.