void replayWALEntry(WALEntry entry, CellScanner cells) throws IOException {
    long timeout = -1L;
    Optional<RpcCall> call = RpcServer.getCurrentCall();
    if (call.isPresent()) {
      long deadline = call.get().getDeadline();
      if (deadline < Long.MAX_VALUE) {
        timeout = deadline - EnvironmentEdgeManager.currentTime();
        if (timeout <= 0) {
          throw new TimeoutIOException("Timeout while replaying edits for " + getRegionInfo());
        }
      }
    }
    if (timeout > 0) {
      try {
        if (!replayLock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
          throw new TimeoutIOException(
            "Timeout while waiting for lock when replaying edits for " + getRegionInfo());
        }
      } catch (InterruptedException e) {
        throw throwOnInterrupt(e);
      }
    } else {
      replayLock.lock();
    }
    try {
      int count = entry.getAssociatedCellCount();
      long sequenceId = entry.getKey().getLogSequenceNumber();
      if (lastReplayedSequenceId >= sequenceId) {
        // we have already replayed this edit, skip
        // remember to advance the CellScanner, as we may have multiple WALEntries, we may still
        // need apply later WALEntries
        for (int i = 0; i < count; i++) {
          // Throw index out of bounds if our cell count is off
          if (!cells.advance()) {
            throw new ArrayIndexOutOfBoundsException("Expected=" + count + ", index=" + i);
          }
        }
        return;
      }
      Map<byte[], List<ExtendedCell>> family2Cells = new TreeMap<>(Bytes.BYTES_COMPARATOR);
      for (int i = 0; i < count; i++) {
        // Throw index out of bounds if our cell count is off
        if (!cells.advance()) {
          throw new ArrayIndexOutOfBoundsException("Expected=" + count + ", index=" + i);
        }
        Cell c = cells.current();
        assert c instanceof ExtendedCell;
        ExtendedCell cell = (ExtendedCell) c;
        if (WALEdit.isMetaEditFamily(cell)) {
          // If there is meta edit, i.e, we have done flush/compaction/open, then we need to apply
          // the previous cells first, and then replay the special meta edit. The meta edit is like
          // a barrier, We need to keep the order. For example, the flush marker will contain a
          // flush sequence number, which makes us possible to drop memstore content, but if we
          // apply some edits which have greater sequence id first, then we can not drop the
          // memstore content when replaying the flush marker, which is not good as we could run out
          // of memory.
          // And usually, a meta edit will have a special WALEntry for it, so this is just a safe
          // guard logic to make sure we do not break things in the worst case.
          if (!family2Cells.isEmpty()) {
            replayWALBatchMutate(family2Cells);
            family2Cells.clear();
          }
          replayWALMetaEdit(cell);
        } else {
          family2Cells.computeIfAbsent(CellUtil.cloneFamily(cell), k -> new ArrayList<>())
            .add(cell);
        }
      }
      // do not forget to apply the remaining cells
      if (!family2Cells.isEmpty()) {
        replayWALBatchMutate(family2Cells);
      }
      mvcc.advanceTo(sequenceId);
      lastReplayedSequenceId = sequenceId;
    } finally {
      replayLock.unlock();
    }
  }
Replay remote wal entry sent by primary replica.
<p/>
Should only call this method on secondary replicas.
hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java