View Javadoc
1   package org.apache.commons.jcs3.engine;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  
24  import org.apache.commons.jcs3.engine.behavior.ICacheElement;
25  import org.apache.commons.jcs3.engine.behavior.ICacheListener;
26  
27  import junit.extensions.ActiveTestSuite;
28  import junit.framework.Test;
29  import junit.framework.TestCase;
30  
31  /**
32   * This test case is designed to makes sure there are no deadlocks in the event queue. The time to
33   * live should be set to a very short interval to make a deadlock more likely.
34   */
35  public class EventQueueConcurrentLoadTest
36      extends TestCase
37  {
38      /** The queue implementation */
39      private static CacheEventQueue<String, String> queue;
40  
41      /** The mock listener */
42      private static CacheListenerImpl<String, String> listen;
43  
44      /** max failure setting */
45      private static final int maxFailure = 3;
46  
47      /** time to wait before retrying on failure. */
48      private static final int waitBeforeRetry = 100;
49  
50      /** very small idle time */
51      private static final int idleTime = 2;
52  
53      /**
54       * Constructor for the TestDiskCache object.
55       * @param testName
56       */
57      public EventQueueConcurrentLoadTest( final String testName )
58      {
59          super( testName );
60      }
61  
62      /**
63       * A unit test suite for JUnit
64       * @return The test suite
65       */
66      public static Test suite()
67      {
68          final ActiveTestSuite suite = new ActiveTestSuite();
69  
70          suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest1" )
71          {
72              @Override
73              public void runTest()
74                  throws Exception
75              {
76                  this.runPutTest( 200, 200 );
77              }
78          } );
79  
80          suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest2" )
81          {
82              @Override
83              public void runTest()
84                  throws Exception
85              {
86                  this.runPutTest( 1200, 1400 );
87              }
88          } );
89  
90          suite.addTest( new EventQueueConcurrentLoadTest( "testRunRemoveTest1" )
91          {
92              @Override
93              public void runTest()
94                  throws Exception
95              {
96                  this.runRemoveTest( 2200 );
97              }
98          } );
99  
100         suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest4" )
101         {
102             @Override
103             public void runTest()
104                 throws Exception
105             {
106                 this.runPutTest( 5200, 6600 );
107             }
108         } );
109 
110         suite.addTest( new EventQueueConcurrentLoadTest( "testRunRemoveTest2" )
111         {
112             @Override
113             public void runTest()
114                 throws Exception
115             {
116                 this.runRemoveTest( 5200 );
117             }
118         } );
119 
120         suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutDelayTest" )
121         {
122             @Override
123             public void runTest()
124                 throws Exception
125             {
126                 this.runPutDelayTest( 100, 6700 );
127             }
128         } );
129 
130         return suite;
131     }
132 
133     /**
134      * Test setup. Create the static queue to be used by all tests
135      */
136     @Override
137     public void setUp()
138     {
139         listen = new CacheListenerImpl<>();
140         queue = new CacheEventQueue<>( listen, 1L, "testCache1", maxFailure, waitBeforeRetry );
141 
142         queue.setWaitToDieMillis( idleTime );
143     }
144 
145     /**
146      * Adds put events to the queue.
147      * @param end
148      * @param expectedPutCount
149      * @throws Exception
150      */
151     public void runPutTest( final int end, final int expectedPutCount )
152         throws Exception
153     {
154         for ( int i = 0; i <= end; i++ )
155         {
156             final CacheElement<String, String> elem = new CacheElement<>( "testCache1", i + ":key", i + "data" );
157             queue.addPutEvent( elem );
158         }
159 
160         while ( !queue.isEmpty() )
161         {
162             synchronized ( this )
163             {
164                 System.out.println( "queue is still busy, waiting 250 millis" );
165                 this.wait( 250 );
166             }
167         }
168         System.out.println( "queue is empty, comparing putCount" );
169 
170         // this becomes less accurate with each test. It should never fail. If
171         // it does things are very off.
172         assertTrue( "The put count [" + listen.putCount + "] is below the expected minimum threshold ["
173             + expectedPutCount + "]", listen.putCount >= ( expectedPutCount - 1 ) );
174 
175     }
176 
177     /**
178      * Add remove events to the event queue.
179      * @param end
180      * @throws Exception
181      */
182     public void runRemoveTest( final int end )
183         throws Exception
184     {
185         for ( int i = 0; i <= end; i++ )
186         {
187             queue.addRemoveEvent( i + ":key" );
188         }
189 
190     }
191 
192     /**
193      * Test putting and a delay. Waits until queue is empty to start.
194      * @param end
195      * @param expectedPutCount
196      * @throws Exception
197      */
198     public void runPutDelayTest( final int end, final int expectedPutCount )
199         throws Exception
200     {
201         while ( !queue.isEmpty() )
202         {
203             synchronized ( this )
204             {
205                 System.out.println( "queue is busy, waiting 250 millis to begin" );
206                 this.wait( 250 );
207             }
208         }
209         System.out.println( "queue is empty, begin" );
210 
211         // get it going
212         final CacheElement<String, String> elem = new CacheElement<>( "testCache1", "a:key", "adata" );
213         queue.addPutEvent( elem );
214 
215         for ( int i = 0; i <= end; i++ )
216         {
217             synchronized ( this )
218             {
219                 if ( i % 2 == 0 )
220                 {
221                     this.wait( idleTime );
222                 }
223                 else
224                 {
225                     this.wait( idleTime / 2 );
226                 }
227             }
228             final CacheElement<String, String> elem2 = new CacheElement<>( "testCache1", i + ":key", i + "data" );
229             queue.addPutEvent( elem2 );
230         }
231 
232         while ( !queue.isEmpty() )
233         {
234             synchronized ( this )
235             {
236                 System.out.println( "queue is still busy, waiting 250 millis" );
237                 this.wait( 250 );
238             }
239         }
240         System.out.println( "queue is empty, comparing putCount" );
241 
242         Thread.sleep( 1000 );
243 
244         // this becomes less accurate with each test. It should never fail. If
245         // it does things are very off.
246         assertTrue( "The put count [" + listen.putCount + "] is below the expected minimum threshold ["
247             + expectedPutCount + "]", listen.putCount >= ( expectedPutCount - 1 ) );
248 
249     }
250 
251     /**
252      * This is a dummy cache listener to use when testing the event queue.
253      */
254     protected static class CacheListenerImpl<K, V>
255         implements ICacheListener<K, V>
256     {
257         /**
258          * <code>putCount</code>
259          */
260         protected int putCount;
261 
262         /**
263          * <code>removeCount</code>
264          */
265         protected int removeCount;
266 
267         /**
268          * @param item
269          * @throws IOException
270          */
271         @Override
272         public void handlePut( final ICacheElement<K, V> item )
273             throws IOException
274         {
275             synchronized ( this )
276             {
277                 putCount++;
278             }
279         }
280 
281         /**
282          * @param cacheName
283          * @param key
284          * @throws IOException
285          */
286         @Override
287         public void handleRemove( final String cacheName, final K key )
288             throws IOException
289         {
290             synchronized ( this )
291             {
292                 removeCount++;
293             }
294 
295         }
296 
297         /**
298          * @param cacheName
299          * @throws IOException
300          */
301         @Override
302         public void handleRemoveAll( final String cacheName )
303             throws IOException
304         {
305             // TODO Auto-generated method stub
306 
307         }
308 
309         /**
310          * @param cacheName
311          * @throws IOException
312          */
313         @Override
314         public void handleDispose( final String cacheName )
315             throws IOException
316         {
317             // TODO Auto-generated method stub
318 
319         }
320 
321         /**
322          * @param id
323          * @throws IOException
324          */
325         @Override
326         public void setListenerId( final long id )
327             throws IOException
328         {
329             // TODO Auto-generated method stub
330 
331         }
332 
333         /**
334          * @return 0
335          * @throws IOException
336          */
337         @Override
338         public long getListenerId()
339             throws IOException
340         {
341             // TODO Auto-generated method stub
342             return 0;
343         }
344 
345     }
346 }