1 /*
2    Copyright (c) 2012, 2021, Oracle and/or its affiliates.
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, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 package testsuite.clusterj;
26 
27 import java.nio.ByteBuffer;
28 
29 import org.junit.Ignore;
30 
31 import com.mysql.clusterj.ClusterJFatalUserException;
32 import com.mysql.clusterj.ClusterJHelper;
33 import com.mysql.clusterj.ColumnMetadata;
34 import com.mysql.clusterj.ColumnType;
35 import com.mysql.clusterj.DynamicObject;
36 
37 import testsuite.clusterj.AbstractClusterJModelTest;
38 import testsuite.clusterj.model.IdBase;
39 
40 @Ignore
41 public class StressTest extends AbstractClusterJModelTest {
42 
43     static protected final Runtime rt = Runtime.getRuntime();
44 
45     private static final int NUMBER_TO_INSERT = 4000;
46 
47     private static final int ITERATIONS = 7;
48 
49     private static final int ITERATIONS_TO_DROP = 3;
50 
51     private static final String STRESS_TEST_TABLE_PROPERTY_NAME = "com.mysql.clusterj.StressTestTable";
52 
53     private static String tableName = ClusterJHelper.getStringProperty(STRESS_TEST_TABLE_PROPERTY_NAME, "stress");
54 
55     private ColumnMetadata[] columnMetadatas;
56 
57     private ColumnMetadata keyMetadata;
58 
59     private Timer timer = new Timer();
60 
61     private static int BYTES_LENGTH = 12000;
62 
63     private static ByteBuffer BYTES = ByteBuffer.allocate(BYTES_LENGTH);
64 
65     static {
66         for (int i = 0; i < BYTES_LENGTH; ++i) {
67             // only printable bytes from ABC..^_`
BYTES.put(byte)((i % 32) + 65)68             BYTES.put((byte)((i % 32) + 65));
69         }
70     }
71 
72     private static final byte[] DIGITS = new byte[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
73 
74     private static int STRING_LENGTH = 12000;
75 
76     private static String STRING;
77 
78     static {
79         StringBuilder builder = new StringBuilder();
80         for (int i = 0; i < STRING_LENGTH; ++i) {
81             // only printable bytes from ABC..^_`
builder.append(byte)((i % 32) + 65)82             builder.append((byte)((i % 32) + 65));
83         }
84         STRING = builder.toString();
85     }
86 
87     @Override
getModelClass()88     java.lang.Class<? extends IdBase> getModelClass() {
89         return Stress.class;
90     }
91 
92     @Override
localSetUp()93     public void localSetUp() {
94         createSessionFactory();
95         session = sessionFactory.getSession();
96         tx = session.currentTransaction();
97         session.deletePersistentAll(Stress.class);
98         columnMetadatas = session.newInstance(Stress.class).columnMetadata();
99         findKeyMetadata();
100     }
101 
testIndy()102     public void testIndy() {
103         insAattr_indy();
104         getA_indy();
105         delA_indy();
106     }
107 
testEach()108     public void testEach() {
109         insAattr_each();
110         getA_each();
111         delA_each();
112     }
113 
testBulk()114     public void testBulk() {
115         insAattr_bulk();
116         getA_bulk();
117         delA_bulk();
118     }
119 
insAattr_indy()120     public void insAattr_indy() {
121         long total = 0;
122         for (int i = 0; i < ITERATIONS; ++i) {
123             // first delete existing rows
124             if (tx.isActive()) tx.rollback();
125             session.deletePersistentAll(Stress.class);
126             // garbage collect what we can before each test
127             gc();
128             timer.start();
129             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
130                 Stress instance = createObject(key);
131                 session.makePersistent(instance);
132             }
133             // drop the first 'n' iterations
134             timer.stop();
135             if (i >= ITERATIONS_TO_DROP) total += timer.time();
136             System.out.println("insAattr_indy: " + timer.time());
137         }
138         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
139     }
140 
insAattr_each()141     public void insAattr_each() {
142         long total = 0;
143         for (int i = 0; i < ITERATIONS; ++i) {
144             // first delete existing rows
145             if (tx.isActive()) tx.rollback();
146             session.deletePersistentAll(Stress.class);
147             // garbage collect what we can before each test
148             gc();
149             timer.start();
150             session.currentTransaction().begin();
151             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
152                 Stress instance = createObject(key);
153                 session.makePersistent(instance);
154                 session.flush();
155             }
156             session.currentTransaction().commit();
157             // drop the first 'n' iterations
158             timer.stop();
159             if (i >= ITERATIONS_TO_DROP) total += timer.time();
160             System.out.println("insAattr_each: " + timer.time());
161         }
162         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
163     }
164 
insAattr_bulk()165     public void insAattr_bulk() {
166         long total = 0;
167         for (int i = 0; i < ITERATIONS; ++i) {
168             // first delete existing rows
169             if (tx.isActive()) tx.rollback();
170             session.deletePersistentAll(Stress.class);
171             // garbage collect what we can before each test
172             gc();
173             timer.start();
174             session.currentTransaction().begin();
175             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
176                 Stress instance = createObject(key);
177                 session.makePersistent(instance);
178             }
179             session.currentTransaction().commit();
180             // drop the first 'n' iterations
181             timer.stop();
182             if (i >= ITERATIONS_TO_DROP) total += timer.time();
183             System.out.println("insAattr_bulk: " + timer.time());
184         }
185         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
186     }
187 
getA_indy()188     public void getA_indy() {
189         long total = 0;
190         for (int i = 0; i < ITERATIONS; ++i) {
191             // garbage collect what we can before each test
192             gc();
193             timer.start();
194             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
195                 session.find(Stress.class, createKey(key));
196             }
197             // drop the first 'n' iterations
198             timer.stop();
199             if (i >= ITERATIONS_TO_DROP) total += timer.time();
200             System.out.println("getA_indy: " + timer.time());
201         }
202         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
203     }
204 
getA_each()205     public void getA_each() {
206         long total = 0;
207         for (int i = 0; i < ITERATIONS; ++i) {
208             // garbage collect what we can before each test
209             gc();
210             timer.start();
211             session.currentTransaction().begin();
212             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
213                 session.find(Stress.class, createKey(key));
214             }
215             session.currentTransaction().commit();
216             // drop the first 'n' iterations
217             timer.stop();
218             if (i >= ITERATIONS_TO_DROP) total += timer.time();
219             System.out.println("getA_each: " + timer.time());
220         }
221         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
222     }
223 
getA_bulk()224     public void getA_bulk() {
225         long total = 0;
226         for (int i = 0; i < ITERATIONS; ++i) {
227             // garbage collect what we can before each test
228             gc();
229             timer.start();
230             session.currentTransaction().begin();
231             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
232                 Stress instance = session.newInstance(Stress.class, createKey(key));
233                 session.load(instance);
234             }
235             session.currentTransaction().commit();
236             // drop the first 'n' iterations
237             timer.stop();
238             if (i >= ITERATIONS_TO_DROP) total += timer.time();
239             System.out.println("getA_bulk: " + timer.time());
240         }
241         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
242     }
243 
delA_indy()244     public void delA_indy() {
245         long total = 0;
246         for (int i = 0; i < ITERATIONS; ++i) {
247             // garbage collect what we can before each test
248             gc();
249             timer.start();
250             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
251                 session.deletePersistent(Stress.class, createKey(key));
252             }
253             // drop the first 'n' iterations
254             timer.stop();
255             if (i >= ITERATIONS_TO_DROP) total += timer.time();
256             System.out.println("delA_indy: " + timer.time());
257         }
258         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
259     }
260 
delA_each()261     public void delA_each() {
262         long total = 0;
263         for (int i = 0; i < ITERATIONS; ++i) {
264             // garbage collect what we can before each test
265             gc();
266             timer.start();
267             session.currentTransaction().begin();
268             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
269                 session.deletePersistent(Stress.class, createKey(key));
270                 session.flush();
271             }
272             session.currentTransaction().commit();
273             // drop the first 'n' iterations
274             timer.stop();
275             if (i >= ITERATIONS_TO_DROP) total += timer.time();
276             System.out.println("delA_each: " + timer.time());
277         }
278         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
279     }
280 
delA_bulk()281     public void delA_bulk() {
282         long total = 0;
283         for (int i = 0; i < ITERATIONS; ++i) {
284             // garbage collect what we can before each test
285             gc();
286             timer.start();
287             session.currentTransaction().begin();
288             for (int key = 0; key < NUMBER_TO_INSERT; ++key) {
289                 session.deletePersistent(Stress.class, createKey(key));
290             }
291             session.currentTransaction().commit();
292             // drop the first 'n' iterations
293             timer.stop();
294             if (i >= ITERATIONS_TO_DROP) total += timer.time();
295             System.out.println("delA_bulk: " + timer.time());
296         }
297         System.out.println("Excluding " + ITERATIONS_TO_DROP + " Average: " + total/(ITERATIONS - ITERATIONS_TO_DROP) + "\n");
298     }
299 
createObject(int key)300     protected Stress createObject(int key) {
301         Stress instance = session.newInstance(Stress.class);
302         for (int columnNumber = 0; columnNumber < columnMetadatas.length; ++columnNumber) {
303             Object value = null;
304             // create value based on java type
305             ColumnMetadata columnMetadata = columnMetadatas[columnNumber];
306             Class<?> cls = columnMetadata.javaType();
307             int length = columnMetadata.maximumLength();
308             if (columnMetadata.isPrimaryKey()) {
309                 value = createKey(key);
310             } else if (int.class == cls) {
311                 value = key + columnNumber;
312             } else if (long.class == cls) {
313                 value = (long)(key + columnNumber);
314             } else if (float.class == cls) {
315                 value = (float)(key + columnNumber);
316             } else if (double.class == cls) {
317                 value = (double)(key + columnNumber);
318             } else if (short.class == cls) {
319                 value = (short)(key + columnNumber);
320             } else if (byte.class == cls) {
321                 value = (byte)(key + columnNumber);
322             } else if (Integer.class == cls) {
323                 value = (int)(key + columnNumber);
324             } else if (Long.class == cls) {
325                 value = (long)(key + columnNumber);
326             } else if (Float.class == cls) {
327                 value = (float)(key + columnNumber);
328             } else if (Double.class == cls) {
329                 value = (double)(key + columnNumber);
330             } else if (Short.class == cls) {
331                 value = (short)(key + columnNumber);
332             } else if (Byte.class == cls) {
333                 value = (byte)(key + columnNumber);
334             } else if (String.class == cls) {
335                 // take 'n' characters from the static String
336                 value = STRING.substring(key + columnNumber, key + columnNumber + length);
337             } else if (byte[].class == cls) {
338                 // take 'n' bytes from the static byte array
339                 value = new byte[length];
340                 BYTES.position((key + columnNumber));
341                 BYTES.get((byte[])value);
342             } else {
343                 throw new ClusterJFatalUserException("Unsupported column type " + cls.getName()
344                         + " for column " + columnMetadata.name());
345             }
346             instance.set(columnNumber, value);
347         }
348         return instance;
349     }
350 
createKey(int key)351     private Object createKey(int key) {
352         Object value = null;
353         Class<?> cls = keyMetadata.javaType();
354         int length = keyMetadata.maximumLength();
355         if (int.class == cls) {
356             value = key;
357         } else if (long.class == cls) {
358             value = (long)key;
359         } else if (String.class == cls) {
360             value = String.valueOf(key);
361         } else if (byte[].class == cls) {
362             String digits = String.valueOf(key);
363             if (keyMetadata.columnType() == ColumnType.Binary) {
364                 // fixed length
365                 value = new byte[length];
366             } else if (keyMetadata.columnType() == ColumnType.Varbinary) {
367                 // variable length
368                 value = new byte[digits.length()];
369             }
370             convertToBytes((byte[])value, digits);
371             if (debug) System.out.println("Key: " + dump((byte[])value));
372         } else throw new ClusterJFatalUserException("Unsupported column type " + cls.getName()
373                 + " for column " + keyMetadata.name());
374         return value;
375     }
376 
findKeyMetadata()377     private void findKeyMetadata() {
378         // TODO currently only supports a single key column
379         for (ColumnMetadata columnMetadata: columnMetadatas) {
380             if (columnMetadata.isPrimaryKey()) {
381                 if (keyMetadata != null) {
382                     throw new RuntimeException("Compound primary keys are not supported.");
383                 }
384                 keyMetadata = columnMetadata;
385             }
386         }
387     }
388 
dump(byte[] value)389     private String dump(byte[] value) {
390         StringBuilder builder = new StringBuilder();
391         for (int i = 0; i < value.length; ++i) {
392             builder.append("0123456789".charAt(value[i] - '0'));
393         }
394         return builder.toString();
395     }
396 
397     /** Convert the digits into a byte[] by translating each digit to a byte
398      *
399      * @param value the byte [] to convert
400      * @param digits the value
401      */
convertToBytes(byte[] value, String digits)402     private void convertToBytes(byte[] value, String digits) {
403         int j = digits.length();
404         for (int i = value.length - 1; i >= 0; --i) {
405             if (j-- > 0) {
406                 int digit = digits.charAt(j) - '0';
407                 value[i] = DIGITS[digit];
408             } else {
409                 // done with digits
410                 value[i] = '0';
411             }
412         }
413     }
414 
415     public static class Stress extends DynamicObject implements IdBase {
416 
Stress()417         public Stress() {}
418 
table()419         public String table() {
420             System.out.println("Stress table being used: " + tableName);
421             return tableName;
422         }
423 
getId()424         public int getId() {
425             return (Integer) get(0);
426         }
427 
setId(int id)428         public void setId(int id) {
429             set(0, id);
430         }
431     }
432 
gc()433     static private void gc() {
434         // empirically determined limit after which no further
435         // reduction in memory usage has been observed
436         //final int nFullGCs = 5;
437         final int nFullGCs = 10;
438         for (int i = 0; i < nFullGCs; i++) {
439             //out.print("gc: ");
440             long oldfree;
441             long newfree = rt.freeMemory();
442             do {
443                 oldfree = newfree;
444                 rt.runFinalization();
445                 rt.gc();
446                 newfree = rt.freeMemory();
447                 //out.print('.');
448             } while (newfree > oldfree);
449             //out.println();
450         }
451     }
452 
453     private static class Timer {
454 
455         private long time;
456 
start()457         public void start() {
458             time = System.nanoTime() / 1000000;
459         }
460 
stop()461         public long stop() {
462             time = (System.nanoTime() / 1000000) - time;
463             return time;
464         }
465 
time()466         public long time() {
467             return time;
468         }
469     }
470 
471 }
472