001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.objectserver.impl;
006:
007: import com.tc.net.groups.ClientID;
008: import com.tc.net.protocol.tcm.ChannelID;
009: import com.tc.object.gtx.GlobalTransactionID;
010: import com.tc.object.tx.ServerTransactionID;
011: import com.tc.object.tx.TransactionID;
012: import com.tc.objectserver.gtx.GlobalTransactionDescriptor;
013: import com.tc.objectserver.persistence.api.PersistenceTransaction;
014: import com.tc.objectserver.persistence.api.TransactionPersistor;
015: import com.tc.objectserver.persistence.api.TransactionStore;
016: import com.tc.objectserver.persistence.impl.TestPersistenceTransaction;
017: import com.tc.objectserver.persistence.impl.TransactionStoreImpl;
018: import com.tc.test.TCTestCase;
019: import com.tc.util.concurrent.NoExceptionLinkedQueue;
020: import com.tc.util.sequence.Sequence;
021:
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.HashMap;
025: import java.util.HashSet;
026: import java.util.Iterator;
027: import java.util.LinkedHashMap;
028: import java.util.LinkedList;
029: import java.util.List;
030: import java.util.Map;
031:
032: public class TransactionStoreTest extends TCTestCase {
033:
034: private TestTransactionPersistor persistor;
035: private TransactionStore store;
036:
037: public void testDeleteByGlobalTransactionID() throws Exception {
038: persistor = new TestTransactionPersistor();
039: store = new TransactionStoreImpl(persistor, persistor);
040: List gtxs = new LinkedList();
041: for (int i = 0; i < 100; i++) {
042: ServerTransactionID sid1 = new ServerTransactionID(
043: new ClientID(new ChannelID(i)),
044: new TransactionID(i));
045: store.getOrCreateTransactionDescriptor(sid1);
046: store.commitTransactionDescriptor(null, sid1);
047: GlobalTransactionDescriptor desc = store
048: .getTransactionDescriptor(sid1);
049: assertNotNull(desc);
050: gtxs.add(desc);
051: }
052: final GlobalTransactionDescriptor originalMin = (GlobalTransactionDescriptor) gtxs
053: .get(0);
054:
055: assertEquals(getGlobalTransactionID(originalMin), store
056: .getLeastGlobalTransactionID());
057:
058: // create a set of transactions to delete
059: Collection toDelete = new HashSet();
060: Collection toDeleteIDs = new HashSet();
061: for (int i = 0; i < 10; i++) {
062:
063: GlobalTransactionDescriptor o = (GlobalTransactionDescriptor) gtxs
064: .remove(0);
065: toDelete.add(o);
066: toDeleteIDs.add(o.getServerTransactionID());
067: }
068: assertFalse(originalMin == gtxs.get(0));
069:
070: // delete the set
071: store
072: .clearCommitedTransactionsBelowLowWaterMark(
073: null,
074: getGlobalTransactionID((GlobalTransactionDescriptor) gtxs
075: .get(0)));
076:
077: GlobalTransactionDescriptor currentMin = (GlobalTransactionDescriptor) gtxs
078: .get(0);
079: // make sure the min has been adjusted properly
080: assertEquals(getGlobalTransactionID(currentMin), store
081: .getLeastGlobalTransactionID());
082: // make sure the deleted set has actually been deleted
083: for (Iterator i = toDelete.iterator(); i.hasNext();) {
084: GlobalTransactionDescriptor desc = (GlobalTransactionDescriptor) i
085: .next();
086: assertNull(store.getTransactionDescriptor(desc
087: .getServerTransactionID()));
088: }
089:
090: // make sure the persistor is told to delete them all
091: assertEquals(toDelete, new HashSet(
092: (Collection) persistor.deleteQueue.poll(1)));
093: }
094:
095: public void testLeastGlobalTransactionID() throws Exception {
096:
097: persistor = new TestTransactionPersistor();
098: store = new TransactionStoreImpl(persistor, persistor);
099:
100: assertEquals(GlobalTransactionID.NULL_ID, store
101: .getLeastGlobalTransactionID());
102:
103: ServerTransactionID stx1 = new ServerTransactionID(
104: new ClientID(new ChannelID(1)), new TransactionID(1));
105:
106: GlobalTransactionDescriptor gtx1 = store
107: .getOrCreateTransactionDescriptor(stx1);
108: assertNotEquals(GlobalTransactionID.NULL_ID, store
109: .getLeastGlobalTransactionID());
110: assertEquals(getGlobalTransactionID(gtx1), store
111: .getLeastGlobalTransactionID());
112:
113: store.commitTransactionDescriptor(null, stx1);
114: assertEquals(getGlobalTransactionID(gtx1), store
115: .getLeastGlobalTransactionID());
116:
117: int min = 10;
118: int max = 20;
119: List gds = new ArrayList();
120: for (int i = min; i < max; i++) {
121: ServerTransactionID stxid = new ServerTransactionID(
122: new ClientID(new ChannelID(2)),
123: new TransactionID(i));
124: GlobalTransactionDescriptor gd = store
125: .getOrCreateTransactionDescriptor(stxid);
126: gds.add(gd);
127: store.commitTransactionDescriptor(null, stxid);
128: }
129:
130: // Still the least Global Txn ID is the same
131: assertEquals(getGlobalTransactionID(gtx1), store
132: .getLeastGlobalTransactionID());
133:
134: store.clearCommitedTransactionsBelowLowWaterMark(null,
135: ((GlobalTransactionDescriptor) gds.get(0))
136: .getServerTransactionID());
137:
138: // Still the least Global Txn ID is the same, since CLIENT(1) is still holding the low water mark
139: assertEquals(getGlobalTransactionID(gtx1), store
140: .getLeastGlobalTransactionID());
141:
142: ServerTransactionID stx2 = new ServerTransactionID(stx1
143: .getSourceID(), stx1.getClientTransactionID().next());
144: store.clearCommitedTransactionsBelowLowWaterMark(null, stx2);
145:
146: // least Global Txn ID is not the same
147: assertNotEquals(getGlobalTransactionID(gtx1), store
148: .getLeastGlobalTransactionID());
149:
150: GlobalTransactionID currentLWM = store
151: .getLeastGlobalTransactionID();
152:
153: // send LWM of the next txn
154: store.clearCommitedTransactionsBelowLowWaterMark(null,
155: ((GlobalTransactionDescriptor) gds.get(1))
156: .getServerTransactionID());
157:
158: // least Global Txn ID is not the same
159: assertNotEquals(currentLWM, store.getLeastGlobalTransactionID());
160: assertTrue(currentLWM.toLong() < store
161: .getLeastGlobalTransactionID().toLong());
162: currentLWM = store.getLeastGlobalTransactionID();
163:
164: // send LWM of the last txn
165: store.clearCommitedTransactionsBelowLowWaterMark(null,
166: ((GlobalTransactionDescriptor) gds.get(gds.size() - 1))
167: .getServerTransactionID());
168:
169: // least Global Txn ID is not the same
170: assertNotEquals(currentLWM, store.getLeastGlobalTransactionID());
171: assertTrue(currentLWM.toLong() < store
172: .getLeastGlobalTransactionID().toLong());
173: currentLWM = store.getLeastGlobalTransactionID();
174:
175: // send LWM above the last txn
176: ServerTransactionID sid = ((GlobalTransactionDescriptor) gds
177: .get(gds.size() - 1)).getServerTransactionID();
178: sid = new ServerTransactionID(sid.getSourceID(), sid
179: .getClientTransactionID().next());
180: store.clearCommitedTransactionsBelowLowWaterMark(null, sid);
181:
182: // least Global Txn ID is not the same, its null
183: assertNotEquals(currentLWM, store.getLeastGlobalTransactionID());
184: assertEquals(GlobalTransactionID.NULL_ID, store
185: .getLeastGlobalTransactionID());
186: }
187:
188: public void testLeastGlobalTransactionIDInPassiveServer()
189: throws Exception {
190:
191: persistor = new TestTransactionPersistor();
192: store = new TransactionStoreImpl(persistor, persistor);
193:
194: assertEquals(GlobalTransactionID.NULL_ID, store
195: .getLeastGlobalTransactionID());
196:
197: ServerTransactionID stx1 = new ServerTransactionID(
198: new ClientID(new ChannelID(1)), new TransactionID(1));
199:
200: GlobalTransactionDescriptor gtx1 = store
201: .getOrCreateTransactionDescriptor(stx1);
202: assertNotEquals(GlobalTransactionID.NULL_ID, store
203: .getLeastGlobalTransactionID());
204: assertEquals(getGlobalTransactionID(gtx1), store
205: .getLeastGlobalTransactionID());
206:
207: store.commitTransactionDescriptor(null, stx1);
208: assertEquals(getGlobalTransactionID(gtx1), store
209: .getLeastGlobalTransactionID());
210:
211: int min = 10;
212: int max = 20;
213: List gds = new ArrayList();
214: for (int i = min; i < max; i++) {
215: ServerTransactionID stxid = new ServerTransactionID(
216: new ClientID(new ChannelID(2)),
217: new TransactionID(i));
218: GlobalTransactionDescriptor gd = store
219: .getOrCreateTransactionDescriptor(stxid);
220: gds.add(gd);
221: }
222:
223: // Still the least Global Txn ID is the same
224: assertEquals(getGlobalTransactionID(gtx1), store
225: .getLeastGlobalTransactionID());
226:
227: // No need to send LWM per client in the PASSIVE, Client(1)'s txn is cleared
228: store.clearCommitedTransactionsBelowLowWaterMark(null,
229: ((GlobalTransactionDescriptor) gds.get(0))
230: .getGlobalTransactionID());
231:
232: // least Global Txn ID is not the same
233: assertNotEquals(getGlobalTransactionID(gtx1), store
234: .getLeastGlobalTransactionID());
235:
236: GlobalTransactionID currentLWM = store
237: .getLeastGlobalTransactionID();
238:
239: // send LWM of the next txn
240: store.clearCommitedTransactionsBelowLowWaterMark(null,
241: ((GlobalTransactionDescriptor) gds.get(1))
242: .getGlobalTransactionID());
243:
244: // least Global Txn ID is STILL the same, since the transactions are not commited.
245: assertEquals(currentLWM, store.getLeastGlobalTransactionID());
246: assertFalse(currentLWM.toLong() < store
247: .getLeastGlobalTransactionID().toLong());
248:
249: // commit transaction
250: store.commitTransactionDescriptor(null,
251: ((GlobalTransactionDescriptor) gds.get(0))
252: .getServerTransactionID());
253:
254: // least Global Txn ID is STILL the same, only when the next LWM msg comes along it clears the data structures.
255: assertEquals(currentLWM, store.getLeastGlobalTransactionID());
256: assertFalse(currentLWM.toLong() < store
257: .getLeastGlobalTransactionID().toLong());
258:
259: // send LWM again
260: store.clearCommitedTransactionsBelowLowWaterMark(null,
261: ((GlobalTransactionDescriptor) gds.get(1))
262: .getGlobalTransactionID());
263:
264: // Now LWM should have moved up
265: assertNotEquals(currentLWM, store.getLeastGlobalTransactionID());
266: assertTrue(currentLWM.toLong() < store
267: .getLeastGlobalTransactionID().toLong());
268: currentLWM = store.getLeastGlobalTransactionID();
269:
270: // send LWM of the last txn
271: store.clearCommitedTransactionsBelowLowWaterMark(null,
272: ((GlobalTransactionDescriptor) gds.get(gds.size() - 1))
273: .getGlobalTransactionID());
274:
275: // least Global Txn ID is STILL the same, since the transactions are not commited.
276: assertEquals(currentLWM, store.getLeastGlobalTransactionID());
277: assertFalse(currentLWM.toLong() < store
278: .getLeastGlobalTransactionID().toLong());
279:
280: for (int i = 1; i < gds.size(); i++) {
281: GlobalTransactionDescriptor gd = (GlobalTransactionDescriptor) gds
282: .get(i);
283: store.commitTransactionDescriptor(null, gd
284: .getServerTransactionID());
285: }
286:
287: // least Global Txn ID is STILL the same, only when the next LWM msg comes along it clears the data structures.
288: assertEquals(currentLWM, store.getLeastGlobalTransactionID());
289: assertFalse(currentLWM.toLong() < store
290: .getLeastGlobalTransactionID().toLong());
291:
292: // send LWM again
293: store.clearCommitedTransactionsBelowLowWaterMark(null,
294: ((GlobalTransactionDescriptor) gds.get(gds.size() - 1))
295: .getGlobalTransactionID());
296:
297: // least Global Txn ID is not the same
298: assertNotEquals(currentLWM, store.getLeastGlobalTransactionID());
299: assertTrue(currentLWM.toLong() < store
300: .getLeastGlobalTransactionID().toLong());
301: currentLWM = store.getLeastGlobalTransactionID();
302:
303: // send LWM above the last txn - we use SID instead of GID here, not a real case in passive
304: ServerTransactionID sid = ((GlobalTransactionDescriptor) gds
305: .get(gds.size() - 1)).getServerTransactionID();
306: sid = new ServerTransactionID(sid.getSourceID(), sid
307: .getClientTransactionID().next());
308: store.clearCommitedTransactionsBelowLowWaterMark(null, sid);
309:
310: // least Global Txn ID is not the same, its null
311: assertNotEquals(currentLWM, store.getLeastGlobalTransactionID());
312: assertEquals(GlobalTransactionID.NULL_ID, store
313: .getLeastGlobalTransactionID());
314: }
315:
316: private GlobalTransactionID getGlobalTransactionID(
317: GlobalTransactionDescriptor gtx) {
318: return gtx.getGlobalTransactionID();
319: }
320:
321: public void testClientShutdown() throws Exception {
322: long sequence = 0;
323: int initialMin = 200;
324: int initialMax = 300;
325: int laterMax = 400;
326: persistor = new TestTransactionPersistor();
327: for (int i = initialMin; i < initialMax; i++) {
328: sequence++;
329: ServerTransactionID stxid = new ServerTransactionID(
330: new ClientID(new ChannelID(i % 2)),
331: new TransactionID(i));
332: persistor.persisted.put(stxid,
333: new GlobalTransactionDescriptor(stxid,
334: new GlobalTransactionID(persistor.next())));
335: }
336: store = new TransactionStoreImpl(persistor, persistor);
337: GlobalTransactionID lowmk1 = store
338: .getLeastGlobalTransactionID();
339:
340: // create more
341: for (int i = initialMax; i < laterMax; i++) {
342: ServerTransactionID stxid = new ServerTransactionID(
343: new ClientID(new ChannelID(i % 2)),
344: new TransactionID(i));
345: store.getOrCreateTransactionDescriptor(stxid);
346: store.commitTransactionDescriptor(null, stxid);
347: }
348: GlobalTransactionID lowmk2 = store
349: .getLeastGlobalTransactionID();
350:
351: assertEquals(lowmk1, lowmk2);
352:
353: ClientID cid0 = new ClientID(new ChannelID(0));
354: store.shutdownNode(null, cid0);
355:
356: // Check if all channel1 IDs are gone
357: for (int i = initialMin; i < laterMax; i++) {
358: ServerTransactionID stxid = new ServerTransactionID(
359: new ClientID(new ChannelID(i % 2)),
360: new TransactionID(i));
361: if (i % 2 == 0) {
362: assertNull(store.getTransactionDescriptor(stxid));
363: } else {
364: assertNotNull(store.getTransactionDescriptor(stxid));
365: }
366: }
367: }
368:
369: public void tests() throws Exception {
370: int initialMin = 200;
371: int initialMax = 300;
372: persistor = new TestTransactionPersistor();
373: for (int i = initialMin; i < initialMax; i++) {
374: ServerTransactionID stxid = new ServerTransactionID(
375: new ClientID(new ChannelID(i)),
376: new TransactionID(i));
377: persistor.persisted.put(stxid,
378: new GlobalTransactionDescriptor(stxid,
379: new GlobalTransactionID(persistor.next())));
380: }
381: store = new TransactionStoreImpl(persistor, persistor);
382:
383: // make sure that the persisted transaction ids get loaded in the
384: // constructor.
385:
386: for (int i = initialMin; i < initialMax; i++) {
387: ServerTransactionID stxid = new ServerTransactionID(
388: new ClientID(new ChannelID(i)),
389: new TransactionID(i));
390: assertNotNull(store.getTransactionDescriptor(stxid));
391: }
392:
393: ChannelID channel1 = new ChannelID(1);
394: ChannelID channel2 = new ChannelID(2);
395: TransactionID tx1 = new TransactionID(1);
396: TransactionID tx2 = new TransactionID(2);
397: ServerTransactionID stxid1 = new ServerTransactionID(
398: new ClientID(channel1), tx1);
399: ServerTransactionID stxid2 = new ServerTransactionID(
400: new ClientID(channel2), tx2);
401:
402: assertNull(store.getTransactionDescriptor(stxid1));
403: GlobalTransactionDescriptor gtx1 = store
404: .getOrCreateTransactionDescriptor(stxid1);
405: assertEquals(gtx1, store.getTransactionDescriptor(stxid1));
406:
407: assertSame(gtx1, store.getTransactionDescriptor(stxid1));
408:
409: assertNull(store.getTransactionDescriptor(stxid2));
410: GlobalTransactionDescriptor gtx2 = store
411: .getOrCreateTransactionDescriptor(stxid2);
412: assertEquals(gtx2, store.getTransactionDescriptor(stxid2));
413:
414: PersistenceTransaction ptx = new TestPersistenceTransaction();
415: store.commitTransactionDescriptor(ptx, stxid1);
416: Object[] args = (Object[]) persistor.storeQueue.poll(1);
417: assertTrue(persistor.storeQueue.isEmpty());
418: assertSame(ptx, args[0]);
419: assertSame(gtx1, args[1]);
420:
421: store.commitTransactionDescriptor(ptx, stxid2);
422: args = (Object[]) persistor.storeQueue.poll(1);
423: assertTrue(persistor.storeQueue.isEmpty());
424: assertSame(ptx, args[0]);
425: assertSame(gtx2, args[1]);
426: }
427:
428: public void testSameGIDAssignedOnRestart() throws Exception {
429: int initialMin = 200;
430: int initialMax = 300;
431: int laterMax = 400;
432: persistor = new TestTransactionPersistor();
433: store = new TransactionStoreImpl(persistor, persistor);
434: Map sid2Gid = new HashMap();
435: for (int i = initialMin; i < initialMax; i++) {
436: ServerTransactionID stxid = new ServerTransactionID(
437: new ClientID(new ChannelID(i % 2)),
438: new TransactionID(i));
439: GlobalTransactionDescriptor desc = store
440: .getOrCreateTransactionDescriptor(stxid);
441: store.commitTransactionDescriptor(null, stxid);
442: assertEquals(stxid, desc.getServerTransactionID());
443: sid2Gid.put(stxid, desc);
444: }
445:
446: // RESTART
447: store = new TransactionStoreImpl(persistor, persistor);
448:
449: // test if we get the same gid
450: GlobalTransactionID maxID = GlobalTransactionID.NULL_ID;
451: for (int i = initialMin; i < initialMax; i++) {
452: ServerTransactionID stxid = new ServerTransactionID(
453: new ClientID(new ChannelID(i % 2)),
454: new TransactionID(i));
455: GlobalTransactionDescriptor desc = (GlobalTransactionDescriptor) sid2Gid
456: .get(stxid);
457: assertEquals(desc, store.getTransactionDescriptor(stxid));
458: if (desc.getGlobalTransactionID().toLong() > maxID.toLong()) {
459: maxID = desc.getGlobalTransactionID();
460: }
461: }
462:
463: // create more
464: for (int i = initialMax; i < laterMax; i++) {
465: ServerTransactionID stxid = new ServerTransactionID(
466: new ClientID(new ChannelID(i % 2)),
467: new TransactionID(i));
468: GlobalTransactionDescriptor desc;
469: desc = store.getOrCreateTransactionDescriptor(stxid);
470: store.commitTransactionDescriptor(null, stxid);
471: assertTrue(maxID.toLong() < desc.getGlobalTransactionID()
472: .toLong());
473: }
474: }
475:
476: private static final class TestTransactionPersistor implements
477: TransactionPersistor, Sequence {
478:
479: public final NoExceptionLinkedQueue deleteQueue = new NoExceptionLinkedQueue();
480: public final LinkedHashMap persisted = new LinkedHashMap();
481: public final NoExceptionLinkedQueue storeQueue = new NoExceptionLinkedQueue();
482: public long sequence = 0;
483:
484: public Collection loadAllGlobalTransactionDescriptors() {
485: return getNewGlobalTransactionDescs(persisted.values());
486: }
487:
488: private Collection getNewGlobalTransactionDescs(Collection c) {
489: Collection newList = new ArrayList(c.size());
490: for (Iterator i = c.iterator(); i.hasNext();) {
491: GlobalTransactionDescriptor oldGD = (GlobalTransactionDescriptor) i
492: .next();
493: newList.add(new GlobalTransactionDescriptor(oldGD
494: .getServerTransactionID(), oldGD
495: .getGlobalTransactionID()));
496: }
497: return newList;
498: }
499:
500: public void saveGlobalTransactionDescriptor(
501: PersistenceTransaction tx,
502: GlobalTransactionDescriptor gtx) {
503: storeQueue.put(new Object[] { tx, gtx });
504: persisted.put(gtx.getServerTransactionID(), gtx);
505: }
506:
507: public long next() {
508: return ++sequence;
509: }
510:
511: public long current() {
512: return sequence;
513: }
514:
515: public void deleteAllGlobalTransactionDescriptors(
516: PersistenceTransaction tx, Collection toDelete) {
517: deleteQueue.put(toDelete);
518: for (Iterator i = toDelete.iterator(); i.hasNext();) {
519: GlobalTransactionDescriptor gd = (GlobalTransactionDescriptor) i
520: .next();
521: persisted.remove(gd.getServerTransactionID());
522: }
523: }
524: }
525: }
|