@VisibleForTesting
  void recover() {
    try (DbSession dbSession = dbClient.openSession(false)) {
      Profiler profiler = Profiler.create(LOGGER).start();
      long beforeDate = system2.now() - minAgeInMs;
      IndexingResult result = new IndexingResult();

      Collection<EsQueueDto> items = dbClient.esQueueDao().selectForRecovery(dbSession, beforeDate, loopLimit);
      while (!items.isEmpty()) {
        IndexingResult loopResult = new IndexingResult();

        groupItemsByDocType(items).asMap().forEach((type, typeItems) -> loopResult.add(doIndex(dbSession, type, typeItems)));
        result.add(loopResult);

        if (loopResult.getSuccessRatio() <= CIRCUIT_BREAKER_IN_PERCENT) {
          LOGGER.error(LOG_PREFIX + "too many failures [{}/{} documents], waiting for next run", loopResult.getFailures(), loopResult.getTotal());
          break;
        }

        if (loopResult.getTotal() == 0L) {
          break;
        }

        items = dbClient.esQueueDao().selectForRecovery(dbSession, beforeDate, loopLimit);
      }
      if (result.getTotal() > 0L) {
        profiler.stopInfo(LOG_PREFIX + format("%d documents processed [%d failures]", result.getTotal(), result.getFailures()));
      }
    } catch (Throwable t) {
      LOGGER.error(LOG_PREFIX + "fail to recover documents", t);
    }
  }
@Test
  public void stop_run_if_too_many_failures() {
    IntStream.range(0, 10).forEach(i -> insertItem(FOO_TYPE, "" + i));
    advanceInTime();

    // 10 docs to process, by groups of 3.
    // The first group successfully recovers only 1 docs --> above 30% of failures --> stop run
    PartiallyFailingIndexer indexer = new PartiallyFailingIndexer(FOO_TYPE, 1);
    MapSettings settings = new MapSettings()
      .setProperty("sonar.search.recovery.loopLimit", "3");
    underTest = newRecoveryIndexer(settings.asConfig(), indexer);
    underTest.recover();

    assertThatLogsContain(ERROR, "Elasticsearch recovery - too many failures [2/3 documents], waiting for next run");
    assertThatQueueHasSize(9);

    // The indexer must have been called once and only once.
    assertThat(indexer.called).hasSize(3);
  }