@Override public Transaction getSnapshot(@Nullable final Runnable beginHook) { checkIsFinished(); return new ReadWriteTransaction(this, beginHook); }
@Override public boolean flush() { checkIsFinished(); final EnvironmentImpl env = getEnvironment(); final boolean result = env.flushTransaction(this, false); if (result) { // if the transaction was upgraded to exclusive during re-playing // then it should be downgraded back after successful flush(). if (!wasCreatedExclusive() && isExclusive() && env.getEnvironmentConfig().getEnvTxnDowngradeAfterFlush()) { env.downgradeTransaction(this); setExclusive(false); } setStarted(System.currentTimeMillis()); } else { incReplayCount(); } return result; }
@Override public void abort() { checkIsFinished(); clearImmutableTrees(); doRevert(); getEnvironment().finishTransaction(this); }
@Override public boolean commit() { checkIsFinished(); return getEnvironment().commitTransaction(this, false); }
@NotNull public StoreImpl openStoreByStructureId(final int structureId) { checkIsFinished(); final EnvironmentImpl env = getEnvironment(); final String storeName = getMetaTree().getStoreNameByStructureId(structureId, env); return storeName == null ? new TemporaryEmptyStore(env) : env.openStoreImpl(storeName, StoreConfig.USE_EXISTING, this, getTreeMetaInfo(storeName)); }
@Override public void revert() { checkIsFinished(); if (isReadonly()) { throw new ExodusException("Attempt ot revert read-only transaction"); } final long oldRoot = getMetaTree().root; final boolean wasExclusive = isExclusive(); final EnvironmentImpl env = getEnvironment(); if (isIdempotent()) { env.holdNewestSnapshotBy(this, false); } else { doRevert(); if (wasExclusive || !env.shouldTransactionBeExclusive(this)) { env.holdNewestSnapshotBy(this, false); } else { env.releaseTransaction(this); setExclusive(true); env.holdNewestSnapshotBy(this); } } if (!env.isRegistered(this)) { throw new ExodusException("Transaction should remain registered after revert"); } if (!checkVersion(oldRoot)) { clearImmutableTrees(); env.runTransactionSafeTasks(); } setStarted(System.currentTimeMillis()); }
boolean flushTransaction(@NotNull final ReadWriteTransaction txn, final boolean forceCommit) { checkIfTransactionCreatedAgainstThis(txn); if (!forceCommit && txn.isIdempotent()) { return true; final long initialHighAddress; final long resultingHighAddress; final boolean isGcTransaction = txn.isGCTransaction(); if (!txn.checkVersion(metaTree.root)) { try { final MetaTreeImpl.Proto[] tree = new MetaTreeImpl.Proto[1]; expiredLoggables = txn.doCommit(tree); writeConfirmed = true; resultingHighAddress = updatedTip.approvedHighAddress; txn.setMetaTree(metaTree = MetaTreeImpl.create(this, updatedTip, proto)); txn.executeCommitHook(); } finally { metaWriteLock.unlock();
@NotNull ITreeMutable getMutableTree(@NotNull final StoreImpl store) { checkIsFinished(); if (getEnvironment().getEnvironmentConfig().getEnvTxnSingleThreadWrites()) { final Thread creatingThread = getCreatingThread(); if (!creatingThread.equals(Thread.currentThread())) { throw new ExodusException("Can't create mutable tree in a thread different from the one which transaction was created in"); } } final int structureId = store.getStructureId(); ITreeMutable result = mutableTrees.get(structureId); if (result == null) { result = getTree(store).getMutableCopy(); mutableTrees.put(structureId, result); } return result; }
Iterable<ExpiredLoggableInfo>[] doCommit(@NotNull final MetaTreeImpl.Proto[] out) { final Set<Map.Entry<Integer, ITreeMutable>> entries = mutableTrees.entrySet(); final Set<Map.Entry<Long, Pair<String, ITree>>> removedEntries = removedStores.entrySet(); final int size = entries.size() + removedEntries.size(); //noinspection unchecked final Iterable<ExpiredLoggableInfo>[] expiredLoggables = new Iterable[size + 1]; int i = 0; final ITreeMutable metaTreeMutable = getMetaTree().tree.getMutableCopy(); for (final Map.Entry<Long, Pair<String, ITree>> entry : removedEntries) { final Pair<String, ITree> value = entry.getValue(); MetaTreeImpl.removeStore(metaTreeMutable, value.getFirst(), entry.getKey()); expiredLoggables[i++] = TreeMetaInfo.getTreeLoggables(value.getSecond()); } removedStores.clear(); for (final Map.Entry<String, TreeMetaInfo> entry : createdStores.entrySet()) { MetaTreeImpl.addStore(metaTreeMutable, entry.getKey(), entry.getValue()); } createdStores.clear(); final Collection<ExpiredLoggableInfo> last; for (final Map.Entry<Integer, ITreeMutable> entry : entries) { final ITreeMutable treeMutable = entry.getValue(); expiredLoggables[i++] = treeMutable.getExpiredLoggables(); MetaTreeImpl.saveTree(metaTreeMutable, treeMutable); } clearImmutableTrees(); mutableTrees.clear(); expiredLoggables[i] = last = metaTreeMutable.getExpiredLoggables(); out[0] = MetaTreeImpl.saveMetaTree(metaTreeMutable, getEnvironment(), last); return expiredLoggables; }
boolean shouldTransactionBeExclusive(@NotNull final ReadWriteTransaction txn) { final int replayCount = txn.getReplayCount(); return replayCount >= ec.getEnvTxnReplayMaxCount() || System.currentTimeMillis() - txn.getCreated() >= ec.getEnvTxnReplayTimeout(); }
void storeRemoved(@NotNull final StoreImpl store) { checkIsFinished(); super.storeRemoved(store); final int structureId = store.getStructureId(); final ITree tree = store.openImmutableTree(getMetaTree()); removedStores.put(structureId, new Pair<>(store.getName(), tree)); mutableTrees.remove(structureId); }
@Nullable @Override TreeMetaInfo getTreeMetaInfo(@NotNull final String name) { checkIsFinished(); final TreeMetaInfo result = createdStores.get(name); return result == null ? super.getTreeMetaInfo(name) : result; }
@Override public void beforeSettingChanged(@NotNull String key, @NotNull Object value, @NotNull Map<String, Object> context) { if (key.equals(EnvironmentConfig.ENV_IS_READONLY)) { if (log.getConfig().getReaderWriterProvider().isReadonly()) { throw new InvalidSettingException("Can't modify read-only state in run time since DataReaderWriterProvider is read-only"); } if (Boolean.TRUE.equals(value)) { suspendGC(); final TransactionBase txn = beginTransaction(); try { if (!txn.isReadonly()) { gc.getUtilizationProfile().forceSave(txn); txn.setCommitHook(new Runnable() { @Override public void run() { EnvironmentConfig.suppressConfigChangeListenersForThread(); ec.setEnvIsReadonly(true); EnvironmentConfig.resumeConfigChangeListenersForThread(); } }); ((ReadWriteTransaction) txn).forceFlush(); } } finally { txn.abort(); } } } }
public boolean forceFlush() { checkIsFinished(); return getEnvironment().flushTransaction(this, true); }
@NotNull @Override public ITree getTree(@NotNull final StoreImpl store) { checkIsFinished(); final ITreeMutable result = mutableTrees.get(store.getStructureId()); if (result == null) { return super.getTree(store); } return result; }
@NotNull public ReadWriteTransaction beginGCTransaction() { if (ec.getEnvIsReadonly()) { throw new ReadonlyTransactionException("Can't start GC transaction on read-only Environment"); } return new ReadWriteTransaction(this, null, ec.getGcUseExclusiveTransaction(), true) { @Override boolean isGCTransaction() { return true; } }; }
@NotNull protected TransactionBase beginTransaction(Runnable beginHook, boolean exclusive, boolean cloneMeta) { checkIsOperative(); return ec.getEnvIsReadonly() ? new ReadonlyTransaction(this, exclusive, beginHook) : new ReadWriteTransaction(this, beginHook, exclusive, cloneMeta); }