Exemplo n.º 1
0
  /**
   * This test checks for the bug described in SR11123. If an IN and its child-subtree is deleted,
   * an INDeleteInfo is written to the log. If there is a BINDelta in the log for a BIN-child of the
   * removed subtree (i.e. compressed), then recovery will apply it to the compressed IN. Since the
   * IN has no data in * it, that is not necessarily a problem. However, reinstantiating the
   * obsolete IN may cause a parent IN to split which is not allowed during IN recovery.
   *
   * <p>Here's the case:
   *
   * <p>| IN1 +---------------------------------+ | | IN2 IN6 / | / | \ BIN3 BIN4 BIN7 BIN8 BIN9
   *
   * <p>IN2 and the subtree below are compressed away. During recovery replay, after the pass where
   * INs and INDeleteINfos are processed, the in-memory tree looks like this:
   *
   * <p>IN1 | IN6 / | \ BIN7 BIN8 BIN9
   *
   * <p>However, let's assume that BINDeltas were written for BIN3, BIN4, BIN5 within the recovery
   * part of the log, before the subtree was compressed. We'll replay those BINDeltas in the
   * following pass, and in the faulty implementation, they cause the ghosts of BIN3, BIN4 to be
   * resurrected and applied to IN6. Let's assume that the max node size is 4 -- we won't be able to
   * connect BIN3, BIN4 because IN6 doesn't have the capacity, and we don't expect to have to do
   * splits.
   */
  private void addData(Database db) throws DatabaseException {

    DatabaseEntry key = new DatabaseEntry();
    DatabaseEntry data = new DatabaseEntry();

    /* Populate a tree so there are 3 levels. */
    for (int i = 0; i < 140; i += 10) {
      IntegerBinding.intToEntry(i, key);
      IntegerBinding.intToEntry(i, data);
      assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
    }

    CheckpointConfig ckptConfig = new CheckpointConfig();
    ckptConfig.setForce(true);
    env.checkpoint(ckptConfig);

    Tree tree = DbInternal.dbGetDatabaseImpl(db).getTree();
    com.sleepycat.je.tree.Key.DUMP_TYPE = com.sleepycat.je.tree.Key.DumpType.BINARY;
    com.sleepycat.je.tree.Key.DUMP_INT_BINDING = true;
    if (DEBUG) {
      tree.dump();
    }

    /*
     * Update a key on the BIN3 and a key on BIN4, to create reason for
     * a BINDelta. Force a BINDelta for BIN3 and BIN4 out to the log.
     */
    IntegerBinding.intToEntry(0, key);
    IntegerBinding.intToEntry(100, data);
    assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
    IntegerBinding.intToEntry(20, key);
    assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));

    EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env);
    BIN bin = (BIN) tree.getFirstNode();
    bin.log(envImpl.getLogManager(), true, false, false, false, null);
    bin = tree.getNextBin(bin, false /* traverseWithinDupTree */);
    bin.log(envImpl.getLogManager(), true, false, false, false, null);
    bin.releaseLatch();

    /*
     * Delete all of left hand side of the tree, so that the subtree root
     * headed by IN2 is compressed.
     */
    for (int i = 0; i < 50; i += 10) {
      IntegerBinding.intToEntry(i, key);
      assertEquals(OperationStatus.SUCCESS, db.delete(null, key));
    }

    /* force a compression */
    env.compress();
    if (DEBUG) {
      tree.dump();
    }
  }
  /**
   * Converts the given DBIN slot, leaving bin/index set to the inserted BIN slot.
   *
   * <p>Enter/leave with bin field latched, although bin field may change.
   *
   * <p>If slot is inserted into current bin, leave bin field unchanged and set index field to
   * inserted slot.
   *
   * <p>If slot is inserted into a different bin, set bin/index fields to inserted slot.
   */
  private void convertDbinSlot(final DBIN dbin, final int dbinIndex, final byte[] binKey) {

    final byte[] newKey = DupKeyData.replaceData(binKey, dbin.getKey(dbinIndex));

    if (DEBUG) {
      System.out.println("DupConvert DBIN LN " + Key.dumpString(newKey, 0));
    }

    /*
     * If the current BIN can hold the new slot, don't bother to do a
     * search to find it.
     */
    if (bin.needsSplitting() || !bin.isKeyInBounds(newKey)) {

      /* Compact keys after finishing with a BIN. */
      bin.compactMemory();

      /* Evict without latches, before moving to a new BIN. */
      bin.releaseLatch();
      envImpl.daemonEviction(false /*backgroundIO*/);

      /* Find a BIN for insertion, split if necessary. */
      bin = dbin.getDatabase().getTree().searchSplitsAllowed(newKey, CacheMode.UNCHANGED);
    }

    final int newIndex =
        bin.insertEntry1(
            null /*ln*/,
            newKey,
            null /*data*/,
            dbin.getLsn(dbinIndex),
            dbin.getState(dbinIndex),
            false);

    if ((newIndex & IN.INSERT_SUCCESS) == 0) {
      throw EnvironmentFailureException.unexpectedState(
          "Key not inserted: " + Key.dumpString(newKey, 0) + " DB: " + dbin.getDatabase().getId());
    }

    index = newIndex & ~IN.INSERT_SUCCESS;

    /*
     * Evict LN from DBIN slot. Although we don't explicitly load DBIN LNs,
     * it may have been loaded by recovery.
     */
    dbin.detachNode(dbinIndex, false /*updateLsn*/, -1 /*lsn*/);

    nConverted += 1;
  }
Exemplo n.º 3
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;
    }
  }