/*************************************************************************** * Copyright (C) 2012 by H-Store Project * * Brown University * * Massachusetts Institute of Technology * * Yale University * * * * Alex Kalinin (akalinin@cs.brown.edu) * * http://www.cs.brown.edu/~akalinin/ * * * * 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 edu.brown.benchmark.tpce.generators; import org.voltdb.catalog.Table; import edu.brown.benchmark.tpce.TPCEConstants; import edu.brown.benchmark.tpce.generators.CustomerSelection.TierId; import edu.brown.benchmark.tpce.generators.TPCEGenerator.InputFile; import edu.brown.benchmark.tpce.util.EGenRandom; public class CustomerAccountsGenerator extends TableGenerator { public enum TaxStatus { eNonTaxable, eTaxableAndWithhold, eTaxableAndDontWithhold; } private static final int percentAccountTaxStatusNonTaxable = 20; private static final int percentAccountTaxStatusTaxableAndWithhold = 50; private static final int percentAccountTaxStatusTaxableAndDontWithhold = 30; private static final int percentAccountsWithPositiveInitialBalance = 80; private static final double accountInitialPositiveBalanceMax = 9999999.99; private static final double accountInitialNegativeBalanceMin = -9999999.99; private int accsToGenerate; // every customer can have a different number of accs private int accsGenerated; // the number of accs already generated for the current customer private final CustomerGenerator customerGenerator; // is used for geenrating just customer ids private final EGenRandom rnd; /* * Number of RNG calls to skip for one row in order * to not use any of the random values from the previous row. */ private final static int rngSkipOneRowCustomerAccount = 10; // real max count in v3.5: 7 private final static int[] minAccountsPerCustRange = {1, 2, 5}; // tier based private final static int[] maxAccountsPerCustRange = {4, 8, 10}; // tier based public final static int MAX_ACCOUNTS_PER_CUST = 10; // should not be more than the last number in the max array private static final int BROKERS_COUNT = TPCEConstants.DEFAULT_LOAD_UNIT / TPCEConstants.BROKERS_DIV; private long startingAccId; private final InputFileHandler taxableNames; private final InputFileHandler nonTaxableNames; private final PersonHandler person; public CustomerAccountsGenerator(Table catalog_tbl, TPCEGenerator generator) { super(catalog_tbl, generator); // do not need the Table for this. Nasty, though. Have to use type casting since we need specific functions customerGenerator = (CustomerGenerator)generator.getTableGen(TPCEConstants.TABLENAME_CUSTOMER, null); rnd = new EGenRandom(EGenRandom.RNG_SEED_TABLE_DEFAULT); taxableNames = generator.getInputFile(InputFile.TAXACC); nonTaxableNames = generator.getInputFile(InputFile.NONTAXACC); person = new PersonHandler(generator.getInputFile(InputFile.LNAME), generator.getInputFile(InputFile.FEMFNAME), generator.getInputFile(InputFile.MALEFNAME)); } private void initNextLoadUnit() { rnd.setSeedNth(EGenRandom.RNG_SEED_TABLE_DEFAULT, customerGenerator.getCurrentCId() * MAX_ACCOUNTS_PER_CUST * rngSkipOneRowCustomerAccount); } /** * Generates a random account ID for the specified customer. * Used for external generators, like trade * * @param rnd External random generator * @param custId Customer ID * @param tier Customer tier * @return Array of two: account ID, the number of accounts for the customer */ public long[] genRandomAccId(EGenRandom rnd, long custId, TierId tier) { long custAcc, accCount, startAcc; accCount = getNumberofAccounts(custId, tier.ordinal() + 1); startAcc = getStartingAccId(custId); custAcc = rnd.int64Range(startAcc, startAcc + accCount - 1); // using the external generator here long[] res = new long[2]; res[0] = custAcc; res[1] = accCount; return res; } public long[] genRandomAccId(EGenRandom rnd, long custId, TierId tier, long acct_id, int accountCount) { long custAcc, startAcc; int accCount; long[] res = new long[2]; accCount = getNumberofAccounts(custId, tier.ordinal() + 1); startAcc = getStartingAccId(custId); custAcc = rnd.int64Range(startAcc, startAcc + accCount - 1); // using the external generator here if (acct_id != -1){ res[0] = acct_id = custAcc; } if(accountCount != -1){ res[1] = accountCount = accCount; } return res; } private int getNumberofAccounts(long cid, int tier) { int minAccountCount = minAccountsPerCustRange[tier - 1]; int mod = maxAccountsPerCustRange[tier - 1] - minAccountCount + 1; int inverseCid = CustomerSelection.getInverseCid(cid); // Note: the calculations below assume load unit contains 1000 customers. if (inverseCid < 200) { // Tier 1 return ((inverseCid % mod) + minAccountCount); } else if (inverseCid < 800) { // Tier 2 return (((inverseCid - 200 + 1) % mod) + minAccountCount); } else { // Tier 3 return (((inverseCid - 800 + 2) % mod) + minAccountCount); } } public static long getStartingAccId(long cid) { //start account ids on the next boundary for the new customer return ((cid - 1) * MAX_ACCOUNTS_PER_CUST + 1); } public static long getEndtingAccId(long cid) { //start account ids on the next boundary for the new customer return ((cid + 1) * MAX_ACCOUNTS_PER_CUST); } public long generateAccountId() { if (customerGenerator.getCurrentCId() % TPCEConstants.DEFAULT_LOAD_UNIT == 0) { initNextLoadUnit(); } if (accsToGenerate == accsGenerated) { long cid = customerGenerator.generateCustomerId(); accsGenerated = 0; accsToGenerate = getNumberofAccounts(cid, CustomerSelection.getTier(cid).ordinal() + 1); startingAccId = getStartingAccId(cid); } accsGenerated++; return startingAccId + accsGenerated - 1; } public long getCurrentCid() { return customerGenerator.getCurrentCId(); } public long generateBrokerId(long accId) { // Customer that own the account (actually, customer id minus 1) long customerId = ((accId - 1) / MAX_ACCOUNTS_PER_CUST) - TPCEConstants.IDENT_SHIFT; // Set the starting broker to be the first broker for the current load unit of customers. long startFromBroker = (customerId / TPCEConstants.DEFAULT_LOAD_UNIT) * MAX_ACCOUNTS_PER_CUST + TPCEConstants.STARTING_BROKER_ID + TPCEConstants.IDENT_SHIFT; // Note: this depends on broker ids being integer numbers from contiguous range. // The method of generating broker ids should be in sync with the BrokerGenerator return rnd.rndNthInt64Range(EGenRandom.RNG_SEED_BASE_BROKER_ID, accId - (10 * TPCEConstants.IDENT_SHIFT), startFromBroker, startFromBroker + BROKERS_COUNT - 1); } public TaxStatus getAccountTaxStatus(long accId) { long oldSeed = rnd.getSeed(); rnd.setSeedNth(EGenRandom.RNG_SEED_BASE_ACCOUNT_TAX_STATUS, accId); int thr = rnd.rndPercentage(); rnd.setSeed(oldSeed); if (thr <= percentAccountTaxStatusNonTaxable) { return TaxStatus.eNonTaxable; } else { if (thr <= percentAccountTaxStatusNonTaxable + percentAccountTaxStatusTaxableAndWithhold) { return TaxStatus.eTaxableAndWithhold; } else { return TaxStatus.eTaxableAndDontWithhold; } } } private String generateAccName(TaxStatus ts, long accId, long cid) { String res = person.getFirstName(cid) + " " + person.getLastName(cid) + " "; if (ts == TaxStatus.eNonTaxable) { int nameInd = (int)accId % nonTaxableNames.getRecordsNum(); res += nonTaxableNames.getTupleByIndex(nameInd)[0]; } else { int nameInd = (int)accId % taxableNames.getRecordsNum(); res += taxableNames.getTupleByIndex(nameInd)[0]; } return res; } private double generateBalance() { if (rnd.rndPercent(percentAccountsWithPositiveInitialBalance)) { return rnd.doubleIncrRange(0.00, accountInitialPositiveBalanceMax, 0.01); } else { return rnd.doubleIncrRange(accountInitialNegativeBalanceMin, 0.00, 0.01); } } @Override public boolean hasNext() { // we either have more customers or are still generating accounts for the last one return customerGenerator.hasNext() || accsGenerated < accsToGenerate; } @Override public Object[] next() { Object tuple[] = new Object[columnsNum]; long accId = generateAccountId(); long cid = customerGenerator.getCurrentCId(); TaxStatus tax = getAccountTaxStatus(accId); tuple[0] = accId; // ca_id tuple[1] = generateBrokerId(accId); // ca_b_id tuple[2] = cid; // ca_c_id tuple[3] = generateAccName(tax, accId, cid); // ca_name tuple[4] = (short)tax.ordinal(); // ca_tax_st tuple[5] = generateBalance(); // ca_bal return tuple; } }