test_class.file: oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgentTest.java
test_class.identifier: LastRevRecoveryAgentTest
test_class.superclass: extends AbstractTwoNodeTest
test_case.body: @Test
      public void recoveryOfModifiedDocument() throws Exception {
          // do not retry merges
          ds1.setMaxBackOffMillis(0);
          ds2.setMaxBackOffMillis(0);
  
          NodeBuilder b1 = ds1.getRoot().builder();
          b1.child("x").child("y").setProperty("p", "v1");
          merge(ds1, b1);
  
          ds1.runBackgroundOperations();
          ds2.runBackgroundOperations();
  
          NodeBuilder b2 = ds2.getRoot().builder();
          b2.child("x").child("y").setProperty("p", "v2");
          merge(ds2, b2);
  
          // simulate a crash of ds2
          long leaseTime = ds2.getClusterInfo().getLeaseTime();
          clock.waitUntil(clock.getTime() + leaseTime * 2);
  
          // this write will conflict because ds2 did not run
          // background ops after setting p=v2
          b1 = ds1.getRoot().builder();
          b1.child("x").child("y").setProperty("p", "v11");
          try {
              merge(ds1, b1);
              fail("CommitFailedException expected");
          } catch (CommitFailedException e) {
              // expected
          }
  
          ds1.getLastRevRecoveryAgent().recover(2);
          ds1.runBackgroundOperations();
  
          // now the write must succeed
          b1 = ds1.getRoot().builder();
          b1.child("x").child("y").setProperty("p", "v11");
          merge(ds1, b1);
  
          // dispose ds2 quietly because it may now throw an exception
          disposeQuietly(ds2);
      }
test_case.class_method_signature: LastRevRecoveryAgentTest.recoveryOfModifiedDocument()
test_case.full_signature: @Test public void recoveryOfModifiedDocument()
test_case.identifier: recoveryOfModifiedDocument
test_case.invocations: setMaxBackOffMillis setMaxBackOffMillis builder getRoot setProperty child child merge runBackgroundOperations runBackgroundOperations builder getRoot setProperty child child merge getLeaseTime getClusterInfo waitUntil getTime builder getRoot setProperty child child merge fail recover getLastRevRecoveryAgent runBackgroundOperations builder getRoot setProperty child child merge disposeQuietly
test_case.modifiers: @Test public
test_case.parameters: ()
test_case.return: void
test_case.signature: void recoveryOfModifiedDocument()
focal_class.declarator: log = LoggerFactory.getLogger(getClass())
focal_class.modifier: private final
focal_class.original_string: private final Logger log = LoggerFactory.getLogger(getClass());
focal_class.type: Logger
focal_class.var_name: log
focal_class.declarator: store
focal_class.modifier: private final
focal_class.original_string: private final DocumentStore store;
focal_class.type: DocumentStore
focal_class.var_name: store
focal_class.declarator: revisionContext
focal_class.modifier: private final
focal_class.original_string: private final RevisionContext revisionContext;
focal_class.type: RevisionContext
focal_class.var_name: revisionContext
focal_class.declarator: missingLastRevUtil
focal_class.modifier: private final
focal_class.original_string: private final MissingLastRevSeeker missingLastRevUtil;
focal_class.type: MissingLastRevSeeker
focal_class.var_name: missingLastRevUtil
focal_class.declarator: afterRecovery
focal_class.modifier: private final
focal_class.original_string: private final Consumer<Integer> afterRecovery;
focal_class.type: Consumer<Integer>
focal_class.var_name: afterRecovery
focal_class.declarator: LOGINTERVALMS = TimeUnit.MINUTES.toMillis(1)
focal_class.modifier: private static final
focal_class.original_string: private static final long LOGINTERVALMS = TimeUnit.MINUTES.toMillis(1);
focal_class.type: long
focal_class.var_name: LOGINTERVALMS
focal_class.file: oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java
focal_class.identifier: LastRevRecoveryAgent
focal_class.class_method_signature: LastRevRecoveryAgent.LastRevRecoveryAgent(DocumentStore store,
                                  RevisionContext revisionContext,
                                  MissingLastRevSeeker seeker,
                                  Consumer<Integer> afterRecovery)
focal_class.full_signature: public  LastRevRecoveryAgent(DocumentStore store,
                                  RevisionContext revisionContext,
                                  MissingLastRevSeeker seeker,
                                  Consumer<Integer> afterRecovery)
focal_class.identifier: LastRevRecoveryAgent
focal_class.modifiers: public
focal_class.parameters: (DocumentStore store,
                                  RevisionContext revisionContext,
                                  MissingLastRevSeeker seeker,
                                  Consumer<Integer> afterRecovery)
focal_class.signature:  LastRevRecoveryAgent(DocumentStore store,
                                  RevisionContext revisionContext,
                                  MissingLastRevSeeker seeker,
                                  Consumer<Integer> afterRecovery)
focal_class.class_method_signature: LastRevRecoveryAgent.LastRevRecoveryAgent(DocumentStore store, RevisionContext context)
focal_class.full_signature: public  LastRevRecoveryAgent(DocumentStore store, RevisionContext context)
focal_class.identifier: LastRevRecoveryAgent
focal_class.modifiers: public
focal_class.parameters: (DocumentStore store, RevisionContext context)
focal_class.signature:  LastRevRecoveryAgent(DocumentStore store, RevisionContext context)
focal_class.class_method_signature: LastRevRecoveryAgent.recover(int clusterId, long waitUntil)
focal_class.full_signature: public int recover(int clusterId, long waitUntil)
focal_class.identifier: recover
focal_class.modifiers: public
focal_class.parameters: (int clusterId, long waitUntil)
focal_class.return: int
focal_class.signature: int recover(int clusterId, long waitUntil)
focal_class.class_method_signature: LastRevRecoveryAgent.recover(int clusterId)
focal_class.full_signature: public int recover(int clusterId)
focal_class.identifier: recover
focal_class.modifiers: public
focal_class.parameters: (int clusterId)
focal_class.return: int
focal_class.signature: int recover(int clusterId)
focal_class.class_method_signature: LastRevRecoveryAgent.recover(Iterable<NodeDocument> suspects, int clusterId)
focal_class.full_signature: public int recover(Iterable<NodeDocument> suspects, int clusterId)
focal_class.identifier: recover
focal_class.modifiers: public
focal_class.parameters: (Iterable<NodeDocument> suspects, int clusterId)
focal_class.return: int
focal_class.signature: int recover(Iterable<NodeDocument> suspects, int clusterId)
focal_class.class_method_signature: LastRevRecoveryAgent.recover(final Iterable<NodeDocument> suspects,
                         final int clusterId, final boolean dryRun)
focal_class.full_signature: public int recover(final Iterable<NodeDocument> suspects,
                         final int clusterId, final boolean dryRun)
focal_class.identifier: recover
focal_class.modifiers: public
focal_class.parameters: (final Iterable<NodeDocument> suspects,
                         final int clusterId, final boolean dryRun)
focal_class.return: int
focal_class.signature: int recover(final Iterable<NodeDocument> suspects,
                         final int clusterId, final boolean dryRun)
focal_class.class_method_signature: LastRevRecoveryAgent.getEarliestBranchCommitMergedAround(@NotNull NodeDocument doc,
                                                           long timeMillis,
                                                           int clusterId)
focal_class.full_signature: @Nullable private Revision getEarliestBranchCommitMergedAround(@NotNull NodeDocument doc,
                                                           long timeMillis,
                                                           int clusterId)
focal_class.identifier: getEarliestBranchCommitMergedAround
focal_class.modifiers: @Nullable private
focal_class.parameters: (@NotNull NodeDocument doc,
                                                           long timeMillis,
                                                           int clusterId)
focal_class.return: Revision
focal_class.signature: Revision getEarliestBranchCommitMergedAround(@NotNull NodeDocument doc,
                                                           long timeMillis,
                                                           int clusterId)
focal_class.class_method_signature: LastRevRecoveryAgent.findNearestAncestorOrSelf(@NotNull Path path,
                                                     @NotNull List<Path> missingDocuments)
focal_class.full_signature: @Nullable private NodeDocument findNearestAncestorOrSelf(@NotNull Path path,
                                                     @NotNull List<Path> missingDocuments)
focal_class.identifier: findNearestAncestorOrSelf
focal_class.modifiers: @Nullable private
focal_class.parameters: (@NotNull Path path,
                                                     @NotNull List<Path> missingDocuments)
focal_class.return: NodeDocument
focal_class.signature: NodeDocument findNearestAncestorOrSelf(@NotNull Path path,
                                                     @NotNull List<Path> missingDocuments)
focal_class.class_method_signature: LastRevRecoveryAgent.recoverCandidates(final ClusterNodeInfoDocument nodeInfo,
                                    final long startTime,
                                    final long waitUntil,
                                    final String info)
focal_class.full_signature: private int recoverCandidates(final ClusterNodeInfoDocument nodeInfo,
                                    final long startTime,
                                    final long waitUntil,
                                    final String info)
focal_class.identifier: recoverCandidates
focal_class.modifiers: private
focal_class.parameters: (final ClusterNodeInfoDocument nodeInfo,
                                    final long startTime,
                                    final long waitUntil,
                                    final String info)
focal_class.return: int
focal_class.signature: int recoverCandidates(final ClusterNodeInfoDocument nodeInfo,
                                    final long startTime,
                                    final long waitUntil,
                                    final String info)
focal_class.class_method_signature: LastRevRecoveryAgent.determineLastModification(NodeDocument doc, int clusterId)
focal_class.full_signature: @Nullable private Revision determineLastModification(NodeDocument doc, int clusterId)
focal_class.identifier: determineLastModification
focal_class.modifiers: @Nullable private
focal_class.parameters: (NodeDocument doc, int clusterId)
focal_class.return: Revision
focal_class.signature: Revision determineLastModification(NodeDocument doc, int clusterId)
focal_class.class_method_signature: LastRevRecoveryAgent.isRecoveryNeeded()
focal_class.full_signature: public boolean isRecoveryNeeded()
focal_class.identifier: isRecoveryNeeded
focal_class.modifiers: public
focal_class.parameters: ()
focal_class.return: boolean
focal_class.signature: boolean isRecoveryNeeded()
focal_class.class_method_signature: LastRevRecoveryAgent.performRecoveryIfNeeded()
focal_class.full_signature: public void performRecoveryIfNeeded()
focal_class.identifier: performRecoveryIfNeeded
focal_class.modifiers: public
focal_class.parameters: ()
focal_class.return: void
focal_class.signature: void performRecoveryIfNeeded()
focal_class.class_method_signature: LastRevRecoveryAgent.getRecoveryCandidateNodes()
focal_class.full_signature: public Iterable<Integer> getRecoveryCandidateNodes()
focal_class.identifier: getRecoveryCandidateNodes
focal_class.modifiers: public
focal_class.parameters: ()
focal_class.return: Iterable<Integer>
focal_class.signature: Iterable<Integer> getRecoveryCandidateNodes()
focal_method.body: public int recover(int clusterId, long waitUntil)
              throws DocumentStoreException {
          ClusterNodeInfoDocument nodeInfo = missingLastRevUtil.getClusterNodeInfo(clusterId);
  
          if (nodeInfo != null) {
              // Check if _lastRev recovery needed for this cluster node
              // state is Active && current time past leaseEnd
              if (nodeInfo.isRecoveryNeeded(revisionContext.getClock().getTime())) {
                  // retrieve the root document's _lastRev
                  NodeDocument root = missingLastRevUtil.getRoot();
                  Revision lastRev = root.getLastRev().get(clusterId);
                  Revision sweepRev = root.getSweepRevisions().getRevision(clusterId);
  
                  // start time is the _lastRev timestamp of the cluster node
                  long startTime;
                  String reason;
                  //lastRev can be null if other cluster node did not got
                  //chance to perform lastRev rollup even once
                  if (lastRev != null) {
                      startTime = lastRev.getTimestamp();
                      reason = "lastRev: " + lastRev.toString();
                  } else {
                      startTime = nodeInfo.getStartTime();
                      reason = String.format(
                              "no lastRev for root, using startTime %d", startTime);
                  }
                  if (sweepRev != null && sweepRev.getTimestamp() < startTime) {
                      startTime = sweepRev.getTimestamp();
                      reason = "sweepRev: " + sweepRev.toString();
                  }
                  // are there branch commits that were merged right before the
                  // process crashed and the recovery needs to go further back?
                  // go through branch commits before startTime and check if their
                  // merge revision is newer than startTime
                  Revision bc = getEarliestBranchCommitMergedAround(root, startTime, clusterId);
                  if (bc != null) {
                      startTime = bc.getTimestamp();
                      reason = "branchRev: " + bc.toString();
                  }
  
                  return recoverCandidates(nodeInfo, startTime, waitUntil, reason);
              }
          }
  
          log.debug("No recovery needed for clusterId {}", clusterId);
          return 0;
      }
focal_method.class_method_signature: LastRevRecoveryAgent.recover(int clusterId, long waitUntil)
focal_method.full_signature: public int recover(int clusterId, long waitUntil)
focal_method.identifier: recover
focal_method.invocations: getClusterNodeInfo isRecoveryNeeded getTime getClock getRoot get getLastRev getRevision getSweepRevisions getTimestamp toString getStartTime format getTimestamp getTimestamp toString getEarliestBranchCommitMergedAround getTimestamp toString recoverCandidates debug
focal_method.modifiers: public
focal_method.parameters: (int clusterId, long waitUntil)
focal_method.return: int
focal_method.signature: int recover(int clusterId, long waitUntil)