/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This file contains original code and/or modifications of original code. * Any modifications made by VoltDB Inc. are licensed under the following * terms and conditions: * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* Copyright (C) 2008 * Evan Jones * Massachusetts Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package com; import java.io.IOException; import org.voltdb.types.TimestampType; public class TPCCSimulation { // type used by at least VoltDBClient and JDBCClient public static enum Transaction { STOCK_LEVEL("Stock Level"), DELIVERY("Delivery"), ORDER_STATUS("Order Status"), PAYMENT("Payment"), NEW_ORDER("New Order"), RESET_WAREHOUSE("Reset Warehouse"); private Transaction(String displayName) { this.displayName = displayName; } public final String displayName; } public interface ProcCaller { public void callResetWarehouse(long w_id, long districtsPerWarehouse, long customersPerDistrict, long newOrdersPerDistrict) throws IOException; public void callStockLevel(short w_id, byte d_id, int threshold) throws IOException; public void callOrderStatus(String proc, Object... paramlist) throws IOException; public void callDelivery(short w_id, int carrier, TimestampType date) throws IOException; public void callPaymentByName(short w_id, byte d_id, double h_amount, short c_w_id, byte c_d_id, String c_last, TimestampType now) throws IOException; public void callPaymentById(short w_id, byte d_id, double h_amount, short c_w_id, byte c_d_id, int c_id, TimestampType now) throws IOException; public void callNewOrder(boolean rollback, Object... paramlist) throws IOException; } private final TPCCSimulation.ProcCaller client; private final RandomGenerator generator; private final Clock clock; public ScaleParameters parameters; private final boolean useWarehouseAffinity; private final long affineWarehouse; private final double m_skewFactor; static long lastAssignedWarehouseId = 1; public TPCCSimulation(TPCCSimulation.ProcCaller client, RandomGenerator generator, Clock clock, ScaleParameters parameters, boolean useWarehouseAffinity, double skewFactor) { assert parameters != null; this.client = client; this.generator = generator; this.clock = clock; this.parameters = parameters; this.useWarehouseAffinity = useWarehouseAffinity; this.affineWarehouse = lastAssignedWarehouseId; m_skewFactor = skewFactor; lastAssignedWarehouseId += 1; if (lastAssignedWarehouseId > parameters.warehouses) lastAssignedWarehouseId = 1; } private short generateWarehouseId() { if (useWarehouseAffinity) return (short)this.affineWarehouse; else return (short)generator.skewedNumber(1, parameters.warehouses, m_skewFactor); } private byte generateDistrict() { return (byte)generator.number(1, parameters.districtsPerWarehouse); } private int generateCID() { return generator.NURand(1023, 1, parameters.customersPerDistrict); } private int generateItemID() { return generator.NURand(8191, 1, parameters.items); } /** Executes a reset warehouse transaction. */ public void doResetWarehouse() throws IOException { long w_id = generateWarehouseId(); client.callResetWarehouse(w_id, parameters.districtsPerWarehouse, parameters.customersPerDistrict, parameters.newOrdersPerDistrict); } /** Executes a stock level transaction. */ public void doStockLevel() throws IOException { int threshold = generator.number(Constants.MIN_STOCK_LEVEL_THRESHOLD, Constants.MAX_STOCK_LEVEL_THRESHOLD); client.callStockLevel(generateWarehouseId(), generateDistrict(), threshold); } /** Executes an order status transaction. */ public void doOrderStatus() throws IOException { int y = generator.number(1, 100); if (y <= 60) { // 60%: order status by last name String cLast = generator .makeRandomLastName(parameters.customersPerDistrict); client.callOrderStatus(Constants.ORDER_STATUS_BY_NAME, generateWarehouseId(), generateDistrict(), cLast); } else { // 40%: order status by id assert y > 60; client.callOrderStatus(Constants.ORDER_STATUS_BY_ID, generateWarehouseId(), generateDistrict(), generateCID()); } } /** Executes a delivery transaction. */ public void doDelivery() throws IOException { int carrier = generator.number(Constants.MIN_CARRIER_ID, Constants.MAX_CARRIER_ID); client.callDelivery(generateWarehouseId(), carrier, clock.getDateTime()); } /** Executes a payment transaction. */ public void doPayment() throws IOException { int x = generator.number(1, 100); int y = generator.number(1, 100); short w_id = generateWarehouseId(); byte d_id = generateDistrict(); short c_w_id; byte c_d_id; if (parameters.warehouses == 1 || x <= 85) { // 85%: paying through own warehouse (or there is only 1 warehouse) c_w_id = w_id; c_d_id = d_id; } else { // 15%: paying through another warehouse: // select in range [1, num_warehouses] excluding w_id c_w_id = (short)generator.numberExcluding(1, parameters.warehouses, w_id); assert c_w_id != w_id; c_d_id = generateDistrict(); } double h_amount = generator.fixedPoint(2, Constants.MIN_PAYMENT, Constants.MAX_PAYMENT); TimestampType now = clock.getDateTime(); if (y <= 60) { // 60%: payment by last name String c_last = generator .makeRandomLastName(parameters.customersPerDistrict); client.callPaymentByName(w_id, d_id, h_amount, c_w_id, c_d_id, c_last, now); } else { // 40%: payment by id assert y > 60; client.callPaymentById(w_id, d_id, h_amount, c_w_id, c_d_id, generateCID(), now); } } /** Executes a new order transaction. */ public void doNewOrder() throws IOException { short warehouse_id = generateWarehouseId(); int ol_cnt = generator.number(Constants.MIN_OL_CNT, Constants.MAX_OL_CNT); // 1% of transactions roll back boolean rollback = generator.number(1, 100) == 1; int[] item_id = new int[ol_cnt]; short[] supply_w_id = new short[ol_cnt]; int[] quantity = new int[ol_cnt]; for (int i = 0; i < ol_cnt; ++i) { if (rollback && i + 1 == ol_cnt) { // LOG.fine("[NOT_ERROR] Causing a rollback on purpose defined in TPCC spec. " // + "You can ignore following 'ItemNotFound' exception."); item_id[i] = parameters.items + 1; } else { item_id[i] = generateItemID(); } // 1% of items are from a remote warehouse // boolean remote = generator.number(1, 100) == 1; boolean remote = false; // TODO : currently cross partition query // fails. will revert soon. if (parameters.warehouses > 1 && remote) { supply_w_id[i] = (short)generator.numberExcluding(1, parameters.warehouses, warehouse_id); } else { supply_w_id[i] = warehouse_id; } quantity[i] = generator.number(1, Constants.MAX_OL_QUANTITY); } TimestampType now = clock.getDateTime(); client.callNewOrder(rollback, warehouse_id, generateDistrict(), generateCID(), now, item_id, supply_w_id, quantity); } /** * Selects and executes a transaction at random. The number of new order * transactions executed per minute is the official "tpmC" metric. See TPC-C * 5.4.2 (page 71). * * @return the transaction that was executed.. */ public int doOne() throws IOException { // This is not strictly accurate: The requirement is for certain // *minimum* percentages to be maintained. This is close to the right // thing, but not precisely correct. See TPC-C 5.2.4 (page 68). int x = generator.number(1, 100); if (x <= 4) { // 4% doStockLevel(); return Transaction.STOCK_LEVEL.ordinal(); } else if (x <= 4 + 4) { // 4% doDelivery(); return Transaction.DELIVERY.ordinal(); } else if (x <= 4 + 4 + 4) { // 4% doOrderStatus(); return Transaction.ORDER_STATUS.ordinal(); } else if (x <= 43 + 4 + 4 + 4) { // 43% doPayment(); return Transaction.PAYMENT.ordinal(); } else { // 45% assert x > 100 - 45; doNewOrder(); return Transaction.NEW_ORDER.ordinal(); } } }