}
if (treeClaimRequired && !treeClaimAcquired) {
if (!_treeHolder.claim(treeWriterClaimRequired)) {
Debug.$assert0.t(false);
throw new InUseException("Thread " + Thread.currentThread().getName() + " failed to get "
+ (treeWriterClaimRequired ? "writer" : "reader") + " claim on " + _tree);
}
treeClaimAcquired = true;
}
checkLevelCache();
final List<PrunedVersion> prunedVersions = new ArrayList<PrunedVersion>();
try {
if (level >= _cacheDepth) {
Debug.$assert0.t(level == _cacheDepth);
//
// Need to lock the tree because we may need to change
// its root.
//
if (!treeClaimAcquired || !_treeHolder.upgradeClaim()) {
treeClaimRequired = true;
treeWriterClaimRequired = true;
throw RetryException.SINGLE;
}
Debug.$assert0.t(valueToStore.getPointerValue() > 0);
insertIndexLevel(key, valueToStore);
break mainRetryLoop;
}
Debug.$assert0.t(buffer == null);
int foundAt = -1;
final LevelCache lc = _levelCache[level];
buffer = quicklyReclaimBuffer(lc, true);
if (buffer != null) {
//
// Start by assuming cached value is okay
//
foundAt = findKey(buffer, key, lc);
if (buffer.isBeforeLeftEdge(foundAt) || buffer.isAfterRightEdge(foundAt)) {
buffer.release();
buffer = null;
}
}
if (buffer == null) {
foundAt = searchTree(key, level, true);
buffer = lc._buffer;
}
Debug.$assert0.t(buffer != null && (buffer.getStatus() & SharedResource.WRITER_MASK) != 0
&& (buffer.getStatus() & SharedResource.CLAIMED_MASK) != 0);
boolean didPrune = false;
boolean splitRequired = false;
if (buffer.isDataPage()) {
keyExisted = (foundAt & EXACT_MASK) != 0;
if (keyExisted) {
oldLongRecordPointer = buffer.fetchLongRecordPointer(foundAt);
}
if (doFetch || doMVCC) {
buffer.fetch(foundAt, spareValue);
if (oldLongRecordPointer != 0) {
if (isLongMVV(spareValue)) {
oldLongRecordPointerMVV = oldLongRecordPointer;
fetchFixupForLongRecords(spareValue, Integer.MAX_VALUE);
}
}
/*
* If it was a long MVV we saved it into the
* variable above. Otherwise it is a primordial
* value that we can't get rid of.
*/
oldLongRecordPointer = 0;
if (doFetch) {
spareValue.copyTo(_spareValue);
fetchFromValueInternal(_spareValue, Integer.MAX_VALUE, buffer);
}
}
/*
* If the Tree is private to an active transaction, and
* if this is a virgin value, then we can store it
* primordially because if the transaction rolls back,
* the entire Tree will be removed.
*/
if (doMVCC && (_spareValue.isDefined() || !_tree.isTransactionPrivate(true))) {
valueToStore = spareValue;
final int valueSize = value.getEncodedSize();
int retries = VERSIONS_OUT_OF_ORDER_RETRY_COUNT;
for (;;) {
try {
/*
* If key didn't exist the value is truly
* non-existent and not just undefined/zero
* length
*/
byte[] spareBytes = spareValue.getEncodedBytes();
int spareSize;
if (keyExisted) {
spareSize = MVV.prune(spareBytes, 0, spareValue.getEncodedSize(),
_persistit.getTransactionIndex(), false, prunedVersions);
spareValue.setEncodedSize(spareSize);
} else {
spareSize = -1;
}
final TransactionStatus tStatus = _transaction.getTransactionStatus();
final int tStep = _transaction.getStep();
if ((options & StoreOptions.ONLY_IF_VISIBLE) != 0) {
/*
* Could be single visit of all versions
* but current TI would still require
* calls to both commitStatus() and
* wwDependency()
*/
_mvvVisitor.initInternal(tStatus, tStep, MvvVisitor.Usage.FETCH);
MVV.visitAllVersions(_mvvVisitor, spareBytes, 0, spareSize);
final int offset = _mvvVisitor.getOffset();
if (!_mvvVisitor.foundVersion()
|| (_mvvVisitor.getLength() > 0 && spareBytes[offset] == MVV.TYPE_ANTIVALUE)) {
// Completely done, nothing to store
keyExisted = false;
break mainRetryLoop;
}
}
// Visit all versions for ww detection
_mvvVisitor.initInternal(tStatus, tStep, MvvVisitor.Usage.STORE);
MVV.visitAllVersions(_mvvVisitor, spareBytes, 0, spareSize);
final int mvvSize = MVV.estimateRequiredLength(spareBytes, spareSize, valueSize);
spareValue.ensureFit(mvvSize);
spareBytes = spareValue.getEncodedBytes();
final long versionHandle = TransactionIndex.tss2vh(
_transaction.getStartTimestamp(), tStep);
int storedLength = MVV.storeVersion(spareBytes, 0, spareSize, spareBytes.length,
versionHandle, value.getEncodedBytes(), 0, valueSize);
incrementMVVCount = (storedLength & MVV.STORE_EXISTED_MASK) == 0;
storedLength &= MVV.STORE_LENGTH_MASK;
spareValue.setEncodedSize(storedLength);
Debug.$assert0.t(MVV.verify(_persistit.getTransactionIndex(), spareBytes, 0,
storedLength));
if (spareValue.getEncodedSize() > maxSimpleValueSize) {
newLongRecordPointerMVV = getLongRecordHelper().storeLongRecord(spareValue,
_transaction.isActive());
}
break;
} catch (final VersionsOutOfOrderException e) {
if (--retries <= 0) {
throw e;
}
}
}
}
}
Debug.$assert0.t(valueToStore.getEncodedSize() <= maxSimpleValueSize);
_rawValueWriter.init(valueToStore);
splitRequired = putLevel(lc, key, _rawValueWriter, buffer, foundAt, treeClaimAcquired);
Debug.$assert0.t((buffer.getStatus() & SharedResource.WRITER_MASK) != 0
&& (buffer.getStatus() & SharedResource.CLAIMED_MASK) != 0);
//
// If a split is required but treeClaimAcquired is false
// then putLevel did not change anything. It just backed out
// so we can repeat after acquiring the claim. We need to
// repeat this after acquiring a tree claim.
//
if (splitRequired && !treeClaimAcquired) {
if (!didPrune && buffer.isDataPage()) {
didPrune = true;
if (buffer.pruneMvvValues(_tree, false, null)) {
continue;
}
}
//
// TODO - is it worth it to try an instantaneous claim
// and retry?
//
treeClaimRequired = true;
buffer.releaseTouched();
buffer = null;
continue;
}
//
// The value has been written to the buffer and the
// buffer is reserved and dirty. No backing out now.
// If we made it to here, any LONG_RECORD value is
// committed.
//
if (buffer.isDataPage()) {
if (!keyExisted) {
_tree.bumpChangeCount();
}
assert buffer.isDirty() : "Buffer must be dirty";
committed = true;
if (incrementMVVCount) {
_transaction.getTransactionStatus().incrementMvvCount();
}
Buffer.deallocatePrunedVersions(_persistit, _volume, prunedVersions);
}
buffer.releaseTouched();
buffer = null;
if (!splitRequired) {
//
// No split means we're totally done.
//
break;
} else {
// Otherwise we need to index the new right
// sibling at the next higher index level.
Debug.$assert0.t(valueToStore.getPointerValue() > 0);
//
// This maneuver sets key to the key value of
// the first record in the newly inserted page.
//
key = _spareKey1;
_spareKey1 = _spareKey2;
_spareKey2 = key;
//
// Bump key generation because it no longer matches
// what's in the LevelCache
//
key.bumpGeneration();
//
// And now cycle back to insert the key/pointer pair
// into the next higher index level.
//
level++;
continue;
}
} catch (final WWRetryException re) {
if (buffer != null) {
buffer.releaseTouched();
buffer = null;
}
if (treeClaimAcquired) {
_treeHolder.release();
treeClaimAcquired = false;
}
try {
sequence(WRITE_WRITE_STORE_A);
final long depends = _persistit.getTransactionIndex().wwDependency(re.getVersionHandle(),
_transaction.getTransactionStatus(), _timeoutMillis);
if (depends != 0 && depends != TransactionStatus.ABORTED) {
// version is from concurrent txn that already
// committed
// or timed out waiting to see. Either
// way, must abort.
_transaction.rollback();
throw new RollbackException();
}
} catch (final InterruptedException ie) {
throw new PersistitInterruptedException(ie);
}
} catch (final RetryException re) {
if (buffer != null) {
buffer.releaseTouched();
buffer = null;
}
if (treeClaimAcquired) {
_treeHolder.release();
treeClaimAcquired = false;
}
final boolean doWait = (options & StoreOptions.WAIT) != 0;
treeClaimAcquired = _treeHolder.claim(true, doWait ? _timeoutMillis : 0);
if (!treeClaimAcquired) {
if (!doWait) {
throw re;
} else {
throw new InUseException("Thread " + Thread.currentThread().getName()
+ " failed to get reader claim on " + _tree);
}
}
} finally {
if (buffer != null) {