1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.store.raw.xact.XactFactory |
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.reference.Property; |
24 | import org.apache.derby.iapi.reference.SQLState; |
25 | |
26 | import org.apache.derby.iapi.services.context.ContextService; |
27 | import org.apache.derby.iapi.services.context.ContextManager; |
28 | import org.apache.derby.iapi.services.daemon.DaemonService; |
29 | import org.apache.derby.iapi.services.daemon.Serviceable; |
30 | import org.apache.derby.iapi.services.locks.LockFactory; |
31 | import org.apache.derby.iapi.services.monitor.ModuleControl; |
32 | import org.apache.derby.iapi.services.monitor.ModuleSupportable; |
33 | import org.apache.derby.iapi.services.monitor.Monitor; |
34 | import org.apache.derby.iapi.services.sanity.SanityManager; |
35 | import org.apache.derby.iapi.services.io.Formatable; |
36 | import org.apache.derby.iapi.services.io.FormatIdUtil; |
37 | import org.apache.derby.iapi.services.uuid.UUIDFactory; |
38 | import org.apache.derby.catalog.UUID; |
39 | |
40 | import org.apache.derby.iapi.store.access.AccessFactoryGlobals; |
41 | import org.apache.derby.iapi.store.access.TransactionController; |
42 | import org.apache.derby.iapi.store.access.TransactionInfo; |
43 | |
44 | import org.apache.derby.iapi.store.access.AccessFactory; |
45 | |
46 | import org.apache.derby.iapi.store.access.xa.XAResourceManager; |
47 | |
48 | import org.apache.derby.iapi.store.raw.LockingPolicy; |
49 | import org.apache.derby.iapi.store.raw.GlobalTransactionId; |
50 | import org.apache.derby.iapi.store.raw.RawStoreFactory; |
51 | import org.apache.derby.iapi.store.raw.Transaction; |
52 | |
53 | import org.apache.derby.iapi.store.raw.data.DataFactory; |
54 | |
55 | import org.apache.derby.iapi.store.raw.log.LogFactory; |
56 | import org.apache.derby.iapi.store.raw.log.LogInstant; |
57 | |
58 | import org.apache.derby.iapi.store.raw.xact.TransactionFactory; |
59 | import org.apache.derby.iapi.store.raw.xact.RawTransaction; |
60 | import org.apache.derby.iapi.store.raw.xact.TransactionId; |
61 | |
62 | import org.apache.derby.iapi.error.StandardException; |
63 | |
64 | import org.apache.derby.impl.store.raw.xact.XactXAResourceManager; |
65 | |
66 | import java.util.Enumeration; |
67 | import java.util.Properties; |
68 | import java.util.Hashtable; |
69 | |
70 | public class XactFactory implements TransactionFactory, ModuleControl, ModuleSupportable |
71 | { |
72 | protected static final String USER_CONTEXT_ID = "UserTransaction"; |
73 | protected static final String NESTED_READONLY_USER_CONTEXT_ID = |
74 | "NestedRawReadOnlyUserTransaction"; |
75 | protected static final String NESTED_UPDATE_USER_CONTEXT_ID = |
76 | "NestedRawUpdateUserTransaction"; |
77 | protected static final String INTERNAL_CONTEXT_ID = "InternalTransaction"; |
78 | protected static final String NTT_CONTEXT_ID = "NestedTransaction"; |
79 | |
80 | /* |
81 | ** Fields |
82 | */ |
83 | |
84 | protected DaemonService rawStoreDaemon; |
85 | |
86 | private UUIDFactory uuidFactory; |
87 | protected ContextService contextFactory; |
88 | protected LockFactory lockFactory; |
89 | protected LogFactory logFactory; |
90 | protected DataFactory dataFactory; |
91 | protected RawStoreFactory rawStoreFactory; |
92 | |
93 | public TransactionTable ttab; |
94 | private long tranId; |
95 | private LockingPolicy[][] lockingPolicies = new LockingPolicy[3][6]; |
96 | |
97 | private boolean inCreateNoLog = false; // creating database, no logging |
98 | |
99 | private XAResourceManager xa_resource; |
100 | |
101 | private Object backupSemaphore = new Object(); |
102 | private long backupBlockingOperations = 0; |
103 | private boolean inBackup = false; |
104 | |
105 | /* |
106 | ** Constructor |
107 | */ |
108 | |
109 | public XactFactory() { |
110 | super(); |
111 | } |
112 | |
113 | /* |
114 | ** Methods of ModuleControl |
115 | */ |
116 | public boolean canSupport(Properties startParams) { |
117 | return true; |
118 | } |
119 | |
120 | public void boot(boolean create, Properties properties) |
121 | throws StandardException |
122 | { |
123 | |
124 | uuidFactory = Monitor.getMonitor().getUUIDFactory(); |
125 | |
126 | contextFactory = ContextService.getFactory(); |
127 | |
128 | lockFactory = |
129 | (LockFactory) Monitor.bootServiceModule(false, this, |
130 | org.apache.derby.iapi.reference.Module.LockFactory, properties); |
131 | |
132 | |
133 | // adding entries to locking policy table which means we support that |
134 | // level of concurrency. |
135 | lockingPolicies[LockingPolicy.MODE_NONE] |
136 | [TransactionController.ISOLATION_NOLOCK] = |
137 | new NoLocking(); |
138 | |
139 | lockingPolicies[LockingPolicy.MODE_RECORD] |
140 | [TransactionController.ISOLATION_NOLOCK] = |
141 | new NoLocking(); |
142 | lockingPolicies[LockingPolicy.MODE_RECORD] |
143 | [TransactionController.ISOLATION_READ_UNCOMMITTED] = |
144 | new RowLocking1(lockFactory); |
145 | lockingPolicies[LockingPolicy.MODE_RECORD] |
146 | [TransactionController.ISOLATION_READ_COMMITTED] = |
147 | new RowLocking2(lockFactory); |
148 | lockingPolicies[LockingPolicy.MODE_RECORD] |
149 | [TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK] = |
150 | new RowLocking2nohold(lockFactory); |
151 | lockingPolicies[LockingPolicy.MODE_RECORD] |
152 | [TransactionController.ISOLATION_REPEATABLE_READ] = |
153 | new RowLockingRR(lockFactory); |
154 | lockingPolicies[LockingPolicy.MODE_RECORD] |
155 | [TransactionController.ISOLATION_SERIALIZABLE] = |
156 | new RowLocking3(lockFactory); |
157 | |
158 | lockingPolicies[LockingPolicy.MODE_CONTAINER] |
159 | [TransactionController.ISOLATION_NOLOCK] = |
160 | new NoLocking(); |
161 | |
162 | // note that current implementation of read uncommitted still gets |
163 | // container and container intent locks to prevent concurrent ddl. Thus |
164 | // the read uncommitted containerlocking implementation is the same as |
165 | // the read committed implementation. Future customer requests may |
166 | // force us to change this - we will then have to figure out how to |
167 | // handle a table being dropped while a read uncommitted scanner is |
168 | // reading it - currently we just block that from happening. |
169 | lockingPolicies[LockingPolicy.MODE_CONTAINER] |
170 | [TransactionController.ISOLATION_READ_UNCOMMITTED] = |
171 | new ContainerLocking2(lockFactory); |
172 | lockingPolicies[LockingPolicy.MODE_CONTAINER] |
173 | [TransactionController.ISOLATION_READ_COMMITTED] = |
174 | new ContainerLocking2(lockFactory); |
175 | lockingPolicies[LockingPolicy.MODE_CONTAINER] |
176 | [TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK] = |
177 | new ContainerLocking2(lockFactory); |
178 | lockingPolicies[LockingPolicy.MODE_CONTAINER] |
179 | [TransactionController.ISOLATION_REPEATABLE_READ] = |
180 | new ContainerLocking3(lockFactory); |
181 | lockingPolicies[LockingPolicy.MODE_CONTAINER] |
182 | [TransactionController.ISOLATION_SERIALIZABLE] = |
183 | new ContainerLocking3(lockFactory); |
184 | |
185 | |
186 | if (create) |
187 | { |
188 | ttab = new TransactionTable(); |
189 | |
190 | String noLog = |
191 | properties.getProperty(Property.CREATE_WITH_NO_LOG); |
192 | |
193 | inCreateNoLog = (noLog != null && Boolean.valueOf(noLog).booleanValue()); |
194 | |
195 | } |
196 | } |
197 | |
198 | public void stop() { |
199 | |
200 | if (rawStoreDaemon != null) |
201 | rawStoreDaemon.stop(); |
202 | |
203 | } |
204 | |
205 | /* |
206 | ** Methods of TransactionFactory |
207 | */ |
208 | |
209 | /** |
210 | Get the LockFactory to use with this store. |
211 | */ |
212 | public LockFactory getLockFactory() { |
213 | return lockFactory; |
214 | } |
215 | |
216 | |
217 | /** |
218 | Database creation finished |
219 | @exception StandardException standard cloudscape error policy |
220 | */ |
221 | public void createFinished() throws StandardException |
222 | { |
223 | if (!inCreateNoLog) |
224 | { |
225 | throw StandardException.newException(SQLState.XACT_CREATE_NO_LOG); |
226 | } |
227 | |
228 | // make sure there is no active update transaction |
229 | if (ttab.hasActiveUpdateTransaction()) |
230 | { |
231 | throw StandardException.newException(SQLState.XACT_CREATE_NO_LOG); |
232 | } |
233 | |
234 | inCreateNoLog = false; |
235 | } |
236 | |
237 | /** |
238 | * Common work done to create local or global transactions. |
239 | * |
240 | * @param rsf the raw store factory creating this xact. |
241 | * @param cm the current context manager to associate the xact with. |
242 | * @param compatibilitySpace |
243 | * if null, use the transaction being created, else if |
244 | * non-null use this compatibilitySpace. |
245 | * |
246 | * @exception StandardException Standard exception policy. |
247 | **/ |
248 | private RawTransaction startCommonTransaction( |
249 | RawStoreFactory rsf, |
250 | ContextManager cm, |
251 | boolean readOnly, |
252 | Object compatibilitySpace, |
253 | String xact_context_id, |
254 | String transName, |
255 | boolean excludeMe) |
256 | throws StandardException |
257 | { |
258 | |
259 | if (SanityManager.DEBUG) |
260 | { |
261 | if (rawStoreFactory != null) |
262 | SanityManager.ASSERT( |
263 | rawStoreFactory == rsf, "raw store factory different"); |
264 | |
265 | SanityManager.ASSERT( |
266 | cm == contextFactory.getCurrentContextManager()); |
267 | } |
268 | |
269 | Xact xact = |
270 | new Xact(this, logFactory, dataFactory, readOnly, compatibilitySpace); |
271 | |
272 | xact.setTransName(transName); |
273 | pushTransactionContext(cm, xact_context_id, xact, |
274 | false /* abortAll */, |
275 | rsf, |
276 | excludeMe /* excludeMe during quiesce state */); |
277 | return xact; |
278 | } |
279 | |
280 | public RawTransaction startTransaction( |
281 | RawStoreFactory rsf, |
282 | ContextManager cm, |
283 | String transName) |
284 | throws StandardException |
285 | { |
286 | return(startCommonTransaction( |
287 | rsf, cm, false, null, USER_CONTEXT_ID, transName, true)); |
288 | } |
289 | |
290 | public RawTransaction startNestedReadOnlyUserTransaction( |
291 | RawStoreFactory rsf, |
292 | Object compatibilitySpace, |
293 | ContextManager cm, |
294 | String transName) |
295 | throws StandardException |
296 | { |
297 | return(startCommonTransaction( |
298 | rsf, cm, true, compatibilitySpace, |
299 | NESTED_READONLY_USER_CONTEXT_ID, transName, false)); |
300 | } |
301 | |
302 | public RawTransaction startNestedUpdateUserTransaction( |
303 | RawStoreFactory rsf, |
304 | ContextManager cm, |
305 | String transName) |
306 | throws StandardException |
307 | { |
308 | return(startCommonTransaction( |
309 | rsf, cm, false, null, |
310 | NESTED_UPDATE_USER_CONTEXT_ID, transName, true)); |
311 | } |
312 | |
313 | public RawTransaction startGlobalTransaction( |
314 | RawStoreFactory rsf, |
315 | ContextManager cm, |
316 | int format_id, |
317 | byte[] global_id, |
318 | byte[] branch_id) |
319 | throws StandardException |
320 | { |
321 | GlobalXactId gid = new GlobalXactId(format_id, global_id, branch_id); |
322 | |
323 | if (ttab.findTransactionContextByGlobalId(gid) != null) |
324 | { |
325 | throw StandardException.newException(SQLState.STORE_XA_XAER_DUPID); |
326 | } |
327 | |
328 | RawTransaction xact = |
329 | startCommonTransaction( |
330 | rsf, cm, false, null, |
331 | USER_CONTEXT_ID, AccessFactoryGlobals.USER_TRANS_NAME, true); |
332 | |
333 | xact.setTransactionId(gid, xact.getId()); |
334 | |
335 | return(xact); |
336 | } |
337 | |
338 | |
339 | |
340 | public RawTransaction findUserTransaction( |
341 | RawStoreFactory rsf, |
342 | ContextManager contextMgr, |
343 | String transName) |
344 | throws StandardException |
345 | { |
346 | if (SanityManager.DEBUG) |
347 | { |
348 | SanityManager.ASSERT( |
349 | contextMgr == contextFactory.getCurrentContextManager(), |
350 | "passed in context mgr not the same as current context mgr"); |
351 | |
352 | if (rawStoreFactory != null) |
353 | SanityManager.ASSERT( |
354 | rawStoreFactory == rsf, "raw store factory different"); |
355 | } |
356 | |
357 | XactContext xc = (XactContext)contextMgr.getContext(USER_CONTEXT_ID); |
358 | if (xc == null) |
359 | return startTransaction(rsf, contextMgr, transName); |
360 | else |
361 | return xc.getTransaction(); |
362 | } |
363 | |
364 | |
365 | public RawTransaction startNestedTopTransaction(RawStoreFactory rsf, ContextManager cm) |
366 | throws StandardException |
367 | { |
368 | |
369 | if (SanityManager.DEBUG) |
370 | { |
371 | if (rawStoreFactory != null) |
372 | SanityManager.ASSERT( |
373 | rawStoreFactory == rsf, "raw store factory different"); |
374 | } |
375 | |
376 | Xact xact = |
377 | new Xact(this, logFactory, dataFactory, false, null); |
378 | |
379 | // hold latches etc. past commit in NTT |
380 | xact.setPostComplete(); |
381 | pushTransactionContext(cm, NTT_CONTEXT_ID, xact, |
382 | true /* abortAll */, |
383 | rsf, |
384 | true /* excludeMe during quiesce state*/); |
385 | return xact; |
386 | } |
387 | |
388 | public RawTransaction startInternalTransaction(RawStoreFactory rsf, ContextManager cm) |
389 | throws StandardException |
390 | { |
391 | if (SanityManager.DEBUG) |
392 | { |
393 | if (rawStoreFactory != null) |
394 | SanityManager.ASSERT( |
395 | rawStoreFactory == rsf, "raw store factory different"); |
396 | } |
397 | |
398 | |
399 | Xact xact = new InternalXact(this, logFactory, dataFactory); |
400 | pushTransactionContext(cm, INTERNAL_CONTEXT_ID, xact, |
401 | true /* abortAll*/, |
402 | rsf, |
403 | true /* excludeMe during quiesce state */); |
404 | return xact; |
405 | } |
406 | |
407 | /* |
408 | * the following TransactionFactory methods are to support recovery and |
409 | * should only be used by recovery! |
410 | */ |
411 | |
412 | /** |
413 | Find the TransactionTableEntry with the given ID and make the passed in |
414 | transaction assume the identity and properties of that |
415 | TransactionTableEntry. |
416 | Used in recovery only. |
417 | */ |
418 | public boolean findTransaction(TransactionId id, RawTransaction tran) |
419 | { |
420 | return ttab.findAndAssumeTransaction(id, tran); |
421 | } |
422 | |
423 | |
424 | /** |
425 | Rollback all active transactions that has updated the raw store. |
426 | Use the recovery Transaction that is passed in to do all the work. |
427 | Used in recovery only. |
428 | |
429 | <P> |
430 | Transactions are rolled back in the following order: |
431 | <OL> |
432 | <LI>internal transactions in reversed beginXact chronological order, |
433 | <LI>all other transactions in reversed beginXact chronological order, |
434 | </NL> |
435 | |
436 | @param recoveryTransaction use this transaction to do all the user |
437 | transaction work |
438 | |
439 | @exception StandardException any exception thrown during rollback |
440 | */ |
441 | public void rollbackAllTransactions( |
442 | RawTransaction recoveryTransaction, |
443 | RawStoreFactory rsf) |
444 | throws StandardException |
445 | { |
446 | if (SanityManager.DEBUG) |
447 | { |
448 | if (rawStoreFactory != null) |
449 | SanityManager.ASSERT( |
450 | rawStoreFactory == rsf, "raw store factory different"); |
451 | |
452 | SanityManager.ASSERT( |
453 | recoveryTransaction != null, "recovery transaction null"); |
454 | } |
455 | |
456 | int irbcount = 0; |
457 | |
458 | // First undo internal transactions if there is any |
459 | if (ttab.hasRollbackFirstTransaction()) |
460 | { |
461 | RawTransaction internalTransaction = startInternalTransaction(rsf, |
462 | recoveryTransaction.getContextManager()); |
463 | |
464 | // make this transaction be aware that it is being used by recovery |
465 | internalTransaction.recoveryTransaction(); |
466 | |
467 | if (SanityManager.DEBUG) |
468 | SanityManager.ASSERT( |
469 | internalTransaction.handlesPostTerminationWork() == false, |
470 | "internal recovery xact handles post termination work"); |
471 | |
472 | while(ttab.getMostRecentRollbackFirstTransaction( |
473 | internalTransaction)) |
474 | { |
475 | irbcount++; |
476 | internalTransaction.abort(); |
477 | } |
478 | |
479 | internalTransaction.close(); |
480 | } |
481 | |
482 | if (SanityManager.DEBUG) |
483 | { |
484 | SanityManager.ASSERT( |
485 | ttab.hasRollbackFirstTransaction() == false, |
486 | "cant rollback user xacts with existing active internal xacts"); |
487 | } |
488 | |
489 | int rbcount = 0; |
490 | |
491 | // recoveryTransacion assumes the identity of the most recent xact |
492 | while(ttab.getMostRecentTransactionForRollback(recoveryTransaction)) |
493 | { |
494 | if (SanityManager.DEBUG) |
495 | { |
496 | SanityManager.ASSERT( |
497 | recoveryTransaction.handlesPostTerminationWork() == false, |
498 | "recovery transaction handles post termination work"); |
499 | } |
500 | |
501 | rbcount++; |
502 | recoveryTransaction.abort(); |
503 | } |
504 | |
505 | if (SanityManager.DEBUG) |
506 | { |
507 | if (rbcount > 0 || irbcount > 0) |
508 | { |
509 | // RESOLVE: put this in the log trace |
510 | // System.out.println( |
511 | // "Recovery rolled back " + irbcount + |
512 | // " internal transactions," |
513 | // + rbcount + " user transactions"); |
514 | } |
515 | } |
516 | |
517 | } |
518 | |
519 | |
520 | /** |
521 | Run through all prepared transactions known to this factory |
522 | and restore their state such that they remain after recovery, and |
523 | can be found and handled by a XA transaction manager. This includes |
524 | creating a context manager for each, pushing a xact context, and |
525 | reclaiming update locks on all data changed by the transaction. |
526 | |
527 | Expected to be called just after the redo and undo recovery loops, |
528 | where the transaction table should be empty except for prepared |
529 | xacts. |
530 | |
531 | Used only in recovery. |
532 | |
533 | @exception StandardException Cloudscape Standard Error policy |
534 | */ |
535 | public void handlePreparedXacts( |
536 | RawStoreFactory rsf) |
537 | throws StandardException |
538 | { |
539 | if (SanityManager.DEBUG) |
540 | { |
541 | |
542 | if (rawStoreFactory != null) |
543 | SanityManager.ASSERT( |
544 | rawStoreFactory == rsf, "raw store factory different"); |
545 | } |
546 | |
547 | int prepared_count = 0; |
548 | |
549 | if (ttab.hasPreparedRecoveredXact()) |
550 | { |
551 | // if there any prepared xacts |
552 | |
553 | // At this point recovery has used one context and one transaction |
554 | // to deal with all transactions. Prepared transactions are to |
555 | // be left in the transaction table, but the must have real and |
556 | // separate CM's and transactions associated with them. |
557 | |
558 | // save old context. Errors may go to funky contexts (the new |
559 | // context we created to bring the prepared transaction into the |
560 | // real world after recovery) after we switch contexts, but any |
561 | // error we get at this point is going to shut down the db. |
562 | |
563 | while (true) |
564 | { |
565 | // allocate new context and associate new xact with it. |
566 | ContextManager cm = contextFactory.newContextManager(); |
567 | contextFactory.setCurrentContextManager(cm); |
568 | |
569 | try { |
570 | RawTransaction rawtran = |
571 | startTransaction( |
572 | rawStoreFactory, cm, |
573 | AccessFactoryGlobals.USER_TRANS_NAME); |
574 | |
575 | if (ttab.getMostRecentPreparedRecoveredXact(rawtran)) |
576 | { |
577 | // found a prepared xact. The reprepare() call will |
578 | // accumulate locks, and change the transaction table entry |
579 | // to not be "in-recovery" so that it won't show up again. |
580 | rawtran.reprepare(); |
581 | |
582 | if (SanityManager.DEBUG) |
583 | prepared_count++; |
584 | } |
585 | else |
586 | { |
587 | // get rid of last transaction allocated. |
588 | rawtran.destroy(); |
589 | break; |
590 | } |
591 | } |
592 | finally |
593 | { |
594 | contextFactory.resetCurrentContextManager(cm); |
595 | } |
596 | } |
597 | |
598 | } |
599 | |
600 | if (SanityManager.DEBUG) |
601 | { |
602 | // RESOLVE - need to only do this under a debug flag. |
603 | // SanityManager.DEBUG_PRINT("", |
604 | // "Recovery re-prepared " + prepared_count + " xa transactions."); |
605 | } |
606 | } |
607 | |
608 | |
609 | /** |
610 | Get the earliest log instant that is still active, ie, the first log |
611 | record logged by the earliest transaction that is still active. |
612 | <BR> |
613 | The logging system must guarentee that the transaction table is |
614 | populated in the order transactions are started. |
615 | Used in recovery only. |
616 | */ |
617 | |
618 | public LogInstant firstUpdateInstant() |
619 | { |
620 | return ttab.getFirstLogInstant(); |
621 | } |
622 | |
623 | /* |
624 | ** Methods of Corruptable |
625 | */ |
626 | |
627 | /** |
628 | Really this is just a convience routine for callers that might not |
629 | have access to a log factory. |
630 | */ |
631 | public StandardException markCorrupt(StandardException originalError) { |
632 | logFactory.markCorrupt(originalError); |
633 | return originalError; |
634 | } |
635 | |
636 | /* |
637 | ** Implementation specific methods. |
638 | */ |
639 | |
640 | public void setNewTransactionId(TransactionId oldxid, Xact t) |
641 | { |
642 | XactId xid; |
643 | boolean excludeMe = true; // by default |
644 | |
645 | if (oldxid != null) |
646 | excludeMe = remove(oldxid); |
647 | |
648 | synchronized(this) |
649 | { |
650 | xid = new XactId(tranId++); |
651 | } |
652 | |
653 | t.setTransactionId(t.getGlobalId(), xid); |
654 | |
655 | // RESOLVE: How does a real global xact id get set? |
656 | |
657 | // If we got rid of the oldxid, that means this transaction object has |
658 | // merely committed and starting the next transaction with the same |
659 | // xact object. In that case, the transaction context will remain the |
660 | // same and won't be pushed. We need to add this transaction with the |
661 | // new id back into the transaction table. If we did not get rid of |
662 | // the old oldxid, that means this is a brand new transaction being |
663 | // created. The pushTransactionContext call will add it to the |
664 | // transaction table with the appropriate flags |
665 | if (oldxid != null) |
666 | add(t, excludeMe); |
667 | } |
668 | |
669 | /* |
670 | ** Set the shortTranId, this is called by the log factory after recovery |
671 | */ |
672 | public void resetTranId() |
673 | { |
674 | XactId xid = (XactId)ttab.largestUpdateXactId(); |
675 | if (xid != null) |
676 | tranId = xid.getId() + 1; |
677 | else |
678 | tranId = 1; |
679 | } |
680 | |
681 | |
682 | /** |
683 | Create a new RawTransaction, a context for it and push the context |
684 | onto the current context manager. Then add the transacion to the |
685 | transaction table. |
686 | |
687 | @param contextName the name of the transaction context |
688 | @param xact the Transaction object |
689 | @param abortAll if true, then any error will abort the whole |
690 | transaction. Otherwise, let XactContext.cleanupOnError decide what to |
691 | do |
692 | @param rsf the raw store factory |
693 | @param excludeMe during systeme quiesce, i.e., this transaction should |
694 | not be allowed to be active during a quiesce state. |
695 | |
696 | |
697 | @exception StandardException Standard Cloudscape error policy |
698 | |
699 | */ |
700 | protected void pushTransactionContext(ContextManager cm, String contextName, |
701 | Xact xact, |
702 | boolean abortAll, |
703 | RawStoreFactory rsf, |
704 | boolean excludeMe) |
705 | throws StandardException |
706 | { |
707 | if (cm.getContext(contextName) != null) |
708 | { |
709 | throw StandardException.newException( |
710 | SQLState.XACT_TRANSACTION_ACTIVE); |
711 | } |
712 | |
713 | XactContext xc = new XactContext(cm, contextName, xact, abortAll, rsf); |
714 | |
715 | // this transaction is now added to the transaction table. |
716 | // This will cause an idle transaction to take on an identity, which is |
717 | // unfortunate. The reason why we have to add the transaction to the |
718 | // table right now is because the transaction table is used to bring |
719 | // system to quisce state to regulate who can go active during quiesce |
720 | // state, and if we add the transaction |
721 | // when it goes active, then there is a window where this transaction |
722 | // can sneak in. The transaction table itself does not keep track of |
723 | // whether transactions can be started or not because quiesce related |
724 | // transactions can start after all other user |
725 | // transactions are excluded. |
726 | // RESOLVE: need to put more thought on the overall requirement and |
727 | // design of the transaction table that satisfies the need of all the |
728 | // clients, namely: checkpoint, recovery, quiesce mode, transaction table. |
729 | |
730 | add(xact, excludeMe); |
731 | |
732 | } |
733 | |
734 | /** |
735 | Add a transaction to the list of transactions that has updated |
736 | the raw store. |
737 | <P> |
738 | This is called underneath the BeginXact log operation's doMe method. |
739 | The logging system must guarentee that transactions are added in the |
740 | true order they are started, as defined by the order of beginXact log |
741 | record in the log. |
742 | */ |
743 | protected void addUpdateTransaction( |
744 | TransactionId id, |
745 | RawTransaction t, |
746 | int transactionStatus) |
747 | { |
748 | if (SanityManager.DEBUG) |
749 | SanityManager.ASSERT( |
750 | id != null, "addding update transaction with null id"); |
751 | |
752 | ttab.addUpdateTransaction(id, t, transactionStatus); |
753 | } |
754 | |
755 | /** |
756 | Remove a transaction from the list of transactions that has updated the |
757 | raw store. |
758 | */ |
759 | protected void removeUpdateTransaction(TransactionId id) |
760 | { |
761 | if (SanityManager.DEBUG) |
762 | SanityManager.ASSERT( |
763 | id != null, "remove update transaction with null id"); |
764 | |
765 | ttab.removeUpdateTransaction(id); |
766 | } |
767 | |
768 | /** |
769 | Change state of transaction to prepared. Used by recovery to update |
770 | the transaction table entry to prepared state. |
771 | */ |
772 | protected void prepareTransaction(TransactionId id) |
773 | { |
774 | if (SanityManager.DEBUG) |
775 | SanityManager.ASSERT( |
776 | id != null, "prepare transaction with null id"); |
777 | |
778 | ttab.prepareTransaction(id); |
779 | } |
780 | |
781 | /** |
782 | Submit this post commit work to the post commit daemon |
783 | */ |
784 | public boolean submitPostCommitWork(Serviceable work) |
785 | { |
786 | if (rawStoreDaemon != null) |
787 | return rawStoreDaemon.enqueue(work, work.serviceASAP()); |
788 | return false; |
789 | } |
790 | |
791 | public void setRawStoreFactory(RawStoreFactory rsf) throws StandardException |
792 | { |
793 | if (SanityManager.DEBUG) |
794 | { |
795 | SanityManager.ASSERT(rsf != null, "rawStoreFactory == null"); |
796 | } |
797 | |
798 | rawStoreFactory = rsf; |
799 | |
800 | // no need to remember raw store factory, |
801 | // just remember which daemon to use |
802 | rawStoreDaemon = rsf.getDaemon(); |
803 | |
804 | // now its ok to look for the log and data factory |
805 | // log factory is booted by the data factory |
806 | logFactory = (LogFactory) Monitor.findServiceModule(this, rsf.getLogFactoryModule()); |
807 | |
808 | // data factory is booted by the raw store implementation |
809 | dataFactory = (DataFactory) Monitor.findServiceModule(this, rsf.getDataFactoryModule()); |
810 | } |
811 | |
812 | /** |
813 | Returns true if there is no in flight updating tranasaction. |
814 | Caller must be aware that if there is no other mechanism to stop |
815 | transactions from starting and ending, then this information is |
816 | outdated as soon as it is reported. |
817 | |
818 | Only call this function in special times - e.g, during recovery |
819 | */ |
820 | public boolean noActiveUpdateTransaction() |
821 | { |
822 | return (ttab.hasActiveUpdateTransaction() == false); |
823 | } |
824 | |
825 | /* |
826 | remove the transaction Id an return false iff the transaction is found |
827 | in the table and it doesn't need exclusion from quiesce state |
828 | */ |
829 | protected boolean remove(TransactionId xactId) |
830 | { |
831 | return ttab.remove(xactId); |
832 | } |
833 | |
834 | protected void add(Xact xact, boolean excludeMe) |
835 | { |
836 | ttab.add(xact, excludeMe); |
837 | } |
838 | |
839 | |
840 | /** |
841 | Make a new UUID for whomever that wants it |
842 | */ |
843 | public UUID makeNewUUID() |
844 | { |
845 | return uuidFactory.createUUID(); |
846 | } |
847 | |
848 | /** |
849 | Decide if a transaction of this contextId needs to flush the log when |
850 | it commits |
851 | */ |
852 | public boolean flushLogOnCommit(String contextName) |
853 | { |
854 | // |
855 | // if this is a user transaction, flush the log |
856 | // if this is an internal or nested top transaction, do not |
857 | // flush, let it age out. |
858 | // |
859 | return (contextName == USER_CONTEXT_ID || |
860 | contextName.equals(USER_CONTEXT_ID)); |
861 | } |
862 | |
863 | |
864 | /** |
865 | Get a locking policy for a transaction. |
866 | */ |
867 | final LockingPolicy getLockingPolicy( |
868 | int mode, |
869 | int isolation, |
870 | boolean stricterOk) |
871 | { |
872 | |
873 | if (mode == LockingPolicy.MODE_NONE) |
874 | isolation = TransactionController.ISOLATION_NOLOCK; |
875 | |
876 | LockingPolicy policy = lockingPolicies[mode][isolation]; |
877 | |
878 | if ((policy != null) || (!stricterOk)) |
879 | return policy; |
880 | |
881 | for (mode++; mode <= LockingPolicy.MODE_CONTAINER; mode++) |
882 | { |
883 | for (int i = isolation; |
884 | i <= TransactionController.ISOLATION_SERIALIZABLE; |
885 | i++) |
886 | { |
887 | policy = lockingPolicies[mode][i]; |
888 | if (policy != null) |
889 | return policy; |
890 | } |
891 | } |
892 | |
893 | return null; |
894 | } |
895 | |
896 | /* |
897 | Return the transaction table to be logged with the checkpoint operation |
898 | */ |
899 | public Formatable getTransactionTable() |
900 | { |
901 | return ttab; |
902 | } |
903 | |
904 | /* |
905 | Use this transaction table, which is gotten from a checkpoint |
906 | operation. Use ONLY during recovery. |
907 | */ |
908 | public void useTransactionTable(Formatable transactionTable) |
909 | throws StandardException |
910 | { |
911 | if (ttab != null && transactionTable != null) |
912 | { |
913 | throw StandardException.newException( |
914 | SQLState.XACT_TRANSACTION_TABLE_IN_USE); |
915 | } |
916 | |
917 | if (ttab == null) |
918 | { |
919 | if (transactionTable == null) |
920 | ttab = new TransactionTable(); |
921 | else |
922 | { |
923 | if (SanityManager.DEBUG) |
924 | { |
925 | if ((transactionTable instanceof TransactionTable) == |
926 | false) |
927 | { |
928 | SanityManager.THROWASSERT( |
929 | "using transaction table which is of class " + |
930 | transactionTable.getClass().getName()); |
931 | } |
932 | } |
933 | ttab = (TransactionTable)transactionTable; |
934 | } |
935 | } |
936 | // else transactionTable must be null, if we already have a transaction |
937 | // table, no need to do anything |
938 | } |
939 | |
940 | public TransactionInfo[] getTransactionInfo() |
941 | { |
942 | if (SanityManager.DEBUG) |
943 | SanityManager.ASSERT(ttab != null, "transaction table is null"); |
944 | return ttab.getTransactionInfo(); |
945 | } |
946 | |
947 | |
948 | // @return false, if the Database creation finished |
949 | public boolean inDatabaseCreation() |
950 | { |
951 | return inCreateNoLog; |
952 | } |
953 | |
954 | /* |
955 | * Return the module providing XAresource interface to the transaction |
956 | * table. |
957 | * |
958 | * @exception StandardException Standard cloudscape exception policy. |
959 | */ |
960 | public /* XAResourceManager */ Object getXAResourceManager() |
961 | throws StandardException |
962 | { |
963 | if (xa_resource == null) |
964 | xa_resource = new XactXAResourceManager(rawStoreFactory, ttab); |
965 | |
966 | return(xa_resource); |
967 | } |
968 | |
969 | |
970 | /** |
971 | * Block the online backup. Backup needs to be blocked while |
972 | * executing any unlogged operations or any opearation that |
973 | * prevents from making a consistent backup. |
974 | * |
975 | * @param wait if <tt>true</tt>, waits until the backup |
976 | * is blocked. |
977 | * @return <tt>true</tt> if backup is blocked. |
978 | * <tt>false</tt> otherwise. |
979 | * @exception StandardException if interrupted while waiting for a |
980 | * backup to complete. |
981 | */ |
982 | protected boolean blockBackup(boolean wait) |
983 | throws StandardException |
984 | { |
985 | synchronized(backupSemaphore) { |
986 | // do not allow backup blocking operations, if online backup is |
987 | // is in progress. |
988 | if (inBackup) |
989 | { |
990 | if(wait) { |
991 | while(inBackup) { |
992 | try { |
993 | backupSemaphore.wait(); |
994 | } catch (InterruptedException ie) { |
995 | throw StandardException.interrupt(ie); |
996 | } |
997 | } |
998 | }else { |
999 | return false; |
1000 | } |
1001 | } |
1002 | |
1003 | // not in online backup, allow backup blocking operations |
1004 | backupBlockingOperations++; |
1005 | return true; |
1006 | } |
1007 | } |
1008 | |
1009 | |
1010 | /** |
1011 | * Unblock the backup, a backup blocking operation finished. |
1012 | */ |
1013 | protected void unblockBackup() |
1014 | { |
1015 | synchronized(backupSemaphore) { |
1016 | if (SanityManager.DEBUG) |
1017 | SanityManager.ASSERT(backupBlockingOperations > 0, |
1018 | "no backup blocking opeations in progress"); |
1019 | |
1020 | backupBlockingOperations--; |
1021 | |
1022 | if (inBackup) { |
1023 | // wake up the online backupthread |
1024 | backupSemaphore.notifyAll(); |
1025 | } |
1026 | } |
1027 | } |
1028 | |
1029 | /** |
1030 | * Checks if there are any backup blocking operations in progress and |
1031 | * prevents new ones from starting until the backup is finished. |
1032 | * If backup blocking operations are in progress and <code> wait </code> |
1033 | * parameter value is <tt>true</tt>, then it will wait for the current |
1034 | * backup blocking operations to finish. |
1035 | * |
1036 | * A Consistent backup can not be made if there are any backup |
1037 | * blocking operations (like unlogged operations) are in progress |
1038 | * |
1039 | * @param wait if <tt>true</tt>, waits for the current backup blocking |
1040 | * operation in progress to finish. |
1041 | * @return <tt>true</tt> if no backup blocking operations are in |
1042 | * progress |
1043 | * <tt>false</tt> otherwise. |
1044 | * @exception StandardException if interrupted or a runtime exception occurs |
1045 | */ |
1046 | public boolean blockBackupBlockingOperations(boolean wait) |
1047 | throws StandardException |
1048 | { |
1049 | synchronized(backupSemaphore) { |
1050 | if (wait) { |
1051 | // set the inBackup state to true first to stop new backup |
1052 | // blocking operation from starting. |
1053 | inBackup= true; |
1054 | try { |
1055 | // wait for backup blocking operation in progress to finish |
1056 | while(backupBlockingOperations > 0) |
1057 | { |
1058 | try { |
1059 | backupSemaphore.wait(); |
1060 | } |
1061 | catch (InterruptedException ie) { |
1062 | // make sure we are not stuck in the backup state |
1063 | // if we caught an interrupt exception and the |
1064 | // calling thread may not have a chance to clear |
1065 | // the in backup state. |
1066 | |
1067 | inBackup = false; |
1068 | backupSemaphore.notifyAll(); |
1069 | throw StandardException.interrupt(ie); |
1070 | } |
1071 | } |
1072 | } |
1073 | catch (RuntimeException rte) { |
1074 | // make sure we are not stuck in backup state if we |
1075 | // caught a run time exception and the calling thread may |
1076 | // not have a chance to clear the in backup state. |
1077 | inBackup= false; |
1078 | backupSemaphore.notifyAll(); |
1079 | throw rte; // rethrow run time exception |
1080 | } |
1081 | } else { |
1082 | // check if any backup blocking operations that are in progress |
1083 | if (backupBlockingOperations == 0) |
1084 | inBackup = true; |
1085 | } |
1086 | |
1087 | } |
1088 | |
1089 | if (SanityManager.DEBUG) { |
1090 | if (inBackup) { |
1091 | SanityManager.ASSERT(backupBlockingOperations == 0 , |
1092 | "store is not in correct state for backup"); |
1093 | } |
1094 | } |
1095 | |
1096 | return inBackup; |
1097 | } |
1098 | |
1099 | |
1100 | /** |
1101 | * Backup completed. Allow backup blocking operations. |
1102 | */ |
1103 | public void unblockBackupBlockingOperations() |
1104 | { |
1105 | synchronized(backupSemaphore) { |
1106 | inBackup = false; |
1107 | backupSemaphore.notifyAll(); |
1108 | } |
1109 | } |
1110 | |
1111 | } |