@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 recover_multiple_times_the_same_document() {
    EsQueueDto item1 = insertItem(FOO_TYPE, "f1");
    EsQueueDto item2 = insertItem(FOO_TYPE, item1.getDocId());
    EsQueueDto item3 = insertItem(FOO_TYPE, item1.getDocId());
    advanceInTime();

    SuccessfulFakeIndexer indexer = new SuccessfulFakeIndexer(FOO_TYPE);
    underTest = newRecoveryIndexer(indexer);
    underTest.recover();

    assertThatQueueHasSize(0);
    assertThat(indexer.called).hasSize(1);
    assertThat(indexer.called.get(0)).extracting(EsQueueDto::getUuid)
      .containsExactlyInAnyOrder(item1.getUuid(), item2.getUuid(), item3.getUuid());

    assertThatLogsContain(TRACE, "Elasticsearch recovery - processing 3 [foos/foo]");
    assertThatLogsContain(INFO, "Elasticsearch recovery - 3 documents processed [0 failures]");
  }