Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/derby/impl/store/raw/xact/TransactionTable.java


1   /*
2   
3      Derby - Class org.apache.derby.impl.store.raw.xact.TransactionTable
4   
5      Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.
6   
7      Licensed under the Apache License, Version 2.0 (the "License");
8      you may not use this file except in compliance with the License.
9      You may obtain a copy of the License at
10  
11        http://www.apache.org/licenses/LICENSE-2.0
12  
13     Unless required by applicable law or agreed to in writing, software
14     distributed under the License is distributed on an "AS IS" BASIS,
15     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16     See the License for the specific language governing permissions and
17     limitations under the License.
18  
19   */
20  
21  package org.apache.derby.impl.store.raw.xact;
22  
23  import org.apache.derby.iapi.services.context.ContextManager;
24  
25  import org.apache.derby.iapi.services.sanity.SanityManager;
26  import org.apache.derby.iapi.services.io.Formatable;
27  import org.apache.derby.iapi.services.io.FormatIdUtil;
28  import org.apache.derby.iapi.services.io.StoredFormatIds;
29  
30  import org.apache.derby.iapi.store.access.TransactionInfo;
31  
32  import org.apache.derby.iapi.store.raw.GlobalTransactionId;
33  
34  import org.apache.derby.iapi.store.raw.log.LogInstant;
35  
36  import org.apache.derby.iapi.store.raw.xact.RawTransaction;
37  import org.apache.derby.iapi.store.raw.xact.TransactionId;
38  
39  import org.apache.derby.iapi.error.StandardException;
40  
41  import org.apache.derby.iapi.services.io.CompressedNumber;
42  
43  import java.util.Hashtable;
44  import java.util.Enumeration;
45  import java.io.ObjectOutput;
46  import java.io.ObjectInput;
47  import java.io.IOException;
48  
49  /**
50    The transaction table is used by the transaction factory to keep track of
51    all transactions that are in the system.
52  
53    <BR> The transction table serves the following purposes: <OL> 
54  
55    <LI> checkpoint - when a checkpoint log record is written out, it writes
56    out also all transactions that have updated the database.  RESOLVE: this is
57    actually not used right now - rather, the transaction table is
58    reconstructed during the redo phase by traversing from the undo LWM.  It is
59    a goal to use this transaction table (and traversing from the redoLWM)
60    instead of rebuilding it to speed up recovery.
61  
62    <LI> Quiesce State - when a  system enters the quiesce state, it needs to account
63    for all transactions in the system, even those which are just started and
64    are in their IDLE state.
65  
66    <LI> TransactionTable VTI - we need to get a snapshot of all transactions
67    in the system for diagnostic purposes.
68    </OL>
69  
70    In order to speed up the time it takes to look up a transaction from the
71    transaction table, each transaction must have a unique transaction Id.
72    This means newly coined transaction must also have a transaction Id.
73  
74    <P>During recovery, there is only one real xact object doing all the
75    recovery work, but there could be many outstanding transactions that are
76    gleamed from the log.  Each of these "recovery transactions" have its on
77    entry into the transaction table but they all share the same Xact object.
78  
79    <P>Multithreading considerations:<BR>
80    TransactionTable must be MT-safe it is called upon by many threads
81    simultaneously (except during recovery)
82  
83    <P><B> This class depends on Hashtable synchronization!! </B>
84  
85  */
86  
87  public class TransactionTable implements Formatable
88  {
89    /*
90     * Fields
91     */
92  
93    private Hashtable trans;
94  
95    private TransactionId largestUpdateXactId;
96  
97    /**
98      MT - not needed for constructor
99    */
100   public TransactionTable()
101   {
102     trans = new Hashtable(17);
103   }
104 
105   /*************************************************************
106    * generic methods called by all clients of transaction table
107    * Must be MT -safe
108    ************************************************************/
109   private TransactionTableEntry findTransactionEntry(TransactionId id)
110   {
111 
112     if (SanityManager.DEBUG)
113       SanityManager.ASSERT(
114                 id != null, "findTransacionEntry with null id");
115 
116     // Hashtable is synchronized
117     return (TransactionTableEntry)trans.get(id);
118   }
119 
120 
121 
122 
123   void add(Xact xact, boolean exclude)
124   {
125     TransactionId id = xact.getId();
126 
127     synchronized(this)
128     {
129       TransactionTableEntry ent = findTransactionEntry(id);
130 
131       if (ent == null)
132       {
133         ent = new TransactionTableEntry
134           (xact, id, 0, 
135            exclude ? TransactionTableEntry.EXCLUDE : 0);
136 
137         trans.put(id, ent);
138 
139         if (SanityManager.DEBUG)
140                 {
141                     if (SanityManager.DEBUG_ON("TranTrace"))
142                     {
143                         SanityManager.DEBUG(
144                             "TranTrace", "adding transaction " + id);
145                         SanityManager.showTrace(new Throwable("TranTrace"));
146                     }
147                 }
148       }
149 
150       if (SanityManager.DEBUG)
151       {
152         if (exclude != ent.needExclusion())
153           SanityManager.THROWASSERT(
154             "adding the same transaction with different exclusion: " +
155             exclude + " " + ent.needExclusion());
156       }
157     }
158 
159     if (SanityManager.DEBUG) {
160 
161       if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
162 
163         if (trans.size() > 50)
164           System.out.println("memoryLeakTrace:TransactionTable " + trans.size());
165       }
166     }
167   }
168 
169   /*
170     remove the transaction Id an return false iff the transaction is found
171     in the table and it doesn't need exclusion during quiesce state
172    */
173   boolean remove(TransactionId id)
174   {
175     if (SanityManager.DEBUG)
176       SanityManager.ASSERT(
177                 id != null, 
178                 "cannot remove transaction from table with null id");
179 
180     if (SanityManager.DEBUG)
181         {
182             if (SanityManager.DEBUG_ON("TranTrace"))
183             {
184                 SanityManager.DEBUG(
185                     "TranTrace", "removing transaction " + id);
186                 SanityManager.showTrace(new Throwable("TranTrace"));
187             }
188         }
189 
190     // Hashtable is synchronized
191      TransactionTableEntry ent = (TransactionTableEntry)trans.remove(id);
192      return (ent == null || ent.needExclusion());
193   }
194 
195 
196   /**
197     Change a transaction to update or add an update transaction to this table.
198 
199     @param tid the transaction id
200     @param tran the transaction to be added
201     @param transactionStatus the transaction status that is stored in the
202         BeginXact log record
203    */
204   public void addUpdateTransaction(TransactionId tid, RawTransaction tran,
205                    int transactionStatus)
206   {
207 
208     // we need to synchronize on the transaction table because we have to
209     // prevent this state change from happening when the transaction table
210     // itself is written out to the checkpoint.  This is the only
211     // protection the TransactionTableEntry has to prevent fields in myxact
212     // from changing underneath it while it is being written out.
213     synchronized(this)
214     {
215       TransactionTableEntry ent = findTransactionEntry(tid);
216 
217       if (ent != null)
218       {
219         // this happens during run time, when a transaction that is
220         // already started changed status to an update transaction
221 
222         ent.updateTransactionStatus((Xact)tran, transactionStatus,
223                       TransactionTableEntry.UPDATE) ;
224       }
225       else
226       {
227         // this happens during recovery, that's why we haven't seen
228         // this transaction before - it is added in the doMe of the 
229         // BeginXact log record.
230         //
231         // No matter what this transaction is, it won't need to be run
232         // in quiesce state because we are in recovery.
233         ent = new TransactionTableEntry((Xact)tran, tid, transactionStatus, 
234                         TransactionTableEntry.UPDATE | 
235                         TransactionTableEntry.EXCLUDE |
236                         TransactionTableEntry.RECOVERY);
237         trans.put(tid, ent);
238 
239       }
240 
241       if (XactId.compare(ent.getXid(), largestUpdateXactId) > 0)
242         largestUpdateXactId = ent.getXid();
243     }
244   }
245 
246   /**
247       Change update transaction to non-update
248 
249     <P>MT - MT safe, since vector is MT-safe.
250 
251     @param id the transaction Id
252     */
253   void removeUpdateTransaction(TransactionId id)
254   {
255     // we need to synchronize on the transaction table because we have to
256     // prevent this state change from happening when the transaction table
257     // itself is written out to the checkpoint.  This is the only
258     // protection the TransactionTableEntry has to prevent fields in myxact
259     // from changing underneath it while it is being written out.
260 
261     synchronized (this)
262     {
263       TransactionTableEntry ent = findTransactionEntry(id);
264 
265       if (SanityManager.DEBUG)
266       {
267         SanityManager.ASSERT(ent != null, 
268          "removing update transaction that is not there");
269       }
270 
271       ent.removeUpdateTransaction();
272 
273       // If we are committing a recovery transaction, remove it from the
274       // transaction table.  The xact object which is doing the work is
275       // not going to be closed even though the transaction is done.
276       if (ent.isRecovery())
277         remove(id);
278     }
279 
280     return;
281   }
282 
283     /**************************************************************************
284      * Transaction table methods used by XA.
285      **************************************************************************
286      */
287 
288     /**
289      * Return the hash table to the XA layer.
290      * <p>
291      * The XA code will do linear read-only operations on the hash table,
292      * write operations are only done in this module.  It is a little ugly
293      * to export the hash table, but I wanted to move the XA specific code
294      * into the XA module, so that we could configure out the XA code if
295      * necessary.
296      * <p>
297      *
298    * Must be MT -safe, depends on sync hash table, and must get 
299      *     synchronized(hash_table) for linear searches.
300      *
301    * @return The ContextManager of the transaction being searched for.
302      *
303      **/
304   public Hashtable getTableForXA()
305   {
306         return(trans);
307   }
308 
309   /**
310       Change transaction to prepared.
311 
312     <P>MT - unsafe, caller is recovery, which is single threaded.
313 
314     @param id the transaction Id
315     */
316   void prepareTransaction(TransactionId id)
317   {
318     // we need to synchronize on the transaction table because we have to
319     // prevent this state change from happening when the transaction table
320     // itself is written out to the checkpoint.  This is the only
321     // protection the TransactionTableEntry has to prevent fields in myxact
322     // from changing underneath it while it is being written out.
323 
324         TransactionTableEntry ent = findTransactionEntry(id);
325 
326         if (SanityManager.DEBUG)
327         {
328             SanityManager.ASSERT(
329                 ent != null, "preparing transaction that is not there");
330         }
331 
332         ent.prepareTransaction();
333 
334     return;
335   }
336 
337     /**
338      * Find a transaction in the table by Global transaction id.
339      * <p>
340      * Only called by XactXAResourceManager.find() during offline recovery
341      * of in-doubt transactions, we do not expect this to be called often 
342      * so performance is not critical.  Just to linear search of id's. 
343      * <p>
344      *
345    * @return The ContextManager of the transaction being searched for.
346      *
347      * @param global_id The global transaction we are searching for.
348      **/
349   public ContextManager findTransactionContextByGlobalId(
350     GlobalXactId global_id)
351   {
352         ContextManager cm              = null;
353 
354         // Need to hold sync while linear searching the hash table.
355         synchronized (trans)
356         {
357             for (Enumeration e = trans.elements(); e.hasMoreElements();) 
358             {
359                 TransactionTableEntry entry = 
360                     (TransactionTableEntry) e.nextElement();
361 
362                 if (entry.getGid() != null && 
363           entry.getGid().equals(global_id))
364                 {
365                     cm = entry.getXact().getContextManager();
366                     break;
367                 }
368             }
369         }
370               
371     return(cm);
372   }
373 
374 
375   /***********************************************************
376    * called when system is being quiesced, must be MT - safe
377    ***********************************************************/
378   /**
379     Return true if there is no transaction actively updating the database.
380     New transaction may be started or old transaction committed
381     right afterward, the caller of this routine must have other ways to
382     stop transactions from starting or ending.
383 
384     <P>MT - safe
385   */
386   boolean hasActiveUpdateTransaction()
387   {
388     synchronized (this)
389     {
390       for (Enumeration e = trans.elements(); e.hasMoreElements(); )
391       {
392         TransactionTableEntry ent = (TransactionTableEntry)e.nextElement();
393         if (ent != null && ent.isUpdate())
394           return true;
395       }
396     }
397     return false;
398   }
399 
400 
401 
402   /************************************************************
403    * methods called only by checkpoint
404    ***********************************************************/
405   /*
406    * Formatable methods
407    */
408 
409   /**
410     Return my format identifier.
411   */
412   public int getTypeFormatId() {
413     return StoredFormatIds.RAW_STORE_TRANSACTION_TABLE;
414   }
415 
416   /**
417     @exception IOException problem reading the transaction table
418   */
419   public void writeExternal(ObjectOutput out) throws IOException 
420   {
421     //don't let the transactions status change while writing out(beetle:5533)
422     //Note: syncing both on trans and this variable could be avoided if
423     //all the routines in this class are sycned on "this" and does not
424     //depend on hash table synchronization. But that will be overkill 
425     //because this routine gets called only on checkpoints and others
426     //are used more often.
427 
428     synchronized(this)
429     {  
430       // don't touch the transaction table when I am being written out
431       synchronized(trans)
432       {
433         int count = 0;
434         int maxcount = trans.size();
435 
436         // first count up the number of active update transactions 
437         for (Enumeration e = trans.elements();
438            e.hasMoreElements(); )
439         {
440           TransactionTableEntry ent = (TransactionTableEntry)e.nextElement();
441           if (ent != null && ent.isUpdate())
442             count++;
443         }
444 
445         CompressedNumber.writeInt(out, count);
446 
447         // now write them out
448         if (count > 0)
449         {
450           for (Enumeration e = trans.elements();
451              e.hasMoreElements() ; )
452           {
453             TransactionTableEntry ent = (TransactionTableEntry)e.nextElement();
454             if (ent != null && ent.isUpdate())
455             {
456               // only writes out update transaction
457               out.writeObject(ent);
458             }
459           }
460         }
461       }
462     }
463   }
464 
465   /************************************************************
466    * methods called only by recovery
467    ************************************************************/
468 
469 
470   /**
471     @exception IOException problem reading the transaction table
472     @exception ClassNotFoundException problem reading the transaction table
473    */
474   public void readExternal(ObjectInput in) 
475      throws IOException, ClassNotFoundException
476   {
477     // RESOLVE: this is only read in checkpoint record, but we have not
478     // finish the work on using this transaction table to cut down on redo
479     // so this transaction table is effectively and futilely thrown away!
480 
481     int count = CompressedNumber.readInt(in);
482     if (count == 0)
483       return;
484 
485     for (int i = 0; i < count; i++)
486     {
487       TransactionTableEntry ent = 
488         (TransactionTableEntry)in.readObject();
489 
490       if (SanityManager.DEBUG)
491         SanityManager.ASSERT(
492                     ent.getXid() != null,
493                     "read in transaction table entry with null id");
494 
495       trans.put(ent.getXid(), ent);
496 
497       if (ent.isUpdate() && 
498                 XactId.compare(ent.getXid(), largestUpdateXactId) > 0)
499             {
500         largestUpdateXactId = ent.getXid();
501             }
502     }
503 
504 
505   }
506 
507   /**
508     Return the largest update transactionId I have seen so far.
509 
510     <P>MT - unsafe, caller is recovery, which is single threaded.
511   */
512   public TransactionId largestUpdateXactId()
513   {
514     return largestUpdateXactId;
515   }
516 
517 
518   /**
519     Is there an active internal transaction in the transaction table.
520 
521     <P>MT - unsafe, caller is recovery, which is single threaded.
522   */
523   public boolean hasRollbackFirstTransaction()
524   {
525     for (Enumeration e = trans.elements();
526        e.hasMoreElements() ; )
527     {
528       TransactionTableEntry ent = (TransactionTableEntry)e.nextElement();
529 
530       if (ent != null && ent.isRecovery() && 
531         (ent.getTransactionStatus() & 
532                      Xact.RECOVERY_ROLLBACK_FIRST) != 0)
533             {
534         return true;
535             }
536     }
537     return false;
538   }
539 
540   /**
541     Is there a prepared transaction in the transaction table.
542 
543     <P>MT - unsafe, caller is recovery, which is single threaded.
544   */
545   public boolean hasPreparedRecoveredXact()
546   {
547     for (Enumeration e = trans.elements(); e.hasMoreElements(); )
548     {
549       TransactionTableEntry ent = (TransactionTableEntry) e.nextElement();
550 
551       if (ent != null && ent.isRecovery() && 
552         (ent.getTransactionStatus() & Xact.END_PREPARED) != 0)
553             {
554         return true;
555             }
556     }
557     return false;
558   }
559 
560 
561   /**
562     Get the most recently added transaction that says it needs to be
563     rolled back first (an InternalXact) from the transaction table and make
564     the passed in transaction assume its identity. 
565     <B> Should only be used in recovery undo !! </B>
566     RESOLVE: (sku)I don't think even these internal transactions need to be
567     rolled back in the reverse order, because they are physical in nature.
568     But it won't hurt.
569 
570     <P>MT - unsafe, caller is recovery, which is single threaded.
571   */
572   public boolean getMostRecentRollbackFirstTransaction(RawTransaction tran)
573   {
574 
575     if (trans.isEmpty())
576     {
577       // set tranaction to idle
578       return findAndAssumeTransaction((TransactionId)null, tran);
579     }
580 
581     TransactionId id = null;
582     for (Enumeration e = trans.elements();
583        e.hasMoreElements() ; )
584     {
585       TransactionTableEntry ent = (TransactionTableEntry)e.nextElement();
586 
587       if (ent != null && ent.isUpdate() && ent.isRecovery() &&
588         (ent.getTransactionStatus() & Xact.RECOVERY_ROLLBACK_FIRST) != 0)
589       {
590         // try to locate the most recent one
591         if (id == null || XactId.compare(id, ent.getXid()) < 0)
592           id = ent.getXid();
593       }
594     }
595 
596     if (id == null)      // set transaction to idle
597     {
598       return findAndAssumeTransaction(id, tran);
599     }
600     else
601     {
602       // there is a rollback first transaction
603       boolean found = 
604                 findAndAssumeTransaction(id, tran);
605 
606       if (SanityManager.DEBUG)
607             {
608                 if (!found)
609                 {
610                     SanityManager.THROWASSERT(
611                         "cannot find transaction " + id + " in table");
612                 }
613             }
614 
615       return true;
616     }
617   }
618 
619   /**
620     Get the most recently non-prepared added transaction from the 
621         transaction table and make the passed in transaction assume its 
622         identity.  Prepared transactions will not be undone.
623 
624     RESOLVE: (sku) I don't think normal user transactions needs to be
625     rolled back in order, but it won't hurt.
626 
627     <B> Should only be used in recovery undo !! </B>
628 
629     <P>MT - unsafe, caller is recovery, which is single threaded.
630   */
631   public boolean getMostRecentTransactionForRollback(RawTransaction tran)
632   {
633         TransactionId id = null;
634 
635         if (!trans.isEmpty())
636     {
637       for (Enumeration e = trans.elements();
638          e.hasMoreElements() ; )
639       {
640         TransactionTableEntry ent =
641            (TransactionTableEntry)e.nextElement();
642 
643         if (ent != null         && 
644                     ent.isUpdate()      && 
645                     ent.isRecovery()    && 
646                     !ent.isPrepared())
647         {
648           // try to locate the most recent one
649           if (id == null || XactId.compare(id, ent.getXid()) < 0)
650             id = ent.getXid();
651         }
652 
653         if (SanityManager.DEBUG)
654         {
655           if (ent != null         && 
656                         ent.isUpdate()      && 
657                         ent.isRecovery()    &&
658             (ent.getTransactionStatus() & 
659                          Xact.RECOVERY_ROLLBACK_FIRST) != 0)
660                     {
661             SanityManager.THROWASSERT(
662                             "still rollback first xacts in the tran table!");
663                     }
664         }
665       }
666 
667       if (SanityManager.DEBUG)
668             {
669                 // if all transactions are prepared then it is possible that
670                 // no transaction will be found, in that case id will be null.
671                 if (id != null)
672                 {
673                     SanityManager.ASSERT(findTransactionEntry(id) != null);
674                 }
675                 else
676                 {
677                     // all transactions in the table must be prepared.
678                     for (Enumeration e = trans.elements(); e.hasMoreElements();)
679                     {
680                         TransactionTableEntry ent =
681                             (TransactionTableEntry)e.nextElement();
682                         SanityManager.ASSERT(ent.isPrepared());
683                     }
684                 }
685             }
686     }
687 
688         return(findAndAssumeTransaction(id, tran));
689   }  
690 
691   /**
692     Get the most recently added transaction that says it is prepared during
693         recovery the transaction table and make the passed in transaction 
694         assume its identity. This routine turns off the isRecovery() state
695     <B> Should only be used in recovery handle prepare after undo !! </B>
696 
697     <P>MT - unsafe, caller is recovery, which is single threaded.
698   */
699 
700     /**
701      * Get the most recent recovered prepared transaction.
702      * <p>
703      * Get the most recently added transaction that says it is prepared during 
704      * recovery the transaction table and make the passed in transaction 
705      * assume its identity. 
706      * <p>
707      * This routine, unlike the redo and rollback getMostRecent*() routines
708      * expects a brand new transaction to be passed in.  If a candidate 
709      * transaction is found, then upon return the transaction table will 
710      * be altered such that the old entry no longer exists, and a new entry
711      * will exist pointing to the transaction passed in.  The new entry will
712      * look the same as if the prepared transaction had been created during
713      * runtime rather than recovery.
714      *
715      * <B> Should only be used in recovery handle prepare after undo !! </B>
716      *
717      * <P>MT - unsafe, caller is recovery, which is single threaded.
718      *
719    * @return true if a candidate transaction has been found.  false if no
720      *         prepared/recovery transactions found in the table.
721      *
722      * @param tran   Newly allocated transaction to add to link to a entry.
723      *
724      **/
725   public boolean getMostRecentPreparedRecoveredXact(
726     RawTransaction tran)
727   {
728         TransactionTableEntry   found_ent   = null;
729 
730         if (!trans.isEmpty())
731     {
732             TransactionId           id          = null;
733             GlobalTransactionId     gid         = null;
734             TransactionTableEntry   ent;
735 
736       for (Enumeration e = trans.elements(); e.hasMoreElements(); )
737       {
738         ent = (TransactionTableEntry)e.nextElement();
739 
740         if (ent != null         && 
741                     ent.isRecovery()    && 
742                     ent.isPrepared())
743         {
744           // try to locate the most recent one
745           if (id == null || XactId.compare(id, ent.getXid()) < 0)
746                     {
747                         found_ent = ent;
748             id        = ent.getXid();
749             gid       = ent.getGid();
750                     }
751         }
752       }
753 
754             if (SanityManager.DEBUG)
755             {
756                 if (found_ent == null)
757                 {
758                     // if no entry's were found then the transaction table
759                     // should have the passed in idle tran, and the rest should
760                     // be non-recover, prepared global transactions.
761                     for (Enumeration e = trans.elements(); e.hasMoreElements();)
762                     {
763                         ent = (TransactionTableEntry)e.nextElement();
764 
765                         if (XactId.compare(ent.getXid(), tran.getId()) != 0)
766                         {
767                             SanityManager.ASSERT(
768                                 !ent.isRecovery() && ent.isPrepared());
769                             SanityManager.ASSERT(ent.getGid() != null);
770                         }
771                     }
772                 }
773             }
774 
775             if (found_ent != null)
776             {
777                 // At this point there are 2 tt entries of interest:
778                 //     new_ent - the read only transaction entry that was 
779                 //               created when we allocated a new transaction.
780                 //               We will just throw this one away after 
781                 //               assuming the identity of the global xact.
782                 //     found_ent
783                 //             - the entry of the transaction that we are going
784                 //               to take over.
785                 TransactionTableEntry new_ent =
786                     (TransactionTableEntry) trans.remove(tran.getId());
787 
788                 // At this point only the found_ent should be in the table.
789                 if (SanityManager.DEBUG)
790                 {
791                   SanityManager.ASSERT(findTransactionEntry(id) == found_ent);
792                 }
793 
794                 ((Xact) tran).assumeGlobalXactIdentity(found_ent);
795 
796                 // transform this recovery entry, into a runtime entry.
797                 found_ent.unsetRecoveryStatus();
798             }
799     }
800 
801         return(found_ent != null);
802   }
803 
804   /**
805     Get the least recently added (oldest) transaction
806     @return the RawTransaction's first log instant
807 
808     <P>MT - safe, caller can be recovery or checkpoint
809   */
810   public LogInstant getFirstLogInstant()
811   {
812     // assume for now that it is acceptable to return null if a transaction
813     // starts right in the middle of this call.
814 
815     if (trans.isEmpty())
816         {
817       return null;
818         }
819     else
820     {
821       LogInstant logInstant = null;
822             
823             // bug 5632: need to sychronize so that another thread does not 
824             // come in and disrupt the for loop, we got an exception on next,
825             // likely because hash table changed by another thread after
826             // hasMoreElements() called, but before nextElement().
827 
828             synchronized (trans)
829             {
830                 for (Enumeration e = trans.elements(); e.hasMoreElements(); )
831                 {
832                     TransactionTableEntry ent =
833                         (TransactionTableEntry)e.nextElement();
834 
835                     if (ent != null && ent.isUpdate())
836                     {
837                         if (logInstant == null || 
838                             ent.getFirstLog().lessThan(logInstant))
839                         {
840                             logInstant = ent.getFirstLog();
841                         }
842                     }
843                 }
844             }
845 
846       return logInstant;
847     }
848   }
849 
850   /**
851     Find a transaction using the transaction id, and make the passed in
852     transaction assume the identity and properties of that transaction.
853 
854     <P>MT - unsafe, caller is recovery, which is single threaded.
855 
856     @param id transaction Id
857     @param tran the transaction that was made to assume the transactionID
858     and all other relavent information stored in the transaction table
859     @return true if transaction can be found, false otherwise
860   */
861   boolean findAndAssumeTransaction(
862     TransactionId       id, 
863     RawTransaction      tran)
864   {
865     // the only caller for this method right now is recovery.  
866         // No need to put in any concurrency control
867     TransactionTableEntry ent = null;
868 
869     if (id != null && !trans.isEmpty())
870     {
871       ent = findTransactionEntry(id);
872 
873       if (SanityManager.DEBUG)
874       {
875         if (ent != null)
876           SanityManager.ASSERT(ent.isRecovery(),
877           "assuming the id of a non-recovery transaction");
878       }
879     }
880 
881     // if no transaction entry found, set transaction to idle
882         ((Xact)tran).assumeIdentity(ent);
883 
884     return(ent != null);
885 
886   }
887 
888   /**********************************************************
889    * Transaction table vti and diagnostics
890    * MT - unsafe, caller is getting a snap shot which may be inconsistent 
891    *********************************************************/
892 
893   /**
894     Get a printable version of the transaction table
895    */
896   public TransactionInfo[] getTransactionInfo()
897   {
898     if (trans.isEmpty())
899       return null;
900 
901     // while taking a snap shot, no adding or removing of transaction
902     TransactionInfo[] tinfo;
903 
904     if (SanityManager.DEBUG)
905       SanityManager.DEBUG("TranTrace", toString());
906 
907     synchronized(this)
908     {
909       int ntran = trans.size();
910       tinfo = new TransactionTableEntry[ntran];
911 
912       LogInstant logInstant = null;
913       int i = 0;
914 
915       for (Enumeration e = trans.elements();
916          e.hasMoreElements(); )
917       {
918         TransactionTableEntry ent =
919           (TransactionTableEntry)e.nextElement();
920 
921         if (ent != null)
922           tinfo[i++] = (TransactionTableEntry)ent.clone();
923 
924         if (SanityManager.DEBUG)
925           SanityManager.ASSERT(ent != null, "transaction table has null entry");
926       }
927     }
928 
929     return tinfo;
930   }
931 
932   public String toString()
933   {
934     if (SanityManager.DEBUG)
935     {
936       StringBuffer str = new StringBuffer(1000).
937         append("\n**************************\n").
938         append(super.toString()).
939         append("\nTransaction Table: size = ").append(trans.size()).
940         append(" largestUpdateXactId = ").append(largestUpdateXactId).
941         append("\n");
942 
943       boolean hasReadOnlyTransaction = false;
944 
945       for (Enumeration e = trans.elements();
946          e.hasMoreElements(); )
947       {
948         TransactionTableEntry ent =
949           (TransactionTableEntry)e.nextElement(); 
950 
951         if (ent != null && ent.isUpdate())
952           str.append(ent.toString());
953 
954         if (ent != null && !ent.isUpdate())
955           hasReadOnlyTransaction = true;
956       }
957 
958       if (hasReadOnlyTransaction)
959       {
960         str.append("\n READ ONLY TRANSACTIONS \n");
961 
962         for (Enumeration e = trans.elements();
963            e.hasMoreElements(); )
964         {
965           TransactionTableEntry ent =
966             (TransactionTableEntry)e.nextElement(); 
967 
968           if (ent != null && !ent.isUpdate())
969             str.append(ent.toString());
970         }
971       }
972       str.append("---------------------------");
973       return str.toString();
974     }
975     else
976       return null;
977   }
978 
979 
980 }
981