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