Esempio n. 1
0
  /**
   * readNextEntry will stop at a bad entry.
   *
   * @return true if an element has been read.
   */
  public boolean readNextEntry() throws DatabaseException, IOException {

    boolean foundEntry = false;

    try {

      /*
       * At this point,
       *  currentEntryOffset is the entry we just read.
       *  nextEntryOffset is the entry we're about to read.
       *  currentEntryPrevOffset is 2 entries ago.
       * Note that readNextEntry() moves all the offset pointers up.
       */

      foundEntry = super.readNextEntry();

      /*
       * Note that initStartingPosition() makes sure that the file header
       * entry is valid.  So by the time we get to this method, we know
       * we're at a file with a valid file header entry.
       */
      lastValidOffset = currentEntryOffset;
      nextUnprovenOffset = nextEntryOffset;
    } catch (DbChecksumException e) {
      Tracer.trace(
          Level.INFO,
          envImpl,
          "Found checksum exception while searching "
              + " for end of log. Last valid entry is at "
              + DbLsn.toString(DbLsn.makeLsn(readBufferFileNum, lastValidOffset))
              + " Bad entry is at "
              + DbLsn.makeLsn(readBufferFileNum, nextUnprovenOffset));
    }
    return foundEntry;
  }
    /*
     * 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();
 }
Esempio n. 4
0
  /** Read in an IN entry. */
  public void readEntry(EnvironmentImpl envImpl, LogEntryHeader header, ByteBuffer entryBuffer) {

    int logVersion = header.getVersion();
    boolean version6OrLater = (logVersion >= 6);
    if (version6OrLater) {
      dbId = new DatabaseId();
      dbId.readFromLog(entryBuffer, logVersion);
      prevFullLsn = LogUtils.readLong(entryBuffer, false /*unpacked*/);
      if (logVersion >= 8) {
        prevDeltaLsn = LogUtils.readPackedLong(entryBuffer);
      }
    }
    /* Read IN. */
    in = newInstanceOfType();
    in.readFromLog(entryBuffer, logVersion);
    if (!version6OrLater) {
      dbId = new DatabaseId();
      dbId.readFromLog(entryBuffer, logVersion);
    }
    if (logVersion < 1) {
      prevFullLsn = DbLsn.NULL_LSN;
    } else if (logVersion == 1) {
      long fileNum = LogUtils.readUnsignedInt(entryBuffer);
      if (fileNum == 0xffffffffL) {
        prevFullLsn = DbLsn.NULL_LSN;
      } else {
        prevFullLsn = DbLsn.makeLsn(fileNum, 0);
      }
    } else if (!version6OrLater) {
      prevFullLsn = LogUtils.readLong(entryBuffer, true /*unpacked*/);
    }
  }
Esempio n. 5
0
 /** @return The last LSN seen in the log for this kind of entry, or null. */
 public long getLastSeen(LogEntryType type) {
   Long typeNumber = (Long) lastOffsetSeen.get(type);
   if (typeNumber != null) {
     return DbLsn.makeLsn(readBufferFileNum, typeNumber.longValue());
   } else {
     return DbLsn.NULL_LSN;
   }
 }
Esempio n. 6
0
 /** @return The last LSN seen in the log for this kind of entry, or null. */
 public long getLastSeen(LogEntryType type) {
   Long typeNumber = lastOffsetSeen.get(type);
   if (typeNumber != null) {
     return DbLsn.makeLsn(window.currentFileNum(), typeNumber.longValue());
   } else {
     return DbLsn.NULL_LSN;
   }
 }
Esempio n. 7
0
  /** Test initializing the last position in the logs. */
  public void testSetLastPosition() throws DatabaseException {

    /*
     * Pretend that the last file is file 79.
     */
    fileManager.setLastPosition( // next available LSN
        DbLsn.makeLsn(79L, 88L), DbLsn.makeLsn(79L, 77), 66L);

    /* Put an entry down, should fit within file 79. */
    fileManager.bumpLsn(11L);
    assertEquals(DbLsn.makeLsn(79L, 88L), fileManager.getLastUsedLsn());
    assertEquals(77L, fileManager.getPrevEntryOffset());

    /* Put another entry in, should go to the next file. */
    fileManager.bumpLsn(22L);
    assertEquals(
        DbLsn.makeLsn(80L, FileManager.firstLogEntryOffset()), fileManager.getLastUsedLsn());
    assertEquals(0, fileManager.getPrevEntryOffset());
  }
  /**
   * Ensure that the next target is in the window. The default behavior is that the next target is
   * the next, following entry, so we can assume that it's in the window. All we have to do is to
   * check if we've gone past the specified end point.
   *
   * @throws DatabaseException
   * @throws FileNotFoundException
   * @throws ChecksumException
   */
  protected void setForwardPosition()
      throws EOFException, DatabaseException, ChecksumException, FileNotFoundException {

    if (finishLsn != DbLsn.NULL_LSN) {
      /* The next log entry has passed the end LSN. */
      long nextLsn = DbLsn.makeLsn(window.currentFileNum(), nextEntryOffset);
      if (DbLsn.compareTo(nextLsn, finishLsn) >= 0) {
        throw new EOFException();
      }
    }
  }
Esempio n. 9
0
  /**
   * readNextEntry will stop at a bad entry.
   *
   * @return true if an element has been read.
   */
  @Override
  public boolean readNextEntry() {

    boolean foundEntry = false;

    try {

      /*
       * At this point,
       *  currentEntryOffset is the entry we just read.
       *  nextEntryOffset is the entry we're about to read.
       *  currentEntryPrevOffset is 2 entries ago.
       * Note that readNextEntry() moves all the offset pointers up.
       */

      foundEntry = super.readNextEntryAllowExceptions();

      /*
       * Note that initStartingPosition() makes sure that the file header
       * entry is valid.  So by the time we get to this method, we know
       * we're at a file with a valid file header entry.
       */
      lastValidOffset = currentEntryOffset;
      nextUnprovenOffset = nextEntryOffset;
    } catch (FileNotFoundException e) {
      throw new EnvironmentFailureException(
          envImpl, EnvironmentFailureReason.LOG_FILE_NOT_FOUND, e);
    } catch (ChecksumException e) {
      LoggerUtils.fine(
          logger,
          envImpl,
          "Found checksum exception while searching for end of log. "
              + "Last valid entry is at "
              + DbLsn.toString(DbLsn.makeLsn(window.currentFileNum(), lastValidOffset))
              + " Bad entry is at "
              + DbLsn.makeLsn(window.currentFileNum(), nextUnprovenOffset));
    }
    return foundEntry;
  }
Esempio n. 10
0
 /*
  * Read the next entry.  If a checksum exception is encountered, attempt
  * to find the other side of the corrupted area and try to re-read this
  * file.
  */
 @Override
 public boolean readNextEntry() {
   long saveCurrentEntryOffset = currentEntryOffset;
   try {
     return super.readNextEntryAllowExceptions();
   } catch (FileNotFoundException e) {
     throw new EnvironmentFailureException(
         envImpl, EnvironmentFailureReason.LOG_FILE_NOT_FOUND, e);
   } catch (ChecksumException e) {
     resyncReader(
         DbLsn.makeLsn(window.currentFileNum(), saveCurrentEntryOffset), dumpCorruptedBounds);
     return super.readNextEntry();
   }
 }
  /** Helper for determining the starting position and opening up a file at the desired location. */
  protected void initStartingPosition(long endOfFileLsn, Long ignoreSingleFileNumber) {
    eof = false;
    if (forward) {

      /*
       * Start off at the startLsn. If that's null, start at the
       * beginning of the log. If there are no log files, set eof.
       */
      if (startLsn != DbLsn.NULL_LSN) {
        window.initAtFileStart(startLsn);
      } else {
        Long firstNum = fileManager.getFirstFileNum();
        if (firstNum == null) {
          eof = true;
        } else {
          window.initAtFileStart(DbLsn.makeLsn(firstNum, 0));
        }
      }

      /*
       * After we read the first entry, the currentEntry will point here.
       */
      nextEntryOffset = window.getEndOffset();
    } else {

      /*
       * Make the read buffer look like it's positioned off the end of
       * the file. Initialize the first LSN we want to read. When
       * traversing the log backwards, we always start at the very end.
       */
      assert startLsn != DbLsn.NULL_LSN;
      window.initAtFileStart(endOfFileLsn);

      /*
       * currentEntryPrevOffset points to the entry we want to start out
       * reading when going backwards. If it's 0, the entry we want to
       * read is in a different file.
       */
      if (DbLsn.getFileNumber(startLsn) == DbLsn.getFileNumber(endOfFileLsn)) {
        currentEntryPrevOffset = DbLsn.getFileOffset(startLsn);
      } else {
        currentEntryPrevOffset = 0;
      }
      currentEntryOffset = DbLsn.getFileOffset(endOfFileLsn);
    }
  }
Esempio n. 12
0
  /** Initialize starting position to the last file with a complete header with a valid checksum. */
  private void startAtLastGoodFile(Long singleFileNum) throws ChecksumException {

    eof = false;
    window.initAtFileStart(DbLsn.makeLsn(0, 0));

    /*
     * Start at what seems like the last file. If it doesn't exist, we're
     * done.
     */
    Long lastNum =
        ((singleFileNum != null) && (singleFileNum.longValue() >= 0))
            ? singleFileNum
            : fileManager.getLastFileNum();
    FileHandle fileHandle = null;

    long fileLen = 0;
    while ((fileHandle == null) && !eof) {
      if (lastNum == null) {
        eof = true;
      } else {
        try {
          try {
            window.initAtFileStart(DbLsn.makeLsn(lastNum, 0));
            fileHandle = fileManager.getFileHandle(lastNum);

            /*
             * Check the size of this file. If it opened
             * successfully but only held a header or is 0 length,
             * backup to the next "last" file unless this is the
             * only file in the log. Note that an incomplete header
             * will end up throwing a checksum exception, but a 0
             * length file will open successfully in read only
             * mode.
             */
            fileLen = fileHandle.getFile().length();
            if (fileLen <= FileManager.firstLogEntryOffset()) {
              lastNum = fileManager.getFollowingFileNum(lastNum, false);
              if (lastNum != null) {
                fileHandle.release();
                fileHandle = null;
              }
            }
          } catch (DatabaseException e) {
            lastNum = attemptToMoveBadFile(e);
            fileHandle = null;
          } catch (ChecksumException e) {
            lastNum = attemptToMoveBadFile(e);
            fileHandle = null;
          } finally {
            if (fileHandle != null) {
              fileHandle.release();
            }
          }
        } catch (IOException e) {
          throw new EnvironmentFailureException(envImpl, EnvironmentFailureReason.LOG_READ, e);
        }
      }
    }

    nextEntryOffset = 0;
  }
Esempio n. 13
0
 public long getLastValidLsn() {
   return DbLsn.makeLsn(readBufferFileNum, lastValidOffset);
 }
 /** Get LSN of the last entry read. */
 public long getLastLsn() {
   return DbLsn.makeLsn(window.currentFileNum(), currentEntryOffset);
 }
Esempio n. 15
0
  /** Test LSN administration. */
  public void testLsnBumping() throws Exception {

    /*
    We are adding these entries:
    +----+------+---------+--------+
    file 0:  |hdr | 30   |   50    |empty   |
    +----+------+---------+--------+
    0    hdr  hdr+30   hdr+80     99

    +----+--------+-------+-------+-----+-------+
    file 1:  |hdr | 40     | 20    | 10    | 5   | empty |
    +----+--------+-------+-------+-----+-------+
    0    hdr   hdr+40  hdr+60  hdr+70  hdr+75

    +-----+-----+--------+
    file 2:  | hdr | 75  |  empty |
    +-----+-----+--------+
    0    hdr   hdr+75

    +-----+-------------------------------+
    file 3:  | hdr | 125                           |
    +-----+-------------------------------+
    0    hdr

    +-----+-----+------+-----+--------------+
    file 4:  | hdr | 10  | 20   | 30  | empty
    +-----+-----+------+-----+--------------+
    0    hdr hdr+10 hdr+30

        */

    try {
      /* Should start out at LSN 0. */

      /* "add" some entries to the log. */
      long hdrSize = FileManager.firstLogEntryOffset();

      fileManager.bumpLsn(30L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(0, hdrSize), fileManager.getLastUsedLsn());
      /* prev entry. */
      assertEquals(0, fileManager.getPrevEntryOffset());

      fileManager.bumpLsn(50L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(0, (hdrSize + 30)), fileManager.getLastUsedLsn());
      assertEquals(hdrSize, fileManager.getPrevEntryOffset());

      /* bump over to a file 1. */
      fileManager.bumpLsn(40L);
      /* item placed here. */
      assertEquals(DbLsn.makeLsn(1, hdrSize), fileManager.getLastUsedLsn());
      assertEquals(0, fileManager.getPrevEntryOffset());

      fileManager.bumpLsn(20L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(1, (hdrSize + 40)), fileManager.getLastUsedLsn());
      assertEquals(hdrSize, fileManager.getPrevEntryOffset());

      fileManager.bumpLsn(10L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(1, (hdrSize + 60)), fileManager.getLastUsedLsn());
      assertEquals(hdrSize + 40, fileManager.getPrevEntryOffset());

      fileManager.bumpLsn(5L);
      /* item placed here. */
      assertEquals(DbLsn.makeLsn(1, (hdrSize + 70)), fileManager.getLastUsedLsn());
      assertEquals(hdrSize + 60, fileManager.getPrevEntryOffset());

      /* bump over to file 2. */
      fileManager.bumpLsn(75L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(2, hdrSize), fileManager.getLastUsedLsn());
      assertEquals(0, fileManager.getPrevEntryOffset());

      /* Ask for something bigger than a file: bump over to file 3. */
      fileManager.bumpLsn(125L);
      /* item placed here. */
      assertEquals(DbLsn.makeLsn(3, hdrSize), fileManager.getLastUsedLsn());
      assertEquals(0, fileManager.getPrevEntryOffset());

      /* bump over to file 4. */
      fileManager.bumpLsn(10L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(4, hdrSize), fileManager.getLastUsedLsn());
      assertEquals(0, fileManager.getPrevEntryOffset());

      fileManager.bumpLsn(20L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(4, (hdrSize + 10)), fileManager.getLastUsedLsn());
      assertEquals(hdrSize, fileManager.getPrevEntryOffset());

      fileManager.bumpLsn(30L);
      /* Item placed here. */
      assertEquals(DbLsn.makeLsn(4, (hdrSize + 30)), fileManager.getLastUsedLsn());
      assertEquals((hdrSize + 10), fileManager.getPrevEntryOffset());

    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }
  }
Esempio n. 16
0
 /** @return The LSN to be used for the next log entry. */
 public long getEndOfLog() {
   return DbLsn.makeLsn(window.currentFileNum(), nextUnprovenOffset);
 }
Esempio n. 17
0
 public long getLastValidLsn() {
   return DbLsn.makeLsn(window.currentFileNum(), lastValidOffset);
 }
  /**
   * 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;
  }
Esempio n. 19
0
 /** @return The LSN to be used for the next log entry. */
 public long getEndOfLog() {
   return DbLsn.makeLsn(readBufferFileNum, nextUnprovenOffset);
 }
Esempio n. 20
0
  public void testEntryData() throws Throwable {

    try {
      ByteBuffer buffer = ByteBuffer.allocate(1000);
      database = new DatabaseImpl("foo", new DatabaseId(1), env, new DatabaseConfig());

      /*
       * For each loggable object, can we write the entry data out?
       */

      /*
       * Tracer records.
       */
      Tracer dMsg = new Tracer("Hello there");
      writeAndRead(buffer, LogEntryType.LOG_TRACE, dMsg, new Tracer());

      /*
       * LNs
       */
      String data = "abcdef";
      LN ln = new LN(data.getBytes());
      LN lnFromLog = new LN();
      writeAndRead(buffer, LogEntryType.LOG_LN, ln, lnFromLog);
      lnFromLog.verify(null);
      assertTrue(LogEntryType.LOG_LN.marshallOutsideLatch());

      FileSummaryLN fsLN = new FileSummaryLN(new FileSummary());
      FileSummaryLN fsLNFromLog = new FileSummaryLN();
      writeAndRead(buffer, LogEntryType.LOG_FILESUMMARYLN, fsLN, fsLNFromLog);
      assertFalse(LogEntryType.LOG_FILESUMMARYLN.marshallOutsideLatch());

      /*
       * INs
       */
      IN in = new IN(database, new byte[] {1, 0, 1, 0}, 7, 5);
      in.latch();
      in.insertEntry(new ChildReference(null, new byte[] {1, 0, 1, 0}, DbLsn.makeLsn(12, 200)));
      in.insertEntry(new ChildReference(null, new byte[] {1, 1, 1, 0}, DbLsn.makeLsn(29, 300)));
      in.insertEntry(new ChildReference(null, new byte[] {0, 0, 1, 0}, DbLsn.makeLsn(35, 400)));

      /* Write it. */
      IN inFromLog = new IN();
      inFromLog.latch();
      writeAndRead(buffer, LogEntryType.LOG_IN, in, inFromLog);
      inFromLog.releaseLatch();
      in.releaseLatch();

      /*
       * IN - long form
       */
      in = new IN(database, new byte[] {1, 0, 1, 0}, 7, 5);
      in.latch();
      in.insertEntry(new ChildReference(null, new byte[] {1, 0, 1, 0}, DbLsn.makeLsn(12, 200)));
      in.insertEntry(new ChildReference(null, new byte[] {1, 1, 1, 0}, DbLsn.makeLsn(29, 300)));
      in.insertEntry(new ChildReference(null, new byte[] {0, 0, 1, 0}, DbLsn.makeLsn(1235, 400)));
      in.insertEntry(
          new ChildReference(null, new byte[] {0, 0, 1, 0}, DbLsn.makeLsn(0xFFFFFFF0L, 400)));

      /* Write it. */
      inFromLog = new IN();
      inFromLog.latch();
      writeAndRead(buffer, LogEntryType.LOG_IN, in, inFromLog);
      inFromLog.releaseLatch();
      in.releaseLatch();

      /*
       * BINs
       */
      BIN bin = new BIN(database, new byte[] {3, 2, 1}, 8, 5);
      bin.latch();
      bin.insertEntry(new ChildReference(null, new byte[] {1, 0, 1, 0}, DbLsn.makeLsn(212, 200)));
      bin.insertEntry(new ChildReference(null, new byte[] {1, 1, 1, 0}, DbLsn.makeLsn(229, 300)));
      bin.insertEntry(new ChildReference(null, new byte[] {0, 0, 1, 0}, DbLsn.makeLsn(235, 400)));
      BIN binFromLog = new BIN();
      binFromLog.latch();
      writeAndRead(buffer, LogEntryType.LOG_BIN, bin, binFromLog);
      binFromLog.verify(null);
      binFromLog.releaseLatch();
      bin.releaseLatch();

      /*
       * DINs
       */
      DIN din =
          new DIN(
              database,
              new byte[] {1, 0, 0, 1},
              7,
              new byte[] {0, 1, 1, 0},
              new ChildReference(null, new byte[] {1, 0, 0, 1}, DbLsn.makeLsn(10, 100)),
              5);
      din.latch();
      din.insertEntry(new ChildReference(null, new byte[] {1, 0, 1, 0}, DbLsn.makeLsn(12, 200)));
      din.insertEntry(new ChildReference(null, new byte[] {1, 1, 1, 0}, DbLsn.makeLsn(29, 300)));
      din.insertEntry(new ChildReference(null, new byte[] {0, 0, 1, 0}, DbLsn.makeLsn(35, 400)));

      /* Write it. */
      DIN dinFromLog = new DIN();
      dinFromLog.latch();
      writeAndRead(buffer, LogEntryType.LOG_DIN, din, dinFromLog);
      din.releaseLatch();
      dinFromLog.releaseLatch();

      /*
       * DBINs
       */
      DBIN dbin = new DBIN(database, new byte[] {3, 2, 1}, 8, new byte[] {1, 2, 3}, 5);
      dbin.latch();
      dbin.insertEntry(new ChildReference(null, new byte[] {1, 0, 1, 0}, DbLsn.makeLsn(212, 200)));
      dbin.insertEntry(new ChildReference(null, new byte[] {1, 1, 1, 0}, DbLsn.makeLsn(229, 300)));
      dbin.insertEntry(new ChildReference(null, new byte[] {0, 0, 1, 0}, DbLsn.makeLsn(235, 400)));
      DBIN dbinFromLog = new DBIN();
      dbinFromLog.latch();
      writeAndRead(buffer, LogEntryType.LOG_DBIN, dbin, dbinFromLog);
      dbinFromLog.verify(null);
      dbin.releaseLatch();
      dbinFromLog.releaseLatch();

      /*
       * Root
       */
      DbTree dbTree = new DbTree(env);
      DbTree dbTreeFromLog = new DbTree();
      writeAndRead(buffer, LogEntryType.LOG_ROOT, dbTree, dbTreeFromLog);

      /*
       * MapLN
       */
      MapLN mapLn = new MapLN(database);
      MapLN mapLnFromLog = new MapLN();
      writeAndRead(buffer, LogEntryType.LOG_MAPLN, mapLn, mapLnFromLog);

      /*
       * UserTxn
       */

      /*
      * Disabled for now because these txns don't compare equal,
             * because one has a name of "main" and the other has a name of
             * null because it was read from the log.

      Txn txn = new Txn(env, new TransactionConfig());
      Txn txnFromLog = new Txn();
      writeAndRead(buffer, LogEntryType.TXN_COMMIT, txn, txnFromLog);
      txn.commit();
            */

      /*
       * TxnCommit
       */
      TxnCommit commit = new TxnCommit(111, DbLsn.makeLsn(10, 10));
      TxnCommit commitFromLog = new TxnCommit();
      writeAndRead(buffer, LogEntryType.LOG_TXN_COMMIT, commit, commitFromLog);

      /*
       * TxnAbort
       */
      TxnAbort abort = new TxnAbort(111, DbLsn.makeLsn(11, 11));
      TxnAbort abortFromLog = new TxnAbort();
      writeAndRead(buffer, LogEntryType.LOG_TXN_ABORT, abort, abortFromLog);

      /*
       * TxnPrepare
       */
      byte[] gid = new byte[64];
      byte[] bqual = new byte[64];
      TxnPrepare prepare = new TxnPrepare(111, new LogUtils.XidImpl(1, gid, bqual));
      TxnPrepare prepareFromLog = new TxnPrepare();
      writeAndRead(buffer, LogEntryType.LOG_TXN_PREPARE, prepare, prepareFromLog);

      prepare = new TxnPrepare(111, new LogUtils.XidImpl(1, null, bqual));
      prepareFromLog = new TxnPrepare();
      writeAndRead(buffer, LogEntryType.LOG_TXN_PREPARE, prepare, prepareFromLog);

      prepare = new TxnPrepare(111, new LogUtils.XidImpl(1, gid, null));
      prepareFromLog = new TxnPrepare();
      writeAndRead(buffer, LogEntryType.LOG_TXN_PREPARE, prepare, prepareFromLog);

      /*
       * IN delete info
       */
      INDeleteInfo info = new INDeleteInfo(77, new byte[1], new DatabaseId(100));
      INDeleteInfo infoFromLog = new INDeleteInfo();
      writeAndRead(buffer, LogEntryType.LOG_IN_DELETE_INFO, info, infoFromLog);

      /*
       * Checkpoint start
       */
      CheckpointStart start = new CheckpointStart(177, "test");
      CheckpointStart startFromLog = new CheckpointStart();
      writeAndRead(buffer, LogEntryType.LOG_CKPT_START, start, startFromLog);

      /*
       * Checkpoint end
       */
      CheckpointEnd end =
          new CheckpointEnd(
              "test",
              DbLsn.makeLsn(20, 55),
              env.getRootLsn(),
              env.getTxnManager().getFirstActiveLsn(),
              Node.getLastId(),
              env.getDbMapTree().getLastDbId(),
              env.getTxnManager().getLastTxnId(),
              177);
      CheckpointEnd endFromLog = new CheckpointEnd();
      writeAndRead(buffer, LogEntryType.LOG_CKPT_END, end, endFromLog);
    } catch (Throwable t) {
      t.printStackTrace();
      throw t;
    }
  }