/** * Create a transaction reading from the most recent committed state not later than the specified * startTime. * * <p>Note: For an {@link IBigdataFederation}, a transaction does not start execution on all * {@link IDataService}s at the same moment. Instead, the transaction startTime is assigned by the * {@link ITransactionService} and then provided each time an {@link ITx} must be created for * isolatation of resources accessible on a {@link IDataService}. * * @param transactionManager The local (client-side) transaction manager. * @param resourceManager Provides access to named indices that are isolated by the transaction. * @param startTime The transaction identifier * @param readsOnCommitTime The timestamp of the commit point against which this transaction is * reading. * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/266">Refactor native long tx id * to thin object</a> * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/546" > Add cache for access to * historical index views on the Journal by name and commitTime. </a> */ public Tx( // final AbstractLocalTransactionManager localTransactionManager, // final IResourceManager resourceManager, // final long startTime, final long readsOnCommitTime // ) { if (localTransactionManager == null) throw new IllegalArgumentException(); if (resourceManager == null) throw new IllegalArgumentException(); this.readOnly = !TimestampUtility.isReadWriteTx(startTime); // if (!TimestampUtility.isReadWriteTx(startTime)) { // // /* // * Note: We only maintain local state for read-write transactions. // */ // // throw new IllegalArgumentException(); // // } // this.localTransactionManager = localTransactionManager; this.indices = readOnly ? null : new ConcurrentHashMap<String, ILocalBTreeView>(); this.resourceManager = resourceManager; this.startTime = startTime; /* * Note: On a new journal, the lastCommitTime is ZERO before the first * commit point. In order to avoid having the ground state view be the * UNISOLATED index view, we use a timestamp which is known to be before * any valid timestamp (and which will be shared by any transaction * started against an empty journal). * * Note: It might be better to put this logic into * Journal#getLastCommitTime(). However, there are other callers for * that method. By making the change here, we can guarantee that its * scope is limited to only the code change made for the following * ticket. * * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/546" > * Add cache for access to historical index views on the Journal by name * and commitTime. </a> */ this.readsOnCommitTime = readsOnCommitTime == ITx.UNISOLATED ? 1 : readsOnCommitTime; this.runState = RunState.Active; // pre-compute the hash code for the transaction. this.hashCode = Long.valueOf(startTime).hashCode(); localTransactionManager.activateTx(this); // report event. ResourceManager.openTx(startTime); }
/** * Return a named index. The index will be isolated at the same level as this transaction. Changes * on the index will be made restart-safe iff the transaction successfully commits. * * @param name The name of the index. * @return The named index or <code>null</code> if no index is registered under that name. * @exception IllegalStateException if the transaction is not active. */ public ILocalBTreeView getIndex(final String name) { if (name == null) throw new IllegalArgumentException(); if (readOnly) { /* * Note: Access to the indices currently goes through the * IResourceManager interface for a read-only transaction. */ throw new UnsupportedOperationException(); } /* * @todo lock could be per index for higher concurrency rather than for * all indices which you might access through this tx. */ lock.lock(); try { if (!isActive()) { throw new IllegalStateException(NOT_ACTIVE); } /* * Test the cache - this is used so that we can recover the same * instance on each call within the same transaction. */ if (indices.containsKey(name)) { // Already defined. return indices.get(name); } final ILocalBTreeView index; /* * See if the index was registered as of the ground state used by * this transaction to isolated indices. * * Note: IResourceManager#getIndex(String name,long timestamp) calls * us when the timestamp identifies an active transaction so we MUST * NOT call that method ourselves! Hence there is some replication * of logic between that method and this one. */ final AbstractBTree[] sources = resourceManager.getIndexSources(name, readsOnCommitTime); // startTime); if (sources == null) { /* * The named index was not registered as of the transaction * ground state. */ if (log.isInfoEnabled()) log.info("No such index: " + name + ", startTime=" + startTime); return null; } if (!sources[0].getIndexMetadata().isIsolatable()) { throw new RuntimeException("Not isolatable: " + name); } /* * Isolate the named btree. */ // if (readOnly) { // // assert sources[0].isReadOnly(); // // if (sources.length == 1) { // // index = sources[0]; // // } else { // // index = new FusedView(sources); // // } // // } else { /* * Setup the view. The write set is always the first element in * the view. */ // the view definition. final AbstractBTree[] b = new AbstractBTree[sources.length + 1]; /* * Create the write set on a temporary store. * * Note: The BTree is NOT registered under a name so it can not * be discovered on the temporary store. This is fine since we * hold onto a hard reference to the BTree in [indices]. */ b[0] = BTree.create(getTemporaryStore(), sources[0].getIndexMetadata().clone()); System.arraycopy(sources, 0, b, 1, sources.length); // create view with isolated write set. index = new IsolatedFusedView(-startTime, b); // report event. ResourceManager.isolateIndex(startTime, name); // } indices.put(name, index); return index; } finally { lock.unlock(); } }