*             uponError
     */
    boolean storeInternal(Key key, Value value, int level, int options) throws PersistitException {

        final boolean doMVCC = (options & StoreOptions.MVCC) > 0;
        final boolean doFetch = (options & StoreOptions.FETCH) > 0;

        // spares used for new splits/levels
        Debug.$assert0.t(key != _spareKey1);

        _storeCausedSplit = false;
        boolean treeClaimRequired = false;
        boolean treeClaimAcquired = false;
        boolean treeWriterClaimRequired = false;
        boolean committed = false;
        boolean incrementMVVCount = false;

        final int maxSimpleValueSize = maxValueSize(key.getEncodedSize());
        final Value spareValue = _persistit.getThreadLocalValue();
        assert !(doMVCC & value == spareValue || doFetch && value == _spareValue) : "storeInternal may be use the supplied Value: "
                + value;

        //
        // First insert the record in the data page
        //
        Buffer buffer = null;

        //
        // The LONG_RECORD pointer that was present before the update, if
        // there is a long record being replaced.
        //
        long oldLongRecordPointer = 0;
        long oldLongRecordPointerMVV = 0;
        //
        // The LONG_RECORD pointer for a new long record value, if the
        // the new value is long.
        //
        long newLongRecordPointer = 0;
        long newLongRecordPointerMVV = 0;

        boolean isLongRecord = value.getEncodedSize() > maxSimpleValueSize;
        if (isLongRecord) {
            //
            // This method may delay significantly for I/O and must
            // be called when there are no other claimed resources.
            //
            newLongRecordPointer = getLongRecordHelper().storeLongRecord(value, _transaction.isActive());
        }

        if (!_ignoreTransactions && ((options & StoreOptions.DONT_JOURNAL) == 0)) {
            _transaction.store(this, key, value);
        }

        boolean keyExisted = false;

        try {

            Value valueToStore = value;
            for (;;) {
                Debug.$assert0.t(buffer == null);
                if (Debug.ENABLED) {
                    Debug.suspend();
                }

                /*
                 * Can't save the old pointer as the state may have changed
                 * since the last claim, could have even been de-allocated, and
                 * just as equally can't hold onto the new one either.
                 */
                oldLongRecordPointerMVV = 0;
                if (!committed && newLongRecordPointerMVV != 0) {
                    _volume.getStructure().deallocateGarbageChain(newLongRecordPointerMVV, 0);
                    newLongRecordPointerMVV = 0;
                    spareValue.changeLongRecordMode(false);
                }

                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();
                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;
                    }

                    Debug.$assert0.t(buffer == null);
                    int foundAt = -1;
                    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 (doMVCC) {
                            valueToStore = spareValue;
                            int valueSize = value.getEncodedSize();
                            /*
                             * If key didn't exist the value is truly
                             * non-existent and not just undefined/zero length
                             */
                            byte[] spareBytes = spareValue.getEncodedBytes();
                            int spareSize = keyExisted ? spareValue.getEncodedSize() : -1;
                            spareSize = MVV.prune(spareBytes, 0, spareSize, _persistit.getTransactionIndex(), false,
                                    prunedVersions);

                            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;
                                }
                            }

                            // Visit all versions for ww detection
                            _mvvVisitor.initInternal(tStatus, tStep, MvvVisitor.Usage.STORE);
                            MVV.visitAllVersions(_mvvVisitor, spareBytes, 0, spareSize);

                            int mvvSize = MVV.estimateRequiredLength(spareBytes, spareSize, valueSize);
                            spareValue.ensureFit(mvvSize);
                            spareBytes = spareValue.getEncodedBytes();

                            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);

                            if (spareValue.getEncodedSize() > maxSimpleValueSize) {
                                newLongRecordPointerMVV = getLongRecordHelper().storeLongRecord(spareValue,
                                        _transaction.isActive());
                            }
                        }
                    }

                    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)) {
                                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 (WWRetryException re) {
                    if (buffer != null) {
                        buffer.releaseTouched();
                        buffer = null;
                    }
                    try {
                        sequence(WRITE_WRITE_STORE_A);
                        // TODO - timeout?
                        long depends = _persistit.getTransactionIndex().wwDependency(re.getVersionHandle(),
                                _transaction.getTransactionStatus(), SharedResource.DEFAULT_MAX_WAIT_TIME);
                        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 (InterruptedException ie) {
                        throw new PersistitInterruptedException(ie);
                    }
                } catch (RetryException re) {
                    if (buffer != null) {
                        buffer.releaseTouched();
                        buffer = null;
                    }

                    if (treeClaimAcquired) {
                        _treeHolder.release();
                        treeClaimAcquired = false;
                    }
                    boolean doWait = (options & StoreOptions.WAIT) != 0;
                    treeClaimAcquired = _treeHolder.claim(true, doWait ? SharedResource.DEFAULT_MAX_WAIT_TIME : 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) {
                        buffer.releaseTouched();
                        buffer = null;
                    }
                }
            }
        } finally {
            if (treeClaimAcquired) {
                _treeHolder.release();
                treeClaimAcquired = false;
            }

            value.changeLongRecordMode(false);
            spareValue.changeLongRecordMode(false);
            if (!committed) {
                //
                // We failed to write the new LONG_RECORD. If there was
                // previously no LONG_RECORD, then deallocate the newly
                // allocated LONG_RECORD chain if we had successfully
                // allocated one.
                //
                if (newLongRecordPointer != oldLongRecordPointer && newLongRecordPointer != 0) {
                    _volume.getStructure().deallocateGarbageChain(newLongRecordPointer, 0);
                }
                if (newLongRecordPointerMVV != 0) {
                    _volume.getStructure().deallocateGarbageChain(newLongRecordPointerMVV, 0);
                }
            } else {
                if (oldLongRecordPointer != newLongRecordPointer && oldLongRecordPointer != 0) {
                    _volume.getStructure().deallocateGarbageChain(oldLongRecordPointer, 0);
                }
                if (oldLongRecordPointerMVV != 0) {
                    _volume.getStructure().deallocateGarbageChain(oldLongRecordPointerMVV, 0);
                }
            }
        }
        _volume.getStatistics().bumpStoreCounter();
        _tree.getStatistics().bumpStoreCounter();
        if (doFetch || doMVCC) {
            _volume.getStatistics().bumpFetchCounter();
            _tree.getStatistics().bumpFetchCounter();
        }
        return keyExisted;
    }