diff --git a/properties/benchmarks/smallbank.properties b/properties/benchmarks/smallbank.properties index 334aa440d2..2a90b6a9d6 100644 --- a/properties/benchmarks/smallbank.properties +++ b/properties/benchmarks/smallbank.properties @@ -3,3 +3,11 @@ ## ------------------------------------------------------------ builder = edu.brown.benchmark.smallbank.SmallBankProjectBuilder + +# The probability that customer's will be randomly selected from the hotspot accounts +# You can change the hotspot size in SmallBankConstants [0% - 100%] +prob_account_hotspot = 90 + +# The probability that the multi-account transactions will be guaranteed +# to be multi-partition [0% - 100%]. +prob_multiaccount_dtxn = 0 diff --git a/src/benchmarks/edu/brown/api/BenchmarkComponent.java b/src/benchmarks/edu/brown/api/BenchmarkComponent.java index 1d5dfd723a..3014367e06 100644 --- a/src/benchmarks/edu/brown/api/BenchmarkComponent.java +++ b/src/benchmarks/edu/brown/api/BenchmarkComponent.java @@ -1091,6 +1091,10 @@ protected final Integer getTransactionWeight(String txnName) { * @return */ protected final Integer getTransactionWeight(String txnName, Integer weightIfNull) { + if (debug.val) + LOG.debug(String.format("Looking for txn weight for '%s' [weightIfNull=%s]", + txnName, weightIfNull)); + Long val = this.m_txnWeights.get(txnName.toUpperCase()); if (val != null) { return (val.intValue()); diff --git a/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankClient.java b/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankClient.java index 5cf639fd8e..19851a5e31 100644 --- a/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankClient.java +++ b/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankClient.java @@ -31,13 +31,17 @@ package edu.brown.benchmark.smallbank; import java.io.IOException; +import java.util.Arrays; import java.util.Random; import org.apache.log4j.Logger; +import org.voltdb.TheHashinator; import org.voltdb.client.ClientResponse; import org.voltdb.client.ProcedureCallback; import edu.brown.api.BenchmarkComponent; +import edu.brown.logging.LoggerUtil; +import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.rand.RandomDistribution.FlatHistogram; import edu.brown.statistics.Histogram; import edu.brown.statistics.ObjectHistogram; @@ -49,6 +53,10 @@ */ public class SmallBankClient extends BenchmarkComponent { private static final Logger LOG = Logger.getLogger(SmallBankClient.class); + private static final LoggerBoolean debug = new LoggerBoolean(LOG.isDebugEnabled()); + static { + LoggerUtil.attachObserver(LOG, debug); + } /** * Each Transaction element provides an ArgGenerator to create the proper @@ -91,6 +99,15 @@ public Object[] genArgs(long acct0, long acct1) { }; } }), + SEND_PAYMENT(SmallBankConstants.FREQUENCY_SEND_PAYMENT, new ArgGenerator() { + public Object[] genArgs(long acct0, long acct1) { + return new Object[] { + acct0, // sendAcct + acct1, // destAcct + 5.00 // amount + }; + } + }), TRANSACT_SAVINGS(SmallBankConstants.FREQUENCY_TRANSACT_SAVINGS, new ArgGenerator() { public Object[] genArgs(long acct0, long acct1) { return new Object[] { @@ -118,24 +135,35 @@ private Transaction(int weight, ArgGenerator ag) { this.ag = ag; } - public Object[] generateParams(Random rand, int numAccounts) { - long acct0, acct1; + public Object[] generateParams(SmallBankClient client) { + long acctIds[] = new long[2]; - // Outside the hotspot - if (rand.nextInt(100) < SmallBankConstants.HOTSPOT_PROBABILITY) { - acct0 = rand.nextInt(numAccounts - SmallBankConstants.HOTSPOT_SIZE) + SmallBankConstants.HOTSPOT_SIZE; - acct1 = rand.nextInt(numAccounts - SmallBankConstants.HOTSPOT_SIZE) + SmallBankConstants.HOTSPOT_SIZE; - LOG.debug(String.format("Random number outside hotspot: %s [%d, %d]", - this, acct0, acct1)); - } - // Inside the hotspot - else { - acct0 = rand.nextInt(SmallBankConstants.HOTSPOT_SIZE); - acct1 = rand.nextInt(SmallBankConstants.HOTSPOT_SIZE); - LOG.debug(String.format("Random number inside hotspot: %s [%d, %d]", - this, acct0, acct1)); - } - return (this.ag.genArgs(acct0, acct1)); + boolean is_hotspot = (client.rand.nextInt(100) < client.prob_account_hotspot); + boolean is_dtxn = (client.rand.nextInt(100) < client.prob_multiaccount_dtxn); + + for (int i = 0; i < acctIds.length; i++) { + // Outside the hotspot + if (is_hotspot == false) { + acctIds[i] = client.rand.nextInt(client.numAccounts - SmallBankConstants.HOTSPOT_SIZE) + SmallBankConstants.HOTSPOT_SIZE; + } + // Inside the hotspot + else { + acctIds[i] = client.rand.nextInt(SmallBankConstants.HOTSPOT_SIZE); + } + + // DTXN + if (i == 1 && is_dtxn) { + if (TheHashinator.hashToPartition(acctIds[0]) == TheHashinator.hashToPartition(acctIds[1])) { + i -= 1; + continue; + } + } + } // FOR + if (debug.val) + LOG.debug(String.format("Accounts: %s [hotspot=%s, dtxn=%s]", + Arrays.toString(acctIds), is_hotspot, is_dtxn)); + + return (this.ag.genArgs(acctIds[0], acctIds[1])); } private final String displayName; @@ -165,6 +193,8 @@ public void clientCallback(ClientResponse clientResponse) { private final SmallBankCallback callbacks[]; private final int numAccounts; private final Random rand = new Random(); + private double prob_account_hotspot = 90d; + private double prob_multiaccount_dtxn = 0.0d; public static void main(String args[]) { BenchmarkComponent.main(SmallBankClient.class, args, false); @@ -172,21 +202,35 @@ public static void main(String args[]) { public SmallBankClient(String args[]) { super(args); + TheHashinator.initialize(this.getCatalogContext().catalog); this.numAccounts = (int)Math.round(SmallBankConstants.NUM_ACCOUNTS * this.getScaleFactor()); + for (String key : m_extraParams.keySet()) { + String value = m_extraParams.get(key); + + // Probability that accounts are chosen from the hotspot + if (key.equalsIgnoreCase("prob_account_hotspot")) { + this.prob_account_hotspot = Double.parseDouble(value); + } + // Probability that multi-accounts will be on different partitions + if (key.equalsIgnoreCase("prob_multiaccount_dtxn")) { + this.prob_multiaccount_dtxn = Double.parseDouble(value); + } + + } // FOR + // Initialize the sampling table Histogram txns = new ObjectHistogram(); for (Transaction t : Transaction.values()) { Integer weight = this.getTransactionWeight(t.callName); - if (weight == null) { - weight = t.weight; - } + if (weight == null) weight = t.weight; txns.put(t, weight); } // FOR + System.err.println(txns); assert(txns.getSampleCount() == 100) : "Invalid txn percentage total: " + txns.getSampleCount() + "\n" + txns; this.txnWeights = new FlatHistogram(this.rand, txns); - if (LOG.isDebugEnabled()) + if (debug.val) LOG.debug("Transaction Workload Distribution:\n" + txns); // Setup callbacks @@ -207,12 +251,12 @@ protected boolean runOnce() throws IOException { Transaction target = this.txnWeights.nextValue(); this.startComputeTime(target.displayName); - Object params[] = target.generateParams(this.rand, this.numAccounts); + Object params[] = target.generateParams(this); this.stopComputeTime(target.displayName); ProcedureCallback callback = this.callbacks[target.ordinal()]; boolean ret = this.getClientHandle().callProcedure(callback, target.callName, params); - LOG.debug("Executing txn " + target); + if (debug.val) LOG.debug("Executing txn " + target); return (ret); } diff --git a/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankConstants.java b/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankConstants.java index 8c0dbb8b7a..214f0c6700 100644 --- a/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankConstants.java +++ b/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankConstants.java @@ -37,6 +37,7 @@ public abstract class SmallBankConstants { public static final int FREQUENCY_AMALGAMATE = 20; public static final int FREQUENCY_BALANCE = 20; public static final int FREQUENCY_DEPOSIT_CHECKING = 20; + public static final int FREQUENCY_SEND_PAYMENT = 0; public static final int FREQUENCY_TRANSACT_SAVINGS = 20; public static final int FREQUENCY_WRITE_CHECK = 20; @@ -56,7 +57,6 @@ public abstract class SmallBankConstants { // Default number of customers in bank public static final int NUM_ACCOUNTS = 1000000; - public static final int HOTSPOT_PROBABILITY = 10; public static final int HOTSPOT_SIZE = 100; // ---------------------------------------------------------------- diff --git a/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankProjectBuilder.java b/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankProjectBuilder.java index eaa25af4fc..8928914c6a 100644 --- a/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankProjectBuilder.java +++ b/src/benchmarks/edu/brown/benchmark/smallbank/SmallBankProjectBuilder.java @@ -35,6 +35,7 @@ import edu.brown.benchmark.smallbank.procedures.Amalgamate; import edu.brown.benchmark.smallbank.procedures.Balance; import edu.brown.benchmark.smallbank.procedures.DepositChecking; +import edu.brown.benchmark.smallbank.procedures.SendPayment; import edu.brown.benchmark.smallbank.procedures.TransactSavings; import edu.brown.benchmark.smallbank.procedures.WriteCheck; import edu.brown.api.BenchmarkComponent; @@ -61,6 +62,7 @@ public class SmallBankProjectBuilder extends AbstractProjectBuilder { Amalgamate.class, Balance.class, DepositChecking.class, + SendPayment.class, TransactSavings.class, WriteCheck.class }; diff --git a/src/benchmarks/edu/brown/benchmark/smallbank/procedures/Amalgamate.java b/src/benchmarks/edu/brown/benchmark/smallbank/procedures/Amalgamate.java index 293ae2aadd..c38159a801 100644 --- a/src/benchmarks/edu/brown/benchmark/smallbank/procedures/Amalgamate.java +++ b/src/benchmarks/edu/brown/benchmark/smallbank/procedures/Amalgamate.java @@ -89,7 +89,7 @@ public VoltTable run(long acctId0, long acctId1) { balResults[0].advanceRow(); balResults[1].advanceRow(); double total = balResults[0].getDouble(0) + balResults[1].getDouble(0); - assert(total >= 0); + // assert(total >= 0); // Update Balance Information voltQueueSQL(ZeroCheckingBalance, acctId0); diff --git a/src/benchmarks/edu/brown/benchmark/smallbank/procedures/SendPayment.java b/src/benchmarks/edu/brown/benchmark/smallbank/procedures/SendPayment.java new file mode 100644 index 0000000000..12e5fa007b --- /dev/null +++ b/src/benchmarks/edu/brown/benchmark/smallbank/procedures/SendPayment.java @@ -0,0 +1,75 @@ +package edu.brown.benchmark.smallbank.procedures; + +import org.voltdb.ProcInfo; +import org.voltdb.SQLStmt; +import org.voltdb.VoltProcedure; +import org.voltdb.VoltTable; + +import edu.brown.benchmark.smallbank.SmallBankConstants; + +/** + * SendPayment Procedure + * @author pavlo + */ +@ProcInfo ( + partitionParam=0 +) +public class SendPayment extends VoltProcedure { + + public final SQLStmt GetAccount = new SQLStmt( + "SELECT * FROM " + SmallBankConstants.TABLENAME_ACCOUNTS + + " WHERE custid = ?" + ); + + public final SQLStmt GetCheckingBalance = new SQLStmt( + "SELECT bal FROM " + SmallBankConstants.TABLENAME_CHECKING + + " WHERE custid = ?" + ); + + public final SQLStmt UpdateCheckingBalance = new SQLStmt( + "UPDATE " + SmallBankConstants.TABLENAME_CHECKING + + " SET bal = bal + ? " + + " WHERE custid = ?" + ); + + public VoltTable[] run(long sendAcct, long destAcct, double amount) { + // Get Account Information + voltQueueSQL(GetAccount, destAcct); + voltQueueSQL(GetAccount, sendAcct); + final VoltTable acctResults[] = voltExecuteSQL(); + if (acctResults[0].getRowCount() != 1) { + String msg = "Invalid sender account '" + sendAcct + "'"; + throw new VoltAbortException(msg); + } + else if (acctResults[1].getRowCount() != 1) { + String msg = "Invalid destination account '" + destAcct + "'"; + throw new VoltAbortException(msg); + } + + // Get the sender's account balance + voltQueueSQL(GetCheckingBalance, sendAcct); + final VoltTable balResults[] = voltExecuteSQL(); + if (balResults[0].getRowCount() != 1) { + String msg = String.format("No %s for customer #%d", + SmallBankConstants.TABLENAME_SAVINGS, + sendAcct); + throw new VoltAbortException(msg); + } + balResults[0].advanceRow(); + double balance = balResults[0].getDouble(0); + + if (balance < 0) { + String msg = String.format("Insufficient %s funds for customer #%d", + SmallBankConstants.TABLENAME_CHECKING, sendAcct); + throw new VoltAbortException(msg); + } + + // Debt + voltQueueSQL(UpdateCheckingBalance, amount*-1d, sendAcct); + + // Credit + voltQueueSQL(UpdateCheckingBalance, amount, destAcct); + + return (voltExecuteSQL(true)); + } +} \ No newline at end of file