All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.orientechnologies.orient.core.type.tree.OMVRBTreePersistent Maven / Gradle / Ivy

/*
 *
 *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
 *  *
 *  *  Licensed under the Apache License, Version 2.0 (the "License");
 *  *  you may not use this file except in compliance with the License.
 *  *  You may obtain a copy of the License at
 *  *
 *  *       http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *  Unless required by applicable law or agreed to in writing, software
 *  *  distributed under the License is distributed on an "AS IS" BASIS,
 *  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *  See the License for the specific language governing permissions and
 *  *  limitations under the License.
 *  *
 *  * For more information: http://www.orientechnologies.com
 *
 */
package com.orientechnologies.orient.core.type.tree;

import com.orientechnologies.common.collection.OLimitedMap;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.profiler.OProfilerMBean;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.index.mvrbtree.OMVRBTree;
import com.orientechnologies.orient.core.index.mvrbtree.OMVRBTreeEntry;
import com.orientechnologies.orient.core.memory.OLowMemoryException;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage;
import com.orientechnologies.orient.core.type.tree.provider.OMVRBTreeProvider;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * Persistent based MVRB-Tree implementation. The difference with the class OMVRBTreePersistent is the level. In facts this class
 * works directly at the storage level, while the other at database level. This class is used for Logical Clusters. It can'be
 * transactional. It uses the entryPoints tree-map to get the closest entry point where start searching a node.
 * 
 */
@SuppressWarnings("serial")
public abstract class OMVRBTreePersistent extends OMVRBTree {

  protected OMVRBTreeProvider                        dataProvider;
  protected ORecord                                        owner;
  protected final Set>      recordsToCommit    = new HashSet>();

  // STORES IN MEMORY DIRECT REFERENCES TO PORTION OF THE TREE
  protected volatile int                                   optimization       = 0;
  protected int                                            entryPointsSize;

  protected float                                          optimizeEntryPointsFactor;
  private final TreeMap> entryPoints;
  private final Map>  cache;
  protected static final OProfilerMBean                    PROFILER           = Orient.instance().getProfiler();

  private static final int                                 OPTIMIZE_MAX_RETRY = 10;

  public OMVRBTreePersistent(OMVRBTreeProvider iProvider) {
    super();
    cache = new OLimitedMap>(256, 0.90f,
        OGlobalConfiguration.MVRBTREE_OPTIMIZE_THRESHOLD.getValueAsInteger()) {
      /**
       * Set the optimization rather than remove eldest element.
       */
      @Override
      protected boolean removeEldestEntry(final Map.Entry> eldest) {
        if (super.removeEldestEntry(eldest))
          // TOO MANY ITEMS: SET THE OPTIMIZATION
          setOptimization(2);
        return false;
      }
    };

    if (comparator != null)
      entryPoints = new TreeMap>(comparator);
    else
      entryPoints = new TreeMap>();

    pageLoadFactor = (Float) OGlobalConfiguration.MVRBTREE_LOAD_FACTOR.getValue();
    dataProvider = iProvider;
    config();
  }

  public OMVRBTreePersistent(OMVRBTreeProvider iProvider, int keySize) {
    this(iProvider);
    this.keySize = keySize;
    dataProvider.setKeySize(keySize);
  }

  @Override
  protected OMVRBTreeEntryPersistent createEntry(OMVRBTreeEntry iParent) {
    adjustPageSize();
    return new OMVRBTreeEntryPersistent(iParent, iParent.getPageSplitItems());
  }

  @Override
  protected OMVRBTreeEntryPersistent createEntry(final K key, final V value) {
    adjustPageSize();
    return new OMVRBTreeEntryPersistent(this, key, value, null);
  }

  /**
   * Create a new entry for {@link #loadEntry(OMVRBTreeEntryPersistent, ORID)}.
   */
  protected OMVRBTreeEntryPersistent createEntry(OMVRBTreeEntryPersistent iParent, ORID iRecordId) {
    return new OMVRBTreeEntryPersistent(this, iParent, iRecordId);
  }

  public OMVRBTreePersistent load() {
    dataProvider.load();

    // RESET LAST SEARCH STATE
    setLastSearchNode(null, null);
    keySize = dataProvider.getKeySize();

    // LOAD THE ROOT OBJECT AFTER ALL
    final ORID rootRid = dataProvider.getRoot();
    if (rootRid != null && rootRid.isValid())
      root = loadEntry(null, rootRid);
    return this;
  }

  protected void initAfterLoad() throws IOException {
  }

  public OMVRBTreePersistent save() {
    commitChanges();
    return this;
  }

  protected void saveTreeNode() throws IOException {
    if (root != null) {
      OMVRBTreeEntryPersistent pRoot = (OMVRBTreeEntryPersistent) root;
      if (pRoot.getProvider().getIdentity().isNew()) {
        // FIRST TIME: SAVE IT
        pRoot.save();
      }
    }

    dataProvider.save();
  }

  /**
   * Lazy loads a node.
   */
  protected OMVRBTreeEntryPersistent loadEntry(final OMVRBTreeEntryPersistent iParent, final ORID iRecordId) {
    // SEARCH INTO THE CACHE
    OMVRBTreeEntryPersistent entry = searchNodeInCache(iRecordId);
    if (entry == null) {
      // NOT FOUND: CREATE IT AND PUT IT INTO THE CACHE
      entry = createEntry(iParent, iRecordId);
      addNodeInMemory(entry);

      // RECONNECT THE LOADED NODE WITH IN-MEMORY PARENT, LEFT AND RIGHT
      if (entry.parent == null && entry.dataProvider.getParent().isValid()) {
        // TRY TO ASSIGN THE PARENT IN CACHE IF ANY
        final OMVRBTreeEntryPersistent parentNode = searchNodeInCache(entry.dataProvider.getParent());
        if (parentNode != null)
          entry.setParent(parentNode);
      }

      if (entry.left == null && entry.dataProvider.getLeft().isValid()) {
        // TRY TO ASSIGN THE PARENT IN CACHE IF ANY
        final OMVRBTreeEntryPersistent leftNode = searchNodeInCache(entry.dataProvider.getLeft());
        if (leftNode != null)
          entry.setLeft(leftNode);
      }

      if (entry.right == null && entry.dataProvider.getRight().isValid()) {
        // TRY TO ASSIGN THE PARENT IN CACHE IF ANY
        final OMVRBTreeEntryPersistent rightNode = searchNodeInCache(entry.dataProvider.getRight());
        if (rightNode != null)
          entry.setRight(rightNode);
      }

    } else {
      // COULD BE A PROBLEM BECAUSE IF A NODE IS DISCONNECTED CAN IT STAY IN CACHE?
      // entry.load();
      if (iParent != null)
        // FOUND: ASSIGN IT ONLY IF NOT NULL
        entry.setParent(iParent);
    }

    entry.checkEntryStructure();

    return entry;
  }

  @Override
  protected int getTreeSize() {
    return dataProvider.getSize();
  }

  protected void setSize(final int iSize) {
    if (dataProvider.setSize(iSize))
      markDirty();
  }

  public int getDefaultPageSize() {
    return dataProvider.getDefaultPageSize();
  }

  @Override
  public void clear() {
    final long timer = PROFILER.startChrono();

    try {
      recordsToCommit.clear();
      entryPoints.clear();
      cache.clear();
      if (root != null)
        try {
          ((OMVRBTreeEntryPersistent) root).delete();
        } catch (Exception e) {
          // IGNORE ANY EXCEPTION
          dataProvider = dataProvider.copy();
        }

      super.clear();
      markDirty();

    } finally {
      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.clear"), "Clear a MVRBTree", timer);
    }
  }

  public void delete() {
    clear();
    dataProvider.delete();
  }

  /**
   * Unload all the in-memory nodes. This is called on transaction rollback.
   */
  public void unload() {
    final long timer = PROFILER.startChrono();

    try {
      // DISCONNECT ALL THE NODES
      for (OMVRBTreeEntryPersistent entryPoint : entryPoints.values())
        entryPoint.disconnectLinked(true);
      entryPoints.clear();
      cache.clear();

      recordsToCommit.clear();
      root = null;

      final ODatabaseDocumentInternal db = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined();
      if (db != null && !db.isClosed() && db.getStorage().getUnderlying() instanceof OAbstractPaginatedStorage) {
        // RELOAD IT
        try {
          load();
        } catch (Exception e) {
          // IGNORE IT
        }
      }

    } catch (Exception e) {
      OLogManager.instance().error(this, "Error on unload the tree: " + dataProvider, e, OStorageException.class);
    } finally {
      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.unload"), "Unload a MVRBTree", timer);
    }
  }

  /**
   * Calls the optimization in soft mode: free resources only if needed.
   */
  protected void optimize() {
    optimize(false);
  }

  /**
   * Optimizes the memory needed by the tree in memory by reducing the number of entries to the configured size.
   * 
   * @return The total freed nodes
   */
  public int optimize(final boolean iForce) {
    if (optimization == -1)
      // IS ALREADY RUNNING
      return 0;

    if (!iForce && optimization == 0)
      // NO OPTIMIZATION IS NEEDED
      return 0;

    // SET OPTIMIZATION STATUS AS RUNNING
    optimization = -1;

    final long timer = PROFILER.startChrono();

    try {
      if (root == null)
        return 0;

      if (OLogManager.instance().isDebugEnabled())
        OLogManager.instance().debug(this, "Starting optimization of MVRB+Tree with %d items in memory...", cache.size());

      // printInMemoryStructure();

      if (entryPoints.size() == 0)
        // FIRST TIME THE LIST IS NULL: START FROM ROOT
        addNodeAsEntrypoint((OMVRBTreeEntryPersistent) root);

      // RECONFIG IT TO CATCH CHANGED VALUES
      config();

      if (OLogManager.instance().isDebugEnabled())
        OLogManager.instance().debug(this, "Found %d items on disk, threshold=%f, entryPoints=%d, nodesInCache=%d", size(),
            (entryPointsSize * optimizeEntryPointsFactor), entryPoints.size(), cache.size());

      final int nodesInMemory = cache.size();

      if (!iForce && nodesInMemory < entryPointsSize * optimizeEntryPointsFactor)
        // UNDER THRESHOLD AVOID TO OPTIMIZE
        return 0;

      lastSearchFound = false;
      lastSearchKey = null;
      lastSearchNode = null;

      int totalDisconnected = 0;

      if (nodesInMemory > entryPointsSize) {
        // REDUCE THE ENTRYPOINTS
        final int distance = nodesInMemory / entryPointsSize + 1;

        final Set> entryPointsToRemove = new HashSet>(nodesInMemory
            - entryPointsSize + 2);

        // REMOVE ENTRYPOINTS AT THE SAME DISTANCE
        int currNode = 0;
        for (final Iterator> it = entryPoints.values().iterator(); it.hasNext();) {
          final OMVRBTreeEntryPersistent currentNode = it.next();

          // JUMP THE FIRST (1 cannot never be the % of distance) THE LAST, ROOT AND LAST USED
          // RECORDS THAT WERE CREATED INSIDE OF TRANSACTION CAN'T BE REMOVED TILL COMMIT
          if (currentNode != root && currentNode != lastSearchNode && !currentNode.dataProvider.getIdentity().isTemporary()
              && it.hasNext())
            if (++currNode % distance != 0) {
              // REMOVE THE NODE
              entryPointsToRemove.add(currentNode);
              it.remove();
            }
        }
        addNodeAsEntrypoint((OMVRBTreeEntryPersistent) lastSearchNode);
        addNodeAsEntrypoint((OMVRBTreeEntryPersistent) root);

        // DISCONNECT THE REMOVED NODES
        for (OMVRBTreeEntryPersistent currentNode : entryPointsToRemove)
          totalDisconnected += currentNode.disconnectLinked(false);

        cache.clear();
        for (OMVRBTreeEntryPersistent entry : entryPoints.values())
          addNodeInCache(entry);
      }

      if (isRuntimeCheckEnabled()) {
        for (OMVRBTreeEntryPersistent entryPoint : entryPoints.values())
          for (OMVRBTreeEntryPersistent e = (OMVRBTreeEntryPersistent) entryPoint.getFirstInMemory(); e != null; e = e
              .getNextInMemory())
            e.checkEntryStructure();
      }

      // COUNT ALL IN-MEMORY NODES BY BROWSING ALL THE ENTRYPOINT NODES
      if (OLogManager.instance().isDebugEnabled())
        OLogManager.instance().debug(this, "After optimization: %d items on disk, threshold=%f, entryPoints=%d, nodesInCache=%d",
            size(), (entryPointsSize * optimizeEntryPointsFactor), entryPoints.size(), cache.size());

      return totalDisconnected;
    } finally {
      optimization = 0;
      if (isRuntimeCheckEnabled()) {
        if (!entryPoints.isEmpty())
          for (OMVRBTreeEntryPersistent entryPoint : entryPoints.values())
            checkTreeStructure(entryPoint.getFirstInMemory());
        else
          checkTreeStructure(root);
      }

      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.optimize"), "Optimize a MVRBTree", timer);

      if (OLogManager.instance().isDebugEnabled())
        OLogManager.instance().debug(this, "Optimization completed in %d ms\n", System.currentTimeMillis() - timer);
    }
  }

  @Override
  public OMVRBTreeEntry getCeilingEntry(K key, PartialSearchMode partialSearchMode) {
    for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) {
      try {
        return super.getCeilingEntry(key, partialSearchMode);
      } catch (OLowMemoryException e) {
        OLogManager.instance().debug(this, "Optimization required during node search %d/%d", i, OPTIMIZE_MAX_RETRY);

        freeMemory(i);
      }
    }

    throw new OLowMemoryException("OMVRBTreePersistent.getCeilingEntry()");
  }

  @Override
  public OMVRBTreeEntry getFloorEntry(K key, PartialSearchMode partialSearchMode) {
    for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) {
      try {
        return super.getFloorEntry(key, partialSearchMode);
      } catch (OLowMemoryException e) {
        OLogManager.instance().debug(this, "Optimization required during node search %d/%d", i, OPTIMIZE_MAX_RETRY);

        freeMemory(i);
      }
    }
    throw new OLowMemoryException("OMVRBTreePersistent.getFloorEntry()");
  }

  @Override
  public OMVRBTreeEntry getHigherEntry(K key) {
    for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) {
      try {
        return super.getHigherEntry(key);
      } catch (OLowMemoryException e) {
        OLogManager.instance().debug(this, "Optimization required during node search %d/%d", i, OPTIMIZE_MAX_RETRY);

        freeMemory(i);
      }
    }
    throw new OLowMemoryException("OMVRBTreePersistent.getHigherEntry)");
  }

  @Override
  public OMVRBTreeEntry getLowerEntry(K key) {
    for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) {
      try {
        return super.getLowerEntry(key);
      } catch (OLowMemoryException e) {
        OLogManager.instance().debug(this, "Optimization required during node search %d/%d", i, OPTIMIZE_MAX_RETRY);

        freeMemory(i);
      }
    }
    throw new OLowMemoryException("OMVRBTreePersistent.getLowerEntry()");
  }

  @Override
  public V put(final K key, final V value) {
    optimize();
    final long timer = PROFILER.startChrono();

    try {
      final V v = internalPut(key, value);
      commitChanges();
      return v;
    } finally {

      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.put"), "Put a value into a MVRBTree", timer);
    }
  }

  @Override
  public void putAll(final Map map) {
    final long timer = PROFILER.startChrono();

    try {
      for (Entry entry : map.entrySet())
        internalPut(entry.getKey(), entry.getValue());

      commitChanges();

    } finally {
      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.putAll"), "Put multiple values into a MVRBTree", timer);
    }
  }

  @Override
  public V remove(final Object key) {
    optimize();
    final long timer = PROFILER.startChrono();

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

          V v = super.remove(key);
          commitChanges();
          return v;

        } catch (OLowMemoryException e) {
          OLogManager.instance().debug(this, "Optimization required during remove %d/%d", i, OPTIMIZE_MAX_RETRY);

          freeMemory(i);

          // AVOID CONTINUE EXCEPTIONS
          optimization = -1;
        }
      }
    } finally {
      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.remove"), "Remove a value from a MVRBTree", timer);
    }

    throw new OLowMemoryException("OMVRBTreePersistent.remove()");
  }

  public int commitChanges() {
    final long timer = PROFILER.startChrono();

    int totalCommitted = 0;
    try {

      if (!recordsToCommit.isEmpty()) {
        final List> tmp = new ArrayList>();

        while (recordsToCommit.iterator().hasNext()) {
          // COMMIT BEFORE THE NEW RECORDS (TO ASSURE RID IN RELATIONSHIPS)
          tmp.addAll(recordsToCommit);

          recordsToCommit.clear();

          for (OMVRBTreeEntryPersistent node : tmp)
            if (node.dataProvider.isEntryDirty()) {
              boolean wasNew = node.dataProvider.getIdentity().isNew();

              // CREATE THE RECORD
              node.save();

              if (debug)
                System.out.printf("\nSaved %s tree node %s: parent %s, left %s, right %s", wasNew ? "new" : "",
                    node.dataProvider.getIdentity(), node.dataProvider.getParent(), node.dataProvider.getLeft(),
                    node.dataProvider.getRight());
            }

          totalCommitted += tmp.size();
          tmp.clear();
        }
      }

      if (dataProvider.isDirty())
        // TREE IS CHANGED AS WELL
        saveTreeNode();

    } catch (IOException e) {
      throw new OStorageException("Error on saving the tree", e);
    } finally {

      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.commitChanges"), "Commit pending changes to a MVRBTree", timer);
    }

    return totalCommitted;
  }

  public void signalNodeChanged(final OMVRBTreeEntry iNode) {
    recordsToCommit.add((OMVRBTreeEntryPersistent) iNode);
  }

  @Override
  public int hashCode() {
    return dataProvider.hashCode();
  }

  protected void adjustPageSize() {
  }

  @Override
  public V get(final Object iKey) {
    final long timer = PROFILER.startChrono();
    try {

      for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) {
        try {
          return super.get(iKey);
        } catch (OLowMemoryException e) {
          OLogManager.instance().debug(this, "Optimization required during node search %d/%d", i, OPTIMIZE_MAX_RETRY);
          freeMemory(i);
        }
      }

      throw new OLowMemoryException("OMVRBTreePersistent.get()");
    } finally {
      PROFILER.stopChrono(PROFILER.getProcessMetric("mvrbtree.get"), "Get a value from a MVRBTree", timer);
    }
  }

  @Override
  public boolean containsKey(final Object iKey) {
    for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) {
      try {
        return super.containsKey(iKey);
      } catch (OLowMemoryException e) {
        OLogManager.instance().debug(this, "Optimization required during node search %d/%d", i, OPTIMIZE_MAX_RETRY);
        freeMemory(i);
      }
    }

    throw new OLowMemoryException("OMVRBTreePersistent.containsKey()");
  }

  @Override
  public boolean containsValue(final Object iValue) {
    for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) {
      try {
        return super.containsValue(iValue);
      } catch (OLowMemoryException e) {
        OLogManager.instance().debug(this, "Optimization required during node search %d/%d", i, OPTIMIZE_MAX_RETRY);
        freeMemory(i);
      }
    }

    throw new OLowMemoryException("OMVRBTreePersistent.containsValue()");
  }

  public OMVRBTreeProvider getProvider() {
    return dataProvider;
  }

  public int getOptimization() {
    return optimization;
  }

  /**
   * Set the optimization to be executed at the next call.
   * 
   * @param iMode
   *          
    *
  • -1 = ALREADY RUNNING
  • *
  • 0 = NO OPTIMIZATION (DEFAULT)
  • *
  • 1 = SOFT MODE
  • *
  • 2 = HARD MODE
  • *
*/ public void setOptimization(final int iMode) { if (iMode > 0 && optimization == -1) // IGNORE IT, ALREADY RUNNING return; optimization = iMode; } /** * Checks if optimization is needed by raising a {@link OLowMemoryException}. */ @Override protected void searchNodeCallback() { if (optimization > 0) throw new OLowMemoryException("Optimization level: " + optimization); } public int getEntryPointSize() { return entryPointsSize; } public void setEntryPointSize(final int entryPointSize) { this.entryPointsSize = entryPointSize; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(128).append('['); if (size() < 10) { OMVRBTreeEntry current = getFirstEntry(); for (int i = 0; i < 10 && current != null; ++i) { if (i > 0) buffer.append(','); buffer.append(current); current = next(current); } } else { buffer.append("size="); final int size = size(); buffer.append(size); final OMVRBTreeEntry firstEntry = getFirstEntry(); if (firstEntry != null) { final int currPageIndex = pageIndex; buffer.append(" "); buffer.append(firstEntry.getFirstKey()); if (size > 1) { buffer.append("-"); buffer.append(getLastEntry().getLastKey()); } pageIndex = currPageIndex; } } return buffer.append(']').toString(); } protected V internalPut(final K key, final V value) throws OLowMemoryException { ORecord rec; if (key instanceof ORecord) { // RECORD KEY: ASSURE IT'S PERSISTENT TO AVOID STORING INVALID RIDs rec = (ORecord) key; if (!rec.getIdentity().isValid()) rec.save(); } if (value instanceof ORecord) { // RECORD VALUE: ASSURE IT'S PERSISTENT TO AVOID STORING INVALID RIDs rec = (ORecord) value; if (!rec.getIdentity().isValid()) rec.save(); } for (int i = 0; i < OPTIMIZE_MAX_RETRY; ++i) { try { return super.put(key, value); } catch (OLowMemoryException e) { OLogManager.instance().debug(this, "Optimization required during put %d/%d", i, OPTIMIZE_MAX_RETRY); freeMemory(i); } } throw new OLowMemoryException("OMVRBTreePersistent.put()"); } /** * Returns the best entry point to start the search. Searches first between entrypoints. If nothing is found "root" is always * returned. */ @Override protected OMVRBTreeEntry getBestEntryPoint(final K iKey) { if (!entryPoints.isEmpty()) { // SEARCHES EXACT OR BIGGER ENTRY Entry> closerNode = entryPoints.floorEntry(iKey); if (closerNode != null) return closerNode.getValue(); // NO WAY: TRY WITH ANY NODE BEFORE THE KEY closerNode = entryPoints.ceilingEntry(iKey); if (closerNode != null) return closerNode.getValue(); } // USE ROOT return super.getBestEntryPoint(iKey); } /** * Remove an entry point from the list */ void removeEntryPoint(final OMVRBTreeEntryPersistent iEntry) { entryPoints.remove(iEntry); } synchronized void removeEntry(final ORID iEntryId) { // DELETE THE NODE FROM THE PENDING RECORDS TO COMMIT for (OMVRBTreeEntryPersistent node : recordsToCommit) { if (node.dataProvider.getIdentity().equals(iEntryId)) { recordsToCommit.remove(node); break; } } } /** * Returns the first Entry in the OMVRBTree (according to the OMVRBTree's key-sort function). Returns null if the OMVRBTree is * empty. */ @Override public OMVRBTreeEntry getFirstEntry() { if (!entryPoints.isEmpty()) { // FIND THE FIRST ELEMENT STARTING FROM THE FIRST ENTRY-POINT IN MEMORY final Map.Entry> entry = entryPoints.firstEntry(); if (entry != null) { OMVRBTreeEntryPersistent e = entry.getValue(); OMVRBTreeEntryPersistent prev; do { prev = (OMVRBTreeEntryPersistent) predecessor(e); if (prev != null) e = prev; } while (prev != null); if (e != null && e.getSize() > 0) pageIndex = 0; return e; } } // SEARCH FROM ROOT return super.getFirstEntry(); } /** * Returns the last Entry in the OMVRBTree (according to the OMVRBTree's key-sort function). Returns null if the OMVRBTree is * empty. */ @Override public OMVRBTreeEntry getLastEntry() { if (!entryPoints.isEmpty()) { // FIND THE LAST ELEMENT STARTING FROM THE FIRST ENTRY-POINT IN MEMORY final Map.Entry> entry = entryPoints.lastEntry(); if (entry != null) { OMVRBTreeEntryPersistent e = entry.getValue(); OMVRBTreeEntryPersistent next; do { next = (OMVRBTreeEntryPersistent) successor(e); if (next != null) e = next; } while (next != null); if (e != null && e.getSize() > 0) pageIndex = e.getSize() - 1; return e; } } // SEARCH FROM ROOT return super.getLastEntry(); } @Override protected void setRoot(final OMVRBTreeEntry iRoot) { if (iRoot == root) return; super.setRoot(iRoot); if (iRoot == null) dataProvider.setRoot(null); else dataProvider.setRoot(((OMVRBTreeEntryPersistent) iRoot).getProvider().getIdentity()); } protected void config() { if (dataProvider.updateConfig()) markDirty(); pageLoadFactor = OGlobalConfiguration.MVRBTREE_LOAD_FACTOR.getValueAsFloat(); optimizeEntryPointsFactor = OGlobalConfiguration.MVRBTREE_OPTIMIZE_ENTRYPOINTS_FACTOR.getValueAsFloat(); entryPointsSize = OGlobalConfiguration.MVRBTREE_ENTRYPOINTS.getValueAsInteger(); } @Override protected void rotateLeft(final OMVRBTreeEntry p) { if (debug && p != null) System.out.printf("\nRotating to the left the node %s", ((OMVRBTreeEntryPersistent) p).dataProvider.getIdentity()); super.rotateLeft(p); } @Override protected void rotateRight(final OMVRBTreeEntry p) { if (debug && p != null) System.out.printf("\nRotating to the right the node %s", ((OMVRBTreeEntryPersistent) p).dataProvider.getIdentity()); super.rotateRight(p); } /** * Removes the node also from the memory. */ @Override protected OMVRBTreeEntry removeNode(final OMVRBTreeEntry p) { final OMVRBTreeEntryPersistent removed = (OMVRBTreeEntryPersistent) super.removeNode(p); removeNodeFromMemory(removed); // this prevents NPE in case if tree contains single node and it was deleted inside of super.removeNode method. if (removed.getProvider() != null) removed.getProvider().delete(); // prevent record saving if it has been deleted. recordsToCommit.remove(removed); return removed; } /** * Removes the node from the memory. * * @param iNode * Node to remove */ protected void removeNodeFromMemory(final OMVRBTreeEntryPersistent iNode) { if (iNode.dataProvider != null && iNode.dataProvider.getIdentity().isValid()) cache.remove(iNode.dataProvider.getIdentity()); if (iNode.getSize() > 0) entryPoints.remove(iNode.getKeyAt(0)); } protected void addNodeInMemory(final OMVRBTreeEntryPersistent iNode) { addNodeAsEntrypoint(iNode); addNodeInCache(iNode); } protected boolean isNodeEntryPoint(final OMVRBTreeEntryPersistent iNode) { if (iNode != null && iNode.getSize() > 0) return entryPoints.containsKey(iNode.getKeyAt(0)); return false; } protected void addNodeAsEntrypoint(final OMVRBTreeEntryPersistent iNode) { if (iNode != null && iNode.getSize() > 0) entryPoints.put(iNode.getKeyAt(0), iNode); } /** * Updates the position of the node between the entry-points. If the node has 0 items, it's simply removed. * * @param iOldKey * Old key to remove * @param iNode * Node to update */ protected void updateEntryPoint(final K iOldKey, final OMVRBTreeEntryPersistent iNode) { final OMVRBTreeEntryPersistent node = entryPoints.remove(iOldKey); if (node != null) { if (node != iNode) OLogManager.instance().warn(this, "Entrypoints nodes are different during update: old %s <-> new %s", node, iNode); addNodeAsEntrypoint(iNode); } } /** * Keeps the node in memory. * * @param iNode * Node to store */ protected void addNodeInCache(final OMVRBTreeEntryPersistent iNode) { if (iNode.dataProvider != null && iNode.dataProvider.getIdentity().isValid()) cache.put(iNode.dataProvider.getIdentity(), iNode); } /** * Searches the node in local cache by RID. * * @param iRid * RID to search * @return Node is found, otherwise NULL */ protected OMVRBTreeEntryPersistent searchNodeInCache(final ORID iRid) { return cache.get(iRid); } public int getNumberOfNodesInCache() { return cache.size(); } /** * Returns all the RID of the nodes in memory. */ protected Set getAllNodesInCache() { return cache.keySet(); } /** * Removes the node from the local cache. * * @param iRid * RID of node to remove */ protected void removeNodeFromCache(final ORID iRid) { cache.remove(iRid); } protected void markDirty() { } public ORecord getOwner() { return owner; } public OMVRBTreePersistent setOwner(ORecord owner) { this.owner = owner; return this; } protected void freeMemory(final int i) { // LOW MEMORY DURING LOAD: THIS MEANS DEEP LOADING OF NODES. EXECUTE THE OPTIMIZATION AND RETRY IT optimize(true); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy