예제 #1
0
 /**
  * Return a <code>BufferInfo</code> reflecting the state of a page containing the specified key.
  * The <code>volumeName</code> and <code>treeName</code> parameters specify a {@link Tree} in
  * which to seach for the key. The <code>level</code> parameter indicates whether the data page,
  * or one of the pages on the index path to that data page should be returned. Level 0 refers to
  * the data path, level 1 is the lowest index level, and level d-1 where d is the number of levels
  * in the the tree represents the three's root page.
  *
  * <p>Specify <code>treeName</code> as <code>null</code> to access the volume's directory tree.
  *
  * @param volumeName the name of the volume
  * @param treeName the name of the tree within the volume, or <code>null</code> for the directory
  *     tree
  * @param key a <code>KeyState</code> representing a key
  * @param level tree level: 0 for root, 1...d-1 for index pages of a tree having depth d.
  * @return a <code>BufferInfo</code> object reflecting the selected page, or <code>null</code> if
  *     the specified tree does not exist.
  * @throws RemoteException
  */
 @Override
 public BufferInfo getBufferInfo(
     final String volumeName, final String treeName, final KeyState key, final int level)
     throws RemoteException {
   try {
     Exchange exchange;
     final Volume volume = _persistit.getVolume(volumeName);
     if (volume == null) {
       return null;
     }
     if (treeName == null) {
       exchange = volume.getStructure().directoryExchange();
     } else {
       exchange = _persistit.getExchange(volume, treeName, false);
     }
     key.copyTo(exchange.getKey());
     final Buffer buffer = exchange.fetchBufferCopy(level);
     final BufferInfo info = new BufferInfo();
     buffer.populateInfo(info);
     return info;
   } catch (final TreeNotFoundException tnfe) {
     return null;
   } catch (final PersistitException pe) {
     throw new WrappedRemoteException(pe);
   }
 }
예제 #2
0
  void register(String hostName, final int rmiPort, final int serverPort) {

    try {
      final ManagementImpl impl = (ManagementImpl) _persistit.getManagement();
      if (hostName == null && rmiPort != -1) {
        try {
          if (hostName == null) {
            final InetAddress addr = InetAddress.getLocalHost();
            try {
              hostName = addr.getHostName() + ":" + rmiPort;
            } catch (final Exception e) {
              hostName = addr.getHostAddress() + ":" + rmiPort;
            }
          }
        } catch (final NumberFormatException nfe) {
        }
      }
      if (rmiPort != -1 && _localRegistryPort != rmiPort) {
        LocateRegistry.createRegistry(rmiPort);
        _localRegistryPort = rmiPort;
      }

      if (hostName != null && hostName.length() > 0) {
        final String name = "//" + hostName + "/PersistitManagementServer";
        UnicastRemoteObject.exportObject(impl, serverPort);
        Naming.rebind(name, impl);
        impl._registered = true;
        impl._registeredHostName = hostName;
        _persistit.getLogBase().rmiServerRegistered.log(hostName);
      }
    } catch (final Exception exception) {
      _persistit.getLogBase().rmiRegisterException.log(hostName, exception);
    }
  }
  /**
   * Test for bug https://bugs.launchpad.net/akiban-persistit/+bug/1045983
   *
   * Truncating a dynamically created volume results in corrupted journal If
   * you dynamically load a volume, truncate it (without adding any trees),
   * and then close it, the next time the database is initialized a fatal
   * exception is thrown:
   *
   * <code><pre>
   *
   * [JOURNAL_COPIER] WARNING Missing volume truncated referenced at journal address 364
   * [main] WARNING Missing volume truncated referenced at journal address 17,004 (6 similar occurrences in 0 seconds)
   * Exception in thread "main" com.persistit.exception.InvalidPageAddressException: Page 1 out of bounds [0-1]
   *  at com.persistit.VolumeStorageV2.readPage(VolumeStorageV2.java:426)
   *  at com.persistit.Buffer.load(Buffer.java:456)
   *  at com.persistit.BufferPool.get(BufferPool.java:780)
   *  at com.persistit.Tree.setRootPageAddress(Tree.java:203)
   *  at com.persistit.VolumeStructure.init(VolumeStructure.java:70)
   *  at com.persistit.VolumeStorageV2.open(VolumeStorageV2.java:217)
   *  at com.persistit.Volume.open(Volume.java:442)
   *  at com.persistit.Persistit.loadVolume(Persistit.java:1066)
   *  at Truncate.main(Truncate.java:30)
   *
   * @throws Exception
   *
   *             </pre></code>
   *
   *             This test is currently disabled pending a fix.
   *
   */
  @Test
  @Ignore
  public void truncateDynamicVolumes() throws Exception {

    VolumeSpecification volumeSpec;
    _persistit.close();

    final Persistit db = new Persistit(_config);

    for (int i = 0; i < 2; i++) {
      try {
        volumeSpec =
            new VolumeSpecification(
                DATA_PATH + "/truncated", null, 16384, 1, 1000, 1, true, false, false);
        final Volume volume = db.loadVolume(volumeSpec);
        volume.truncate();
        // the following may be omitted, and the problem still exhibited
        final Exchange dbex = db.getExchange("truncated", "greetings", true);
        dbex.getKey().append("ave");
        dbex.getValue().put("mundus");
        dbex.store();
        dbex.getKey().to(Key.BEFORE);
        while (dbex.next()) {
          System.out.println(dbex.getKey().reset().decode() + " " + dbex.getValue().get());
        }
        db.releaseExchange(dbex);
        // the preceding may be omitted, and the problem still exhibited
      } finally {
        db.close();
      }
    }
  }
예제 #4
0
  @Override
  public LogicalRecord[] getLogicalRecordArray(
      final String volumeName,
      final String treeName,
      final String keyFilterString,
      final KeyState fromKey,
      final Key.Direction direction,
      final int maxCount,
      final int maxValueBytes,
      final boolean decodeStrings)
      throws RemoteException {
    LogicalRecord[] records = new LogicalRecord[maxCount];
    int count = 0;
    final boolean forward = direction == Key.GT || direction == Key.GTEQ;
    Exchange exchange = null;
    try {
      if (treeName.equals(VolumeStructure.DIRECTORY_TREE_NAME)) {
        exchange = _persistit.getVolume(volumeName).getStructure().directoryExchange();
      } else {
        exchange = _persistit.getExchange(volumeName, treeName, false);
      }
      exchange.ignoreMVCCFetch(true);
      KeyFilter filter = null;
      if (keyFilterString != null && keyFilterString.length() > 0) {
        filter = new KeyFilter(keyFilterString);
      }
      fromKey.copyTo(exchange.getKey());
      for (; count < maxCount; count++) {
        if (!exchange.traverse(direction, filter, maxValueBytes)) {
          break;
        } else {
          final LogicalRecord record = new LogicalRecord();
          record._key = new KeyState(exchange.getKey());
          record._value = new ValueState(exchange.getValue(), maxValueBytes);

          if (decodeStrings) {
            record._keyString = _displayFilter.toKeyDisplayString(exchange);
            record._valueString = _displayFilter.toValueDisplayString(exchange);
          }

          if (forward) {
            records[count] = record;
          } else {
            records[maxCount - count - 1] = record;
          }
        }
      }
    } catch (final Exception e) {
      throw new WrappedRemoteException(e);
    } finally {
      exchange.ignoreMVCCFetch(false);
    }
    if (count < maxCount) {
      final LogicalRecord[] trimmed = new LogicalRecord[count];
      System.arraycopy(records, forward ? 0 : maxCount - count, trimmed, 0, count);
      records = trimmed;
    }
    return records;
  }
예제 #5
0
 private Exchange getExchange() throws PersistitException {
   try {
     final Volume volume = _persistit.getSystemVolume();
     return _persistit.getExchange(volume, CLASS_INDEX_TREE_NAME, true);
   } catch (final PersistitException pe) {
     throw new ConversionException(pe);
   }
 }
예제 #6
0
 /**
  * Attempts to flush and force all dirty data in Persistit by invoking {@link Persistit#flush} and
  * {@link Persistit#force}.
  *
  * @throws RemoteException
  */
 @Override
 public void flushAndForce() throws RemoteException {
   try {
     _persistit.flush();
     _persistit.force();
   } catch (final PersistitException e) {
     throw new WrappedRemoteException(e);
   }
 }
예제 #7
0
 void corrupt(final String error) throws CorruptVolumeException {
   Debug.$assert0.t(false);
   if (_exchange != null) {
     _persistit.getLogBase().corruptVolume.log(error + Util.NEW_LINE + _exchange.toStringDetail());
   } else {
     _persistit.getLogBase().corruptVolume.log(error);
   }
   throw new CorruptVolumeException(error);
 }
예제 #8
0
 public void setIoLogFile(final String path) throws RemoteException {
   try {
     if (path == null || path.isEmpty()) {
       _persistit.getIOMeter().setLogFile(null);
     } else {
       _persistit.getIOMeter().setLogFile(path);
     }
   } catch (final IOException e) {
     throw new WrappedRemoteException(e);
   }
 }
예제 #9
0
  void unregister() {
    if (_registered) {
      try {
        final ManagementImpl impl = (ManagementImpl) _persistit.getManagement();

        UnicastRemoteObject.unexportObject(impl, true);
        _registered = false;
        _persistit.getLogBase().rmiServerUnregister.log(_registeredHostName);
      } catch (final Exception exception) {
        _persistit.getLogBase().rmiUnregisterException.log(_registeredHostName, exception);
      }
    }
  }
예제 #10
0
  /**
   * Returns an array containing a <code>TreeInfo</code> element for each <code>Tree</code> in the
   * specified volume. If there is no volume with the specified name or if Persistit is not
   * initialized then this method returns an empty array.
   *
   * @param volumeName The name (or unique partial name) of the volume for which information is
   *     being requested.
   * @return The array
   */
  @Override
  public TreeInfo[] getTreeInfoArray(final String volumeName) throws RemoteException {
    if (volumeName == null) {
      return new TreeInfo[0];
    }
    final Volume volume = _persistit.getVolume(volumeName);
    if (volume == null) return new TreeInfo[0];

    try {
      final String[] treeNames = volume.getTreeNames();
      TreeInfo[] results = new TreeInfo[treeNames.length + 1];
      int count = 0;
      results[count++] = new TreeInfo(volume.getDirectoryTree());
      for (int index = 0; index < treeNames.length; index++) {
        final TreeInfo info = volume.getTreeInfo(treeNames[index]);
        if (info != null) {
          results[count++] = info;
        }
      }
      if (count < results.length) {
        final TreeInfo[] temp = new TreeInfo[count];
        System.arraycopy(results, 0, temp, 0, count);
        results = temp;
      }
      return results;
    } catch (final PersistitException pe) {
      throw new WrappedRemoteException(pe);
    }
  }
  @Test
  public void useOldVSpecInducesExpectedFailure() throws Exception {
    VolumeSpecification volumeSpec;
    final Configuration configuration = _persistit.getConfiguration();
    _persistit.close();
    int remainingJournalFiles = 0;
    configuration.setUseOldVSpec(true);

    for (int i = 5; --i >= 0; ) {
      final Persistit db = new Persistit(_config);
      try {
        volumeSpec =
            new VolumeSpecification(
                DATA_PATH + "/hwdemo" + i, null, 16384, 1, 1000, 1, true, false, false);
        db.loadVolume(volumeSpec);
        final Exchange dbex = db.getExchange("hwdemo" + i, "greetings", true);
        dbex.getKey().append("Hello");
        dbex.getValue().put("World");
        dbex.store();
        dbex.getKey().to(Key.BEFORE);
        db.releaseExchange(dbex);
      } finally {
        if (i == 0) {
          db.copyBackPages();
        }
        remainingJournalFiles = db.getJournalManager().getJournalFileCount();
        db.close();
      }
    }
    assertTrue("Should be only one remaining journal file", remainingJournalFiles > 1);
  }
예제 #12
0
 /**
  * Attempts to close Persistit by invoking {@link Persistit#close}.
  *
  * @return <code>true</code> if the attempt to close Persistit was successful; otherwise <code>
  *     false</code>
  * @throws RemoteException
  */
 @Override
 public boolean close() throws RemoteException {
   try {
     _persistit.close();
     return true;
   } catch (final PersistitException e) {
     throw new WrappedRemoteException(e);
   }
 }
예제 #13
0
 /**
  * Returns an array containing a <code>VolumeInfo</code> element for each open volume. If
  * Persistit is not initialized then this method returns an empty array.
  *
  * @return The array
  */
 @Override
 public VolumeInfo[] getVolumeInfoArray() {
   final List<Volume> volumes = _persistit.getVolumes();
   final VolumeInfo[] result = new VolumeInfo[volumes.size()];
   for (int index = 0; index < volumes.size(); index++) {
     result[index] = new VolumeInfo(volumes.get(index));
   }
   Arrays.sort(result);
   return result;
 }
예제 #14
0
 /**
  * Returns an array containing a <code>RecordInfo</code> element for each record in the page
  * specified by <code>volumeName</code> and <code>pageAddress</code>. If Persistit is not
  * initialized, or if there is no unique <code>Volume</code> for the specified <code>volumeName
  * </code>, or if there is no page associated with the specified <code>pageAddress</code> or if
  * there is any transient condition that causes the attempt to retrieve records to fail, then this
  * method returns an empty array.
  *
  * @param volumeName
  * @param pageAddress
  * @return the array
  */
 @Override
 public RecordInfo[] getRecordInfoArray(final String volumeName, final long pageAddress)
     throws RemoteException {
   final Volume volume = _persistit.getVolume(volumeName);
   if (volume == null) return new RecordInfo[0];
   try {
     final Buffer buffer = volume.getPool().getBufferCopy(volume, pageAddress);
     return buffer.getRecords();
   } catch (final PersistitException pe) {
     throw new WrappedRemoteException(pe);
   }
 }
예제 #15
0
 /**
  * Populates a supplied array of {@link BufferInfo} objects to reflect the current states of
  * selected buffers from the <code>BufferPool</code> for the specified <code>bufferSize</code>.
  * The selection criteria include the <code>traversalType</code>, <code>includeMask</code> and
  * <code>excludeMask</code>. See {@link #getBufferInfoArray} for a similar method that simply
  * returns a fresh array on each invocation. This method is available for management applications
  * that need to perform frequently refreshes.
  *
  * <p>This method returns the actual number of buffers selected by the supplied criteria. This
  * number may be larger than the size of the supplied array; in this case, information about the
  * first N buffers in the set is returned in the array, where N is the size of the array. An
  * application can use the {@link BufferPoolInfo#getBufferCount} method to determine the maximum
  * number of <code>BufferInfo</code> objects that could be populated.
  *
  * <p>The <code>traversalType</code> must be one of the following:
  *
  * <dl>
  *   <dt>0
  *   <dd>all buffers in the buffer pool, in order by <code>poolIndex</code>.
  *   <dt>1
  *   <dd>buffers on the least-recently-used queue, ordered from least- to most-recently used.
  *   <dt>2
  *   <dd>Buffers on the invalid buffer queue. These buffers will be consumed first whenever a new
  *       page is copied into the pool.
  * </dl>
  *
  * <p>The <code>includeMask</code> and <code>excludeMask</code> are applied to each buffer's state
  * to determine whether that buffer should be included in the set returned by this method. If
  * <code>includeMask</code> is <code>null</code> then all buffers are included. Otherwise, only
  * those buffers whose state is selected by <code>includeMask</code> and is not selected by <code>
  * excludeMask</code> are included. Mask values are Strings in which each character denotes an
  * attribute of a <code>Buffer</code> to be included or excluded from the selection. These
  * characters are as follows:
  *
  * <dl>
  *   <dt>v
  *   <dd>Buffer must be VALID
  *   <dt>d
  *   <dd>Buffer must be DIRTY
  *   <dt>w
  *   <dd>Buffer must have a WRITER claim
  *   <dt>r
  *   <dd>Buffer must have a READER claim
  *   <dt>p
  *   <dd>Buffer must be PERMANENT. The head page for each {@link Volume} occupies a PERMANENT
  *       buffer.
  *       <p>If Persistit is not initialized then this method returns an empty array.
  *
  * @param bufferSize the buffer size of interest
  * @param traversalType the traversal type, described above
  * @param includeMask the buffer selection include mask, described above
  * @param excludeMask the buffer selection exclude mask, described above
  * @return the array
  */
 @Override
 public int populateBufferInfoArray(
     final BufferInfo[] results,
     final int bufferSize,
     final int traversalType,
     final String includeMask,
     final String excludeMask) {
   final BufferPool pool = _persistit.getBufferPool(bufferSize);
   if (pool == null) return -1;
   final int count =
       pool.populateInfo(results, traversalType, makeStatus(includeMask), makeStatus(excludeMask));
   return count;
 }
예제 #16
0
 /**
  * Returns a <code>BufferInfo</code> reflecting the status of the buffer containing the page
  * specified by the supplied <code>volumeName</code> and <code>pageAddress</code>. If Persisit is
  * not initialized or of the attempt the find the specified page fails, this method returns <code>
  * null</code>
  *
  * @param volumeName the name of the volume
  * @param pageAddress the page address
  * @return the BufferInfo for the buffer containing the designated page, of <code>null</code> if
  *     there is none.
  */
 @Override
 public BufferInfo getBufferInfo(final String volumeName, final long pageAddress)
     throws RemoteException {
   final Volume volume = _persistit.getVolume(volumeName);
   if (volume == null) return null;
   try {
     final Buffer buffer = volume.getPool().getBufferCopy(volume, pageAddress);
     final BufferInfo info = new BufferInfo();
     buffer.populateInfo(info);
     return info;
   } catch (final PersistitException pe) {
     throw new WrappedRemoteException(pe);
   }
 }
예제 #17
0
  @Override
  public LogicalRecordCount getLogicalRecordCount(
      final String volumeName,
      final String treeName,
      final String keyFilterString,
      final KeyState fromKey,
      final Key.Direction direction,
      final int maxCount)
      throws RemoteException {
    int count = 0;
    Exchange exchange = null;
    KeyState endKeyState = null;

    try {
      exchange = _persistit.getExchange(volumeName, treeName, false);
      exchange.getAuxiliaryKey2().clear();
      KeyFilter filter = null;
      if (keyFilterString != null && keyFilterString.length() > 0) {
        filter = new KeyFilter(keyFilterString);
      }
      fromKey.copyTo(exchange.getKey());
      for (; count < maxCount; count++) {
        if (!exchange.traverse(direction, filter, 0)) {
          break;
        } else {
          exchange.getKey().copyTo(exchange.getAuxiliaryKey2());
        }
      }
      endKeyState = new KeyState(exchange.getAuxiliaryKey2());
    } catch (final Exception pe) {
      throw new WrappedRemoteException(pe);
    } finally {
      if (exchange != null) _persistit.releaseExchange(exchange);
    }
    return new LogicalRecordCount(endKeyState, count);
  }
예제 #18
0
 /**
  * Returns a <code>TreeInfo</code> for a specified <code>Volume</code> and </code>Tree</code>. If
  * Persisit is not initialized, or if no no volume or tree with corresponding names is found, or
  * if there is a transient error in acquiring the information, this method returns <code>null
  * </code>.
  *
  * @param volumeName The name (or partial name) of the volume
  * @param treeName The name of the tree
  * @return the <code>TreeInfo</code>
  */
 @Override
 public TreeInfo getTreeInfo(final String volumeName, final String treeName)
     throws RemoteException {
   final Volume volume = _persistit.getVolume(volumeName);
   if (volume == null) return null;
   try {
     Tree tree = null;
     if (VolumeStructure.DIRECTORY_TREE_NAME.equals(treeName)) {
       tree = volume.getDirectoryTree();
     } else {
       tree = volume.getTree(treeName, false);
     }
     if (tree != null) return new TreeInfo(tree);
   } catch (final PersistitException pe) {
     throw new WrappedRemoteException(pe);
   }
   return null;
 }
예제 #19
0
  /**
   * Returns an array containing a <code>BufferPoolInfo</code> element for each buffer pool. If
   * Persistit is not initialized then this method returns an empty array.
   *
   * @return The array
   */
  @Override
  public BufferPoolInfo[] getBufferPoolInfoArray() {
    final HashMap<Integer, BufferPool> bufferPoolTable = _persistit.getBufferPoolHashMap();
    final int size = bufferPoolTable.size();
    final BufferPoolInfo[] result = new BufferPoolInfo[size];
    int index = 0;
    for (int bufferSize = Buffer.MIN_BUFFER_SIZE;
        bufferSize <= Buffer.MAX_BUFFER_SIZE;
        bufferSize *= 2) {
      final BufferPool pool = bufferPoolTable.get(new Integer(bufferSize));

      if (pool != null && index < size) {
        final BufferPoolInfo info = new BufferPoolInfo();
        pool.populateBufferPoolInfo(info);
        result[index++] = info;
      }
    }
    return result;
  }
예제 #20
0
  /**
   * Returns an array of {@link BufferInfo} objects reflecting the states of selected buffers from
   * the <code>BufferPool</code> for the specified <code>bufferSize</code>. The selection criteria
   * include the <code>traversalType</code>, <code>includeMask</code> and <code>excludeMask</code>.
   * See {@link #populateBufferInfoArray} for a similar method that reuses a previously obtained
   * result array.
   *
   * <p>The <code>traversalType</code> must be one of the following:
   *
   * <dl>
   *   <dt>0
   *   <dd>all buffers in the buffer pool, in order by <code>poolIndex</code>.
   *   <dt>1
   *   <dd>buffers on the least-recently-used queue, ordered from least- to most-recently used.
   *   <dt>2
   *   <dd>Buffers on the invalid buffer queue. These buffers will be consumed first whenever a new
   *       page is copied into the pool.
   * </dl>
   *
   * <p>The <code>includeMask</code> and <code>excludeMask</code> are applied to each buffer's state
   * to determine whether that buffer should be included in the set returned by this method. If
   * <code>includeMask</code> is <code>null</code> then all buffers are included. Otherwise, only
   * those buffers whose state is selected by <code>includeMask</code> and is not selected by <code>
   * excludeMask</code> are included. Mask values are Strings in which each character denotes an
   * attribute of a <code>Buffer</code> to be included or excluded from the selection. These
   * characters are as follows:
   *
   * <dl>
   *   <dt>v
   *   <dd>Buffer must be VALID
   *   <dt>d
   *   <dd>Buffer must be DIRTY
   *   <dt>w
   *   <dd>Buffer must have a WRITER claim
   *   <dt>r
   *   <dd>Buffer must have a READER claim
   *   <dt>p
   *   <dd>Buffer must be PERMANENT. The head page for each {@link Volume} occupies a PERMANENT
   *       buffer.
   *       <p>If Persistit is not initialized then this method returns an empty array.
   *
   * @param bufferSize the buffer size of interest
   * @param traversalType the traversal type, described above
   * @param includeMask the buffer selection include mask, described above
   * @param excludeMask the buffer selection exclude mask, described above
   * @return the array
   */
  @Override
  public BufferInfo[] getBufferInfoArray(
      final int bufferSize,
      final int traversalType,
      final String includeMask,
      final String excludeMask) {
    final BufferPool pool = _persistit.getBufferPool(bufferSize);
    if (pool == null) return new BufferInfo[0];
    BufferInfo[] results = new BufferInfo[pool.getBufferCount()];
    final int count =
        pool.populateInfo(results, traversalType, makeStatus(includeMask), makeStatus(excludeMask));

    if (count < results.length) {
      final BufferInfo[] temp = new BufferInfo[count];
      System.arraycopy(results, 0, temp, 0, count);
      results = temp;
    }
    return results;
  }
예제 #21
0
 @Override
 public TransactionInfo getTransactionInfo() {
   final TransactionInfo info = _transactionInfoCache;
   if (System.currentTimeMillis() - info.getAcquisitionTime() > MAX_STALE) {
     final List<Transaction> transactions = new ArrayList<Transaction>();
     synchronized (info) {
       info.commitCount = 0;
       info.rollbackCount = 0;
       info.rollbackSinceCommitCount = 0;
       _persistit.populateTransactionList(transactions);
       for (final Transaction txn : transactions) {
         info.commitCount += txn.getCommittedTransactionCount();
         info.rollbackCount += txn.getRolledBackTransactionCount();
         info.rollbackSinceCommitCount += txn.getRolledBackSinceLastCommitCount();
       }
       info.updateAcquisitonTime();
     }
   }
   return info;
 }
예제 #22
0
  /**
   * Look up and return the ClassInfo for an integer handle. This is used when decoding an <code>
   * Object</code> from a <code>com.persistit.Value</code> to associate the encoded integer handle
   * value with the corresponding class.
   *
   * @param handle The handle
   * @return The associated ClassInfo, or <i>null</i> if there is none.
   */
  public ClassInfo lookupByHandle(final int handle) {
    final AtomicReferenceArray<ClassInfoEntry> hashTable = _hashTable;
    ClassInfoEntry cie = hashTable.get(handle % hashTable.length());
    while (cie != null) {
      if (cie._classInfo.getHandle() == handle) return cie._classInfo;
      cie = cie._next;
    }

    _cacheMisses.incrementAndGet();

    synchronized (this) {
      _sessionId.assign();
      Exchange ex = null;
      try {
        ex = getExchange();
        final Transaction txn = ex.getTransaction();
        txn.begin();
        try {
          ex.clear().append(BY_HANDLE).append(handle).fetch();
          txn.commit();
        } catch (final Exception e) {
          _persistit.getLogBase().exception.log(e);
          throw new ConversionException(e);
        } finally {
          txn.end();
        }
        final Value value = ex.getValue();
        if (value.isDefined()) {
          value.setStreamMode(true);
          final int storedId = value.getInt();
          final String storedName = value.getString();
          final long storedSuid = value.getLong();
          if (storedId != handle) {
            throw new IllegalStateException(
                "ClassInfo stored for handle=" + handle + " has invalid stored handle=" + storedId);
          }
          final Class<?> cl =
              Class.forName(storedName, false, Thread.currentThread().getContextClassLoader());

          long suid = 0;
          final ObjectStreamClass osc = ObjectStreamClass.lookupAny(cl);
          if (osc != null) suid = osc.getSerialVersionUID();
          if (storedSuid != suid) {
            throw new ConversionException(
                "Class "
                    + cl.getName()
                    + " persistent SUID="
                    + storedSuid
                    + " does not match current class SUID="
                    + suid);
          }
          final ClassInfo ci = new ClassInfo(cl, suid, handle, osc);
          hashClassInfo(ci);
          return ci;
        } else {
          final ClassInfo ci = new ClassInfo(null, 0, handle, null);
          hashClassInfo(ci);
          return ci;
        }
      } catch (final ClassNotFoundException cnfe) {
        throw new ConversionException(cnfe);
      } catch (final PersistitException pe) {
        throw new ConversionException(pe);
      } finally {
        if (ex != null) releaseExchange(ex);
      }
    }
  }
예제 #23
0
 private void releaseExchange(final Exchange ex) {
   _persistit.releaseExchange(ex);
 }
예제 #24
0
 @Override
 public JournalInfo getJournalInfo() {
   final JournalInfo info = new JournalInfo();
   _persistit.getJournalManager().populateJournalInfo(info);
   return info;
 }
예제 #25
0
  /**
   * Look up and return the ClassInfo for a class. This is used when encoding an <code>Object</code>
   * into a <code>com.persistit.Value</code>.
   *
   * @param clazz The <code>Class</code>
   * @return The ClassInfo for the specified Class.
   */
  public ClassInfo lookupByClass(final Class<?> clazz) {
    final AtomicReferenceArray<ClassInfoEntry> hashTable = _hashTable;

    ObjectStreamClass osc = null;
    long suid = 0;

    final int nh = clazz.getName().hashCode() & 0x7FFFFFFF;
    ClassInfoEntry cie = hashTable.get(nh % hashTable.length());

    while (cie != null) {
      if (clazz.equals(cie._classInfo.getDescribedClass())) {
        return cie._classInfo;
      }
      if (cie._classInfo.getDescribedClass() != null
          && cie._classInfo.getName().equals(clazz.getName())) {
        if (osc == null) {
          osc = ObjectStreamClass.lookupAny(clazz);
          if (osc != null) {
            suid = osc.getSerialVersionUID();
          }
        }
        if (suid == cie._classInfo.getSUID()) {
          return cie._classInfo;
        }
      }
      cie = cie._next;
    }

    if (osc == null) {
      osc = ObjectStreamClass.lookupAny(clazz);
    }
    if (osc != null) {
      suid = osc.getSerialVersionUID();
    }

    _cacheMisses.incrementAndGet();

    /**
     * To update the tree, this class uses a unique SessionId and results in using a unique
     * Transaction context unrelated to the application context. Therefore if an application does
     * this:
     *
     * <pre>
     * <code>
     * txn.begin();
     * value.put(new SomeClass());
     * txn.rollback();
     * txn.end();
     * </code>
     * </pre>
     *
     * the class SomeClass will be registered even though the enclosing transaction rolled back.
     * This is important because other concurrent threads may have started using the handle for
     * SomeClass. Therefore this class ensures that a non-nested transaction to insert the new
     * ClassInfo into the system volume has committed before adding the handle to the hash table.
     */
    synchronized (this) {
      final SessionId saveSessionId = _persistit.getSessionId();
      Exchange ex = null;
      try {
        _persistit.setSessionId(_sessionId);
        ex = getExchange();
        final Transaction txn = ex.getTransaction();
        final ClassInfo ci;
        final int handle;
        txn.begin();
        ex.clear().append(BY_NAME).append(clazz.getName()).append(suid).fetch();
        final Value value = ex.getValue();
        try {
          if (value.isDefined()) {
            value.setStreamMode(true);

            handle = value.getInt();
            final String storedName = value.getString();
            final long storedSuid = value.getLong();

            if (storedSuid != suid || !clazz.getName().equals(storedName)) {
              throw new ConversionException(
                  "Class "
                      + clazz.getName()
                      + " persistent SUID="
                      + storedSuid
                      + " does not match current class SUID="
                      + suid);
            }
            ci = new ClassInfo(clazz, suid, handle, osc);
          } else {
            //
            // Store a new ClassInfo record
            //
            ex.clear().append(NEXT_ID).fetch();
            handle = Math.max(_testIdFloor, value.isDefined() ? value.getInt() : HANDLE_BASE) + 1;
            value.clear().put(handle);
            ex.store();

            value.clear();
            value.setStreamMode(true);
            value.put(handle);
            value.put(clazz.getName());
            value.put(suid);

            ex.clear().append(BY_NAME).append(clazz.getName()).append(suid).store();

            ex.clear().append(BY_HANDLE).append(handle).store();

            ci = new ClassInfo(clazz, suid, handle, osc);
          }
          txn.commit();
          hashClassInfo(ci);
          return ci;
        } finally {
          txn.end();
        }
      } catch (final PersistitException pe) {
        throw new ConversionException(pe);
      } finally {
        if (ex != null) {
          releaseExchange(ex);
        }
        _persistit.setSessionId(saveSessionId);
      }
    }
  }
예제 #26
0
 @Override
 public RecoveryInfo getRecoveryInfo() {
   final RecoveryInfo info = new RecoveryInfo();
   _persistit.getRecoveryManager().populateRecoveryInfo(info);
   return info;
 }
예제 #27
0
  /**
   * Create a new LONG_RECORD chain and stores the supplied byte array in the pages of this chain.
   * The chain is written in right-to-left order so that any page having a right pointer points to a
   * valid successor.
   *
   * <p>Each page is written with its own timestamp (necessary to satisfy write order invariant).
   * Therefore a checkpoint could occur during the middle, after some pages have been assigned a
   * timestamp and before others. This means that a crash recovery could recover the tail of a
   * chain, but not its head. This does not cause corruption, but does cause permanent loss of the
   * pages that were recovered at the right end but never linked to a data page. Current remedy:
   * save/reload data. Such dangling chains can be detected by IntegrityCheck and a future remedy
   * would be for IntegrityCheck to move them back to the garbage chain.
   *
   * <p>If this method is called in the context of a transaction, it writes each page immediately to
   * the journal. This allows recovery to rebuild the long record for a recovered transaction that
   * committed after the keystone checkpoint.
   *
   * @param value The value. Must be in "long record mode"
   * @param inTxn indicates whether this operation is within the context of a transaction.
   * @throws PersistitException
   */
  long storeLongRecord(final Value value, final boolean inTxn) throws PersistitException {
    value.changeLongRecordMode(true);

    // Calculate how many LONG_RECORD pages we will need.
    //
    boolean completed = false;
    final int longSize = value.getLongSize();
    final byte[] longBytes = value.getLongBytes();
    final byte[] rawBytes = value.getEncodedBytes();
    final int maxSegmentSize = _volume.getPool().getBufferSize() - HEADER_SIZE;

    Debug.$assert0.t(value.isLongRecordMode());
    Debug.$assert0.t(rawBytes.length == LONGREC_SIZE);

    System.arraycopy(longBytes, 0, rawBytes, LONGREC_PREFIX_OFFSET, LONGREC_PREFIX_SIZE);

    long looseChain = 0;

    sequence(LONG_RECORD_ALLOCATE_A);

    Buffer buffer = null;
    int offset =
        LONGREC_PREFIX_SIZE
            + (((longSize - LONGREC_PREFIX_SIZE - 1) / maxSegmentSize) * maxSegmentSize);
    try {
      for (; ; ) {
        while (offset >= LONGREC_PREFIX_SIZE) {
          buffer = _volume.getStructure().allocPage();
          final long timestamp = _persistit.getTimestampAllocator().updateTimestamp();
          buffer.writePageOnCheckpoint(timestamp);
          buffer.init(PAGE_TYPE_LONG_RECORD);

          int segmentSize = longSize - offset;
          if (segmentSize > maxSegmentSize) segmentSize = maxSegmentSize;

          Debug.$assert0.t(
              segmentSize >= 0
                  && offset >= 0
                  && offset + segmentSize <= longBytes.length
                  && HEADER_SIZE + segmentSize <= buffer.getBytes().length);

          System.arraycopy(longBytes, offset, buffer.getBytes(), HEADER_SIZE, segmentSize);

          final int end = HEADER_SIZE + segmentSize;
          if (end < buffer.getBufferSize()) {
            buffer.clearBytes(end, buffer.getBufferSize());
          }
          buffer.setRightSibling(looseChain);
          looseChain = buffer.getPageAddress();
          buffer.setDirtyAtTimestamp(timestamp);
          if (inTxn) {
            buffer.writePage();
          }
          buffer.releaseTouched();
          offset -= maxSegmentSize;
          buffer = null;
        }

        final long page = looseChain;
        looseChain = 0;
        Buffer.writeLongRecordDescriptor(value.getEncodedBytes(), longSize, page);
        completed = true;

        return page;
      }
    } finally {
      if (buffer != null) buffer.releaseTouched();
      if (looseChain != 0) {
        _volume.getStructure().deallocateGarbageChain(looseChain, 0);
      }
      if (!completed) {
        value.changeLongRecordMode(false);
      }
    }
  }
예제 #28
0
 /**
  * Indicates whether Persistit is currently in the initialized state.
  *
  * @return The state
  */
 @Override
 public boolean isInitialized() {
   return _persistit.isInitialized();
 }
예제 #29
0
 @Override
 public String transactionReport(final int max) {
   return _persistit.transactionReport(max);
 }
예제 #30
0
 /**
  * Returns the <code>VolumeInfo</code> for the volume specified by the supplied <code>volumeName
  * </code>. If Persisit is not initialized or there is no unique volume corresponding with the
  * supplied name, then this method returns <code>null</code>.
  *
  * @param volumeName
  * @return the <code>VolumeInfo</code>
  */
 @Override
 public VolumeInfo getVolumeInfo(final String volumeName) {
   final Volume volume = _persistit.getVolume(volumeName);
   if (volume == null) return null;
   else return new VolumeInfo(volume);
 }