@Override
 protected void doClose() throws Exception {
   assertTrue(warmCalled);
   if (VERBOSE) {
     System.out.println("TEST: now close SearcherManagers");
   }
   nrtDeletesThread.close();
   nrtDeletes.close();
   nrtNoDeletesThread.close();
   nrtNoDeletes.close();
 }
 @Override
 protected IndexSearcher getFinalSearcher() throws Exception {
   if (VERBOSE) {
     System.out.println("TEST: finalSearcher maxGen=" + maxGen);
   }
   nrtDeletesThread.waitForGeneration(maxGen);
   return nrtDeletes.acquire();
 }
  @Override
  protected void doAfterWriter(final ExecutorService es) throws Exception {
    final double minReopenSec = 0.01 + 0.05 * random().nextDouble();
    final double maxReopenSec = minReopenSec * (1.0 + 10 * random().nextDouble());

    if (VERBOSE) {
      System.out.println(
          "TEST: make SearcherManager maxReopenSec="
              + maxReopenSec
              + " minReopenSec="
              + minReopenSec);
    }

    genWriter = new TrackingIndexWriter(writer);

    final SearcherFactory sf =
        new SearcherFactory() {
          @Override
          public IndexSearcher newSearcher(IndexReader r) throws IOException {
            TestControlledRealTimeReopenThread.this.warmCalled = true;
            IndexSearcher s = new IndexSearcher(r, es);
            s.search(new TermQuery(new Term("body", "united")), 10);
            return s;
          }
        };

    nrtNoDeletes = new SearcherManager(writer, false, sf);
    nrtDeletes = new SearcherManager(writer, true, sf);

    nrtDeletesThread =
        new ControlledRealTimeReopenThread<>(genWriter, nrtDeletes, maxReopenSec, minReopenSec);
    nrtDeletesThread.setName("NRTDeletes Reopen Thread");
    nrtDeletesThread.setPriority(
        Math.min(Thread.currentThread().getPriority() + 2, Thread.MAX_PRIORITY));
    nrtDeletesThread.setDaemon(true);
    nrtDeletesThread.start();

    nrtNoDeletesThread =
        new ControlledRealTimeReopenThread<>(genWriter, nrtNoDeletes, maxReopenSec, minReopenSec);
    nrtNoDeletesThread.setName("NRTNoDeletes Reopen Thread");
    nrtNoDeletesThread.setPriority(
        Math.min(Thread.currentThread().getPriority() + 2, Thread.MAX_PRIORITY));
    nrtNoDeletesThread.setDaemon(true);
    nrtNoDeletesThread.start();
  }
 @Override
 protected void deleteDocuments(Term id) throws Exception {
   final long gen = genWriter.deleteDocuments(id);
   // randomly verify the delete "took":
   if (random().nextInt(20) == 7) {
     if (VERBOSE) {
       System.out.println(Thread.currentThread().getName() + ": nrt: verify del " + id);
     }
     nrtDeletesThread.waitForGeneration(gen);
     final IndexSearcher s = nrtDeletes.acquire();
     if (VERBOSE) {
       System.out.println(Thread.currentThread().getName() + ": nrt: got searcher=" + s);
     }
     try {
       assertEquals(0, s.search(new TermQuery(id), 10).totalHits);
     } finally {
       nrtDeletes.release(s);
     }
   }
   lastGens.set(gen);
 }
  @Override
  protected void addDocument(Term id, Iterable<? extends IndexableField> doc) throws Exception {
    final long gen = genWriter.addDocument(doc);

    // Randomly verify the add "took":
    if (random().nextInt(20) == 2) {
      if (VERBOSE) {
        System.out.println(Thread.currentThread().getName() + ": nrt: verify " + id);
      }
      nrtNoDeletesThread.waitForGeneration(gen);
      final IndexSearcher s = nrtNoDeletes.acquire();
      if (VERBOSE) {
        System.out.println(Thread.currentThread().getName() + ": nrt: got searcher=" + s);
      }
      try {
        assertEquals(1, s.search(new TermQuery(id), 10).totalHits);
      } finally {
        nrtNoDeletes.release(s);
      }
    }
    lastGens.set(gen);
  }
  // Relies on wall clock time, so it can easily false-fail when the machine is otherwise busy:
  @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-5737")
  // LUCENE-5461
  public void testCRTReopen() throws Exception {
    // test behaving badly

    // should be high enough
    int maxStaleSecs = 20;

    // build crap data just to store it.
    String s = "        abcdefghijklmnopqrstuvwxyz     ";
    char[] chars = s.toCharArray();
    StringBuilder builder = new StringBuilder(2048);
    for (int i = 0; i < 2048; i++) {
      builder.append(chars[random().nextInt(chars.length)]);
    }
    String content = builder.toString();

    final SnapshotDeletionPolicy sdp =
        new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy());
    final Directory dir = new NRTCachingDirectory(newFSDirectory(createTempDir("nrt")), 5, 128);
    IndexWriterConfig config =
        new IndexWriterConfig(Version.LUCENE_4_6, new MockAnalyzer(random()));
    config.setIndexDeletionPolicy(sdp);
    config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
    final IndexWriter iw = new IndexWriter(dir, config);
    SearcherManager sm = new SearcherManager(iw, true, new SearcherFactory());
    final TrackingIndexWriter tiw = new TrackingIndexWriter(iw);
    ControlledRealTimeReopenThread<IndexSearcher> controlledRealTimeReopenThread =
        new ControlledRealTimeReopenThread<>(tiw, sm, maxStaleSecs, 0);

    controlledRealTimeReopenThread.setDaemon(true);
    controlledRealTimeReopenThread.start();

    List<Thread> commitThreads = new ArrayList<>();

    for (int i = 0; i < 500; i++) {
      if (i > 0 && i % 50 == 0) {
        Thread commitThread =
            new Thread(
                new Runnable() {
                  @Override
                  public void run() {
                    try {
                      iw.commit();
                      IndexCommit ic = sdp.snapshot();
                      for (String name : ic.getFileNames()) {
                        // distribute, and backup
                        // System.out.println(names);
                        assertTrue(slowFileExists(dir, name));
                      }
                    } catch (Exception e) {
                      throw new RuntimeException(e);
                    }
                  }
                });
        commitThread.start();
        commitThreads.add(commitThread);
      }
      Document d = new Document();
      d.add(new TextField("count", i + "", Field.Store.NO));
      d.add(new TextField("content", content, Field.Store.YES));
      long start = System.currentTimeMillis();
      long l = tiw.addDocument(d);
      controlledRealTimeReopenThread.waitForGeneration(l);
      long wait = System.currentTimeMillis() - start;
      assertTrue("waited too long for generation " + wait, wait < (maxStaleSecs * 1000));
      IndexSearcher searcher = sm.acquire();
      TopDocs td = searcher.search(new TermQuery(new Term("count", i + "")), 10);
      sm.release(searcher);
      assertEquals(1, td.totalHits);
    }

    for (Thread commitThread : commitThreads) {
      commitThread.join();
    }

    controlledRealTimeReopenThread.close();
    sm.close();
    iw.close();
    dir.close();
  }
  /*
   * LUCENE-3528 - NRTManager hangs in certain situations
   */
  public void testThreadStarvationNoDeleteNRTReader() throws IOException, InterruptedException {
    IndexWriterConfig conf = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()));
    conf.setMergePolicy(NoMergePolicy.INSTANCE);
    Directory d = newDirectory();
    final CountDownLatch latch = new CountDownLatch(1);
    final CountDownLatch signal = new CountDownLatch(1);

    LatchedIndexWriter _writer = new LatchedIndexWriter(d, conf, latch, signal);
    final TrackingIndexWriter writer = new TrackingIndexWriter(_writer);
    final SearcherManager manager = new SearcherManager(_writer, false, null);
    Document doc = new Document();
    doc.add(newTextField("test", "test", Field.Store.YES));
    writer.addDocument(doc);
    manager.maybeRefresh();
    Thread t =
        new Thread() {
          @Override
          public void run() {
            try {
              signal.await();
              manager.maybeRefresh();
              writer.deleteDocuments(new TermQuery(new Term("foo", "barista")));
              manager.maybeRefresh(); // kick off another reopen so we inc. the internal gen
            } catch (Exception e) {
              e.printStackTrace();
            } finally {
              latch.countDown(); // let the add below finish
            }
          }
        };
    t.start();
    _writer.waitAfterUpdate = true; // wait in addDocument to let some reopens go through
    final long lastGen =
        writer.updateDocument(
            new Term("foo", "bar"),
            doc); // once this returns the doc is already reflected in the last reopen

    assertFalse(manager.isSearcherCurrent()); // false since there is a delete in the queue

    IndexSearcher searcher = manager.acquire();
    try {
      assertEquals(2, searcher.getIndexReader().numDocs());
    } finally {
      manager.release(searcher);
    }
    final ControlledRealTimeReopenThread<IndexSearcher> thread =
        new ControlledRealTimeReopenThread<>(writer, manager, 0.01, 0.01);
    thread.start(); // start reopening
    if (VERBOSE) {
      System.out.println("waiting now for generation " + lastGen);
    }

    final AtomicBoolean finished = new AtomicBoolean(false);
    Thread waiter =
        new Thread() {
          @Override
          public void run() {
            try {
              thread.waitForGeneration(lastGen);
            } catch (InterruptedException ie) {
              Thread.currentThread().interrupt();
              throw new RuntimeException(ie);
            }
            finished.set(true);
          }
        };
    waiter.start();
    manager.maybeRefresh();
    waiter.join(1000);
    if (!finished.get()) {
      waiter.interrupt();
      fail("thread deadlocked on waitForGeneration");
    }
    thread.close();
    thread.join();
    IOUtils.close(manager, _writer, d);
  }