1 /* -*- mode: java; c-basic-offset: 4; indent-tabs-mode: nil; -*- 2 * vim:expandtab:shiftwidth=4:tabstop=4:smarttab: 3 * 4 * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License, version 2.0, 8 * as published by the Free Software Foundation. 9 * 10 * This program is also distributed with certain software (including 11 * but not limited to OpenSSL) that is licensed under separate terms, 12 * as designated in a particular file or component or in included license 13 * documentation. The authors of MySQL hereby grant you an additional 14 * permission to link the program and your derivative works with the 15 * separately licensed software that they have included with MySQL. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License, version 2.0, for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 25 */ 26 27 package com.mysql.cluster.crund; 28 29 import java.util.ArrayList; 30 import java.util.EnumSet; 31 import java.util.HashSet; 32 import java.util.List; 33 import java.util.Set; 34 35 /** 36 * This class benchmarks standard database operations over a series 37 * of transactions on an increasing data set. 38 * <p> 39 * The abstract database operations are variations of: Create, 40 * Read, Update, Navigate, and Delete -- hence, the benchmark's name: CRUND. 41 * <p> 42 * The actual operations are defined by subclasses to allow measuring the 43 * operation performance across different datastore implementations. 44 * 45 * @see <a href="http://www.urbandictionary.com/define.php?term=crund">Urban Dictionary: crund</a> 46 * <ol> 47 * <li> used to debase people who torture others with their illogical 48 * attempts to make people laugh; 49 * <li> reference to cracking obsolete jokes; 50 * <li> a dance form; 51 * <li> to hit hard or smash. 52 * </ol> 53 */ 54 abstract public class CrundDriver extends Driver { 55 56 enum XMode { INDY, EACH, BULK } 57 58 // benchmark settings 59 protected final EnumSet< XMode > xMode = EnumSet.noneOf(XMode.class); 60 protected boolean renewConnection; 61 protected boolean renewOperations; 62 protected boolean logSumOfOps; 63 protected boolean allowExtendedPC; 64 protected int nOpsStart; 65 protected int nOpsEnd; 66 protected int nOpsScale; 67 protected int maxVarbinaryBytes; 68 protected int maxVarcharChars; 69 protected int maxBlobBytes; 70 protected int maxTextChars; 71 protected final Set<String> exclude = new HashSet<String>(); 72 protected final Set<String> include = new HashSet<String>(); 73 74 // the name of the test currently being performed 75 protected String operationName; 76 77 /** The errors for the current test */ 78 protected StringBuilder errorBuffer; 79 80 /** Throw an exception if an error is reported */ 81 protected boolean failOnError; 82 83 // ---------------------------------------------------------------------- 84 // benchmark intializers/finalizers 85 // ---------------------------------------------------------------------- 86 init()87 protected void init() throws Exception { 88 out.println(); 89 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 90 out.println("initializing benchmark ..."); 91 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 92 super.init(); 93 94 /* 95 // XXX support multiple load instances 96 // initialize load classes 97 if (doJdbc) { 98 assert (jdbcLoad == null); 99 jdbcLoad = new JdbcLoad(this); 100 jdbcLoad.init(); 101 } 102 if (doClusterj) { 103 assert (clusterjLoad == null); 104 clusterjLoad = new ClusterjLoad(this); 105 clusterjLoad.init(); 106 } 107 if (doNdbjtie) { 108 assert (ndbjtieLoad == null); 109 ndbjtieLoad = new NdbjtieLoad(this); 110 ndbjtieLoad.init(); 111 } 112 */ 113 initLoad(); 114 } 115 close()116 protected void close() throws Exception { 117 out.println(); 118 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 119 out.println("closing benchmark ..."); 120 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 121 122 /* 123 // XXX support multiple load instances 124 // close load classes 125 if (doJdbc) { 126 assert (jdbcLoad != null); 127 jdbcLoad.close(); 128 jdbcLoad = null; 129 } 130 if (doClusterj) { 131 assert (clusterjLoad != null); 132 clusterjLoad.close(); 133 clusterjLoad = null; 134 } 135 if (doNdbjtie) { 136 assert (ndbjtieLoad != null); 137 ndbjtieLoad.close(); 138 ndbjtieLoad = null; 139 } 140 */ 141 closeLoad(); 142 143 super.close(); 144 } 145 initProperties()146 protected void initProperties() { 147 super.initProperties(); 148 149 out.print("setting crund properties ..."); 150 151 final StringBuilder msg = new StringBuilder(); 152 final String eol = System.getProperty("line.separator"); 153 154 // parse execution modes 155 final String[] xm = props.getProperty("xMode", "").split(","); 156 for (int i = 0; i < xm.length; i++) { 157 if (!"".equals(xm[i])) 158 xMode.add(XMode.valueOf(XMode.class, xm[i])); 159 } 160 161 renewConnection = parseBoolean("renewConnection", false); 162 renewOperations = parseBoolean("renewOperations", false); 163 logSumOfOps = parseBoolean("logSumOfOps", true); 164 allowExtendedPC = parseBoolean("allowExtendedPC", false); 165 failOnError = parseBoolean("failOnError", false); 166 167 nOpsStart = parseInt("nOpsStart", 256); 168 if (nOpsStart < 1) { 169 msg.append("[ignored] nOpsStart: " + nOpsStart + eol); 170 nOpsStart = 256; 171 } 172 nOpsEnd = parseInt("nOpsEnd", nOpsStart); 173 if (nOpsEnd < nOpsStart) { 174 msg.append("[ignored] nOpsEnd: "+ nOpsEnd + eol); 175 nOpsEnd = nOpsStart; 176 } 177 nOpsScale = parseInt("nOpsScale", 2); 178 if (nOpsScale < 2) { 179 msg.append("[ignored] nOpsScale: " + nOpsScale + eol); 180 nOpsScale = 2; 181 } 182 183 maxVarbinaryBytes = parseInt("maxVarbinaryBytes", 100); 184 if (maxVarbinaryBytes < 0) { 185 msg.append("[ignored] maxVarbinaryBytes: " 186 + maxVarbinaryBytes + eol); 187 maxVarbinaryBytes = 100; 188 } 189 maxVarcharChars = parseInt("maxVarcharChars", 100); 190 if (maxVarcharChars < 0) { 191 msg.append("[ignored] maxVarcharChars: " 192 + maxVarcharChars + eol); 193 maxVarcharChars = 100; 194 } 195 196 maxBlobBytes = parseInt("maxBlobBytes", 1000); 197 if (maxBlobBytes < 0) { 198 msg.append("[ignored] maxBlobBytes: " 199 + maxBlobBytes + eol); 200 maxBlobBytes = 1000; 201 } 202 maxTextChars = parseInt("maxTextChars", 1000); 203 if (maxTextChars < 0) { 204 msg.append("[ignored] maxTextChars: " 205 + maxTextChars + eol); 206 maxTextChars = 1000; 207 } 208 209 // initialize exclude set 210 final String[] excludeProperty = props.getProperty("exclude", "").split(","); 211 for (int i = 0; i < excludeProperty.length; i++) { 212 String excludeTest = excludeProperty[i]; 213 if (!excludeTest.isEmpty()) { 214 exclude.add(excludeTest); 215 } 216 } 217 218 // initialize include set 219 final String[] includeProperty = props.getProperty("include", "").split(","); 220 for (int i = 0; i < includeProperty.length; ++i) { 221 String includeTest = includeProperty[i]; 222 if (!includeTest.isEmpty()) { 223 include.add(includeTest); 224 } 225 } 226 227 if (msg.length() == 0) { 228 out.println(" [ok: " 229 + "nOps=" + nOpsStart + ".." + nOpsEnd + "]"); 230 } else { 231 out.println(); 232 out.print(msg.toString()); 233 } 234 } 235 printProperties()236 protected void printProperties() { 237 super.printProperties(); 238 239 out.println(); 240 out.println("crund settings ..."); 241 out.println("xMode: " + xMode); 242 out.println("renewConnection: " + renewConnection); 243 out.println("renewOperations: " + renewOperations); 244 out.println("logSumOfOps: " + logSumOfOps); 245 out.println("allowExtendedPC: " + allowExtendedPC); 246 out.println("nOpsStart: " + nOpsStart); 247 out.println("nOpsEnd: " + nOpsEnd); 248 out.println("nOpsScale: " + nOpsScale); 249 out.println("maxVarbinaryBytes: " + maxVarbinaryBytes); 250 out.println("maxVarcharChars: " + maxVarcharChars); 251 out.println("maxBlobBytes: " + maxBlobBytes); 252 out.println("maxTextChars: " + maxTextChars); 253 out.println("exclude: " + exclude); 254 out.println("include: " + include); 255 } 256 257 // ---------------------------------------------------------------------- 258 // benchmark operations 259 // ---------------------------------------------------------------------- 260 261 // XXX move to generic load class 262 // a database operation to be benchmarked 263 protected abstract class Op { 264 final protected String name; 265 Op(String name)266 public Op(String name) { this.name = name; } 267 getName()268 public String getName() { return name; } 269 run(int nOps)270 public abstract void run(int nOps) throws Exception; 271 }; 272 273 // XXX move to generic load class 274 // the list of database operations to be benchmarked 275 protected final List<Op> ops = new ArrayList<Op>(); 276 277 // manages list of database operations initOperations()278 abstract protected void initOperations() throws Exception; closeOperations()279 abstract protected void closeOperations() throws Exception; 280 runTests()281 protected void runTests() throws Exception { 282 initConnections(); 283 runLoads(); 284 closeConnections(); 285 } 286 runLoads()287 protected void runLoads() throws Exception { 288 /* 289 // XXX support multiple load instances 290 if (doJdbc) 291 runLoads(jdbcLoad); 292 if (doClusterj) 293 runLoads(clusterjLoad); 294 if (doNdbjtie) 295 runLoads(ndbjtieLoad); 296 */ 297 runLoad(); 298 } 299 runLoad()300 protected void runLoad() throws Exception { 301 assert (nOpsStart <= nOpsEnd && nOpsScale > 1); 302 for (int i = nOpsStart; i <= nOpsEnd; i *= nOpsScale) { 303 try { 304 out.println(); 305 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 306 // XXX support multiple load instances 307 //out.print("running load nOps = " + i + " on " 308 // + load.getDescriptor()); 309 out.println("running load [" + i + " nOps] on " + descr); 310 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 311 runSeries(i); 312 } catch (Exception ex) { 313 // already in rollback for database/orm exceptions 314 throw ex; 315 } 316 } 317 } 318 runSeries(int nOps)319 protected void runSeries(int nOps) throws Exception { 320 if (nRuns == 0) 321 return; // nothing to do 322 323 for (int i = 1; i <= nRuns; i++) { 324 out.println(); 325 out.println("------------------------------------------------------------"); 326 out.println("run " + i + " of " + nRuns + " [" + nOps + " nOps]"); 327 out.println("------------------------------------------------------------"); 328 // XXX runLoad(load); 329 runLoad(nOps); 330 } 331 332 // XXX support multiple load instances 333 //writeLogBuffers(load.getDescriptor()); 334 writeLogBuffers(descr); 335 clearLogBuffers(); 336 } 337 runLoad(int nOps)338 protected void runLoad(int nOps) throws Exception { 339 // log buffers 340 if (logRealTime) { 341 rtimes.append(nOps); 342 ta = 0; 343 } 344 if (logMemUsage) { 345 musage.append(nOps); 346 ma = 0; 347 } 348 349 // pre-run cleanup 350 if (renewConnection) { 351 // XXX move to generic load class? 352 closeOperations(); 353 closeConnection(); 354 initConnection(); 355 initOperations(); 356 } else if (renewOperations) { 357 closeOperations(); 358 initOperations(); 359 } 360 clearData(); 361 362 runSequence(nOps); 363 364 if (logSumOfOps) { 365 out.println(); 366 out.println("total"); 367 if (logRealTime) { 368 out.println("tx real time " + ta 369 + "\tms"); 370 } 371 if (logMemUsage) { 372 out.println("net mem usage " 373 + (ma >= 0 ? "+" : "") + ma 374 + "\tKiB"); 375 } 376 } 377 378 // log buffers 379 if (logHeader) { 380 if (logSumOfOps) { 381 header.append("\ttotal"); 382 } 383 logHeader = false; 384 } 385 if (logRealTime) { 386 if (logSumOfOps) { 387 rtimes.append("\t" + ta); 388 } 389 rtimes.append(endl); 390 } 391 if (logMemUsage) { 392 if (logSumOfOps) { 393 musage.append("\t" + ma); 394 } 395 musage.append(endl); 396 } 397 } 398 399 // XXX move to generic load class runSequence(int nOps)400 protected void runSequence(int nOps) throws Exception { 401 for (Op op : ops) { 402 // pre-tx cleanup 403 if (!allowExtendedPC) { 404 // effectively prevent caching beyond Tx scope by clearing 405 // any data/result caches before the next transaction 406 clearPersistenceContext(); 407 } 408 runOperation(op, nOps); 409 reportErrors(); 410 } 411 } 412 413 // XXX move to generic load class runOperation(Op op, int nOps)414 protected void runOperation(Op op, int nOps) throws Exception { 415 operationName = op.getName(); 416 // if there is an include list and this test is included, or 417 // there is not an include list and this test is not excluded 418 if ((include.size() != 0 && include.contains(operationName)) 419 || (include.size() == 0 && !exclude.contains(operationName))) { 420 begin(operationName); 421 op.run(nOps); 422 finish(operationName); 423 } 424 } 425 426 /** Add an error to the existing errors */ appendError(String where)427 protected void appendError(String where) { 428 if (errorBuffer == null) { 429 errorBuffer = new StringBuilder(); 430 } 431 errorBuffer.append("Error in operation "); 432 errorBuffer.append(operationName); 433 errorBuffer.append(": "); 434 errorBuffer.append(where); 435 errorBuffer.append('\n'); 436 } 437 438 /** Report errors and reset the error buffer */ reportErrors()439 protected void reportErrors() { 440 if (errorBuffer != null) { 441 if (failOnError) { 442 throw new RuntimeException(errorBuffer.toString()); 443 } 444 System.out.println(errorBuffer.toString()); 445 errorBuffer = null; 446 } 447 } 448 // XXX move to generic load class 449 // reports an error if a condition is not met verify(boolean cond)450 static protected final void verify(boolean cond) { 451 //assert (cond); 452 if (!cond) 453 throw new RuntimeException("data verification failed."); 454 } 455 456 // XXX move to generic load class verify(int exp, int act)457 static protected final void verify(int exp, int act) { 458 if (exp != act) 459 throw new RuntimeException("data verification failed:" 460 + " expected = " + exp 461 + ", actual = " + act); 462 } 463 464 // XXX move to generic load class verify(String where, int exp, int act)465 protected final void verify(String where, int exp, int act) { 466 if (exp != act) 467 appendError(" data verification failed:" 468 + " expected = " + exp 469 + ", actual = " + act); 470 } 471 472 // XXX move to generic load class verify(String exp, String act)473 static protected final void verify(String exp, String act) { 474 if ((exp == null && act != null) 475 || (exp != null && !exp.equals(act))) 476 throw new RuntimeException("data verification failed:" 477 + " expected = '" + exp + "'" 478 + ", actual = '" + act + "'"); 479 } 480 481 // ---------------------------------------------------------------------- 482 // helpers 483 // ---------------------------------------------------------------------- 484 485 // XXX move to generic load class myString(int n)486 static final protected String myString(int n) { 487 final StringBuilder s = new StringBuilder(); 488 switch (n) { 489 case 1: 490 s.append('i'); 491 break; 492 case 2: 493 for (int i = 0; i < 10; i++) s.append('x'); 494 break; 495 case 3: 496 for (int i = 0; i < 100; i++) s.append('c'); 497 break; 498 case 4: 499 for (int i = 0; i < 1000; i++) s.append('m'); 500 break; 501 case 5: 502 for (int i = 0; i < 10000; i++) s.append('X'); 503 break; 504 case 6: 505 for (int i = 0; i < 100000; i++) s.append('C'); 506 break; 507 case 7: 508 for (int i = 0; i < 1000000; i++) s.append('M'); 509 break; 510 default: 511 throw new IllegalArgumentException("unsupported 10**n = " + n); 512 } 513 return s.toString(); 514 } 515 516 // XXX move to generic load class myBytes(String s)517 static final protected byte[] myBytes(String s) { 518 final char[] c = s.toCharArray(); 519 final int n = c.length; 520 final byte[] b = new byte[n]; 521 for (int i = 0; i < n; i++) b[i] = (byte)c[i]; 522 return b; 523 } 524 525 // XXX move to generic load class 526 // some string and byte constants 527 static final protected String string1 = myString(1); 528 static final protected String string2 = myString(2); 529 static final protected String string3 = myString(3); 530 static final protected String string4 = myString(4); 531 static final protected String string5 = myString(5); 532 static final protected String string6 = myString(6); 533 static final protected String string7 = myString(7); 534 static final protected byte[] bytes1 = myBytes(string1); 535 static final protected byte[] bytes2 = myBytes(string2); 536 static final protected byte[] bytes3 = myBytes(string3); 537 static final protected byte[] bytes4 = myBytes(string4); 538 static final protected byte[] bytes5 = myBytes(string5); 539 static final protected byte[] bytes6 = myBytes(string6); 540 static final protected byte[] bytes7 = myBytes(string7); 541 static final protected String[] strings 542 = { string1, string2, string3, string4, string5, string6, string7 }; 543 static final protected byte[][] bytes 544 = { bytes1, bytes2, bytes3, bytes4, bytes5, bytes6, bytes7 }; 545 546 // ---------------------------------------------------------------------- 547 // datastore operations 548 // ---------------------------------------------------------------------- 549 initConnections()550 protected void initConnections() throws Exception { 551 out.println(); 552 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 553 out.println("initializing connections ..."); 554 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 555 556 /* 557 // XXX support multiple load instances 558 if (doJdbc) { 559 assert (jdbcLoad != null); 560 jdbcLoad.initConnection(); 561 } 562 if (doClusterj) { 563 assert (clusterjLoad != null); 564 clusterjLoad.initConnection(); 565 } 566 if (doNdbjtie) { 567 assert (ndbjtieLoad != null); 568 ndbjtieLoad.initConnection(); 569 } 570 */ 571 initConnection(); 572 573 // XXX move to generic load class 574 initOperations(); 575 } 576 closeConnections()577 protected void closeConnections() throws Exception { 578 out.println(); 579 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 580 out.println("closing connections ..."); 581 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); 582 583 // XXX move to generic load class 584 closeOperations(); 585 586 closeConnection(); 587 /* 588 // XXX support multiple load instances 589 if (doJdbc) { 590 assert (jdbcLoad != null); 591 jdbcLoad.closeConnection(); 592 } 593 if (doClusterj) { 594 assert (clusterjLoad != null); 595 clusterjLoad.closeConnection(); 596 } 597 if (doNdbjtie) { 598 assert (ndbjtieLoad != null); 599 ndbjtieLoad.closeConnection(); 600 } 601 */ 602 } 603 initLoad()604 abstract protected void initLoad() throws Exception; closeLoad()605 abstract protected void closeLoad() throws Exception; initConnection()606 abstract protected void initConnection() throws Exception; closeConnection()607 abstract protected void closeConnection() throws Exception; clearPersistenceContext()608 abstract protected void clearPersistenceContext() throws Exception; clearData()609 abstract protected void clearData() throws Exception; 610 } 611