Package com.orientechnologies.orient.core.type.tree

Source Code of com.orientechnologies.orient.core.type.tree.OMVRBTreePersistent

/*
* Copyright 1999-2010 Luca Garulli (l.garulli--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.
*/
package com.orientechnologies.orient.core.type.tree;

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

import com.orientechnologies.common.collection.OMVRBTree;
import com.orientechnologies.common.collection.OMVRBTreeEntry;
import com.orientechnologies.common.collection.OMVRBTreeEventListener;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.profiler.OProfiler;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.record.ODatabaseRecord;
import com.orientechnologies.orient.core.exception.OSerializationException;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.record.impl.ORecordBytes;
import com.orientechnologies.orient.core.record.impl.ORecordBytesLazy;
import com.orientechnologies.orient.core.serialization.OMemoryInputStream;
import com.orientechnologies.orient.core.serialization.OMemoryOutputStream;
import com.orientechnologies.orient.core.serialization.OSerializableStream;
import com.orientechnologies.orient.core.serialization.serializer.record.OSerializationThreadLocal;
import com.orientechnologies.orient.core.serialization.serializer.stream.OStreamSerializer;
import com.orientechnologies.orient.core.serialization.serializer.stream.OStreamSerializerFactory;

/**
* 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 linked list to get the best entry point for searching a node.
*
*/
@SuppressWarnings("serial")
public abstract class OMVRBTreePersistent<K, V> extends OMVRBTree<K, V> implements OMVRBTreeEventListener<K, V>,
    OSerializableStream {
  protected OStreamSerializer                          keySerializer;
  protected OStreamSerializer                          valueSerializer;

  protected final Set<OMVRBTreeEntryPersistent<K, V>>  recordsToCommit            = new HashSet<OMVRBTreeEntryPersistent<K, V>>();

  protected final String                              clusterName;
  protected ORecordBytesLazy                          record;
  protected String                                    fetchPlan;

  // STORES IN MEMORY DIRECT REFERENCES TO PORTION OF THE TREE
  protected int                                        optimizeThreshold;
  private int                                          insertionCounter          = 0;
  protected int                                        entryPointsSize;
  protected float                                      optimizeEntryPointsFactor;
  protected List<OMVRBTreeEntryPersistent<K, V>>      entryPoints                = new ArrayList<OMVRBTreeEntryPersistent<K, V>>(
                                                                                    entryPointsSize);

  protected Map<ORID, OMVRBTreeEntryPersistent<K, V>>  cache                      = new HashMap<ORID, OMVRBTreeEntryPersistent<K, V>>();
  private final OMemoryOutputStream                    entryRecordBuffer;
  public final static byte                            CURRENT_PROTOCOL_VERSION  = 0;

  public OMVRBTreePersistent(final String iClusterName, final ORID iRID) {
    this(iClusterName, null, null);
    record.setIdentity(iRID.getClusterId(), iRID.getClusterPosition());
  }

  public OMVRBTreePersistent(String iClusterName, final OStreamSerializer iKeySerializer, final OStreamSerializer iValueSerializer) {
    // MINIMIZE I/O USING A LARGER PAGE THAN THE DEFAULT USED IN MEMORY
    super(OGlobalConfiguration.MVRBTREE_NODE_PAGE_SIZE.getValueAsInteger(), 0.7f);
    config();

    clusterName = iClusterName;
    record = new ORecordBytesLazy(this);

    keySerializer = iKeySerializer;
    valueSerializer = iValueSerializer;

    entryRecordBuffer = new OMemoryOutputStream(getPageSize() * 15);

    setListener(this);
  }

  public abstract OMVRBTreePersistent<K, V> load() throws IOException;

  public abstract OMVRBTreePersistent<K, V> save() throws IOException;

  /**
   * Lazy loads a node.
   */
  protected abstract OMVRBTreeEntryPersistent<K, V> loadEntry(OMVRBTreeEntryPersistent<K, V> iParent, ORID iRecordId)
      throws IOException;

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

    try {
      if (root != null) {
        ((OMVRBTreeEntryPersistent<K, V>) root).delete();
        super.clear();
        getListener().signalTreeChanged(this);
      }

      recordsToCommit.clear();
      entryPoints.clear();
      cache.clear();

    } catch (IOException e) {
      OLogManager.instance().error(this, "Error on deleting the tree: " + record.getIdentity(), e, OStorageException.class);
    } finally {

      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.clear", timer);
    }
  }

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

    try {
      // DISCONNECT ALL THE NODES
      for (OMVRBTreeEntryPersistent<K, V> entryPoint : entryPoints)
        entryPoint.disconnectLinked(true);
      entryPoints.clear();

      recordsToCommit.clear();
      cache.clear();
      root = null;

      load();

    } catch (IOException e) {
      OLogManager.instance().error(this, "Error on unload the tree: " + record.getIdentity(), e, OStorageException.class);
    } finally {

      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.unload", timer);
    }
  }

  /**
   * Optimize the tree memory consumption by keeping part of nodes as entry points and clearing all the rest.
   *
   * @return
   */
  @SuppressWarnings("unchecked")
  public int optimize(final boolean iForce) {
    final long timer = OProfiler.getInstance().startChrono();

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

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

      // printInMemoryStructure();

      OMVRBTreeEntryPersistent<K, V> pRoot = (OMVRBTreeEntryPersistent<K, V>) root;

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

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

      int nodes = 0;
      List<OMVRBTreeEntryPersistent<K, V>> tmp = null;

      if (isRuntimeCheckEnabled())
        tmp = new ArrayList<OMVRBTreeEntryPersistent<K, V>>();

      for (OMVRBTreeEntryPersistent<K, V> entryPoint : entryPoints) {
        for (OMVRBTreeEntryPersistent<K, V> e = (OMVRBTreeEntryPersistent<K, V>) entryPoint.getFirstInMemory(); e != null; e = e
            .getNextInMemory()) {

          if (isRuntimeCheckEnabled()) {
            for (OMVRBTreeEntryPersistent<K, V> t : tmp)
              if (t != e && t.record.getIdentity().equals(e.record.getIdentity())) {
                OLogManager.instance().error(this, "Found Node loaded in memory twice with different instances: " + e);
                continue;
              }

            tmp.add(e);
          }

          ++nodes;
        }
      }

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

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

      if (debug)
        System.out.printf("\n------------\nOptimizing: total items %d, root is %s...", size(), pRoot.toString());

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

      // COMPUTE THE DISTANCE BETWEEN NODES
      final int distance;
      if (nodes <= entryPointsSize)
        distance = 1;
      else
        distance = nodes / entryPointsSize + 1;

      final List<OMVRBTreeEntryPersistent<K, V>> newEntryPoints = new ArrayList<OMVRBTreeEntryPersistent<K, V>>(entryPointsSize + 1);

      OLogManager.instance().debug(this, "Compacting nodes with distance = %d", distance);

      // PICK NEW ENTRYPOINTS AT EQUAL DISTANCE
      int nodeCounter = 0;
      OMVRBTreeEntryPersistent<K, V> lastNode = null;
      OMVRBTreeEntryPersistent<K, V> currNode;

      for (int i = 0; i < entryPoints.size(); ++i) {
        currNode = entryPoints.get(i);
        for (OMVRBTreeEntryPersistent<K, V> e = (OMVRBTreeEntryPersistent<K, V>) currNode.getFirstInMemory(); e != null; e = e
            .getNextInMemory()) {

          boolean alreadyPresent = false;

          // CHECK THAT THE NODE IS NOT PART OF A NEXT ENTRY-POINTS: THIS IS THE CASE WHEN THE TREE CHUNKS ARE CONNECTED
          // BETWEEN THEM
          for (int k = i + 1; k < entryPoints.size(); ++k)
            if (e == entryPoints.get(k)) {
              alreadyPresent = true;
              break;
            }

          if (alreadyPresent)
            continue;

          ++nodeCounter;

          if (newEntryPoints.size() == 0 || nodeCounter % distance == 0) {
            for (OMVRBTreeEntryPersistent<K, V> ep : newEntryPoints)
              if (ep == e) {
                alreadyPresent = true;
                break;
              }

            if (alreadyPresent)
              --nodeCounter;
            else
              newEntryPoints.add(e);
          }

          lastNode = e;
        }
      }

      if (newEntryPoints.size() > 1 && newEntryPoints.get(newEntryPoints.size() - 1) != lastNode)
        // ADD THE LAST ONE IF IT'S NOT YET PRESENT
        newEntryPoints.add(lastNode);

      // INSERT ROOT BETWEEN ENTRY-POINTS
      int cmp;
      for (int i = 0; i < newEntryPoints.size(); ++i) {
        cmp = ((Comparable<K>) pRoot.getFirstKey()).compareTo(newEntryPoints.get(i).getFirstKey());
        if (cmp < 0) {
          newEntryPoints.add(i, pRoot);
          break;
        } else if (cmp == 0)
          // ALREADY PRESENT: DO NOTHING
          break;
      }

      // REMOVE NOT REFERENCED ENTRY POINTS
      for (OMVRBTreeEntryPersistent<K, V> entryPoint : entryPoints) {
        if (entryPoint.parent == null && entryPoint.left == null && entryPoint.right == null && entryPoint != root) {
          // CHECK IF IT'S NOT PART OF NEW ENTRYPOINTS
          boolean found = false;
          for (OMVRBTreeEntryPersistent<K, V> e : newEntryPoints) {
            if (e == entryPoint) {
              // IT'S AN ENTRYPOINT
              found = true;
              break;
            }
          }

          if (!found) {
            // NODE NOT REFERENCED: REMOVE IT FROM THE CACHE
            cache.remove(entryPoint.record.getIdentity());
            entryPoint.clear();
          }
        }
      }

      final List<OMVRBTreeEntryPersistent<K, V>> oldEntryPoints = entryPoints;
      entryPoints = newEntryPoints;

      // FREE ALL THE NODES READING THE OLD ENTRY POINTS BUT THE NEW ENTRY POINTS
      int totalDisconnected = 0;
      for (OMVRBTreeEntryPersistent<K, V> entryPoint : oldEntryPoints) {
        totalDisconnected += entryPoint.disconnectLinked(false);
      }

      oldEntryPoints.clear();

      if (isRuntimeCheckEnabled()) {
        for (OMVRBTreeEntryPersistent<K, V> entryPoint : entryPoints)
          for (OMVRBTreeEntryPersistent<K, V> e = (OMVRBTreeEntryPersistent<K, V>) entryPoint.getFirstInMemory(); e != null; e = e
              .getNextInMemory())
            e.checkEntryStructure();

        if (OLogManager.instance().isDebugEnabled()) {
          // COUNT ALL IN-MEMORY NODES BY BROWSING ALL THE ENTRYPOINT NODES
          nodes = 0;
          for (OMVRBTreeEntryPersistent<K, V> entryPoint : entryPoints)
            for (OMVRBTreeEntryPersistent<K, V> e = (OMVRBTreeEntryPersistent<K, V>) entryPoint.getFirstInMemory(); e != null; e = e
                .getNextInMemory())
              ++nodes;

          OLogManager.instance().debug(this, "Now Found %d nodes in memory and threshold=%d. EntryPoints=%d", nodes,
              (entryPointsSize * optimizeEntryPointsFactor), entryPoints.size());
        }
      }

      OLogManager.instance().debug(this, "Optimization done: MVRB-Tree nodes reduced to %d items", cache.size());

      return totalDisconnected;
    } finally {
      // System.out.println("End of optimization.");
      // printInMemoryStructure();

      if (isRuntimeCheckEnabled()) {
        if (entryPoints.size() > 0)
          for (OMVRBTreeEntryPersistent<K, V> entryPoint : entryPoints)
            checkTreeStructure(entryPoint.getFirstInMemory());
        else
          checkTreeStructure(root);
      }

      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.optimize", timer);

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

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

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

      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.put", timer);
    }
  }

  @Override
  public void putAll(final Map<? extends K, ? extends V> map) {
    final long timer = OProfiler.getInstance().startChrono();

    try {
      for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
        internalPut(entry.getKey(), entry.getValue());
      }
      commitChanges();

    } finally {
      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.putAll", timer);
    }
  }

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

    try {
      V v = super.remove(key);
      commitChanges();
      return v;
    } finally {

      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.remove", timer);
    }
  }

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

    int totalCommitted = 0;
    try {
      if (recordsToCommit.size() > 0) {
        final List<OMVRBTreeEntryPersistent<K, V>> tmp = new ArrayList<OMVRBTreeEntryPersistent<K, V>>();

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

          recordsToCommit.clear();

          for (OMVRBTreeEntryPersistent<K, V> node : tmp)
            if (node.record.isDirty()) {
              boolean wasNew = node.record.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.record.getIdentity(), node.parentRid, node.leftRid, node.rightRid);

              if (wasNew) {
                if (node.record.getIdentity().getClusterPosition() < -1) {
                  // TX RECORD
                  if (cache.get(node.record) != node)
                    // INSERT A COPY TO PREVENT CHANGES
                    cache.put(node.record.getIdentity().copy(), node);
                } else {
                  // NEW RID: MAKE DIRTY THE LINKED NODES
                  if (node.parent != null)
                    (node.parent).markDirty();
                  if (node.left != null)
                    (node.left).markDirty();
                  if (node.right != null)
                    (node.right).markDirty();
                }

                cache.put(node.record.getIdentity(), node);
              }
            }

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

      if (record.isDirty()) {
        // TREE IS CHANGED AS WELL
        save();
      }

    } catch (IOException e) {
      OLogManager.instance().exception("Error on saving the tree", e, OStorageException.class);

    } finally {

      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.commitChanges", timer);
    }

    return totalCommitted;
  }

  public OSerializableStream fromStream(final byte[] iStream) throws OSerializationException {
    final long timer = OProfiler.getInstance().startChrono();

    final ORID rootRid = new ORecordId();

    try {
      final OMemoryInputStream stream = new OMemoryInputStream(iStream);

      byte protocolVersion = stream.peek();
      if (protocolVersion != -1) {
        // @COMPATIBILITY BEFORE 0.9.25
        stream.getAsByte();
        if (protocolVersion != CURRENT_PROTOCOL_VERSION)
          throw new OSerializationException(
              "The index has been created with a previous version of OrientDB. Soft transitions between version is a featured supported since 0.9.25. In order to use it with this version of OrientDB you need to export and import your database. "
                  + protocolVersion + "<->" + CURRENT_PROTOCOL_VERSION);
      }

      rootRid.fromStream(stream.getAsByteArrayFixed(ORecordId.PERSISTENT_SIZE));

      size = stream.getAsInteger();
      if (protocolVersion == -1)
        // @COMPATIBILITY BEFORE 0.9.25
        lastPageSize = stream.getAsShort();
      else
        lastPageSize = stream.getAsInteger();

      serializerFromStream(stream);

      // LOAD THE ROOT OBJECT AFTER ALL
      if (rootRid.isValid())
        root = loadEntry(null, rootRid);

      return this;

    } catch (Exception e) {

      OLogManager.instance().error(this, "Error on unmarshalling OMVRBTreePersistent object from record: %s", e,
          OSerializationException.class, rootRid);

    } finally {
      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.fromStream", timer);
    }
    return this;
  }

  public byte[] toStream() throws OSerializationException {
    final long timer = OProfiler.getInstance().startChrono();

    // CHECK IF THE RECORD IS PENDING TO BE MARSHALLED
    final Integer identityRecord = System.identityHashCode(record);
    final Set<Integer> marshalledRecords = OSerializationThreadLocal.INSTANCE.get();
    if (marshalledRecords.contains(identityRecord)) {
      // ALREADY IN STACK, RETURN EMPTY
      return new byte[] {};
    } else
      marshalledRecords.add(identityRecord);

    OMemoryOutputStream stream = entryRecordBuffer;

    try {
      stream.add(CURRENT_PROTOCOL_VERSION);

      if (root != null) {
        OMVRBTreeEntryPersistent<K, V> pRoot = (OMVRBTreeEntryPersistent<K, V>) root;
        if (pRoot.record.getIdentity().isNew()) {
          // FIRST TIME: SAVE IT
          pRoot.save();
        }

        stream.addAsFixed(pRoot.record.getIdentity().toStream());
      } else
        stream.addAsFixed(ORecordId.EMPTY_RECORD_ID_STREAM);

      stream.add(size);
      stream.add(lastPageSize);

      stream.add(keySerializer.getName());
      stream.add(valueSerializer.getName());

      record.fromStream(stream.getByteArray());
      return record.toStream();

    } catch (IOException e) {
      throw new OSerializationException("Error on marshalling RB+Tree", e);
    } finally {
      marshalledRecords.remove(identityRecord);

      OProfiler.getInstance().stopChrono("OMVRBTreePersistent.toStream", timer);
    }
  }

  public void signalTreeChanged(final OMVRBTree<K, V> iTree) {
    record.setDirty();
  }

  public void signalNodeChanged(final OMVRBTreeEntry<K, V> iNode) {
    recordsToCommit.add((OMVRBTreeEntryPersistent<K, V>) iNode);
  }

  @Override
  public int hashCode() {
    final ORID rid = record.getIdentity();
    return rid == null ? 0 : rid.hashCode();
  }

  public ORecordBytes getRecord() {
    return record;
  }

  protected void adjustPageSize() {
  }

  public V get(final Object iKey, final String iFetchPlan) {
    fetchPlan = iFetchPlan;
    return get(iKey);
  }

  public String getClusterName() {
    return clusterName;
  }

  public String getFetchPlan() {
    return fetchPlan;
  }

  public void setFetchPlan(String fetchPlan) {
    this.fetchPlan = fetchPlan;
  }

  public int getOptimizeThreshold() {
    return optimizeThreshold;
  }

  public void setOptimizeThreshold(int optimizeThreshold) {
    this.optimizeThreshold = optimizeThreshold;
  }

  public int getEntryPointSize() {
    return entryPointsSize;
  }

  public void setEntryPointSize(int entryPointSize) {
    this.entryPointsSize = entryPointSize;
  }

  @Override
  public String toString() {

    final StringBuilder buffer = new StringBuilder();
    buffer.append("size=");

    buffer.append(size);

    if (size > 0) {
      final int currPageIndex = pageIndex;
      buffer.append(" ");
      buffer.append(getFirstEntry().getFirstKey());
      buffer.append("-");
      buffer.append(getLastEntry().getLastKey());
      pageIndex = currPageIndex;
    }

    return buffer.toString();
  }

  private V internalPut(final K key, final V value) {
    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();
    }

    final V previous = super.put(key, value);

    if (optimizeThreshold > -1 && insertionCounter > optimizeThreshold) {
      insertionCounter = 0;
      optimize(false);
    } else
      ++insertionCounter;

    return previous;
  }

  public Object getKeySerializer() {
    return keySerializer;
  }

  public Object getValueSerializer() {
    return valueSerializer;
  }

  /**
   * Returns the best entry point to start the search.
   */
  @SuppressWarnings("unchecked")
  @Override
  protected OMVRBTreeEntry<K, V> getBestEntryPoint(final Object iKey) {
    final Comparable<? super K> key = (Comparable<? super K>) iKey;

    OMVRBTreeEntryPersistent<K, V> bestNode = null;
    int entryPointSize = entryPoints.size();

    if (entryPointSize == 0)
      // TREE EMPTY: RETURN ROOT
      return root;

    // 2^ CHANCE - TRY TO SEE IF LAST ENTRYPOINT IS GOOD: THIS IS VERY COMMON CASE ON INSERTION WITH AN INCREMENTING KEY
    OMVRBTreeEntryPersistent<K, V> e = entryPoints.get(entryPointSize - 1);
    int cmp = key.compareTo(e.getFirstKey());
    if (cmp <= 0)
      return e;

    // SEARCH THE CLOSEST KEY
    if (entryPointSize < OMVRBTreeEntry.BINARY_SEARCH_THRESHOLD) {
      // LINEAR SEARCH
      for (int i = 0; i < entryPointSize; ++i) {
        e = entryPoints.get(i);

        if (e.serializedKeys == null) {
          // CLEAN WRONG ENTRY (WHY THEY ARE WRONG?)
          OLogManager.instance().error(this, "Found wrong entrypoint in position %d", i);
          entryPoints.remove(i);
          --i;
          continue;
        }

        cmp = key.compareTo(e.getFirstKey());
        if (cmp < 0) {
          // RETURN THE PREVIOUS ONE OF CURRENT IF IT'S NULL
          return bestNode != null ? bestNode : e;
        } else if (cmp >= 0 && key.compareTo(e.getLastKey()) <= 0)
          // PERFECT MATCH, VERY LUCKY: RETURN THE CURRENT = 0 READS
          return e;

        // SET THE CURRENT AS BEST NODE
        bestNode = e;
      }
    } else {
      // BINARY SEARCH
      int low = 0;
      int high = entryPointSize - 1;
      int mid = 0;

      while (low <= high) {
        mid = (low + high) >>> 1;
        e = entryPoints.get(mid);

        if (e.serializedKeys == null) {
          // CLEAN WRONG ENTRY (WHY THEY ARE WRONG?)
          OLogManager.instance().error(this, "Found wrong entrypoint in position %d", mid);
          entryPoints.remove(mid);
          low = 0;
          entryPointSize = entryPoints.size();
          high = entryPointSize - 1;
          continue;
        }

        cmp = key.compareTo(e.getFirstKey());

        if (cmp >= 0 && key.compareTo(e.getLastKey()) <= 0)
          // PERFECT MATCH, VERY LUCKY: RETURN THE CURRENT = 0 READS
          return e;

        if (low == high)
          break;

        if (cmp > 0)
          low = mid + 1;
        else
          high = mid;

        // SET THE CURRENT AS BEST NODE
        bestNode = e;
      }

      if (mid > 0 && key.compareTo(bestNode.getFirstKey()) < 0)
        // GET THE PREVIOUS ONE
        bestNode = entryPoints.get(mid - 1);
    }

    // RETURN THE LATEST ONE
    return bestNode;
  }

  /**
   * Remove an entry point from the list
   */
  void removeEntryPoint(final OMVRBTreeEntryPersistent<K, V> iEntry) {
    for (int i = 0; i < entryPoints.size(); ++i)
      if (entryPoints.get(i) == iEntry) {
        entryPoints.remove(i);
        break;
      }
  }

  synchronized void removeEntry(final ORID iEntryId) {
    // DELETE THE NODE FROM THE PENDING RECORDS TO COMMIT
    for (OMVRBTreeEntryPersistent<K, V> node : recordsToCommit) {
      if (node.record.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
  protected OMVRBTreeEntry<K, V> getFirstEntry() {
    if (entryPoints.size() > 0) {
      // FIND THE FIRST ELEMENT STARTING FROM THE FIRST NODE
      OMVRBTreeEntryPersistent<K, V> e = entryPoints.get(0);

      while (e.getLeft() != null) {
        e = (OMVRBTreeEntryPersistent<K, V>) e.getLeft();
      }
      return e;
    }

    return super.getFirstEntry();
  }

  @Override
  protected void setRoot(final OMVRBTreeEntry<K, V> iRoot) {
    if (iRoot == root)
      return;

    super.setRoot(iRoot);
    if (listener != null)
      listener.signalTreeChanged(this);
  }

  protected void config() {
    lastPageSize = OGlobalConfiguration.MVRBTREE_NODE_PAGE_SIZE.getValueAsInteger();
    pageLoadFactor = OGlobalConfiguration.MVRBTREE_LOAD_FACTOR.getValueAsFloat();
    optimizeEntryPointsFactor = OGlobalConfiguration.MVRBTREE_OPTIMIZE_ENTRYPOINTS_FACTOR.getValueAsFloat();
    optimizeThreshold = OGlobalConfiguration.MVRBTREE_OPTIMIZE_THRESHOLD.getValueAsInteger();
    entryPointsSize = OGlobalConfiguration.MVRBTREE_ENTRYPOINTS.getValueAsInteger();
  }

  protected void serializerFromStream(final OMemoryInputStream stream) throws IOException {
    keySerializer = OStreamSerializerFactory.get(stream.getAsString());
    valueSerializer = OStreamSerializerFactory.get(stream.getAsString());
  }

  @Override
  protected void rotateLeft(final OMVRBTreeEntry<K, V> p) {
    if (debug && p != null)
      System.out.printf("\nRotating to the left the node %s", ((OMVRBTreeEntryPersistent<K, V>) p).record.getIdentity());
    super.rotateLeft(p);
  }

  @Override
  protected void rotateRight(final OMVRBTreeEntry<K, V> p) {
    if (debug && p != null)
      System.out.printf("\nRotating to the right the node %s", ((OMVRBTreeEntryPersistent<K, V>) p).record.getIdentity());
    super.rotateRight(p);
  }

  protected ODatabaseRecord getDatabase() {
    final ODatabaseRecord database = ODatabaseRecordThreadLocal.INSTANCE.get();
    record.setDatabase(database);
    return database;
  }

  protected boolean isEntryPoint(final OMVRBTreeEntry<K, V> iEntry) {
    for (OMVRBTreeEntryPersistent<K, V> entryPoint : entryPoints) {
      if (entryPoint == iEntry) {
        // IT'S AN ENTRYPOINT
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of com.orientechnologies.orient.core.type.tree.OMVRBTreePersistent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.