/**
   * Flush the nodes in order, from the lowest level to highest level. As a flush dirties its
   * parent, add it to the dirty map, thereby cascading the writes up the tree. If flushAll wasn't
   * specified, we need only cascade up to the highest level that existed before the checkpointing
   * started.
   *
   * <p>Note that all but the top level INs and the BINDeltas are logged provisionally. That's
   * because we don't need to process lower INs because the higher INs will end up pointing at them.
   */
  private void flushDirtyNodes(boolean flushAll, boolean allowDeltas, boolean flushExtraLevel)
      throws DatabaseException {

    LogManager logManager = envImpl.getLogManager();

    SortedMap dirtyMap = selectDirtyINs(flushAll, flushExtraLevel);

    while (dirtyMap.size() > 0) {

      /* Work on one level's worth of nodes in ascending level order. */
      Integer currentLevel = (Integer) dirtyMap.firstKey();
      boolean logProvisionally = (currentLevel.intValue() != highestFlushLevel);

      Set nodeSet = (Set) dirtyMap.get(currentLevel);
      Iterator iter = nodeSet.iterator();

      /* Flush all those nodes */
      while (iter.hasNext()) {
        IN target = (IN) iter.next();
        target.latch();
        boolean triedToFlush = false;

        /*
         * Only flush the ones that are still dirty -- some
         * may have been written out by the evictor. Also
         * check if the db is still valid -- since INs of
         * deleted databases are left on the in-memory tree
         * until the evictor lazily clears them out, there may
         * be dead INs around.
         */
        if (target.getDirty() && (!target.getDatabase().getIsDeleted())) {
          flushIN(target, logManager, dirtyMap, logProvisionally, allowDeltas);
          triedToFlush = true;
        } else {
          target.releaseLatch();
        }

        Tracer.trace(
            Level.FINE,
            envImpl,
            "Checkpointer: node="
                + target.getNodeId()
                + " level="
                + Integer.toHexString(target.getLevel())
                + " flushed="
                + triedToFlush);
      }

      /* We're done with this level. */
      dirtyMap.remove(currentLevel);

      /* We can stop at this point. */
      if (currentLevel.intValue() == highestFlushLevel) {
        break;
      }
    }
  }
Example #2
0
 public void putIN(IN in) {
   Integer level = new Integer(in.getLevel());
   Set inSet = (Set) get(level);
   if (inSet == null) {
     inSet = new HashSet();
     put(level, inSet);
   }
   inSet.add(in);
 }
  /**
   * Add a node to the dirty map. The dirty map is keyed by level (Integers) and holds sets of IN
   * references.
   */
  private void addToDirtyMap(Map dirtyMap, IN in) {
    Integer inLevel = new Integer(in.getLevel());
    Set inSet = (Set) dirtyMap.get(inLevel);

    /* If this level doesn't exist in the map yet, make a new entry. */
    if (inSet == null) {
      inSet = new HashSet();
      dirtyMap.put(inLevel, inSet);
    }

    /* Add to the set. */
    inSet.add(in);
  }
  /*
   * Scan the INList for all dirty INs. Arrange them in level sorted
   * map for level ordered flushing.
   */
  private SortedMap selectDirtyINs(boolean flushAll, boolean flushExtraLevel)
      throws DatabaseException {

    SortedMap newDirtyMap = new TreeMap();

    INList inMemINs = envImpl.getInMemoryINs();
    inMemINs.latchMajor();

    /*
     * Opportunistically recalculate the environment wide memory
     * count.  Incurs no extra cost because we're walking the IN
     * list anyway.  Not the best in terms of encapsulation as
     * prefereably all memory calculations are done in
     * MemoryBudget, but done this way to avoid any extra
     * latching.
     */
    long totalSize = 0;
    MemoryBudget mb = envImpl.getMemoryBudget();

    try {
      Iterator iter = inMemINs.iterator();
      while (iter.hasNext()) {
        IN in = (IN) iter.next();
        in.latch();
        totalSize = mb.accumulateNewUsage(in, totalSize);
        boolean isDirty = in.getDirty();
        in.releaseLatch();
        if (isDirty) {
          Integer level = new Integer(in.getLevel());
          Set dirtySet;
          if (newDirtyMap.containsKey(level)) {
            dirtySet = (Set) newDirtyMap.get(level);
          } else {
            dirtySet = new HashSet();
            newDirtyMap.put(level, dirtySet);
          }
          dirtySet.add(in);
        }
      }

      // Later release: refresh env count.
      // mb.refreshCacheMemoryUsage(totalSize);

      /*
       * If we're flushing all for cleaning, we must flush to
       * the point that there are no nodes with LSNs in the
       * cleaned files.  We could figure this out by perusing
       * every node to see what children it has, but that's so
       * expensive that instead we'll flush to the root.
       */
      if (newDirtyMap.size() > 0) {
        if (flushAll) {
          highestFlushLevel = envImpl.getDbMapTree().getHighestLevel();
        } else {
          highestFlushLevel = ((Integer) newDirtyMap.lastKey()).intValue();
          if (flushExtraLevel) {
            highestFlushLevel += 1;
          }
        }
      }

    } finally {
      inMemINs.releaseMajorLatchIfHeld();
    }

    return newDirtyMap;
  }