/* This file is part of VoltDB. * Copyright (C) 2008-2010 VoltDB L.L.C. * * 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 org.voltdb.benchmark.multisite; import java.io.IOException; import org.voltdb.VoltTable; import org.voltdb.benchmark.multisite.procedures.ChangeSeat; import org.voltdb.benchmark.multisite.procedures.FindOpenSeats; import org.voltdb.benchmark.multisite.procedures.UpdateReservation; import org.voltdb.client.ClientResponse; import org.voltdb.client.ProcedureCallback; import org.voltdb.compiler.VoltProjectBuilder; import edu.brown.api.BenchmarkComponent; import edu.brown.hstore.Hstoreservice.Status; public class MultisiteClient extends BenchmarkComponent { /* * BenchmarkController and ClientMain requirements */ public static enum Transaction { kFindOpenSeats("Find open seats"), kUpdateReservation("Update reservation"), kChangeSeat("Change seat"); private Transaction(String displayName) { this.displayName = displayName; } public final String displayName; } public static void main(String args[]) { edu.brown.api.BenchmarkComponent.main(MultisiteClient.class, args, false); } public MultisiteClient(String[] args) { super(args); for (String arg : args) { String[] parts = arg.split("=",2); if (parts.length == 1) continue; if (parts[1].startsWith("${")) continue; if (parts[0].equals("sf")) m_scalefactor = Integer.parseInt(parts[1]); else if (parts[0].equals("multipartition")) m_multipartition = Integer.parseInt(parts[1]); } } @Override public String[] getTransactionDisplayNames() { String names[] = new String[Transaction.values().length]; int ii = 0; for (Transaction transaction : Transaction.values()) { names[ii++] = transaction.displayName; } return names; } @Override public void runLoop() { try { while (true) { this.getClientHandle().backpressureBarrier(); executeTransaction(); } } catch (IOException e) { /* * Client has no clean mechanism for terminating with the DB. */ return; } catch (InterruptedException e) { throw new RuntimeException(e); } } /** Retrieved via reflection by BenchmarkController */ public static final Class<? extends VoltProjectBuilder> m_projectBuilderClass = MultisiteProjectBuilder.class; /** Retrieved via reflection by BenchmarkController */ public static final Class<? extends BenchmarkComponent> m_loaderClass = org.voltdb.benchmark.multisite.Loader.class; /** Retrieved via reflection by BenchmarkController */ public static final String m_jarFileName = "multisite.jar"; /* * Application logic. */ private int m_multipartition = 10; private int m_scalefactor = 1; // used only for the random number helpers in this context private Loader m_loader = new Loader(new String[] {}); private boolean executeTransaction() throws IOException { int val = m_loader.number(1,100); boolean queued = false; if (val <= m_multipartition) { queued = runUpdateReservation(); } if ((val % 2) == 1) { queued = runFindOpenSeats(); } queued = runChangeSeat(); return queued; } class RunChangeSeatCallback implements ProcedureCallback { @Override public void clientCallback(ClientResponse clientResponse) { if (clientResponse.getStatus() == Status.ABORT_CONNECTION_LOST){ return; } incrementTransactionCounter(clientResponse, Transaction.kChangeSeat.ordinal()); if (clientResponse.getStatus() == Status.OK) { assert(clientResponse.getResults().length == 1); assert(clientResponse.getResults()[0].getRowCount() == 1); assert(clientResponse.getResults()[0].asScalarLong() == 1 || clientResponse.getResults()[0].asScalarLong() == 0); } } } private boolean runChangeSeat() throws IOException { // fid, cid, seatnum // cids are assigned to flights such that: // cid = (maxfid * (seat -1) + fid) % maxcust. // see the Loader reservation loader for details int maxfid = Loader.kMaxFlights / m_scalefactor; int maxcid = Loader.kMaxCustomers / m_scalefactor; int fid = m_loader.number(1, maxfid); int seat = m_loader.number(1, 150); int cid = (maxfid * (seat -1 ) + fid) % maxcid; if (cid == 0) cid = maxcid; // cids are 1-based return this.getClientHandle().callProcedure(new RunChangeSeatCallback(), ChangeSeat.class.getSimpleName(), fid, cid, seat); } class RunUpdateReservationCallback implements ProcedureCallback { private final int m_rid, m_maxfid; RunUpdateReservationCallback(int rid, int maxfid) { m_rid = rid; m_maxfid = maxfid; } @Override public void clientCallback(ClientResponse clientResponse) { if (clientResponse.getStatus() == Status.ABORT_CONNECTION_LOST){ return; } incrementTransactionCounter(clientResponse, Transaction.kUpdateReservation.ordinal()); VoltTable[] results = clientResponse.getResults(); if (m_rid < (150 * .5 * m_maxfid)) { assert (results.length == 1); assert (results[0].getRowCount() == 1); assert (results[0].asScalarLong() == 1); } } } private boolean runUpdateReservation() throws IOException { // there are roughly 150 * 0.80 * maxflights reservations in the system int maxfid = Loader.kMaxFlights / m_scalefactor; int rid = m_loader.number(1, (int) (150 * 0.8 * maxfid)); int value = m_loader.number(1, 1 << 20); return this.getClientHandle().callProcedure(new RunUpdateReservationCallback(rid, maxfid), UpdateReservation.class.getSimpleName(), rid, value); } class RunFindOpenSeats implements ProcedureCallback { @Override public void clientCallback(ClientResponse clientResponse) { if (clientResponse.getStatus() == Status.ABORT_CONNECTION_LOST){ return; } incrementTransactionCounter(clientResponse, Transaction.kFindOpenSeats.ordinal()); VoltTable[] results = clientResponse.getResults(); assert (results.length == 1); assert (results[0].getRowCount() < 150); // there is some tiny probability of an empty flight .. maybe 1/(20**150) // if you hit this assert (with valid code), play the lottery! assert (results[0].getRowCount() > 1); } } private boolean runFindOpenSeats() throws IOException { // find a random fid int maxfid = Loader.kMaxFlights / m_scalefactor; int fid = m_loader.number(1, maxfid); return this.getClientHandle().callProcedure(new RunFindOpenSeats(), FindOpenSeats.class.getSimpleName(), fid); } }