]> review.fuel-infra Code Review - packages/trusty/mysql-wsrep-5.6.git/blob
2b35ca5195f58450e03d67a391e8d6de1b654956
[packages/trusty/mysql-wsrep-5.6.git] /
1 /*
2    Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
16 */
17
18 package testsuite.clusterj;
19
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Comparator;
23 import java.util.List;
24 import java.util.Random;
25 import java.util.Set;
26 import java.util.TreeSet;
27
28 import com.mysql.clusterj.Query;
29 import com.mysql.clusterj.Session;
30 import com.mysql.clusterj.query.QueryDomainType;
31
32 import testsuite.clusterj.model.Customer;
33 import testsuite.clusterj.model.Order;
34 import testsuite.clusterj.model.OrderLine;
35
36 public class MultithreadedTest extends AbstractClusterJModelTest {
37
38     @Override
39     protected boolean getDebug() {
40         return false;
41     }
42
43     private int numberOfThreads = 50;
44     private int numberOfNewCustomersPerThread = 5;
45     private int numberOfNewOrdersPerNewCustomer = 5;
46     private int numberOfUpdatesPerThread = 2;
47
48     private int maximumOrderLinesPerOrder = 5;
49     private int maximumQuantityPerOrderLine = 100;
50     private int maximumUnitPrice = 100;
51
52
53     private int numberOfInitialCustomers = 10;
54     private int nextCustomerId = numberOfInitialCustomers;
55     private int nextOrderId = 0;
56     private int nextOrderLineId = 0;
57
58     private int numberOfUpdatedOrderLines = 0;
59     private int numberOfDeletedOrders = 0;
60     private int numberOfDeletedOrderLines = 0;
61
62     private ThreadGroup threadGroup;
63
64     /** Customers */
65     List<Customer> customers = new ArrayList<Customer>();
66
67     /** Orders */
68     List<Order> orders = new ArrayList<Order>();
69
70     /** Order lines */
71     Set<OrderLine> orderlines = new TreeSet<OrderLine>(
72             new Comparator<OrderLine>() {
73                 public int compare(OrderLine o1, OrderLine o2) {
74                     return o1.getId() - o2.getId();
75                 }
76             }
77         );
78
79     @Override
80     public void localSetUp() {
81         createSessionFactory();
82         session = sessionFactory.getSession();
83         // first delete all customers, orders, and order lines
84         tx = session.currentTransaction();
85         tx.begin();
86         session.deletePersistentAll(Customer.class);
87         session.deletePersistentAll(Order.class);
88         session.deletePersistentAll(OrderLine.class);
89         tx.commit();
90         // start out with some customers
91         createCustomerInstances(nextCustomerId);
92         // add new customer instances
93         tx.begin();
94         session.makePersistentAll(customers);
95         tx.commit();
96         // get rid of them when we're done
97         addTearDownClasses(Customer.class);
98         addTearDownClasses(Order.class);
99         addTearDownClasses(OrderLine.class);
100     }
101
102     private void createCustomerInstances(int numberToCreate) {
103         for (int i = 0; i < numberToCreate; ++i) {
104             Customer customer = session.newInstance(Customer.class);
105             customer.setId(i);
106             customer.setName("Customer number " + i);
107             customer.setMagic(i * 100);
108             customers.add(customer);
109         }
110     }
111
112     /** The test method creates numberOfThreads threads and starts them.
113      * Once the threads are started, the main thread waits until all threads complete.
114      * The main thread then checks that the proper number of instances are
115      * created in the database and verifies that all orders are consistent
116      * with their order lines. Inconsistency might be due to thread interaction
117      * or improper database updates.
118      */
119     public void test() {
120         List<Thread> threads = new ArrayList<Thread>();
121         // create thread group
122         threadGroup = new ThreadGroup("Stuff");
123         // create uncaught exception handler
124         MyUncaughtExceptionHandler uncaughtExceptionHandler = new MyUncaughtExceptionHandler();
125         Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
126         // create all threads
127         for (int i = 0; i < numberOfThreads ; ++i) {
128             Thread thread = new Thread(threadGroup, new StuffToDo());
129             threads.add(thread);
130             thread.start();
131         }
132         // wait until all threads have finished
133         for (Thread t: threads) {
134             try {
135                 t.join();
136             } catch (InterruptedException e) {
137                 throw new RuntimeException("Interrupted while joining threads.");
138             }
139         }
140         // if any uncaught exceptions (from threads) signal an error
141         for (Throwable thrown: uncaughtExceptionHandler.getUncaughtExceptions()) {
142             error("Caught exception: " + thrown.getClass().getName() + ": " + thrown.getMessage());
143             StackTraceElement[] elements = thrown.getStackTrace();
144             for (StackTraceElement element: elements) {
145                 error("        at " + element.toString());
146             }
147         }
148         // summarize for the record
149         if (getDebug()) {
150             System.out.println("Number of threads: " + numberOfThreads + 
151                 "; number of new customers per thread: " + numberOfNewCustomersPerThread +
152                 "; number of orders per new customer: " + numberOfNewOrdersPerNewCustomer);
153             System.out.println("Created " + nextCustomerId + " customers; " +
154                 nextOrderId + " orders; and " + nextOrderLineId + " order lines.");
155             System.out.println("Deleted " + numberOfDeletedOrders + " orders; and " +
156                 numberOfDeletedOrderLines + " order lines.");
157             System.out.println("Updated " + numberOfUpdatedOrderLines + " order lines.");
158         }
159         errorIfNotEqual("Failed to create customers.",
160                 numberOfThreads * numberOfNewCustomersPerThread + numberOfInitialCustomers, nextCustomerId);
161         errorIfNotEqual("Failed to create orders. ",
162                 numberOfThreads * numberOfNewCustomersPerThread * numberOfNewOrdersPerNewCustomer, nextOrderId);
163         // double check the orders to make sure they were updated correctly
164         Session session = sessionFactory.getSession();
165         QueryDomainType<OrderLine> queryOrderType = session.getQueryBuilder().createQueryDefinition(OrderLine.class);
166         queryOrderType.where(queryOrderType.get("orderId").equal(queryOrderType.param("orderId")));
167         Query<OrderLine> query = session.createQuery(queryOrderType);        
168         for (Order order: orders) {
169             int orderId = order.getId();
170             // replace order with its persistent representation
171             order = session.find(Order.class, orderId);
172             double expectedTotal = order.getValue();
173             double actualTotal = 0.0d;
174             for (OrderLine orderLine: getOrderLines(session, query, orderId)) {
175                 actualTotal += orderLine.getTotalValue();
176             }
177             errorIfNotEqual("For order " + orderId + ", order value does not equal sum of order line values.",
178                     expectedTotal, actualTotal);
179         }
180         failOnError();
181     }
182
183     /** This class implements the logic per thread. For each thread created,
184      * the run method is invoked. 
185      * Each thread uses its own session and shares the customer, order, and order line
186      * collections. Collections are synchronized to avoid threading conflicts.
187      * Each thread creates numberOfNewCustomersPerThread customers, each of which
188      * contains a random number of orders, each of which contains a random number
189      * of order lines.
190      * Each thread then updates numberOfUpdatesPerThread orders by changing one
191      * order line.
192      * Each thread then deletes one order and its associated order lines.
193      */
194     class StuffToDo implements Runnable {
195
196         private Random myRandom = new Random();
197
198         public void run() {
199             // get my own session
200             Session session = sessionFactory.getSession();
201             QueryDomainType<OrderLine> queryOrderType = session.getQueryBuilder().createQueryDefinition(OrderLine.class);
202             queryOrderType.where(queryOrderType.get("orderId").equal(queryOrderType.param("orderId")));
203             Query<OrderLine> query = session.createQuery(queryOrderType);
204             for (int i = 0; i < numberOfNewCustomersPerThread; ++i) {
205                 // create a new customer
206                 createCustomer(session, String.valueOf(Thread.currentThread().getId()));
207                 for (int j = 0; j < numberOfNewOrdersPerNewCustomer ; ++j) {
208                     // create a new order
209                     createOrder(session, myRandom);
210                 }
211             }
212             // update orders
213             for (int j = 0; j < numberOfUpdatesPerThread; ++j) {
214                 updateOrder(session, myRandom, query);
215             }
216             // delete an order
217             deleteOrder(session, myRandom, query);
218         }
219
220     }
221
222     /** Create a new customer.
223      * 
224      * @param session the session
225      * @param threadId the thread id of the creating thread
226      */
227     private void createCustomer(Session session, String threadId) {
228         Customer customer = session.newInstance(Customer.class);
229         int id = getNextCustomerId();
230         customer.setId(id);
231         customer.setName("Customer number " + id + " thread " + threadId);
232         customer.setMagic(id * 10000);
233         session.makePersistent(customer); // autocommit for this
234         addCustomer(customer);
235     }
236
237     /** Create a new order. Add a new order with a random number of order lines
238      * and a random unit price and quantity.
239      * 
240      * @param session the session
241      * @param random a random number generator
242      */
243     public void createOrder(Session session, Random random) {
244         session.currentTransaction().begin();
245         // get an order number
246         int orderid = getNextOrderId();
247         Order order = session.newInstance(Order.class);
248         order.setId(orderid);
249         // get a random customer number
250         int customerId = random .nextInt(nextCustomerId);
251         order.setCustomerId(customerId);
252         order.setDescription("Order " + orderid + " for Customer " + customerId);
253         Double orderValue = 0.0d;
254         // now create some order lines
255         int numberOfOrderLines = random.nextInt(maximumOrderLinesPerOrder);
256         for (int i = 0; i < numberOfOrderLines; ++i) {
257             int orderLineNumber = getNextOrderLineId();
258             OrderLine orderLine = session.newInstance(OrderLine.class);
259             orderLine.setId(orderLineNumber);
260             orderLine.setOrderId(orderid);
261             long quantity = random.nextInt(maximumQuantityPerOrderLine);
262             orderLine.setQuantity(quantity);
263             float unitPrice = ((float)random.nextInt(maximumUnitPrice)) / 4;
264             orderLine.setUnitPrice(unitPrice);
265             double orderLineValue = unitPrice * quantity;
266             orderValue += orderLineValue;
267             if (getDebug()) System.out.println("For order " + orderid + " orderline " + orderLineNumber + 
268                     " order line value " + orderLineValue + " order value " + orderValue);
269             orderLine.setTotalValue(orderLineValue);
270             addOrderLine(orderLine);
271             session.persist(orderLine);
272         }
273         order.setValue(orderValue);
274         session.persist(order);
275         session.currentTransaction().commit();
276         addOrder(order);
277     }
278
279     /** Update an order; change one or more order lines
280      * 
281      * @param session the session
282      * @param random a random number generator
283      * @param query 
284      */
285     public void updateOrder(Session session, Random random, Query<OrderLine> query) {
286         session.currentTransaction().begin();
287         Order order = null;
288         // pick an order to update; prevent anyone else from updating the same order
289         order = removeOrderFromOrdersCollection(random);
290         if (order == null) {
291             return;
292         }
293         int orderId = order.getId();
294         // replace order with its persistent representation
295         order = session.find(Order.class, orderId);
296         List<OrderLine> orderLines = getOrderLines(session, query, orderId);
297         int numberOfOrderLines = orderLines.size();
298         OrderLine orderLine = null;
299         double orderValue = order.getValue();
300         if (numberOfOrderLines > 0) {
301             int index = random.nextInt(numberOfOrderLines);
302             orderLine = orderLines.get(index);
303             orderValue -= orderLine.getTotalValue();
304             updateOrderLine(orderLine, random);
305             orderValue += orderLine.getTotalValue();
306         }
307         order.setValue(orderValue);
308         session.updatePersistent(orderLine);
309         session.updatePersistent(order);
310         session.currentTransaction().commit();
311         // put order back now that we're done updating it
312         addOrder(order);
313     }
314
315     /** Update an order line by randomly changing unit price and quantity.
316      * 
317      * @param orderLine the order line to update
318      * @param random a random number generator
319      */
320     private void updateOrderLine(OrderLine orderLine, Random random) {
321         int orderid = orderLine.getOrderId();
322         int orderLineNumber = orderLine.getId();
323         double previousValue = orderLine.getTotalValue();
324         long quantity = random.nextInt(maximumQuantityPerOrderLine );
325         orderLine.setQuantity(quantity);
326         float unitPrice = ((float)random.nextInt(maximumUnitPrice)) / 4;
327         orderLine.setUnitPrice(unitPrice);
328         double orderLineValue = unitPrice * quantity;
329         orderLine.setTotalValue(orderLineValue);
330         if (getDebug()) System.out.println("For order " + orderid + " orderline " + orderLineNumber + 
331                 " previous order line value "  + previousValue + " new order line value " + orderLineValue);
332         synchronized (orderlines) {
333             ++numberOfUpdatedOrderLines;
334         }
335     }
336
337     /** Delete an order from the database.
338      * 
339      * @param session the session
340      * @param random a random number generator
341      * @param query the query instance to query for OrderLines by OrderId
342      */
343     public void deleteOrder(Session session, Random random, Query<OrderLine> query) {
344         session.currentTransaction().begin();
345         Order order = null;
346         // pick an order to delete
347         order = removeOrderFromOrdersCollection(random);
348         if (order == null) {
349             return;
350         }
351         int orderId = order.getId();
352         List<OrderLine> orderLines = getOrderLines(session, query, orderId);
353         removeOrderLinesFromOrderLinesCollection(orderLines);
354         session.deletePersistentAll(orderLines);
355         session.deletePersistent(order);
356         session.currentTransaction().commit();
357     }
358
359     private List<OrderLine> getOrderLines(Session session, Query<OrderLine> query, int orderId) {
360         query.setParameter("orderId", orderId);
361         return query.getResultList();
362     }
363
364     private Order removeOrderFromOrdersCollection(Random random) {
365         synchronized(orders) {
366             int numberOfOrders = orders.size();
367             if (numberOfOrders < 10) {
368                 return null;
369             }
370             int orderId = random.nextInt(numberOfOrders);
371             ++numberOfDeletedOrders;
372             return orders.remove(orderId);
373         }
374     }
375
376     private void removeOrderLinesFromOrderLinesCollection(Collection<OrderLine> orderLinesToRemove) {
377         synchronized(orderlines) {
378             orderlines.removeAll(orderLinesToRemove);
379             numberOfDeletedOrderLines += orderLinesToRemove.size();
380         }
381     }
382
383     /** Add a new customer to the list of customers
384      * @param customer
385      */
386     private void addCustomer(Customer customer) {
387         synchronized(customers) {
388             customers.add(customer);
389         }
390     }
391
392     /** Get the next customer number (multithread safe)
393      * @return
394      */
395     private int getNextCustomerId() {
396         synchronized(customers) {
397             return nextCustomerId++;
398         }
399     }
400     
401     /** Get the next order number (multithread safe)
402      * @return
403      */
404     private int getNextOrderId() {
405         synchronized(orders) {
406             return nextOrderId++;
407         }
408     }
409
410     /** Get the next order line number (multithread safe)
411      * @return
412      */
413     private int getNextOrderLineId() {
414         synchronized(orderlines) {
415             return nextOrderLineId++;
416         }
417     }
418     
419     /** Add an order to the list of orders.
420      * 
421      * @param order the order
422      */
423     private void addOrder(Order order) {
424         synchronized(orders) {
425             orders.add(order);
426         }
427     }
428
429     /** Add an order line to the list of order lines.
430      * 
431      * @param orderLine the order line
432      */
433     private void addOrderLine(OrderLine orderLine) {
434         synchronized(orderlines) {
435             orderlines.add(orderLine);
436         }
437     }
438
439 }