/**
   * Converts the given DIN and its descendants.
   *
   * <p>Enter/leave with bin field latched, although bin field will change to last inserted slot.
   */
  private void convertDin(final DIN din, final byte[] binKey) {
    din.latch();
    try {
      for (int i = 0; i < din.getNEntries(); i += 1) {

        final IN child = din.fetchIN(i, CacheMode.DEFAULT);

        assert (!child.isBINDelta(false));

        if (child instanceof DBIN) {
          final DBIN dbin = (DBIN) child;
          dbin.latch();
          try {
            for (int j = 0; j < dbin.getNEntries(); j += 1) {
              if (!isLNDeleted(dbin, j)) {
                convertDbinSlot(dbin, j, binKey);
              }
            }
            assert dbin.verifyMemorySize();

            /* Count DBIN obsolete. */
            if (dbin.getLastLoggedLsn() != DbLsn.NULL_LSN) {
              localTracker.countObsoleteNodeInexact(
                  dbin.getLastLoggedLsn(), dbin.getLogType(), 0, dbin.getDatabase());
            }
          } finally {
            dbin.releaseLatch();
          }
        } else {
          convertDin((DIN) child, binKey);
        }

        /* Evict DIN child. */
        din.detachNode(i, false /*updateLsn*/, -1 /*lsn*/);
      }

      assert din.verifyMemorySize();

      /* Count DIN and DupCountLN obsolete. */
      if (din.getLastLoggedLsn() != DbLsn.NULL_LSN) {
        localTracker.countObsoleteNodeInexact(
            din.getLastLoggedLsn(), din.getLogType(), 0, din.getDatabase());
      }
      final ChildReference dupCountRef = din.getDupCountLNRef();
      if (dupCountRef != null && dupCountRef.getLsn() != DbLsn.NULL_LSN) {
        localTracker.countObsoleteNodeInexact(
            dupCountRef.getLsn(), LogEntryType.LOG_DUPCOUNTLN, 0, din.getDatabase());
      }
    } finally {
      din.releaseLatch();
    }
  }
  /**
   * Converts the given DIN and its descendants.
   *
   * <p>Enter/leave with bin field latched, although bin field will change to last inserted slot.
   */
  private void convertDin(final DIN din, final byte[] binKey) {
    din.latch();
    try {
      for (int i = 0; i < din.getNEntries(); i += 1) {
        final IN child = (IN) din.fetchTargetWithExclusiveLatch(i);
        if (child instanceof DBIN) {
          final DBIN dbin = (DBIN) child;
          dbin.latch();
          try {
            for (int j = 0; j < dbin.getNEntries(); j += 1) {
              if (!isLNDeleted(dbin, j)) {
                convertDbinSlot(dbin, j, binKey);
              }
            }
            assert dbin.verifyMemorySize();

            /* Count DBIN obsolete. */
            if (dbin.getLastLoggedVersion() != DbLsn.NULL_LSN) {
              localTracker.countObsoleteNodeInexact(
                  dbin.getLastLoggedVersion(), dbin.getLogType(), 0, dbin.getDatabase());
            }
          } finally {
            dbin.releaseLatch();
          }
        } else {
          convertDin((DIN) child, binKey);
        }

        /* Evict DIN child. */
        din.updateNode(i, null, null);
        envImpl.getInMemoryINs().remove(child);
      }
      assert din.verifyMemorySize();

      /* Count DIN and DupCountLN obsolete. */
      if (din.getLastLoggedVersion() != DbLsn.NULL_LSN) {
        localTracker.countObsoleteNodeInexact(
            din.getLastLoggedVersion(), din.getLogType(), 0, din.getDatabase());
      }
      final ChildReference dupCountRef = din.getDupCountLNRef();
      if (dupCountRef != null && dupCountRef.getLsn() != DbLsn.NULL_LSN) {
        localTracker.countObsoleteNodeInexact(
            dupCountRef.getLsn(), LogEntryType.LOG_DUPCOUNTLN, 0, din.getDatabase());
      }
    } finally {
      din.releaseLatch();
    }
  }
Exemple #3
0
  /**
   * Compress this BIN by removing any entries that are deleted. Deleted entries are those that have
   * LN's marked deleted or if the knownDeleted flag is set. Caller is responsible for latching and
   * unlatching this node.
   *
   * @param binRef is used to determine the set of keys to be checked for deletedness, or is null to
   *     check all keys.
   * @param canFetch if false, don't fetch any non-resident children. We don't want some callers of
   *     compress, such as the evictor, to fault in other nodes.
   * @return true if we had to requeue the entry because we were unable to get locks, false if all
   *     entries were processed and therefore any remaining deleted keys in the BINReference must
   *     now be in some other BIN because of a split.
   */
  @Override
  public boolean compress(
      BINReference binRef, boolean canFetch, LocalUtilizationTracker localTracker)
      throws DatabaseException {

    boolean ret = false;
    boolean setNewIdKey = false;
    boolean anyLocksDenied = false;
    DatabaseImpl db = getDatabase();
    EnvironmentImpl envImpl = db.getDbEnvironment();
    BasicLocker lockingTxn = BasicLocker.createBasicLocker(envImpl);

    try {
      for (int i = 0; i < getNEntries(); i++) {

        /*
         * We have to be able to lock the LN before we can compress the
         * entry.  If we can't, then, skip over it.
         *
         * We must lock the LN even if isKnownDeleted is true, because
         * locks protect the aborts. (Aborts may execute multiple
         * operations, where each operation latches and unlatches. It's
         * the LN lock that protects the integrity of the whole
         * multi-step process.)
         *
         * For example, during abort, there may be cases where we have
         * deleted and then added an LN during the same txn.  This
         * means that to undo/abort it, we first delete the LN (leaving
         * knownDeleted set), and then add it back into the tree.  We
         * want to make sure the entry is in the BIN when we do the
         * insert back in.
         */
        boolean deleteEntry = false;
        Node n = null;

        if (binRef == null
            || isEntryPendingDeleted(i)
            || isEntryKnownDeleted(i)
            || binRef.hasDeletedKey(new Key(getKey(i)))) {

          if (canFetch) {
            if (db.isDeferredWriteMode() && getLsn(i) == DbLsn.NULL_LSN) {
              /* Null LSNs are ok in DW. [#15588] */
              n = getTarget(i);
            } else {
              n = fetchTarget(i);
            }
          } else {
            n = getTarget(i);
            if (n == null) {
              /* Punt, we don't know the state of this child. */
              continue;
            }
          }

          if (n == null) {
            /* Cleaner deleted the log file.  Compress this LN. */
            deleteEntry = true;
          } else if (isEntryKnownDeleted(i)) {
            LockResult lockRet = lockingTxn.nonBlockingLock(n.getNodeId(), LockType.READ, db);
            if (lockRet.getLockGrant() == LockGrantType.DENIED) {
              anyLocksDenied = true;
              continue;
            }

            deleteEntry = true;
          } else {
            if (!n.containsDuplicates()) {
              LN ln = (LN) n;
              LockResult lockRet = lockingTxn.nonBlockingLock(ln.getNodeId(), LockType.READ, db);
              if (lockRet.getLockGrant() == LockGrantType.DENIED) {
                anyLocksDenied = true;
                continue;
              }

              if (ln.isDeleted()) {
                deleteEntry = true;
              }
            }
          }

          /* Remove key from BINReference in case we requeue it. */
          if (binRef != null) {
            binRef.removeDeletedKey(new Key(getKey(i)));
          }
        }

        /* At this point, we know we can delete. */
        if (deleteEntry) {
          boolean entryIsIdentifierKey =
              Key.compareKeys(getKey(i), getIdentifierKey(), getKeyComparator()) == 0;
          if (entryIsIdentifierKey) {

            /*
             * We're about to remove the entry with the idKey so
             * the node will need a new idkey.
             */
            setNewIdKey = true;
          }

          /*
           * When deleting a deferred-write LN entry, we count the
           * last logged LSN as obsolete.
           */
          if (localTracker != null && db.isDeferredWriteMode() && n instanceof LN) {
            LN ln = (LN) n;
            long lsn = getLsn(i);
            if (ln.isDirty() && lsn != DbLsn.NULL_LSN) {
              localTracker.countObsoleteNode(lsn, ln.getLogType(), ln.getLastLoggedSize(), db);
            }
          }

          boolean deleteSuccess = deleteEntry(i, true);
          assert deleteSuccess;

          /*
           * Since we're deleting the current entry, bump the current
           * index back down one.
           */
          i--;
        }
      }
    } finally {
      if (lockingTxn != null) {
        lockingTxn.operationEnd();
      }
    }

    if (anyLocksDenied && binRef != null) {
      db.getDbEnvironment().addToCompressorQueue(binRef, false);
      ret = true;
    }

    if (getNEntries() != 0 && setNewIdKey) {
      setIdentifierKey(getKey(0));
    }

    /* This BIN is empty and expendable. */
    if (getNEntries() == 0) {
      setGeneration(0);
    }

    return ret;
  }