View Javadoc

1   package net.sf.ehcache.transaction.local;
2   
3   import junit.framework.AssertionFailedError;
4   import junit.framework.TestCase;
5   import net.sf.ehcache.CacheManager;
6   import net.sf.ehcache.Ehcache;
7   import net.sf.ehcache.Element;
8   import net.sf.ehcache.TransactionController;
9   import net.sf.ehcache.store.DefaultElementValueComparator;
10  import net.sf.ehcache.store.ElementValueComparator;
11  import net.sf.ehcache.transaction.DeadLockException;
12  import net.sf.ehcache.transaction.TransactionException;
13  import net.sf.ehcache.transaction.TransactionInterruptedException;
14  import net.sf.ehcache.transaction.TransactionTimeoutException;
15  
16  import java.util.Arrays;
17  import java.util.concurrent.CyclicBarrier;
18  import java.util.concurrent.atomic.AtomicBoolean;
19  
20  /***
21   * @author lorban
22   */
23  public class LocalTransactionTest extends TestCase {
24  
25      private final ElementValueComparator elementValueComparator = new DefaultElementValueComparator();
26      private CacheManager cacheManager;
27      private Ehcache cache1;
28      private Ehcache cache2;
29      private TransactionController transactionController;
30  
31      @Override
32      protected void setUp() throws Exception {
33          cacheManager = new CacheManager(LocalTransactionTest.class.getResourceAsStream("/ehcache-tx-local.xml"));
34          transactionController = cacheManager.getTransactionController();
35          transactionController.begin();
36          cache1 = cacheManager.getEhcache("txCache1");
37          cache1.removeAll();
38          cache2 = cacheManager.getEhcache("txCache2");
39          cache2.removeAll();
40          transactionController.commit();
41      }
42  
43      @Override
44      protected void tearDown() throws Exception {
45          if (transactionController.getCurrentTransactionContext() != null) {
46              transactionController.rollback();
47          }
48          cacheManager.shutdown();
49      }
50  
51      public void testTransactionContextLifeCycle() throws Exception {
52          assertNull(transactionController.getCurrentTransactionContext());
53          transactionController.begin();
54          try {
55              transactionController.begin();
56              fail("expected TransactionException");
57          } catch (TransactionException e) {
58              // expected
59          }
60          assertNotNull(transactionController.getCurrentTransactionContext());
61          transactionController.commit();
62          try {
63              transactionController.commit();
64              fail("expected TransactionException");
65          } catch (Exception e) {
66              // expected
67          }
68          try {
69              transactionController.rollback();
70              fail("expected TransactionException");
71          } catch (Exception e) {
72              // expected
73          }
74          assertNull(transactionController.getCurrentTransactionContext());
75      }
76  
77      public void testInterruption() throws Exception {
78          transactionController.begin();
79          Thread.currentThread().interrupt();
80          try {
81              cache1.get(1);
82              fail("expected TransactionInterruptedException");
83          } catch (TransactionInterruptedException e) {
84              // expected
85          }
86  
87          // make sure interrupted status got cleared
88          cache1.get(1);
89  
90          cache1.put(new Element(1, "one"));
91  
92          final CyclicBarrier barrier = new CyclicBarrier(2);
93          TxThread tx2 = new TxThread() {
94              @Override
95              public void exec() throws Exception {
96                  transactionController.begin();
97  
98                  // awake tx1
99                  barrier.await();
100                 try {
101                     cache1.put(new Element(1, "one#tx2"));
102                 } catch (TransactionInterruptedException e) {
103                     // expected
104                 }
105 
106                 transactionController.commit();
107             }
108         };
109         tx2.start();
110 
111         barrier.await();
112         tx2.interrupt();
113 
114         tx2.join();
115         tx2.assertNotFailed();
116 
117         transactionController.commit();
118     }
119 
120     public void testTimeout() throws Exception {
121         transactionController.setDefaultTransactionTimeout(1);
122         transactionController.begin();
123 
124         cache1.put(new Element(1, "one"));
125 
126         Thread.sleep(1500);
127 
128         try {
129             cache1.get(1);
130             fail("expected TransactionTimeoutException");
131         } catch (TransactionTimeoutException e) {
132             // expected
133         }
134 
135         try {
136             cache1.getQuiet(1);
137             fail("expected TransactionTimeoutException");
138         } catch (TransactionTimeoutException e) {
139             // expected
140         }
141 
142         try {
143             cache1.getKeys();
144             fail("expected TransactionTimeoutException");
145         } catch (TransactionTimeoutException e) {
146             // expected
147         }
148 
149         try {
150             cache1.getSize();
151             fail("expected TransactionTimeoutException");
152         } catch (TransactionTimeoutException e) {
153             // expected
154         }
155 
156         try {
157             cache1.removeAll();
158             fail("expected TransactionTimeoutException");
159         } catch (TransactionTimeoutException e) {
160             // expected
161         }
162 
163         try {
164             cache1.put(new Element(2, "two"));
165             fail("expected TransactionTimeoutException");
166         } catch (TransactionTimeoutException e) {
167             // expected
168         }
169 
170         try {
171             cache1.remove(1);
172             fail("expected TransactionTimeoutException");
173         } catch (TransactionTimeoutException e) {
174             // expected
175         }
176 
177         transactionController.rollback();
178     }
179 
180     public void testDefaultTimeout() throws Exception {
181         transactionController.setDefaultTransactionTimeout(1);
182         transactionController.begin();
183 
184         Thread.sleep(1500);
185 
186         try {
187             transactionController.commit();
188             fail("expected TransactionTimeoutException");
189         } catch (TransactionTimeoutException e) {
190             // expected
191         }
192     }
193 
194     public void testCopyOnRead() throws Exception {
195         transactionController.begin();
196         Object putValue = new Object[]{"one#1"};
197         cache1.put(new Element(1, putValue));
198 
199         Element one = cache1.get(1);
200         Object getValue = one.getObjectValue();
201 
202         assertFalse(putValue == getValue);
203         transactionController.commit();
204     }
205 
206     public void testCopyOnWrite() throws Exception {
207         transactionController.begin();
208         Object[] putValue = new Object[]{"one#1"};
209         cache1.put(new Element(1, putValue));
210         putValue[0] = "one#2";
211 
212         Element one = cache1.get(1);
213         Object[] getValue = (Object[]) one.getObjectValue();
214 
215         assertEquals("one#1", getValue[0]);
216         transactionController.commit();
217     }
218 
219     public void testTwoPuts() throws Exception {
220         transactionController.begin();
221         cache1.put(new Element(1, new Object[]{"one#1"}));
222         transactionController.commit();
223 
224         transactionController.begin();
225         cache1.put(new Element(1, new Object[]{"one#2"}));
226         transactionController.commit();
227     }
228 
229     public void testTwoCaches() throws Exception {
230         transactionController.begin();
231 
232         cache1.put(new Element(1, "one"));
233         cache2.put(new Element(1, "one"));
234 
235         TxThread tx2 = new TxThread() {
236             @Override
237             public void exec() {
238                 transactionController.begin();
239 
240                 assertNull(cache1.get(1));
241                 assertNull(cache2.get(1));
242 
243                 transactionController.commit();
244             }
245         };
246         tx2.start();
247         tx2.join();
248         tx2.assertNotFailed();
249 
250         transactionController.commit();
251     }
252 
253 
254     public void testPut() throws Exception {
255         transactionController.begin();
256 
257         cache1.put(new Element(1, "one"));
258 
259         assertEquals(new Element(1, "one"), cache1.get(1));
260 
261         TxThread tx2 = new TxThread() {
262             @Override
263             public void exec() {
264                 transactionController.begin();
265 
266                 assertNull(cache1.get(1));
267 
268                 transactionController.commit();
269             }
270         };
271         tx2.start();
272         tx2.join();
273         tx2.assertNotFailed();
274 
275         transactionController.commit();
276 
277         transactionController.begin();
278         assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
279         transactionController.commit();
280 
281         TxThread tx3 = new TxThread() {
282             @Override
283             public void exec() {
284                 transactionController.begin();
285 
286                 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
287 
288                 transactionController.commit();
289             }
290         };
291         tx3.start();
292         tx3.join();
293         tx3.assertNotFailed();
294     }
295 
296     public void testRemove() throws Exception {
297         transactionController.begin();
298         cache1.put(new Element(1, "one"));
299         transactionController.commit();
300 
301         transactionController.begin();
302 
303         assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
304         cache1.put(new Element(2, "two"));
305         assertTrue(cache1.remove(1));
306         assertNull(cache1.get(1));
307         assertTrue(elementValueComparator.equals(new Element(2, "two"), cache1.get(2)));
308 
309 
310         TxThread tx2 = new TxThread() {
311             @Override
312             public void exec() {
313                 transactionController.begin(1);
314 
315                 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
316 
317                 try {
318                     cache1.remove(1);
319                     fail("expected DeadLockException");
320                 } catch (DeadLockException e) {
321                     // expected
322                 }
323 
324                 transactionController.rollback();
325             }
326         };
327         tx2.start();
328         tx2.join();
329         tx2.assertNotFailed();
330 
331         transactionController.commit();
332 
333         transactionController.begin();
334         assertNull(cache1.get(1));
335         assertTrue(elementValueComparator.equals(new Element(2, "two"), cache1.get(2)));
336         transactionController.commit();
337     }
338 
339     public void testRollback() throws Exception {
340         transactionController.begin();
341         cache1.put(new Element(1, "one"));
342         transactionController.rollback();
343 
344         transactionController.begin();
345         assertNull(cache1.get(1));
346         transactionController.rollback();
347     }
348 
349     public void testRollbackOnly() throws Exception {
350         transactionController.begin();
351 
352         cache1.put(new Element(1, "one"));
353 
354         transactionController.setRollbackOnly();
355         try {
356             transactionController.commit();
357             fail("expected TransactionException");
358         } catch (TransactionException e) {
359             // expected
360         }
361 
362         transactionController.begin();
363 
364         assertNull(cache1.get(1));
365 
366         transactionController.commit();
367     }
368 
369     public void testTwoConcurrentUpdates() throws Exception {
370         final long WAIT_TIME = 1500;
371         final long ERROR_MARGIN = 200;
372         final long[] times = new long[2];
373 
374         //TX 0
375         transactionController.begin();
376 
377         cache1.put(new Element(1, "tx1-one"));
378 
379         TxThread tx2 = new TxThread() {
380             @Override
381             public void exec() throws InterruptedException {
382                 //TX 1
383                 transactionController.begin(4);
384 
385                 times[0] = System.currentTimeMillis();
386                 cache1.put(new Element(1, "tx2-one"));
387                 times[1] = System.currentTimeMillis();
388 
389                 Thread.sleep(WAIT_TIME);
390 
391                 //TX 1
392                 transactionController.commit();
393             }
394         };
395         tx2.start();
396         tx2.join(WAIT_TIME);
397 
398         //TX 0
399         transactionController.commit();
400 
401         //TX 2
402         transactionController.begin();
403         assertTrue(elementValueComparator.equals(new Element(1, "tx1-one"), cache1.get(1)));
404         //TX 2
405         transactionController.commit();
406 
407         tx2.join();
408         tx2.assertNotFailed();
409 
410         assertTrue("expected TX1 to be on hold for more than " + WAIT_TIME + "ms, waited: " + (times[1] - times[0]), times[1] - times[0]
411                 >= (WAIT_TIME - ERROR_MARGIN));
412 
413         // TX 3
414         transactionController.begin();
415         assertTrue(elementValueComparator.equals(new Element(1, "tx2-one"), cache1.get(1)));
416         // TX 3
417         transactionController.commit();
418     }
419 
420     public void testDeadlock() throws Exception {
421         final String[] losingTx = new String[1];
422 
423         transactionController.begin(2);
424 
425         cache1.put(new Element(1, "tx1-one"));
426 
427         final CyclicBarrier barrier1 = new CyclicBarrier(2);
428         final CyclicBarrier barrier2 = new CyclicBarrier(2);
429         TxThread tx2 = new TxThread() {
430             @Override
431             public void exec() throws Exception {
432                 transactionController.begin();
433 
434                 cache1.put(new Element(2, "tx2-two"));
435 
436                 // awake tx1
437                 barrier1.await();
438                 barrier2.await();
439 
440                 try {
441                     cache1.put(new Element(1, "tx2-one"));
442 
443                     cache1.put(new Element("tx2", ""));
444 
445                     transactionController.commit();
446                 } catch (DeadLockException e) {
447                     losingTx[0] = "tx2";
448                     transactionController.rollback();
449                 }
450             }
451         };
452         tx2.start();
453         barrier1.await();
454 
455         try {
456             cache1.put(new Element(2, "tx1-two"));
457 
458             cache1.put(new Element("tx1", ""));
459 
460             transactionController.commit();
461         } catch (DeadLockException e) {
462             losingTx[0] = "tx1";
463             transactionController.rollback();
464         }
465 
466         // awake tx2
467         barrier2.await();
468         tx2.join();
469         tx2.assertNotFailed();
470 
471 
472         transactionController.begin();
473         Element el1 = cache1.get(1);
474         Element el2 = cache1.get(2);
475 
476         // make sure one TX lost
477         assertNotNull(losingTx[0]);
478 
479         // make sure both elements are from the same TX
480         String el1TxName = ((String) el1.getValue()).substring(0, 3);
481         String el2TxName = ((String) el2.getValue()).substring(0, 3);
482         assertEquals(el1TxName, el2TxName);
483 
484         // make sure the winning TX could insert its unique element
485         String winningTx = losingTx[0].equals("tx1") ? "tx2" : "tx1";
486         assertNotNull(cache1.get(winningTx));
487 
488         // make sure the losing TX could NOT insert its unique element
489         assertNull(cache1.get(losingTx[0]));
490 
491         transactionController.commit();
492     }
493 
494     public void testGetKeys() throws Exception {
495         transactionController.begin();
496 
497         cache1.put(new Element(1, "one"));
498         assertEquals(1, cache1.getKeys().size());
499         assertTrue(cache1.getKeys().containsAll(Arrays.asList(1)));
500 
501         cache1.put(new Element(2, "two"));
502         assertEquals(2, cache1.getKeys().size());
503         assertTrue(cache1.getKeys().containsAll(Arrays.asList(1, 2)));
504 
505         cache1.remove(1);
506         assertEquals(1, cache1.getKeys().size());
507         assertTrue(cache1.getKeys().containsAll(Arrays.asList(2)));
508 
509         transactionController.commit();
510 
511 
512         transactionController.begin();
513 
514         cache1.put(new Element(1, "one"));
515 
516         final AtomicBoolean tx2Success = new AtomicBoolean(false);
517         TxThread tx2 = new TxThread() {
518             @Override
519             public void exec() {
520                 transactionController.begin();
521 
522                 assertEquals(1, cache1.getKeys().size());
523                 assertTrue(cache1.getKeys().containsAll(Arrays.asList(2)));
524 
525                 transactionController.commit();
526                 tx2Success.set(true);
527             }
528         };
529         tx2.start();
530         tx2.join();
531         tx2.assertNotFailed();
532 
533         transactionController.commit();
534         assertTrue(tx2Success.get());
535     }
536 
537     public void testRemoveAll() throws Exception {
538         transactionController.begin();
539 
540         cache1.put(new Element(1, "one"));
541         cache1.put(new Element(2, "two"));
542         assertEquals(2, cache1.getSize());
543 
544         transactionController.commit();
545 
546 
547         transactionController.begin();
548 
549         assertEquals(2, cache1.getSize());
550         cache1.removeAll();
551         assertEquals(0, cache1.getSize());
552 
553         final CyclicBarrier barrier = new CyclicBarrier(2);
554         TxThread tx2 = new TxThread() {
555             @Override
556             public void exec() throws Exception {
557                 transactionController.begin();
558 
559                 assertEquals(2, cache1.getSize());
560 
561                 cache1.put(new Element(3, "three"));
562 
563                 //wake up tx1
564                 barrier.await();
565                 barrier.await();
566 
567                 transactionController.commit();
568             }
569         };
570         tx2.start();
571         barrier.await();
572         assertEquals(0, cache1.getSize());
573 
574         // wake up tx2
575         barrier.await();
576 
577         tx2.join();
578         tx2.assertNotFailed();
579 
580         transactionController.commit();
581     }
582 
583     public void testGetSize() throws Exception {
584         transactionController.begin();
585         assertEquals(0, cache1.getSize());
586         cache1.put(new Element(1, "one"));
587         cache1.put(new Element(2, "two"));
588         assertEquals(2, cache1.getSize());
589 
590         TxThread tx2 = new TxThread() {
591             @Override
592             public void exec() {
593                 transactionController.begin();
594 
595                 assertEquals(0, cache1.getSize());
596                 cache1.put(new Element(3, "three"));
597                 assertEquals(1, cache1.getSize());
598 
599                 transactionController.commit();
600             }
601         };
602         tx2.start();
603         tx2.join();
604         tx2.assertNotFailed();
605 
606         assertEquals(3, cache1.getSize());
607 
608         transactionController.commit();
609     }
610 
611     public void testPutIfAbsent() throws Exception {
612         transactionController.begin();
613         assertNull(cache1.putIfAbsent(new Element(0, "zero")));
614         transactionController.commit();
615 
616         transactionController.begin();
617         assertNull(cache1.putIfAbsent(new Element(1, "one")));
618         assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.putIfAbsent(new Element(1, "one#2"))));
619         assertTrue(cache1.remove(1));
620         assertNull(cache1.putIfAbsent(new Element(1, "one")));
621 
622         final CyclicBarrier barrier = new CyclicBarrier(2);
623         TxThread tx2 = new TxThread() {
624             @Override
625             public void exec() throws Exception {
626                 transactionController.begin();
627 
628                 assertTrue(elementValueComparator.equals(new Element(0, "zero"), cache1.putIfAbsent(new Element(0, "zero#2"))));
629 
630                 assertEquals(1, cache1.getSize());
631                 cache1.put(new Element(2, "two"));
632                 assertEquals(2, cache1.getSize());
633 
634                 // awake tx1
635                 barrier.await();
636                 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.putIfAbsent(new Element(1, "one#tx2"))));
637 
638                 transactionController.commit();
639             }
640         };
641         tx2.start();
642         barrier.await();
643 
644         assertEquals(2, cache1.getSize());
645         assertNull(cache1.get(2));
646 
647         transactionController.commit();
648         tx2.join();
649         tx2.assertNotFailed();
650 
651 
652         transactionController.begin();
653 
654         assertEquals(3, cache1.getSize());
655         assertTrue(elementValueComparator.equals(new Element(0, "zero"), cache1.get(0)));
656         assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
657         assertTrue(elementValueComparator.equals(new Element(2, "two"), cache1.get(2)));
658 
659         transactionController.commit();
660     }
661 
662     public void testRemoveElement() throws Exception {
663         transactionController.begin();
664         assertEquals(0, cache1.getSize());
665 
666         cache1.put(new Element(1, "one"));
667         assertEquals(1, cache1.getSize());
668 
669         final CyclicBarrier barrier = new CyclicBarrier(2);
670         TxThread tx2 = new TxThread() {
671             @Override
672             public void exec() throws Exception {
673                 transactionController.begin();
674 
675                 // awake tx1
676                 barrier.await();
677                 assertTrue(cache1.removeElement(new Element(1, "one")));
678 
679                 barrier.await();
680                 assertEquals(0, cache1.getSize());
681                 transactionController.commit();
682             }
683         };
684         tx2.start();
685 
686         barrier.await();
687         transactionController.commit();
688 
689         transactionController.begin();
690         assertEquals(1, cache1.getSize());
691         transactionController.commit();
692         barrier.await(); // awake tx2
693 
694         tx2.join();
695         tx2.assertNotFailed();
696 
697         transactionController.begin();
698         assertEquals(0, cache1.getSize());
699         assertFalse(cache1.removeElement(new Element(1, "one")));
700         cache1.put(new Element(1, "one"));
701         assertTrue(cache1.removeElement(new Element(1, "one")));
702         transactionController.commit();
703     }
704 
705     public void testReplace() throws Exception {
706         transactionController.begin();
707 
708         assertNull(cache1.replace(new Element(1, "one")));
709         cache1.put(new Element(1, "one"));
710         assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.replace(new Element(1, "one#2"))));
711         assertTrue(elementValueComparator.equals(new Element(1, "one#2"), cache1.replace(new Element(1, "one#3"))));
712 
713         assertTrue(elementValueComparator.equals(new Element(1, "one#3"), cache1.get(1)));
714 
715         transactionController.commit();
716     }
717 
718     public void testReplace2Args() throws Exception {
719         transactionController.begin();
720 
721         assertFalse(cache1.replace(new Element(1, "one"), new Element(1, "one#2")));
722         cache1.put(new Element(1, "one"));
723         assertTrue(cache1.replace(new Element(1, "one"), new Element(1, "one#2")));
724         assertFalse(cache1.replace(new Element(1, "one"), new Element(1, "one#3")));
725         assertTrue(cache1.replace(new Element(1, "one#2"), new Element(1, "one")));
726 
727         assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
728 
729         transactionController.commit();
730     }
731 
732     public void testMemoryOnly() throws Exception {
733         Ehcache txCache = cacheManager.getEhcache("txCacheMemoryOnly");
734         transactionController.begin();
735         txCache.removeAll();
736         transactionController.commit();
737 
738         transactionController.begin();
739         txCache.put(new Element(1, "one"));
740         assertEquals(new Element(1, "one"), txCache.get(1));
741         assertEquals(1, txCache.getSize());
742 
743         txCache.put(new Element(2, "two"));
744         assertEquals(new Element(2, "two"), txCache.get(2));
745         assertEquals(2, txCache.getSize());
746         transactionController.commit();
747 
748         transactionController.begin();
749         assertEquals(new Element(1, "one"), txCache.get(1));
750         assertEquals(new Element(2, "two"), txCache.get(2));
751 
752         txCache.put(new Element(1, "one#2"));
753         txCache.put(new Element(2, "two#2"));
754 
755         assertEquals(new Element(1, "one#2"), txCache.get(1));
756         assertEquals(new Element(2, "two#2"), txCache.get(2));
757         assertNull(txCache.get(3));
758         assertEquals(2, txCache.getSize());
759         transactionController.commit();
760 
761         transactionController.begin();
762         assertEquals(new Element(1, "one#2"), txCache.get(1));
763         assertEquals(new Element(2, "two#2"), txCache.get(2));
764         transactionController.commit();
765     }
766 
767     public void testPersistent() throws Exception {
768         Ehcache txCachePersistent = cacheManager.getEhcache("txCachePersistent");
769         transactionController.begin();
770         txCachePersistent.removeAll();
771         transactionController.commit();
772 
773         transactionController.begin();
774         txCachePersistent.put(new Element(1, "one"));
775         assertEquals(new Element(1, "one"), txCachePersistent.get(1));
776         assertEquals(1, txCachePersistent.getSize());
777 
778         txCachePersistent.put(new Element(2, "two"));
779         assertEquals(new Element(2, "two"), txCachePersistent.get(2));
780         assertEquals(2, txCachePersistent.getSize());
781         transactionController.commit();
782 
783         transactionController.begin();
784         assertEquals(new Element(1, "one"), txCachePersistent.get(1));
785         assertEquals(new Element(2, "two"), txCachePersistent.get(2));
786 
787         txCachePersistent.put(new Element(1, "one#2"));
788         txCachePersistent.put(new Element(2, "two#2"));
789 
790         assertEquals(new Element(1, "one#2"), txCachePersistent.get(1));
791         assertEquals(new Element(2, "two#2"), txCachePersistent.get(2));
792         assertNull(txCachePersistent.get(3));
793         assertEquals(2, txCachePersistent.getSize());
794         transactionController.commit();
795 
796         transactionController.begin();
797         assertEquals(new Element(1, "one#2"), txCachePersistent.get(1));
798         assertEquals(new Element(2, "two#2"), txCachePersistent.get(2));
799         transactionController.commit();
800     }
801 
802     public void testOverflow() throws Exception {
803         final Ehcache txCacheOverflow = cacheManager.getEhcache("txCacheOverflow");
804 
805         transactionController.begin();
806         txCacheOverflow.put(new Element(1, "one"));
807         assertEquals(new Element(1, "one"), txCacheOverflow.get(1));
808         assertEquals(1, txCacheOverflow.getSize());
809 
810         txCacheOverflow.put(new Element(2, "two"));
811         assertEquals(new Element(2, "two"), txCacheOverflow.get(2));
812         assertEquals(2, txCacheOverflow.getSize());
813         transactionController.commit();
814 
815         transactionController.begin();
816         assertEquals(new Element(1, "one"), txCacheOverflow.get(1));
817         assertEquals(new Element(2, "two"), txCacheOverflow.get(2));
818 
819         txCacheOverflow.put(new Element(1, "one#2"));
820         txCacheOverflow.put(new Element(2, "two#2"));
821 
822         assertEquals(new Element(1, "one#2"), txCacheOverflow.get(1));
823         assertEquals(new Element(2, "two#2"), txCacheOverflow.get(2));
824         assertNull(txCacheOverflow.get(3));
825         assertEquals(2, txCacheOverflow.getSize());
826         transactionController.commit();
827 
828         transactionController.begin();
829         assertEquals(new Element(1, "one#2"), txCacheOverflow.get(1));
830         assertEquals(new Element(2, "two#2"), txCacheOverflow.get(2));
831         transactionController.commit();
832     }
833 
834     private static class TxThread extends Thread {
835         private volatile boolean failed;
836 
837         @Override
838         public final void run() {
839             try {
840                 exec();
841             } catch (Throwable t) {
842                 t.printStackTrace();
843                 failed = true;
844             }
845         }
846 
847         public void exec() throws Exception {
848         }
849 
850         public void assertNotFailed() {
851             if (failed) {
852                 throw new AssertionFailedError("TxThread failed");
853             }
854         }
855     }
856 
857 }