| /* |
| |
| Derby - Class org.apache.derby.impl.store.raw.xact.TransactionTable |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to you under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derby.impl.store.raw.xact; |
| |
| import org.apache.derby.iapi.services.context.ContextManager; |
| |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| import org.apache.derby.iapi.services.io.Formatable; |
| import org.apache.derby.iapi.services.io.StoredFormatIds; |
| |
| import org.apache.derby.iapi.store.access.TransactionInfo; |
| |
| import org.apache.derby.iapi.store.raw.GlobalTransactionId; |
| |
| import org.apache.derby.iapi.store.raw.log.LogInstant; |
| |
| import org.apache.derby.iapi.store.raw.xact.RawTransaction; |
| import org.apache.derby.iapi.store.raw.xact.TransactionId; |
| |
| import org.apache.derby.iapi.services.io.CompressedNumber; |
| |
| import java.util.ArrayList; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import java.io.ObjectOutput; |
| import java.io.ObjectInput; |
| import java.io.IOException; |
| |
| /** |
| The transaction table is used by the transaction factory to keep track of |
| all transactions that are in the system. |
| |
| <BR> The transction table serves the following purposes: <OL> |
| |
| <LI> checkpoint - when a checkpoint log record is written out, it writes |
| out also all transactions that have updated the database. RESOLVE: this is |
| actually not used right now - rather, the transaction table is |
| reconstructed during the redo phase by traversing from the undo LWM. It is |
| a goal to use this transaction table (and traversing from the redoLWM) |
| instead of rebuilding it to speed up recovery. |
| |
| <LI> Quiesce State - when a system enters the quiesce state, it needs to account |
| for all transactions in the system, even those which are just started and |
| are in their IDLE state. |
| |
| <LI> TransactionTable VTI - we need to get a snapshot of all transactions |
| in the system for diagnostic purposes. |
| </OL> |
| |
| In order to speed up the time it takes to look up a transaction from the |
| transaction table, each transaction must have a unique transaction Id. |
| This means newly coined transaction must also have a transaction Id. |
| |
| <P>During recovery, there is only one real xact object doing all the |
| recovery work, but there could be many outstanding transactions that are |
| gleamed from the log. Each of these "recovery transactions" have its on |
| entry into the transaction table but they all share the same Xact object. |
| |
| <P>Multithreading considerations:<BR> |
| TransactionTable must be MT-safe it is called upon by many threads |
| simultaneously (except during recovery) |
| |
| <P>Methods that are only called during |
| recovery don't need to take MT considerations, and can safely use iterators |
| with no additional synchronization. |
| |
| */ |
| |
| public class TransactionTable implements Formatable |
| { |
| /* |
| * Fields |
| */ |
| |
| private final ConcurrentHashMap<TransactionId, TransactionTableEntry> trans; |
| |
| private TransactionId largestUpdateXactId; |
| |
| /** |
| MT - not needed for constructor |
| */ |
| public TransactionTable() |
| { |
| trans = new ConcurrentHashMap<TransactionId, TransactionTableEntry>(); |
| } |
| |
| /************************************************************* |
| * generic methods called by all clients of transaction table |
| * Must be MT -safe |
| ************************************************************/ |
| private TransactionTableEntry findTransactionEntry(TransactionId id) |
| { |
| |
| if (SanityManager.DEBUG) |
| SanityManager.ASSERT( |
| id != null, "findTransacionEntry with null id"); |
| |
| // Hashtable is synchronized |
| return trans.get(id); |
| } |
| |
| /** |
| * Interface for visiting entries in the transaction table. |
| * @see #visitEntries(EntryVisitor) |
| */ |
| static interface EntryVisitor { |
| /** |
| * Visit an entry. {@link #visitEntries(EntryVisitor)} will call this |
| * method once for each entry in the transaction table, or until |
| * {@code false} is returned by this method. |
| * |
| * @param entry the {@code TransactionTableEntry} being visited |
| * @return {@code true} if the scan of the transaction table should |
| * continue, or {@code false} if the visitor has completed its work |
| * and no more entries need to be visited |
| */ |
| boolean visit(TransactionTableEntry entry); |
| } |
| |
| /** |
| * <p> |
| * Visit all the entries in the transaction table. |
| * </p> |
| * |
| * <p> |
| * MT - MT safe |
| * </p> |
| * |
| * <p> |
| * Entries that are added to or removed from the transaction table while |
| * it's being traversed, may or may not be visited. All the entries that |
| * are present in the map when this method is called, and have not been |
| * removed when the method returns, will have been visited exactly once |
| * (except if the {@code visit()} method returns false before all entries |
| * have been visited, in which case the traversal of the map will stop |
| * earlier). |
| * </p> |
| * |
| * <p> |
| * Note however that this method does not guarantee that a single |
| * {@code TransactionTableEntry} is not accessed concurrently by multiple |
| * threads. If the visitor accesses some of the entry's mutable state, the |
| * caller must ensure that appropriate synchronization protection is in |
| * place. For example, if accessing the update state of the entry, the |
| * caller must synchronize on "this" (the {@code TransactionTable} |
| * instance). |
| * </p> |
| * |
| * @param visitor the visitor to apply on each transaction table entry |
| */ |
| void visitEntries(EntryVisitor visitor) { |
| for (Object entry : trans.values()) { |
| if (!visitor.visit((TransactionTableEntry) entry)) { |
| // The visitor returned false, meaning that it's done with |
| // all of its work and we can stop the scan. |
| break; |
| } |
| } |
| } |
| |
| |
| void add(Xact xact, boolean exclude) |
| { |
| TransactionId id = xact.getId(); |
| |
| TransactionTableEntry newEntry = new TransactionTableEntry( |
| xact, id, 0, exclude ? TransactionTableEntry.EXCLUDE : 0); |
| |
| synchronized(this) |
| { |
| Object oldEntry = trans.put(id, newEntry); |
| |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT( |
| oldEntry == null, |
| "Trying to add a transaction that's already " + |
| "in the transaction table"); |
| |
| if (SanityManager.DEBUG_ON("TranTrace")) |
| { |
| SanityManager.DEBUG( |
| "TranTrace", "adding transaction " + id); |
| SanityManager.showTrace(new Throwable("TranTrace")); |
| } |
| } |
| } |
| |
| if (SanityManager.DEBUG) { |
| |
| if (SanityManager.DEBUG_ON("memoryLeakTrace")) { |
| |
| if (trans.size() > 50) |
| System.out.println("memoryLeakTrace:TransactionTable " + trans.size()); |
| } |
| } |
| } |
| |
| /** |
| remove the transaction Id an return false iff the transaction is found |
| in the table and it doesn't need exclusion during quiesce state |
| */ |
| boolean remove(TransactionId id) |
| { |
| if (SanityManager.DEBUG) |
| SanityManager.ASSERT( |
| id != null, |
| "cannot remove transaction from table with null id"); |
| |
| if (SanityManager.DEBUG) |
| { |
| if (SanityManager.DEBUG_ON("TranTrace")) |
| { |
| SanityManager.DEBUG( |
| "TranTrace", "removing transaction " + id); |
| SanityManager.showTrace(new Throwable("TranTrace")); |
| } |
| } |
| |
| // Hashtable is synchronized |
| TransactionTableEntry ent = trans.remove(id); |
| return (ent == null || ent.needExclusion()); |
| } |
| |
| |
| /** |
| Change a transaction to update or add an update transaction to this table. |
| |
| @param tid the transaction id |
| @param tran the transaction to be added |
| @param transactionStatus the transaction status that is stored in the |
| BeginXact log record |
| */ |
| public void addUpdateTransaction(TransactionId tid, RawTransaction tran, |
| int transactionStatus) |
| { |
| |
| // we need to synchronize on the transaction table because we have to |
| // prevent this state change from happening when the transaction table |
| // itself is written out to the checkpoint. This is the only |
| // protection the TransactionTableEntry has to prevent fields in myxact |
| // from changing underneath it while it is being written out. |
| synchronized(this) |
| { |
| TransactionTableEntry ent = findTransactionEntry(tid); |
| |
| if (ent != null) |
| { |
| // this happens during run time, when a transaction that is |
| // already started changed status to an update transaction |
| |
| ent.updateTransactionStatus((Xact)tran, transactionStatus, |
| TransactionTableEntry.UPDATE) ; |
| } |
| else |
| { |
| // this happens during recovery, that's why we haven't seen |
| // this transaction before - it is added in the doMe of the |
| // BeginXact log record. |
| // |
| // No matter what this transaction is, it won't need to be run |
| // in quiesce state because we are in recovery. |
| ent = new TransactionTableEntry((Xact)tran, tid, transactionStatus, |
| TransactionTableEntry.UPDATE | |
| TransactionTableEntry.EXCLUDE | |
| TransactionTableEntry.RECOVERY); |
| trans.put(tid, ent); |
| |
| } |
| |
| if (XactId.compare(ent.getXid(), largestUpdateXactId) > 0) |
| largestUpdateXactId = ent.getXid(); |
| } |
| } |
| |
| /** |
| Change update transaction to non-update |
| |
| <P>MT - MT safe, since vector is MT-safe. |
| |
| @param id the transaction Id |
| */ |
| void removeUpdateTransaction(TransactionId id) |
| { |
| // we need to synchronize on the transaction table because we have to |
| // prevent this state change from happening when the transaction table |
| // itself is written out to the checkpoint. This is the only |
| // protection the TransactionTableEntry has to prevent fields in myxact |
| // from changing underneath it while it is being written out. |
| |
| synchronized (this) |
| { |
| TransactionTableEntry ent = findTransactionEntry(id); |
| |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(ent != null, |
| "removing update transaction that is not there"); |
| } |
| |
| ent.removeUpdateTransaction(); |
| |
| // If we are committing a recovery transaction, remove it from the |
| // transaction table. The xact object which is doing the work is |
| // not going to be closed even though the transaction is done. |
| if (ent.isRecovery()) |
| remove(id); |
| } |
| |
| return; |
| } |
| |
| /************************************************************************** |
| * Transaction table methods used by XA. |
| ************************************************************************** |
| */ |
| |
| /** |
| Change transaction to prepared. |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| |
| @param id the transaction Id |
| */ |
| void prepareTransaction(TransactionId id) |
| { |
| // we need to synchronize on the transaction table because we have to |
| // prevent this state change from happening when the transaction table |
| // itself is written out to the checkpoint. This is the only |
| // protection the TransactionTableEntry has to prevent fields in myxact |
| // from changing underneath it while it is being written out. |
| |
| TransactionTableEntry ent = findTransactionEntry(id); |
| |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT( |
| ent != null, "preparing transaction that is not there"); |
| } |
| |
| ent.prepareTransaction(); |
| |
| return; |
| } |
| |
| /** |
| * Find a transaction in the table by Global transaction id. |
| * <p> |
| * This routine use to be only called during offline recovery so performance |
| * was not critical. Since that time more calls have been made, including |
| * one in startGlobalTransaction() so a linear search may no longer |
| * be appropriate. See DERBY-828. |
| * |
| * @return The ContextManager of the transaction being searched for. |
| * |
| * @param global_id The global transaction we are searching for. |
| **/ |
| public ContextManager findTransactionContextByGlobalId( |
| final GlobalXactId global_id) |
| { |
| for (TransactionTableEntry entry : trans.values()) { |
| GlobalTransactionId entry_gid = entry.getGid(); |
| if (entry_gid != null && entry_gid.equals(global_id)) { |
| return entry.getXact().getContextManager(); |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| /*********************************************************** |
| * called when system is being quiesced, must be MT - safe |
| ***********************************************************/ |
| /** |
| Return true if there is no transaction actively updating the database. |
| New transaction may be started or old transaction committed |
| right afterward, the caller of this routine must have other ways to |
| stop transactions from starting or ending. |
| |
| <P>MT - safe |
| */ |
| boolean hasActiveUpdateTransaction() |
| { |
| synchronized (this) |
| { |
| for (TransactionTableEntry entry : trans.values()) { |
| if (entry.isUpdate()) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /************************************************************ |
| * methods called only by checkpoint |
| ***********************************************************/ |
| /* |
| * Formatable methods |
| */ |
| |
| /** |
| Return my format identifier. |
| */ |
| public int getTypeFormatId() { |
| return StoredFormatIds.RAW_STORE_TRANSACTION_TABLE; |
| } |
| |
| /** |
| @exception IOException problem reading the transaction table |
| */ |
| public void writeExternal(final ObjectOutput out) throws IOException |
| { |
| //don't let the transactions status change while writing out(beetle:5533) |
| // We don't care if transactions are added or removed from the table |
| // while we're writing it out, as long as the number of update |
| // transactions is constant. Synchronizing on "this" prevents other |
| // threads from adding or removing update transactions. |
| |
| synchronized(this) |
| { |
| int count = 0; |
| for (TransactionTableEntry entry : trans.values()) { |
| if (entry.isUpdate()) { |
| count++; |
| } |
| } |
| |
| CompressedNumber.writeInt(out, count); |
| |
| // now write them out |
| if (count > 0) |
| { |
| // Count the number of writes in debug builds. |
| int writeCount = 0; |
| |
| for (TransactionTableEntry entry : trans.values()) { |
| if (entry.isUpdate()) { |
| // only write out update transactions |
| out.writeObject(entry); |
| if (SanityManager.DEBUG) { |
| writeCount++; |
| } |
| } |
| } |
| |
| // Verify that we wrote the expected number of transactions. |
| if (SanityManager.DEBUG) { |
| SanityManager.ASSERT(count == writeCount); |
| } |
| } |
| } |
| } |
| |
| /************************************************************ |
| * methods called only by recovery |
| ************************************************************/ |
| |
| |
| /** |
| @exception IOException problem reading the transaction table |
| @exception ClassNotFoundException problem reading the transaction table |
| */ |
| public void readExternal(ObjectInput in) |
| throws IOException, ClassNotFoundException |
| { |
| // RESOLVE: this is only read in checkpoint record, but we have not |
| // finish the work on using this transaction table to cut down on redo |
| // so this transaction table is effectively and futilely thrown away! |
| |
| int count = CompressedNumber.readInt(in); |
| if (count == 0) |
| return; |
| |
| for (int i = 0; i < count; i++) |
| { |
| TransactionTableEntry ent = |
| (TransactionTableEntry)in.readObject(); |
| |
| if (SanityManager.DEBUG) |
| SanityManager.ASSERT( |
| ent.getXid() != null, |
| "read in transaction table entry with null id"); |
| |
| trans.put(ent.getXid(), ent); |
| |
| if (ent.isUpdate() && |
| XactId.compare(ent.getXid(), largestUpdateXactId) > 0) |
| { |
| largestUpdateXactId = ent.getXid(); |
| } |
| } |
| |
| |
| } |
| |
| /** |
| Return the largest update transactionId I have seen so far. |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| */ |
| public TransactionId largestUpdateXactId() |
| { |
| return largestUpdateXactId; |
| } |
| |
| |
| /** |
| Is there an active internal transaction in the transaction table. |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| */ |
| public boolean hasRollbackFirstTransaction() |
| { |
| for (TransactionTableEntry ent : trans.values()) |
| { |
| if (ent != null && ent.isRecovery() && |
| (ent.getTransactionStatus() & |
| Xact.RECOVERY_ROLLBACK_FIRST) != 0) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| Is there a prepared transaction that are recovered |
| durring the recovery in the transaction table. |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| */ |
| public boolean hasPreparedRecoveredXact() |
| { |
| return hasPreparedXact(true); |
| } |
| |
| |
| /** |
| Is there a prepared transaction in the transaction table. |
| <P>MT - unsafe, called during boot, which is single threaded. |
| */ |
| public boolean hasPreparedXact() |
| { |
| return hasPreparedXact(false); |
| } |
| |
| /** |
| * Is there a prepared transaction in the transaction table. |
| * |
| * <P>MT - unsafe, caller is recovery/at boot, which is single threaded. |
| * |
| * @param recovered <code> true </code> to search for transaction |
| * that are in prepared during recovery. |
| * recovered tranaction. |
| * <code> false > to search for just prepared |
| * transactons. |
| * @return <code> true if there is a prepared transaction and |
| * recovered when <code> recovered </code> argument is |
| * <code> true </code> |
| */ |
| |
| private boolean hasPreparedXact(boolean recovered) |
| { |
| for (TransactionTableEntry ent : trans.values()) |
| { |
| if (ent != null && |
| (ent.getTransactionStatus() & Xact.END_PREPARED) != 0) |
| { |
| if (recovered) { |
| if(ent.isRecovery()) |
| return true; |
| } else { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| |
| |
| /** |
| Get the most recently added transaction that says it needs to be |
| rolled back first (an InternalXact) from the transaction table and make |
| the passed in transaction assume its identity. |
| <B> Should only be used in recovery undo !! </B> |
| RESOLVE: (sku)I don't think even these internal transactions need to be |
| rolled back in the reverse order, because they are physical in nature. |
| But it won't hurt. |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| */ |
| public boolean getMostRecentRollbackFirstTransaction(RawTransaction tran) |
| { |
| |
| if (trans.isEmpty()) |
| { |
| // set tranaction to idle |
| return findAndAssumeTransaction((TransactionId)null, tran); |
| } |
| |
| TransactionId id = null; |
| for (TransactionTableEntry ent : trans.values()) |
| { |
| if (ent != null && ent.isUpdate() && ent.isRecovery() && |
| (ent.getTransactionStatus() & Xact.RECOVERY_ROLLBACK_FIRST) != 0) |
| { |
| // try to locate the most recent one |
| if (id == null || XactId.compare(id, ent.getXid()) < 0) |
| id = ent.getXid(); |
| } |
| } |
| |
| if (id == null) // set transaction to idle |
| { |
| return findAndAssumeTransaction(id, tran); |
| } |
| else |
| { |
| // there is a rollback first transaction |
| boolean found = |
| findAndAssumeTransaction(id, tran); |
| |
| if (SanityManager.DEBUG) |
| { |
| if (!found) |
| { |
| SanityManager.THROWASSERT( |
| "cannot find transaction " + id + " in table"); |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| /** |
| Get the most recently non-prepared added transaction from the |
| transaction table and make the passed in transaction assume its |
| identity. Prepared transactions will not be undone. |
| |
| RESOLVE: (sku) I don't think normal user transactions needs to be |
| rolled back in order, but it won't hurt. |
| |
| <B> Should only be used in recovery undo !! </B> |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| */ |
| public boolean getMostRecentTransactionForRollback(RawTransaction tran) |
| { |
| TransactionId id = null; |
| |
| if (!trans.isEmpty()) |
| { |
| for (TransactionTableEntry ent : trans.values()) |
| { |
| if (ent != null && |
| ent.isUpdate() && |
| ent.isRecovery() && |
| !ent.isPrepared()) |
| { |
| // try to locate the most recent one |
| if (id == null || XactId.compare(id, ent.getXid()) < 0) |
| id = ent.getXid(); |
| } |
| |
| if (SanityManager.DEBUG) |
| { |
| if (ent != null && |
| ent.isUpdate() && |
| ent.isRecovery() && |
| (ent.getTransactionStatus() & |
| Xact.RECOVERY_ROLLBACK_FIRST) != 0) |
| { |
| SanityManager.THROWASSERT( |
| "still rollback first xacts in the tran table!"); |
| } |
| } |
| } |
| |
| if (SanityManager.DEBUG) |
| { |
| // if all transactions are prepared then it is possible that |
| // no transaction will be found, in that case id will be null. |
| if (id != null) |
| { |
| SanityManager.ASSERT(findTransactionEntry(id) != null); |
| } |
| else |
| { |
| // all transactions in the table must be prepared. |
| for (TransactionTableEntry ent : trans.values()) |
| { |
| SanityManager.ASSERT(ent.isPrepared()); |
| } |
| } |
| } |
| } |
| |
| return(findAndAssumeTransaction(id, tran)); |
| } |
| |
| /** |
| Get the most recently added transaction that says it is prepared during |
| recovery the transaction table and make the passed in transaction |
| assume its identity. This routine turns off the isRecovery() state |
| <B> Should only be used in recovery handle prepare after undo !! </B> |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| */ |
| |
| /** |
| * Get the most recent recovered prepared transaction. |
| * <p> |
| * Get the most recently added transaction that says it is prepared during |
| * recovery the transaction table and make the passed in transaction |
| * assume its identity. |
| * <p> |
| * This routine, unlike the redo and rollback getMostRecent*() routines |
| * expects a brand new transaction to be passed in. If a candidate |
| * transaction is found, then upon return the transaction table will |
| * be altered such that the old entry no longer exists, and a new entry |
| * will exist pointing to the transaction passed in. The new entry will |
| * look the same as if the prepared transaction had been created during |
| * runtime rather than recovery. |
| * |
| * <B> Should only be used in recovery handle prepare after undo !! </B> |
| * |
| * <P>MT - unsafe, caller is recovery, which is single threaded. |
| * |
| * @return true if a candidate transaction has been found. false if no |
| * prepared/recovery transactions found in the table. |
| * |
| * @param tran Newly allocated transaction to add to link to a entry. |
| * |
| **/ |
| public boolean getMostRecentPreparedRecoveredXact( |
| RawTransaction tran) |
| { |
| TransactionTableEntry found_ent = null; |
| |
| if (!trans.isEmpty()) |
| { |
| TransactionId id = null; |
| GlobalTransactionId gid = null; |
| |
| for (TransactionTableEntry ent : trans.values()) |
| { |
| if (ent != null && |
| ent.isRecovery() && |
| ent.isPrepared()) |
| { |
| // try to locate the most recent one |
| if (id == null || XactId.compare(id, ent.getXid()) < 0) |
| { |
| found_ent = ent; |
| id = ent.getXid(); |
| gid = ent.getGid(); |
| } |
| } |
| } |
| |
| if (SanityManager.DEBUG) |
| { |
| if (found_ent == null) |
| { |
| // if no entry's were found then the transaction table |
| // should have the passed in idle tran, and the rest should |
| // be non-recover, prepared global transactions. |
| for (TransactionTableEntry ent : trans.values()) |
| { |
| if (XactId.compare(ent.getXid(), tran.getId()) != 0) |
| { |
| SanityManager.ASSERT( |
| !ent.isRecovery() && ent.isPrepared()); |
| SanityManager.ASSERT(ent.getGid() != null); |
| } |
| } |
| } |
| } |
| |
| if (found_ent != null) |
| { |
| // At this point there are 2 tt entries of interest: |
| // new_ent - the read only transaction entry that was |
| // created when we allocated a new transaction. |
| // We will just throw this one away after |
| // assuming the identity of the global xact. |
| // found_ent |
| // - the entry of the transaction that we are going |
| // to take over. |
| TransactionTableEntry new_ent = |
| trans.remove(tran.getId()); |
| |
| // At this point only the found_ent should be in the table. |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(findTransactionEntry(id) == found_ent); |
| } |
| |
| ((Xact) tran).assumeGlobalXactIdentity(found_ent); |
| |
| // transform this recovery entry, into a runtime entry. |
| found_ent.unsetRecoveryStatus(); |
| } |
| } |
| |
| return(found_ent != null); |
| } |
| |
| /** |
| Get the least recently added (oldest) transaction |
| @return the RawTransaction's first log instant |
| |
| <P>MT - safe, caller can be recovery or checkpoint |
| */ |
| public LogInstant getFirstLogInstant() |
| { |
| // assume for now that it is acceptable to return null if a transaction |
| // starts right in the middle of this call. |
| |
| LogInstant logInstant = null; |
| for (TransactionTableEntry entry : trans.values()) { |
| if (entry.isUpdate()) { |
| if (logInstant == null || |
| entry.getFirstLog().lessThan(logInstant)) { |
| logInstant = entry.getFirstLog(); |
| } |
| } |
| } |
| |
| return logInstant; |
| } |
| |
| /** |
| Find a transaction using the transaction id, and make the passed in |
| transaction assume the identity and properties of that transaction. |
| |
| <P>MT - unsafe, caller is recovery, which is single threaded. |
| |
| @param id transaction Id |
| @param tran the transaction that was made to assume the transactionID |
| and all other relevant information stored in the transaction table |
| @return true if transaction can be found, false otherwise |
| */ |
| boolean findAndAssumeTransaction( |
| TransactionId id, |
| RawTransaction tran) |
| { |
| // the only caller for this method right now is recovery. |
| // No need to put in any concurrency control |
| TransactionTableEntry ent = null; |
| |
| if (id != null && !trans.isEmpty()) |
| { |
| ent = findTransactionEntry(id); |
| |
| if (SanityManager.DEBUG) |
| { |
| if (ent != null) |
| SanityManager.ASSERT(ent.isRecovery(), |
| "assuming the id of a non-recovery transaction"); |
| } |
| } |
| |
| // if no transaction entry found, set transaction to idle |
| ((Xact)tran).assumeIdentity(ent); |
| |
| return(ent != null); |
| |
| } |
| |
| /********************************************************** |
| * Transaction table vti and diagnostics |
| * MT - unsafe, caller is getting a snap shot which may be inconsistent |
| *********************************************************/ |
| |
| /** |
| Get a printable version of the transaction table |
| */ |
| public TransactionInfo[] getTransactionInfo() |
| { |
| if (trans.isEmpty()) |
| return null; |
| |
| if (SanityManager.DEBUG) { |
| SanityManager.DEBUG("TranTrace", toString()); |
| } |
| |
| final ArrayList<TransactionTableEntry> tinfo = new ArrayList<TransactionTableEntry>(); |
| |
| // Get clones of all the entries in the transaction table. |
| for (TransactionTableEntry entry : trans.values()) { |
| tinfo.add((TransactionTableEntry) entry.clone()); |
| } |
| |
| return tinfo.toArray(new TransactionTableEntry[tinfo.size()]); |
| } |
| |
| public String toString() |
| { |
| if (SanityManager.DEBUG) |
| { |
| final StringBuffer str = new StringBuffer(1000). |
| append("\n**************************\n"). |
| append(super.toString()). |
| append("\nTransaction Table: size = ").append(trans.size()). |
| append(" largestUpdateXactId = ").append(largestUpdateXactId). |
| append("\n"); |
| |
| boolean hasReadOnlyTransaction = false; |
| |
| for (TransactionTableEntry entry : trans.values()) { |
| if (entry.isUpdate()) { |
| str.append(entry); |
| } else { |
| hasReadOnlyTransaction = true; |
| } |
| } |
| |
| if (hasReadOnlyTransaction) |
| { |
| str.append("\n READ ONLY TRANSACTIONS \n"); |
| |
| for (TransactionTableEntry entry : trans.values()) { |
| if (!entry.isUpdate()) { |
| str.append(entry); |
| } |
| } |
| } |
| str.append("---------------------------"); |
| return str.toString(); |
| } |
| else |
| return null; |
| } |
| |
| |
| } |
| |