/*
     * Reposition to the specified file, and fill starting at
     * startOffset. Position the window's buffer to point at the log entry
     * indicated by targetOffset
     */
    public void slideAndFill(
        long windowfileNum, long windowStartOffset, long targetOffset, boolean forward)
        throws ChecksumException, FileNotFoundException, DatabaseException {

      FileHandle fileHandle = fileManager.getFileHandle(windowfileNum);
      try {
        startOffset = windowStartOffset;
        setFileNum(windowfileNum, fileHandle.getLogVersion());
        boolean foundData = fillFromFile(fileHandle, targetOffset);

        /*
         * When reading backwards, we need to guarantee there is no log
         * gap, throws out an EnvironmentFailreException if it exists.
         */
        if (!foundData && !forward) {
          throw EnvironmentFailureException.unexpectedState(
              "Detected a log file gap when reading backwards. "
                  + "Target position = "
                  + DbLsn.getNoFormatString(DbLsn.makeLsn(windowfileNum, targetOffset))
                  + " starting position = "
                  + DbLsn.getNoFormatString(DbLsn.makeLsn(windowfileNum, windowStartOffset))
                  + " end position = "
                  + DbLsn.getNoFormatString(DbLsn.makeLsn(windowfileNum, endOffset)));
        }
      } finally {
        fileHandle.release();
      }
    }
 @Override
 public String toString() {
   StringBuilder sb = new StringBuilder();
   long start = DbLsn.makeLsn(fileNum, startOffset);
   long end = DbLsn.makeLsn(fileNum, endOffset);
   sb.append("window covers ");
   sb.append(DbLsn.getNoFormatString(start)).append(" to ");
   sb.append(DbLsn.getNoFormatString(end));
   sb.append(" positioned at ");
   long target = DbLsn.makeLsn(fileNum, startOffset + readBuffer.position());
   sb.append(DbLsn.getNoFormatString(target));
   return sb.toString();
 }
示例#3
0
  void setPosition(long startLsn)
      throws ChecksumException, FileNotFoundException, DatabaseException {

    if (startLsn == DbLsn.NULL_LSN) {
      return;
    }

    /*
     * An assertion: a reposition should never make the reader lose ground.
     */
    if (forward) {
      if (DbLsn.compareTo(getLastLsn(), startLsn) > 0) {
        throw EnvironmentFailureException.unexpectedState(
            "Feeder forward scanning should not be repositioned to "
                + " a position earlier than the current position. Current"
                + " lsn = "
                + DbLsn.getNoFormatString(getLastLsn())
                + " reposition = "
                + DbLsn.getNoFormatString(startLsn));
      }
    } else {
      if (DbLsn.compareTo(getLastLsn(), startLsn) < 0) {
        throw EnvironmentFailureException.unexpectedState(
            "Feeder backward scanning should not be repositioned to "
                + " a position later than the current position. Current"
                + " lsn = "
                + DbLsn.getNoFormatString(getLastLsn())
                + " reposition = "
                + DbLsn.getNoFormatString(startLsn));
      }
    }

    long fileNum = DbLsn.getFileNumber(startLsn);
    long offset = DbLsn.getFileOffset(startLsn);

    if (window.containsLsn(fileNum, offset)) {
      window.positionBuffer(offset);
    } else {
      window.slideAndFill(fileNum, offset, offset);
    }

    if (forward) {
      nextEntryOffset = offset;
    } else {
      currentEntryPrevOffset = offset;
    }
    nReposition++;
  }
示例#4
0
 /** Print out the contents of an entry. */
 @Override
 public StringBuilder dumpEntry(StringBuilder sb, boolean verbose) {
   in.dumpLog(sb, verbose);
   dbId.dumpLog(sb, verbose);
   if (prevFullLsn != DbLsn.NULL_LSN) {
     sb.append("<prevFullLsn>");
     sb.append(DbLsn.getNoFormatString(prevFullLsn));
     sb.append("</prevFullLsn>");
   }
   if (prevDeltaLsn != DbLsn.NULL_LSN) {
     sb.append("<prevDeltaLsn>");
     sb.append(DbLsn.getNoFormatString(prevDeltaLsn));
     sb.append("</prevDeltaLsn>");
   }
   return sb;
 }
    /* Position the readBuffer to the targetOffset. */
    public void positionBuffer(long targetOffset) {

      assert containsOffset(targetOffset)
          : this + " doesn't contain " + DbLsn.getNoFormatString(targetOffset);

      threadSafeBufferPosition(readBuffer, (int) (targetOffset - startOffset));
    }
示例#6
0
 /* Wrap the call to logger to reduce runtime overhead. */
 private void logFinest(long lsn, UndoReader undo, RevertInfo revertTo) {
   if ((logger != null) && (logger.isLoggable(Level.FINEST))) {
     LoggerUtils.finest(
         logger,
         envImpl,
         "undoLsn=" + DbLsn.getNoFormatString(lsn) + " undo=" + undo + " revertInfo=" + revertTo);
   }
 }
示例#7
0
  /** @see Loggable#dumpLog */
  public void dumpLog(StringBuilder sb, boolean verbose) {
    sb.append(" matchpointVLSN=").append(matchpointVLSN.getSequence());
    sb.append(" matchpointLSN=");
    sb.append(DbLsn.getNoFormatString(matchpointLSN));

    /* Make sure the active txns are listed in order, partially for the sake
     * of the LoggableTest unit test, which expects the toString() for two
     * equivalent objects to always display the same, and partially for
     * ease of debugging.
     */
    List<Long> displayTxnIds = new ArrayList<Long>(activeTxnIds);
    Collections.sort(displayTxnIds);
    sb.append(" activeTxnIds=").append(displayTxnIds);
    sb.append("\" time=\"").append(time);
  }
  /**
   * Converts the bin/index slot, whether a singleton LN or a DIN root.
   *
   * <p>Enter/leave with bin field latched, although bin field may change.
   *
   * <p>When a singleton LN is converted, leaves with bin/index fields unchanged.
   *
   * <p>When a dup tree is converted, leaves with bin/index fields set to last inserted slot. This
   * is the slot of the highest key in the dup tree.
   */
  private void convertBinSlot() {
    if (DEBUG) {
      System.out.println(
          "DupConvert BIN LSN "
              + DbLsn.getNoFormatString(bin.getLsn(index))
              + " index "
              + index
              + " nEntries "
              + bin.getNEntries());
    }
    /* Delete slot if LN is deleted. */
    final boolean isDeleted;
    if (isLNDeleted(bin, index)) {
      deleteSlot();
      return;
    }

    final Node node = bin.fetchLNOrDIN(index, CacheMode.DEFAULT);

    if (!node.containsDuplicates()) {
      if (DEBUG) {
        System.out.println("DupConvert BIN LN " + Key.dumpString(bin.getKey(index), 0));
      }
      /* Fetching a non-deleted LN updates the slot key; we're done. */
      assert node instanceof LN;
      nConverted += 1;
      return;
    }

    /*
     * Delete the slot containing the DIN before re-inserting the dup tree,
     * so that the DIN slot key doesn't interfere with insertions.
     *
     * The DIN is evicted and memory usage is decremented. This is not
     * exactly correct because we keep a local reference to the DIN until
     * the dup tree is converted, but we tolerate this temporary
     * inaccuracy.
     */
    final byte[] binKey = bin.getKey(index);
    final DIN din = (DIN) node;
    deleteSlot();
    convertDin(din, binKey);
  }
  /**
   * Transfer a lock from another transaction to this one. Used for master-> replica transitions,
   * when a node has to transform a MasterTxn into a ReplayTxn. Another approach would be to have
   * this importunate ReplayTxn call lock() on the lsn, but that path is not available because we do
   * not have a handle on a databaseImpl.
   */
  public void stealLockFromMasterTxn(Long lsn) {

    LockAttemptResult result = lockManager.stealLock(lsn, this, LockType.WRITE);

    /*
     * Assert, and if something strange happened, opt to invalidate
     * the environment and wipe the slate clean.
     */
    if (!result.success) {
      throw EnvironmentFailureException.unexpectedState(
          envImpl,
          "Transferring from master to replica state, txn "
              + getId()
              + " was unable to transfer lock for "
              + DbLsn.getNoFormatString(lsn)
              + ", lock grant type="
              + result.lockGrant);
    }

    addLock(Long.valueOf(lsn), LockType.WRITE, result.lockGrant);
    addLogInfo(lsn);
  }
示例#10
0
 @Override
 public String toString() {
   return "lsn=" + DbLsn.getNoFormatString(lsn) + " node=" + nodeId;
 }
  /**
   * Ensure that the next target is in the window. The default behavior is that the next target is
   * the next previous entry.
   *
   * @throws DatabaseException
   */
  protected void setBackwardPosition()
      throws ChecksumException, FileNotFoundException, EOFException, DatabaseException {

    /*
     * currentEntryPrevOffset is the entry before the current entry.
     * currentEntryOffset is the entry we just read (or the end of the
     * file if we're starting out.
     */
    if ((currentEntryPrevOffset != 0) && window.containsOffset(currentEntryPrevOffset)) {

      /* The next log entry has passed the start LSN. */
      long nextLsn = DbLsn.makeLsn(window.currentFileNum(), currentEntryPrevOffset);
      if (finishLsn != DbLsn.NULL_LSN) {
        if (DbLsn.compareTo(nextLsn, finishLsn) == -1) {
          throw new EOFException(
              "finish="
                  + DbLsn.getNoFormatString(finishLsn)
                  + "next="
                  + DbLsn.getNoFormatString(nextLsn));
        }
      }

      /* This log entry starts in this buffer, just reposition. */
      window.positionBuffer(currentEntryPrevOffset);
    } else {

      /*
       * The start of the log entry is not in this read buffer so
       * we must fill the buffer again.
       *
       * 1) The target log entry is in a different file from the
       * current window's file. Move the window to the previous
       * file and start the read from the target LSN.
       *
       * 2) The target log entry is the same file but the log entry
       * is larger than the read chunk size. Start the next read
       * buffer from the target LSN. It's going to take multiple
       * reads to get the log entry, and we might as well get as
       * much as possible.
       *
       * 3) In the same file, and the log entry fits within one
       * read buffer. Try to position the next buffer chunk so the
       * target entry is held within the buffer, all the way at the
       * end. That way, since we're reading backwards, there will be
       * more buffered data available for following reads.
       */
      long nextFile;
      long nextWindowStart;
      long nextTarget;

      if (currentEntryPrevOffset == 0) {
        /* Case 1: Go to another file. */
        currentEntryPrevOffset = fileManager.getFileHeaderPrevOffset(window.currentFileNum());

        Long prevFileNum = fileManager.getFollowingFileNum(window.currentFileNum(), false);
        if (prevFileNum == null) {
          throw new EOFException("No file following " + window.currentFileNum());
        }

        /*
         *  Check finishLSN  before proceeding, in case we should stop
         *  the search before attempting to set the file reader to a
         *  position in the previous file. In  [#22407] we threw a
         *  spurious EFE complaining that we cannot read backwards over
         *  a cleaned file because the previous file had  been cleaned
         *  away.
         */
        if (finishLsn != DbLsn.NULL_LSN && prevFileNum < DbLsn.getFileNumber(finishLsn)) {
          throw new EOFException(
              "finish="
                  + DbLsn.getNoFormatString(finishLsn)
                  + " nextFile=0x"
                  + Long.toHexString(prevFileNum));
        }

        if (window.currentFileNum() - prevFileNum.longValue() != 1) {
          handleGapInBackwardsScan(prevFileNum);
        }

        nextFile = prevFileNum;
        nextWindowStart = currentEntryPrevOffset;
        nextTarget = currentEntryPrevOffset;
      } else if ((currentEntryOffset - currentEntryPrevOffset) > window.capacity()) {

        /*
         * Case 2: The entry is in the same file, but is bigger
         * than one buffer. Position it at the front of the buffer.
         */
        nextFile = window.currentFileNum();
        nextWindowStart = currentEntryPrevOffset;
        nextTarget = currentEntryPrevOffset;
      } else {

        /*
         * Case 3: In same file, but not in this buffer. The target
         * entry will fit in one buffer.
         */
        nextFile = window.currentFileNum();
        long newPosition = currentEntryOffset - window.capacity();
        nextWindowStart = (newPosition < 0) ? 0 : newPosition;
        nextTarget = currentEntryPrevOffset;
      }

      /* The next log entry has passed the start LSN. */
      long nextLsn = DbLsn.makeLsn(nextFile, currentEntryPrevOffset);
      if (finishLsn != DbLsn.NULL_LSN) {
        if (DbLsn.compareTo(nextLsn, finishLsn) == -1) {
          throw new EOFException(
              "finish="
                  + DbLsn.getNoFormatString(finishLsn)
                  + " next="
                  + DbLsn.getNoFormatString(nextLsn));
        }
      }

      window.slideAndFill(nextFile, nextWindowStart, nextTarget, forward);
    }

    /* The current entry will start at this offset. */
    currentEntryOffset = currentEntryPrevOffset;
  }
示例#12
0
  private void summarizeCheckpointInfo(CheckpointInfoTextFormatter f) {
    System.out.println("\nPer checkpoint interval info:");

    /*
     * Print out checkpoint interval info.
     * If the log looks like this:
     *
     * start of log
     * ckpt1 start
     * ckpt1 end
     * ckpt2 start
     * ckpt2 end
     * end of log
     *
     * There are 3 ckpt intervals
     * start of log->ckpt1 end
     * ckpt1 end -> ckpt2 end
     * ckpt2 end -> end of log
     */
    System.out.println(
        f.format("lnTxn")
            + f.format("ln")
            + f.format("mapLNTxn")
            + f.format("mapLN")
            + f.format("end to end")
            + // ckpt n-1 end -> ckpt n end
            f.format("end to start")
            + // ckpt n-1 end -> ckpt n start
            f.format("start to end")
            + // ckpt n start -> ckpt n end
            f.format("maxLNReplay")
            + f.format("ckptEnd"));

    long logFileMax = envImpl.getConfigManager().getLong(EnvironmentParams.LOG_FILE_MAX);

    Iterator<CheckpointCounter> iter = ckptList.iterator();
    CheckpointCounter prevCounter = null;
    while (iter.hasNext()) {
      CheckpointCounter c = iter.next();
      StringBuilder sb = new StringBuilder();

      /* Entry type counts. */
      int maxTxnLNs = c.preStartLNTxnCount + c.postStartLNTxnCount;
      sb.append(f.format(maxTxnLNs));
      int maxLNs = c.preStartLNCount + c.postStartLNCount;
      sb.append(f.format(maxLNs));
      sb.append(f.format(c.preStartMapLNTxnCount + c.postStartMapLNTxnCount));
      sb.append(f.format(c.preStartMapLNCount + c.postStartMapLNCount));

      /* Checkpoint interval distance. */
      long end = (c.endCkptLsn == DbLsn.NULL_LSN) ? getLastLsn() : c.endCkptLsn;
      long endToEndDistance = 0;

      FileManager fileMgr = envImpl.getFileManager();
      if (prevCounter == null) {
        endToEndDistance = DbLsn.getWithCleaningDistance(end, fileMgr, firstLsnRead, logFileMax);
      } else {
        endToEndDistance =
            DbLsn.getWithCleaningDistance(end, fileMgr, prevCounter.endCkptLsn, logFileMax);
      }
      sb.append(f.format(endToEndDistance));

      /*
       * Interval between last checkpoint end and this checkpoint start.
       */
      long start = (c.startCkptLsn == DbLsn.NULL_LSN) ? getLastLsn() : c.startCkptLsn;
      long endToStartDistance = 0;

      if (prevCounter == null) {
        endToStartDistance =
            DbLsn.getWithCleaningDistance(start, fileMgr, firstLsnRead, logFileMax);
      } else {
        endToStartDistance =
            DbLsn.getWithCleaningDistance(start, fileMgr, prevCounter.endCkptLsn, logFileMax);
      }
      sb.append(f.format(endToStartDistance));

      /*
       * Interval between ckpt start and ckpt end.
       */
      long startToEndDistance = 0;
      if ((c.startCkptLsn != DbLsn.NULL_LSN) && (c.endCkptLsn != DbLsn.NULL_LSN)) {
        startToEndDistance =
            DbLsn.getWithCleaningDistance(c.endCkptLsn, fileMgr, c.startCkptLsn, logFileMax);
      }
      sb.append(f.format(startToEndDistance));

      /*
       * The maximum number of LNs to replay includes the portion of LNs
       * from checkpoint start to checkpoint end of the previous
       * interval.
       */
      int maxReplay = maxLNs + maxTxnLNs;
      if (prevCounter != null) {
        maxReplay += prevCounter.postStartLNTxnCount;
        maxReplay += prevCounter.postStartLNCount;
      }
      sb.append(f.format(maxReplay));

      if (c.endCkptLsn == DbLsn.NULL_LSN) {
        sb.append("   ").append(DbLsn.getNoFormatString(getLastLsn()));
      } else {
        sb.append("   ").append(DbLsn.getNoFormatString(c.endCkptLsn));
      }

      System.out.println(sb.toString());
      prevCounter = c;
    }
  }
  /**
   * Determine whether a checkpoint should be run. 1. If the force parameter is specified, always
   * checkpoint. 2. If the config object specifies time or log size, use that. 3. If the environment
   * is configured to use log size based checkpointing, check the log. 4. Lastly, use time based
   * checking.
   */
  private boolean isRunnable(CheckpointConfig config) throws DatabaseException {

    /* Figure out if we're using log size or time to determine interval.*/
    long useBytesInterval = 0;
    long useTimeInterval = 0;
    DbLsn nextLsn = null;
    try {
      if (config.getForce()) {
        return true;
      } else if (config.getKBytes() != 0) {
        useBytesInterval = config.getKBytes() << 10;
      } else if (config.getMinutes() != 0) {
        // convert to millis
        useTimeInterval = config.getMinutes() * 60 * 1000;
      } else if (logSizeBytesInterval != 0) {
        useBytesInterval = logSizeBytesInterval;
      } else {
        useTimeInterval = timeInterval;
      }

      /*
       * If our checkpoint interval is defined by log size, check
       * on how much log has grown since the last checkpoint.
       */
      if (useBytesInterval != 0) {
        nextLsn = envImpl.getFileManager().getNextLsn();
        if (nextLsn.getNoCleaningDistance(lastCheckpointEnd, logFileMax) >= useBytesInterval) {
          return true;
        } else {
          return false;
        }
      } else if (useTimeInterval != 0) {

        /*
         * Our checkpoint is determined by time.  If enough
         * time has passed and some log data has been written,
         * do a checkpoint.
         */
        DbLsn lastUsedLsn = envImpl.getFileManager().getLastUsedLsn();
        if (((System.currentTimeMillis() - lastCheckpointMillis) >= useTimeInterval)
            && (lastUsedLsn.compareTo(lastCheckpointEnd) != 0)) {
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    } finally {
      StringBuffer sb = new StringBuffer();
      sb.append("size interval=").append(useBytesInterval);
      if (nextLsn != null) {
        sb.append(" nextLsn=").append(nextLsn.getNoFormatString());
      }
      if (lastCheckpointEnd != null) {
        sb.append(" lastCkpt=");
        sb.append(lastCheckpointEnd.getNoFormatString());
      }
      sb.append(" time interval=").append(useTimeInterval);
      sb.append(" force=").append(config.getForce());

      Tracer.trace(Level.FINEST, envImpl, sb.toString());
    }
  }
示例#14
0
  /**
   * Rollback the changes to this txn's write locked nodes up to but not including the entry at the
   * specified matchpoint. When we log a transactional entry, we record the LSN of the original,
   * before-this-transaction version as the abort LSN. This means that if there are multiple updates
   * to a given record in a single transaction, each update only references that original version
   * and its true predecessor.
   *
   * <p>This was done to streamline abort processing, so that an undo reverts directly to the
   * original version rather than stepping through all the intermediates. The intermediates are
   * skipped. However, undo to a matchpoint may need to stop at an intermediate point, so we need to
   * create a true chain of versions.
   *
   * <p>To do so, we read the transaction backwards from the last logged LSN to reconstruct a
   * transaction chain that links intermediate versions of records. For example, suppose our
   * transaction looks like this and that we are undoing up to LSN 250
   *
   * <p>lsn=100 node=A (version 1) lsn=200 node=B (version 1) <-- matchpointLsn lsn=300 node=C
   * (version 1) lsn=400 node=A (version 2) lsn=500 node=B (version 2) lsn=600 node=A (version 3)
   * lsn=700 node=A (version 4)
   *
   * <p>To setup the old versions, We walk from LSN 700 -> 100 700 (A) rolls back to 600 600 (A)
   * rolls back to 400 500 (B) rolls back to 200 400 (A) rolls back to 100 300 (C) rolls back to an
   * empty slot (NULL_LSN).
   *
   * <p>A partial rollback also requires resetting the lastLoggedLsn field, because these operations
   * are no longer in the btree and their on-disk entries are no longer valid.
   *
   * <p>Lastly, the appropriate write locks must be released.
   *
   * @param matchpointLsn the rollback should go up to but not include this LSN.
   */
  private void undoWrites(long matchpointLsn, List<Long> rollbackLsns) throws DatabaseException {

    /*
     * Generate a map of nodeId->List of intermediate LSNs for this node.
     * to re-create the transaction chain.
     */
    TreeLocation location = new TreeLocation();
    Long undoLsn = lastLoggedLsn;
    TxnChain chain = new TxnChain(undoLsn, id, matchpointLsn, undoDatabases, envImpl);

    try {
      while ((undoLsn != DbLsn.NULL_LSN) && DbLsn.compareTo(undoLsn, matchpointLsn) > 0) {

        UndoReader undo = new UndoReader(envImpl, undoLsn, undoDatabases);

        RevertInfo revertTo = chain.pop();

        logFinest(undoLsn, undo, revertTo);

        /*
         * When we undo this log entry, we've logically truncated
         * it from the log. Remove it from the btree and mark it
         * obsolete.
         */
        RecoveryManager.rollbackUndo(logger, Level.FINER, undo, revertTo, location, undoLsn);

        countObsoleteInexact(undoLsn, undo);
        rollbackLsns.add(undoLsn);

        /*
         * Move on to the previous log entry for this txn and update
         * what is considered to be the end of the transaction chain.
         */
        undoLsn = undo.logEntry.getUserTxn().getLastLsn();
        lastLoggedLsn = undoLsn;
      }

      /*
       * Correct the fields which hold LSN and VLSN state that may
       * now be changed.
       */
      lastApplied = chain.getLastValidVLSN();
      if (!updateLoggedForTxn()) {
        firstLoggedLsn = NULL_LSN;
      }

    } catch (DatabaseException e) {
      LoggerUtils.traceAndLogException(
          envImpl, "Txn", "undo", "For LSN=" + DbLsn.getNoFormatString(undoLsn), e);
      throw e;
    } catch (RuntimeException e) {
      throw EnvironmentFailureException.unexpectedException(
          "Txn undo for LSN=" + DbLsn.getNoFormatString(undoLsn), e);
    }

    if (lastLoggedLsn == DbLsn.NULL_LSN) {
      /*
       * The whole txn is rolled back, and it may not appear again. This
       * is the equivalent of an abort. Do any delete processing for an
       * abort which is needed.
       *
       * Set database state for deletes before releasing any write
       * locks.
       */
      setDeletedDatabaseState(false);
    }

    /* Clear any write locks that are no longer needed. */
    clearWriteLocks(chain.getRemainingLockedNodes());
  }