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