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