Package org.apache.lucene.search

Source Code of org.apache.lucene.search.TestSearcherManager

package org.apache.lucene.search;

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.ConcurrentMergeScheduler;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.ThreadedIndexingAndSearchingTestCase;
import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
import org.apache.lucene.util.NamedThreadFactory;
import org.apache.lucene.util._TestUtil;

@SuppressCodecs({ "SimpleText", "Memory", "Direct" })
public class TestSearcherManager extends ThreadedIndexingAndSearchingTestCase {

  boolean warmCalled;

  private SearcherLifetimeManager.Pruner pruner;

  public void testSearcherManager() throws Exception {
    pruner = new SearcherLifetimeManager.PruneByAge(TEST_NIGHTLY ? _TestUtil.nextInt(random(), 1, 20) : 1);
    runTest("TestSearcherManager");
  }

  @Override
  protected IndexSearcher getFinalSearcher() throws Exception  {
    if (!isNRT) {
      writer.commit();
    }
    assertTrue(mgr.maybeRefresh() || mgr.isSearcherCurrent());
    return mgr.acquire();
  }

  private SearcherManager mgr;
  private SearcherLifetimeManager lifetimeMGR;
  private final List<Long> pastSearchers = new ArrayList<Long>();
  private boolean isNRT;

  @Override
  protected void doAfterWriter(final ExecutorService es) throws Exception {
    final SearcherFactory factory = new SearcherFactory() {
      @Override
      public IndexSearcher newSearcher(IndexReader r) throws IOException {
        IndexSearcher s = new IndexSearcher(r, es);
        TestSearcherManager.this.warmCalled = true;
        s.search(new TermQuery(new Term("body", "united")), 10);
        return s;
      }
    };
    if (random().nextBoolean()) {
      // TODO: can we randomize the applyAllDeletes?  But
      // somehow for final searcher we must apply
      // deletes...
      mgr = new SearcherManager(writer, true, factory);
      isNRT = true;
    } else {
      // SearcherManager needs to see empty commit:
      writer.commit();
      mgr = new SearcherManager(dir, factory);
      isNRT = false;
      assertMergedSegmentsWarmed = false;
    }

    lifetimeMGR = new SearcherLifetimeManager();
  }

  @Override
  protected void doSearching(ExecutorService es, final long stopTime) throws Exception {

    Thread reopenThread = new Thread() {
      @Override
      public void run() {
        try {
          if (VERBOSE) {
            System.out.println("[" + Thread.currentThread().getName() + "]: launch reopen thread");
          }

          while(System.currentTimeMillis() < stopTime) {
            Thread.sleep(_TestUtil.nextInt(random(), 1, 100));
            writer.commit();
            Thread.sleep(_TestUtil.nextInt(random(), 1, 5));
            boolean block = random().nextBoolean();
            if (block) {
              mgr.maybeRefreshBlocking();
              lifetimeMGR.prune(pruner);
            } else if (mgr.maybeRefresh()) {
              lifetimeMGR.prune(pruner);
            }
          }
        } catch (Throwable t) {
          if (VERBOSE) {
            System.out.println("TEST: reopen thread hit exc");
            t.printStackTrace(System.out);
          }
          failed.set(true);
          throw new RuntimeException(t);
        }
      }
      };
    reopenThread.setDaemon(true);
    reopenThread.start();

    runSearchThreads(stopTime);

    reopenThread.join();
  }

  @Override
  protected IndexSearcher getCurrentSearcher() throws Exception {
    if (random().nextInt(10) == 7) {
      // NOTE: not best practice to call maybeReopen
      // synchronous to your search threads, but still we
      // test as apps will presumably do this for
      // simplicity:
      if (mgr.maybeRefresh()) {
        lifetimeMGR.prune(pruner);
      }
    }

    IndexSearcher s = null;

    synchronized(pastSearchers) {
      while (pastSearchers.size() != 0 && random().nextDouble() < 0.25) {
        // 1/4 of the time pull an old searcher, ie, simulate
        // a user doing a follow-on action on a previous
        // search (drilling down/up, clicking next/prev page,
        // etc.)
        final Long token = pastSearchers.get(random().nextInt(pastSearchers.size()));
        s = lifetimeMGR.acquire(token);
        if (s == null) {
          // Searcher was pruned
          pastSearchers.remove(token);
        } else {
          break;
        }
      }
    }

    if (s == null) {
      s = mgr.acquire();
      if (s.getIndexReader().numDocs() != 0) {
        Long token = lifetimeMGR.record(s);
        synchronized(pastSearchers) {
          if (!pastSearchers.contains(token)) {
            pastSearchers.add(token);
          }
        }
      }
    }

    return s;
  }

  @Override
  protected void releaseSearcher(IndexSearcher s) throws Exception {
    s.getIndexReader().decRef();
  }

  @Override
  protected void doClose() throws Exception {
    assertTrue(warmCalled);
    if (VERBOSE) {
      System.out.println("TEST: now close SearcherManager");
    }
    mgr.close();
    lifetimeMGR.close();
  }

  public void testIntermediateClose() throws IOException, InterruptedException {
    Directory dir = newDirectory();
    // Test can deadlock if we use SMS:
    IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
        TEST_VERSION_CURRENT, new MockAnalyzer(random())).setMergeScheduler(new ConcurrentMergeScheduler()));
    writer.addDocument(new Document());
    writer.commit();
    final CountDownLatch awaitEnterWarm = new CountDownLatch(1);
    final CountDownLatch awaitClose = new CountDownLatch(1);
    final AtomicBoolean triedReopen = new AtomicBoolean(false);
    final ExecutorService es = random().nextBoolean() ? null : Executors.newCachedThreadPool(new NamedThreadFactory("testIntermediateClose"));
    final SearcherFactory factory = new SearcherFactory() {
      @Override
      public IndexSearcher newSearcher(IndexReader r) {
        try {
          if (triedReopen.get()) {
            awaitEnterWarm.countDown();
            awaitClose.await();
          }
        } catch (InterruptedException e) {
          //
        }
        return new IndexSearcher(r, es);
      }
    };
    final SearcherManager searcherManager = random().nextBoolean()
        ? new SearcherManager(dir, factory)
        : new SearcherManager(writer, random().nextBoolean(), factory);
    if (VERBOSE) {
      System.out.println("sm created");
    }
    IndexSearcher searcher = searcherManager.acquire();
    try {
      assertEquals(1, searcher.getIndexReader().numDocs());
    } finally {
      searcherManager.release(searcher);
    }
    writer.addDocument(new Document());
    writer.commit();
    final AtomicBoolean success = new AtomicBoolean(false);
    final Throwable[] exc = new Throwable[1];
    Thread thread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          triedReopen.set(true);
          if (VERBOSE) {
            System.out.println("NOW call maybeReopen");
          }
          searcherManager.maybeRefresh();
          success.set(true);
        } catch (AlreadyClosedException e) {
          // expected
        } catch (Throwable e) {
          if (VERBOSE) {
            System.out.println("FAIL: unexpected exc");
            e.printStackTrace(System.out);
          }
          exc[0] = e;
          // use success as the barrier here to make sure we see the write
          success.set(false);

        }
      }
    });
    thread.start();
    if (VERBOSE) {
      System.out.println("THREAD started");
    }
    awaitEnterWarm.await();
    if (VERBOSE) {
      System.out.println("NOW call close");
    }
    searcherManager.close();
    awaitClose.countDown();
    thread.join();
    try {
      searcherManager.acquire();
      fail("already closed");
    } catch (AlreadyClosedException ex) {
      // expected
    }
    assertFalse(success.get());
    assertTrue(triedReopen.get());
    assertNull("" + exc[0], exc[0]);
    writer.close();
    dir.close();
    if (es != null) {
      es.shutdown();
      es.awaitTermination(1, TimeUnit.SECONDS);
    }
  }
 
  public void testCloseTwice() throws Exception {
    // test that we can close SM twice (per Closeable's contract).
    Directory dir = newDirectory();
    new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, null)).close();
    SearcherManager sm = new SearcherManager(dir, null);
    sm.close();
    sm.close();
    dir.close();
  }

  public void testEnsureOpen() throws Exception {
    Directory dir = newDirectory();
    new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, null)).close();
    SearcherManager sm = new SearcherManager(dir, null);
    IndexSearcher s = sm.acquire();
    sm.close();
   
    // this should succeed;
    sm.release(s);
   
    try {
      // this should fail
      sm.acquire();
    } catch (AlreadyClosedException e) {
      // ok
    }
   
    try {
      // this should fail
      sm.maybeRefresh();
    } catch (AlreadyClosedException e) {
      // ok
    }
    dir.close();
  }

  public void testListenerCalled() throws Exception {
    Directory dir = newDirectory();
    IndexWriter iw = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, null));
    final AtomicBoolean afterRefreshCalled = new AtomicBoolean(false);
    SearcherManager sm = new SearcherManager(iw, false, new SearcherFactory());
    sm.addListener(new ReferenceManager.RefreshListener() {
      @Override
      public void beforeRefresh() {
      }
      @Override
      public void afterRefresh(boolean didRefresh) {
        if (didRefresh) {
          afterRefreshCalled.set(true);
        }
      }
    });
    iw.addDocument(new Document());
    iw.commit();
    assertFalse(afterRefreshCalled.get());
    sm.maybeRefreshBlocking();
    assertTrue(afterRefreshCalled.get());
    sm.close();
    iw.close();
    dir.close();
  }

  public void testEvilSearcherFactory() throws Exception {
    final Random random = random();
    final Directory dir = newDirectory();
    final RandomIndexWriter w = new RandomIndexWriter(random, dir);
    w.commit();

    final IndexReader other = DirectoryReader.open(dir);

    final SearcherFactory theEvilOne = new SearcherFactory() {
      @Override
      public IndexSearcher newSearcher(IndexReader ignored) {
        return LuceneTestCase.newSearcher(other);
      }
      };

    try {
      new SearcherManager(dir, theEvilOne);
    } catch (IllegalStateException ise) {
      // expected
    }
    try {
      new SearcherManager(w.w, random.nextBoolean(), theEvilOne);
    } catch (IllegalStateException ise) {
      // expected
    }
    w.close();
    other.close();
    dir.close();
  }
 
  public void testMaybeRefreshBlockingLock() throws Exception {
    // make sure that maybeRefreshBlocking releases the lock, otherwise other
    // threads cannot obtain it.
    final Directory dir = newDirectory();
    final RandomIndexWriter w = new RandomIndexWriter(random(), dir);
    w.close();
   
    final SearcherManager sm = new SearcherManager(dir, null);
   
    Thread t = new Thread() {
      @Override
      public void run() {
        try {
          // this used to not release the lock, preventing other threads from obtaining it.
          sm.maybeRefreshBlocking();
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      }
    };
    t.start();
    t.join();
   
    // if maybeRefreshBlocking didn't release the lock, this will fail.
    assertTrue("failde to obtain the refreshLock!", sm.maybeRefresh());
   
    sm.close();
    dir.close();
  }
 
}
TOP

Related Classes of org.apache.lucene.search.TestSearcherManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.