From 654d7e7cc76bf98ad29d93cfa53c37cafa79a1f0 Mon Sep 17 00:00:00 2001 From: Arthur Martella Date: Wed, 26 Jun 2019 16:03:25 -0400 Subject: New multi-threaded test client script Issue-ID: MUSIC-420 Signed-off-by: Martella, Arthur Change-Id: I275dce864ed1fc0922f0a2f7f0a4e6a3f5b976f7 --- .../music/mdbc/examples/MdbcTestMultiClient.java | 609 +++++++++++++++++++++ 1 file changed, 609 insertions(+) create mode 100755 mdbc-server/src/main/java/org/onap/music/mdbc/examples/MdbcTestMultiClient.java diff --git a/mdbc-server/src/main/java/org/onap/music/mdbc/examples/MdbcTestMultiClient.java b/mdbc-server/src/main/java/org/onap/music/mdbc/examples/MdbcTestMultiClient.java new file mode 100755 index 0000000..b2fb403 --- /dev/null +++ b/mdbc-server/src/main/java/org/onap/music/mdbc/examples/MdbcTestMultiClient.java @@ -0,0 +1,609 @@ +/* + * ============LICENSE_START==================================================== + * org.onap.music.mdbc + * ============================================================================= + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END====================================================== + */ +package org.onap.music.mdbc.examples; + +import java.sql.*; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; + +public class MdbcTestMultiClient implements Runnable { + private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); + + private String connectionString = null; + private int threadId = -1; + + private List connectionStrings = new ArrayList(); + private static final String defaultConnection = "jdbc:avatica:remote:url=http://localhost:30000/test;serialization=protobuf"; + private String lastName = "Lastname"; + private int baseId = 700; + private int baseIdRange = 50; + private int maxCalls = 50; + private int maxTimeMs = 60000; + private int minDelayBetweenTestsMs = 1000; + private int additionalDelayBetweenTestsMs = 1000; + private boolean doDelete = true; + private boolean doUpdate = true; + private int connectionCloseChancePct = 50; + private int skipInitialSelectPct = 25; + private int selectInsteadOfUpdatePct = 25; + + private boolean explainConnection = true; + + public static class Employee { + public final int empid; + public String lastname; + public String firstname; + public String address; + public String city; + + public Employee(int empid, String lastname, String firstname, String address, String city) { + super(); + this.empid = empid; + this.lastname = lastname; + this.firstname = firstname; + this.address = address; + this.city = city; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public int getEmpid() { + return empid; + } + + @Override + public String toString() { + return "Employee: " + empid + ", " + lastname + ", " + firstname + ", " + address + ", " + city; + } + + + } + + public MdbcTestMultiClient(String[] args) { + char currState = (char)0, noState = (char)0; + for (String arg : args) { + if (currState==noState) { + switch (arg) { + case "-?": + case "--help": + showHelp(); + break; + case "-c": + case "--connection": + currState = 'c'; + break; + case "-n": + case "--name": + currState = 'n'; + break; + case "-b": + case "--baseId": + currState = 'b'; + break; + case "-r": + case "--baseRange": + currState = 'r'; + break; + case "-m": + case "--maxCalls": + currState = 'm'; + break; + case "-t": + case "--maxTime": + currState = 't'; + break; + case "-d": + case "--minDelay": + currState = 'd'; + break; + case "-a": + case "--addDelay": + currState = 'a'; + break; + case "-u": + case "--update": + currState = 'u'; + break; + case "-x": + case "--delete": + currState = 'x'; + break; + case "-l": + case "--closeChance": + currState = 'l'; + break; + case "-s": + case "--skipInitialSelect": + currState = 's'; + break; + case "-i": + case "--selectNotUpdate": + currState = 'i'; + break; + default: + System.out.println("Didn't understand switch " + arg); + } + } else { + try { + switch (currState) { + case 'c': + connectionStrings.add(arg); + break; + case 'n': + lastName = arg; + break; + case 'b': + baseId = Integer.parseInt(arg); + break; + case 'r': + baseIdRange = Integer.parseInt(arg); + break; + case 'm': + maxCalls = Integer.parseInt(arg); + break; + case 't': + maxTimeMs = Integer.parseInt(arg); + break; + case 'd': + minDelayBetweenTestsMs = Integer.parseInt(arg); + break; + case 'a': + additionalDelayBetweenTestsMs = Integer.parseInt(arg); + break; + case 'u': + doUpdate = arg.toUpperCase().startsWith("Y"); + break; + case 'x': + doDelete = arg.toUpperCase().startsWith("Y"); + break; + case 'l': + connectionCloseChancePct = Integer.parseInt(arg); + break; + case 's': + skipInitialSelectPct = Integer.parseInt(arg); + break; + case 'i': + selectInsteadOfUpdatePct = Integer.parseInt(arg); + break; + default: + System.out.println("Bad state " + currState + "????"); + } + } catch (NumberFormatException e) { + System.out.println("Bad integer " + arg + " for switch " + currState); + } + currState = noState; + } + } + if (connectionStrings.isEmpty()) connectionStrings.add(defaultConnection); + } + + private void showHelp() { + System.out.println( + "-?; --help: Show help\n" + + "-c; --connection: MDBC connection string, may appear multiple times\n" + + "-n; --name: Last name in persons table, default \"Lastname\"\n" + + "-b; --baseId: Base ID, default 700\n" + + "-r; --baseRange: Range of ID, default 50\n" + + "-m; --maxCalls: Max number of commits (each may be 1+ updates), default 50\n" + + "-t; --maxTime: Max time in ms test will run, default 60000\n" + + "-d; --minDelay: Min delay between tests in ms, default 1000\n" + + "-a; --addDelay: Max randomized additional delay between tests in ms, default 1000\n" + + "-u; --update: Generate update statements; default Y\n" + + "-x; --delete: Generate delete statements; default Y\n" + + "-l; --closeChance: Percent chance of closing connection after each commit, default 50\n" + + "-s; --skipInitialSelect: Percent chance of skipping each initial select in a transaction, default 25\n" + + "-i; --selectNotUpdate: Percent chance of each action in a transaction being a select instead of an update, default 25" + ); + + } + + public MdbcTestMultiClient(MdbcTestMultiClient that, int i) { + this.connectionString = that.connectionStrings.get(i); + this.threadId = i; + + this.lastName = that.lastName; + this.lastName = that.lastName; + this.baseId = that.baseId; + this.baseIdRange = that.baseIdRange; + this.maxCalls = that.maxCalls; + this.maxTimeMs = that.maxTimeMs; + this.minDelayBetweenTestsMs = that.minDelayBetweenTestsMs; + this.additionalDelayBetweenTestsMs = that.additionalDelayBetweenTestsMs; + this.doDelete = that.doDelete; + this.doUpdate = that.doUpdate; + this.connectionCloseChancePct = that.connectionCloseChancePct; + this.skipInitialSelectPct = that.skipInitialSelectPct; + this.selectInsteadOfUpdatePct = that.selectInsteadOfUpdatePct; + } + + private static String[] tableNames = {"persons", "persons2"}; + + private void doTest(Connection connection, Random r) throws SQLException { + doLog("skipInitialSelectPct = " + skipInitialSelectPct + ", selectInsteadOfUpdatePct = " + selectInsteadOfUpdatePct); + HashMap> employeeMaps = new HashMap> (); +// HashMap employeeMap = new HashMap(); + + for (String tableName : tableNames) { + if (r.nextInt(100) qsClass = querySt.getClass(); + while (qsClass!=null) { + doLog(">>> " + qsClass.getName()); + qsClass = qsClass.getSuperclass(); + } + doLog("connection is a "); + qsClass = connection.getClass(); + while (qsClass!=null) { + doLog(">>> " + qsClass.getName()); + qsClass = qsClass.getSuperclass(); + } + explainConnection = false; + } + + HashMap employeeMap = new HashMap(); + employeeMaps.put(tableName, employeeMap); + + ResultSet rs = executeQueryTimed("select * from " + tableName, querySt, false); + while (rs.next()) { + // doLog("PersonId = " + rs.getInt("personId") + ", lastname = " + rs.getString("lastname") + ", firstname = " + rs.getString("firstname")); + Employee emp = new Employee(rs.getInt("personId"), rs.getString("lastname"), rs.getString("firstname"), rs.getString("address"), rs.getString("city")); + employeeMap.put(rs.getInt("personId"), emp); + doLog("Found: " + emp); + } + querySt.close(); + } + } + + Statement insertStmt = connection.createStatement(); + boolean firstTry = true; +// String tableName = tableNames[r.nextInt(tableNames.length)]; +// HashMap employeeMap = employeeMaps.get(tableName); +// executeUpdateTimed(generateStatement(employeeMap, r, tableName), insertStmt, true); + + while (firstTry || r.nextBoolean()) { + firstTry = false; + String tableName = tableNames[r.nextInt(tableNames.length)]; + HashMap employeeMap = employeeMaps.get(tableName); + if (r.nextInt(100) employeeMap, Random r, String tableName) { + String toRet = null; + + boolean pickInsert = ((employeeMap==null) || (r.nextInt(8) >= employeeMap.size())); + if (!pickInsert) { + if (doDelete && doUpdate) { + if (r.nextBoolean()) toRet = generateDelete(employeeMap, r, tableName); else toRet = generateUpdate(employeeMap, r, tableName); + } else if (doDelete) { + toRet = generateDelete(employeeMap, r, tableName); + } else if (doUpdate) { + toRet = generateUpdate(employeeMap, r, tableName); + } + } + if (toRet==null) { + toRet = generateInsert(employeeMap, r, tableName); + } + + doLog("Generated statement: " + toRet); + + return toRet; + } + + private String generateInsert(HashMap employeeMap, Random r, String tableName) { + String toRet = null; + + Integer id = null; + int range = baseIdRange; + while (id==null) { + id = baseId + r.nextInt(range); + if (employeeMap!=null && employeeMap.containsKey(id)) id = null; + if (employeeMap==null) id+=baseIdRange; + range+=(baseIdRange/5); + } + Employee newEmp = new Employee(id, lastName, Character.toUpperCase(randomLetter(r)) + generateLetters(r, 4+r.nextInt(4)), generateLetters(r, 4).toUpperCase(), generateLetters(r, 4).toUpperCase()); + toRet = "insert into " + tableName + " values (" + id + ", '" + newEmp.getLastname() + "', '" + newEmp.getFirstname() + "', '" + newEmp.getAddress() + "', '" + newEmp.getCity() + "')"; + if (employeeMap!=null) employeeMap.put(id, newEmp); + + return toRet; + } + + private String generateUpdate(HashMap employeeMap, Random r, String tableName) { + String toRet = null; + + Employee toUpd = chooseTarget(employeeMap, r); + if (toUpd!=null) { + String newFirst = null; + if (toUpd.getFirstname().length()<=3 || r.nextBoolean()) { + newFirst = toUpd.getFirstname() + randomLetter(r); + } else { + newFirst = toUpd.getFirstname().substring(0, toUpd.getFirstname().length()-1); + } +// toRet = "update " + tableName + " set firstname = '" + newFirst + "' where personid = " + toUpd.getEmpid(); + toRet = "update " + tableName + " set firstname = '" + newFirst + "' where personid = " + toUpd.getEmpid() + " and lastname = '" + toUpd.getLastname() + "'"; + toUpd.setFirstname(newFirst); + } + + return toRet; + } + + private String generateLetters(Random r, int count) { + StringBuffer toRet = new StringBuffer(); + for (int i=0; i employeeMap, Random r, String tableName) { + String toRet = null; + + Employee toDel = chooseTarget(employeeMap, r); + if (toDel!=null) { + toRet = "delete from " + tableName + " where personid = " + toDel.getEmpid() + " and lastname = '" + toDel.getLastname() + "'"; + employeeMap.remove(toDel.getEmpid()); + } + + return toRet; + } + + + + private Employee chooseTarget(HashMap employeeMap, Random r) { + Employee toPick = null; + int count = 0; + for (int id : employeeMap.keySet()) { + Employee emp = employeeMap.get(id); + if (!emp.getLastname().equals(lastName)) continue; + count++; + if (r.nextInt(count)==0) toPick = emp; + } + return toPick; + } + + public void run() { + try { + Class.forName("org.apache.calcite.avatica.remote.Driver"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + System.exit(1); + } + + Connection connection = null; + + int calls = 0; + long startTime = new java.util.Date().getTime(); + Random r = new Random(); + boolean done = false; + + while (!done) { + if (connection==null) { + try { + doLog("Opening new connection"); + connection = DriverManager.getConnection(connectionString); + connection.setAutoCommit(false); + } catch (SQLException e) { + e.printStackTrace(); + return; + } + } else { + doLog("Keeping open connection"); + } + + if (calls==0) { + long initialDelay = 1 + (1000*threadId); + synchronized(Thread.currentThread()) { + doLog("Delaying for " + initialDelay); + try { + Thread.currentThread().wait(initialDelay); + } catch (InterruptedException e) { + e.printStackTrace(); + done = true; + } + doLog("Done"); + } + } + + try { + doLog("Running test"); + doTest(connection, r); + doLog("Test complete"); + } catch (SQLException e1) { + e1.printStackTrace(); + done = true; + if (connection!=null) { + try { + doLog("Closing connection in catch block"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + done = true; + } finally { + connection = null; + } + } + } + + if (!done && connection!=null && r.nextInt(100)maxCalls || msElapsed > maxTimeMs) done = true; + + if (!done) { + long delay = r.nextInt(minDelayBetweenTestsMs); + while (r.nextBoolean()) delay += r.nextInt(additionalDelayBetweenTestsMs); + if (delay>0) { + doLog("Delaying for " + delay + " ms"); + synchronized(Thread.currentThread()) { + try { + Thread.currentThread().wait(delay); + } catch (InterruptedException e) { + e.printStackTrace(); + done = true; + } + } + doLog("Delaying done"); + } + } + + doLog(""); + } + + if (connection!=null) { + try { + doLog("Closing connection at end"); + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + doLog("All done."); + } + + private void doLog(String string) { + System.out.println(">> Thread " + threadId + " " + sdf.format(new java.util.Date()) + " >> " + string); + } + + public static void main(String[] args) { + MdbcTestMultiClient mtc = new MdbcTestMultiClient(args); + mtc.runTests(); + } + + private void runTests() { + for (int i=0; i