📄 transactiontable.java
字号:
/* Derby - Class org.apache.derby.impl.store.raw.xact.TransactionTable Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed 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.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.services.io.Formatable;import org.apache.derby.iapi.services.io.FormatIdUtil;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.error.StandardException;import org.apache.derby.iapi.services.io.CompressedNumber;import java.util.Hashtable;import java.util.Enumeration;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><B> This class depends on Hashtable synchronization!! </B>*/public class TransactionTable implements Formatable{ /* * Fields */ private Hashtable trans; private TransactionId largestUpdateXactId; /** MT - not needed for constructor */ public TransactionTable() { trans = new Hashtable(17); } /************************************************************* * 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 (TransactionTableEntry)trans.get(id); } void add(Xact xact, boolean exclude) { TransactionId id = xact.getId(); synchronized(this) { TransactionTableEntry ent = findTransactionEntry(id); if (ent == null) { ent = new TransactionTableEntry (xact, id, 0, exclude ? TransactionTableEntry.EXCLUDE : 0); trans.put(id, ent); if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON("TranTrace")) { SanityManager.DEBUG( "TranTrace", "adding transaction " + id); SanityManager.showTrace(new Throwable("TranTrace")); } } } if (SanityManager.DEBUG) { if (exclude != ent.needExclusion()) SanityManager.THROWASSERT( "adding the same transaction with different exclusion: " + exclude + " " + ent.needExclusion()); } } 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 = (TransactionTableEntry)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. ************************************************************************** */ /** * Return the hash table to the XA layer. * <p> * The XA code will do linear read-only operations on the hash table, * write operations are only done in this module. It is a little ugly * to export the hash table, but I wanted to move the XA specific code * into the XA module, so that we could configure out the XA code if * necessary. * <p> * * Must be MT -safe, depends on sync hash table, and must get * synchronized(hash_table) for linear searches. * * @return The ContextManager of the transaction being searched for. * **/ public Hashtable getTableForXA() { return(trans); } /** 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( GlobalXactId global_id) { ContextManager cm = null; // Need to hold sync while linear searching the hash table. synchronized (trans) { for (Enumeration e = trans.elements(); e.hasMoreElements();) { TransactionTableEntry entry = (TransactionTableEntry) e.nextElement(); if (entry != null) { GlobalTransactionId entry_gid = entry.getGid(); if (entry_gid != null && entry_gid.equals(global_id)) { cm = entry.getXact().getContextManager(); break; } } } } return(cm); } /*********************************************************** * 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 (Enumeration e = trans.elements(); e.hasMoreElements(); ) { TransactionTableEntry ent = (TransactionTableEntry)e.nextElement(); if (ent != null && ent.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(ObjectOutput out) throws IOException { //don't let the transactions status change while writing out(beetle:5533) //Note: syncing both on trans and this variable could be avoided if //all the routines in this class are sycned on "this" and does not //depend on hash table synchronization. But that will be overkill //because this routine gets called only on checkpoints and others //are used more often. synchronized(this) { // don't touch the transaction table when I am being written out synchronized(trans) { int count = 0; int maxcount = trans.size(); // first count up the number of active update transactions for (Enumeration e = trans.elements(); e.hasMoreElements(); ) { TransactionTableEntry ent = (TransactionTableEntry)e.nextElement(); if (ent != null && ent.isUpdate()) count++; } CompressedNumber.writeInt(out, count); // now write them out if (count > 0) { for (Enumeration e = trans.elements(); e.hasMoreElements() ; ) { TransactionTableEntry ent = (TransactionTableEntry)e.nextElement(); if (ent != null && ent.isUpdate()) { // only writes out update transaction out.writeObject(ent); } } } } } } /************************************************************ * 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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -