MissingLastRevSeeker { public boolean isRecoveryNeeded() { long now = clock.getTime(); return StreamSupport.stream(getAllClusters().spliterator(), false) .anyMatch(info -> info != null && info.isRecoveryNeeded(now)); } MissingLastRevSeeker(DocumentStore store, Clock clock); @NotNull Iterable<ClusterNodeInfoDocument> getAllClusters(); @Nullable ClusterNodeInfoDocument getClusterNodeInfo(final int clusterId); @NotNull Iterable<NodeDocument> getCandidates(final long startTime); boolean acquireRecoveryLock(int clusterId, int recoveredBy); void releaseRecoveryLock(int clusterId, boolean success); NodeDocument getRoot(); boolean isRecoveryNeeded(); boolean isRecoveryNeeded(@NotNull ClusterNodeInfoDocument nodeInfo);  }
@Test public void isRecoveryNeeded() throws Exception { ClusterNodeInfo.getInstance(store, NOOP, null, null, 1); clock.waitUntil(clock.getTime() + DEFAULT_LEASE_DURATION_MILLIS + 1); ClusterNodeInfo.getInstance(store, NOOP, null, null, 2); assertTrue(seeker.isRecoveryNeeded()); assertTrue(getClusterNodeInfo(1).isRecoveryNeeded(clock.getTime())); assertFalse(getClusterNodeInfo(2).isRecoveryNeeded(clock.getTime())); assertTrue(seeker.acquireRecoveryLock(1, 2)); seeker.releaseRecoveryLock(1, true); assertFalse(seeker.isRecoveryNeeded()); assertFalse(getClusterNodeInfo(1).isRecoveryNeeded(clock.getTime())); assertFalse(getClusterNodeInfo(2).isRecoveryNeeded(clock.getTime())); }