/** * 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); } }
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(); } } }
@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; }
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); } }
/** * 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); } }
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); }
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); } }
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); } } }
/** * 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); }
/** * 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); } }
/** * 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; }
/** * 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); } }
/** * 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; }
/** * 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); } }
@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); }
/** * 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; }
/** * 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; }
/** * 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; }
@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; }
/** * 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); } } }
private void releaseExchange(final Exchange ex) { _persistit.releaseExchange(ex); }
@Override public JournalInfo getJournalInfo() { final JournalInfo info = new JournalInfo(); _persistit.getJournalManager().populateJournalInfo(info); return info; }
/** * 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); } } }
@Override public RecoveryInfo getRecoveryInfo() { final RecoveryInfo info = new RecoveryInfo(); _persistit.getRecoveryManager().populateRecoveryInfo(info); return info; }
/** * 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); } } }
/** * Indicates whether Persistit is currently in the initialized state. * * @return The state */ @Override public boolean isInitialized() { return _persistit.isInitialized(); }
@Override public String transactionReport(final int max) { return _persistit.transactionReport(max); }
/** * 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); }