com.oltpbenchmark.benchmarks.seats.SEATSWorker.java Source code

Java tutorial

Introduction

Here is the source code for com.oltpbenchmark.benchmarks.seats.SEATSWorker.java

Source

/***************************************************************************
 *  Copyright (C) 2011 by H-Store Project                                  *
 *  Brown University                                                       *
 *  Massachusetts Institute of Technology                                  *
 *  Yale University                                                        *
 *                                                                         *
 *  http://hstore.cs.brown.edu/                                            *
 *                                                                         *
 *  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.                                        *
 ***************************************************************************/
/* This file is part of VoltDB. 
 * Copyright (C) 2009 Vertica Systems Inc.
 *
 * 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.oltpbenchmark.benchmarks.seats;

import java.sql.Timestamp;
import java.sql.SQLException;
import java.util.*;

import org.apache.commons.collections15.map.ListOrderedMap;
import org.apache.log4j.Logger;

import com.oltpbenchmark.api.Procedure;
import com.oltpbenchmark.api.Procedure.UserAbortException;
import com.oltpbenchmark.api.TransactionType;
import com.oltpbenchmark.api.Worker;
import com.oltpbenchmark.benchmarks.seats.procedures.DeleteReservation;
import com.oltpbenchmark.benchmarks.seats.procedures.FindFlights;
import com.oltpbenchmark.benchmarks.seats.procedures.FindOpenSeats;
import com.oltpbenchmark.benchmarks.seats.procedures.NewReservation;
import com.oltpbenchmark.benchmarks.seats.procedures.UpdateCustomer;
import com.oltpbenchmark.benchmarks.seats.procedures.UpdateReservation;
import com.oltpbenchmark.benchmarks.seats.util.CustomerId;
import com.oltpbenchmark.benchmarks.seats.util.FlightId;
import com.oltpbenchmark.types.TransactionStatus;
import com.oltpbenchmark.util.RandomGenerator;
import com.oltpbenchmark.util.StringUtil;

public class SEATSWorker extends Worker {
    private static final Logger LOG = Logger.getLogger(SEATSWorker.class);

    /**
     * Airline Benchmark Transactions
     */
    private static enum Transaction {
        DeleteReservation(DeleteReservation.class), FindFlights(FindFlights.class), FindOpenSeats(
                FindOpenSeats.class), NewReservation(NewReservation.class), UpdateCustomer(
                        UpdateCustomer.class), UpdateReservation(UpdateReservation.class);

        private Transaction(Class<? extends Procedure> proc_class) {
            this.proc_class = proc_class;
            this.execName = proc_class.getSimpleName();
            this.displayName = StringUtil.title(this.name().replace("_", " "));
        }

        public final Class<? extends Procedure> proc_class;
        public final String displayName;
        public final String execName;

        protected static final Map<Integer, Transaction> idx_lookup = new HashMap<Integer, Transaction>();
        protected static final Map<String, Transaction> name_lookup = new HashMap<String, Transaction>();
        static {
            for (Transaction vt : EnumSet.allOf(Transaction.class)) {
                Transaction.idx_lookup.put(vt.ordinal(), vt);
                Transaction.name_lookup.put(vt.name(), vt);
            }
        }

        public static Transaction get(Integer idx) {
            assert (idx >= 0);
            return (Transaction.idx_lookup.get(idx));
        }

        public static Transaction get(String name) {
            return (Transaction.name_lookup.get(name));
        }

        public String getDisplayName() {
            return (this.displayName);
        }

        public String getExecName() {
            return (this.execName);
        }
    }

    // -----------------------------------------------------------------
    // RESERVED SEAT BITMAPS
    // -----------------------------------------------------------------

    public enum CacheType {
        PENDING_INSERTS(SEATSConstants.CACHE_LIMIT_PENDING_INSERTS), PENDING_UPDATES(
                SEATSConstants.CACHE_LIMIT_PENDING_UPDATES), PENDING_DELETES(
                        SEATSConstants.CACHE_LIMIT_PENDING_DELETES),;

        private CacheType(int limit) {
            this.limit = limit;
        }

        private final int limit;
    }

    protected final Map<CacheType, LinkedList<Reservation>> CACHE_RESERVATIONS = new HashMap<SEATSWorker.CacheType, LinkedList<Reservation>>();
    {
        for (CacheType ctype : CacheType.values()) {
            CACHE_RESERVATIONS.put(ctype, new LinkedList<Reservation>());
        } // FOR
    }

    protected final Map<CustomerId, Set<FlightId>> CACHE_CUSTOMER_BOOKED_FLIGHTS = new HashMap<CustomerId, Set<FlightId>>();
    protected final Map<FlightId, BitSet> CACHE_BOOKED_SEATS = new HashMap<FlightId, BitSet>();

    private static final BitSet FULL_FLIGHT_BITSET = new BitSet(SEATSConstants.FLIGHTS_NUM_SEATS);
    static {
        for (int i = 0; i < SEATSConstants.FLIGHTS_NUM_SEATS; i++)
            FULL_FLIGHT_BITSET.set(i);
    } // STATIC

    protected BitSet getSeatsBitSet(FlightId flight_id) {
        BitSet seats = CACHE_BOOKED_SEATS.get(flight_id);
        if (seats == null) {
            //            synchronized (CACHE_BOOKED_SEATS) {
            seats = CACHE_BOOKED_SEATS.get(flight_id);
            if (seats == null) {
                seats = new BitSet(SEATSConstants.FLIGHTS_NUM_SEATS);
                CACHE_BOOKED_SEATS.put(flight_id, seats);
            }
            //            } // SYNCH
        }
        return (seats);
    }

    /**
     * Returns true if the given BitSet for a Flight has all of its seats reserved 
     * @param seats
     * @return
     */
    protected boolean isFlightFull(BitSet seats) {
        assert (FULL_FLIGHT_BITSET.size() == seats.size());
        return FULL_FLIGHT_BITSET.equals(seats);
    }

    /**
     * Returns true if the given Customer already has a reservation booked on the target Flight
     * @param customer_id
     * @param flight_id
     * @return
     */
    protected boolean isCustomerBookedOnFlight(CustomerId customer_id, FlightId flight_id) {
        Set<FlightId> flights = CACHE_CUSTOMER_BOOKED_FLIGHTS.get(customer_id);
        return (flights != null && flights.contains(flight_id));
    }

    protected Set<FlightId> getCustomerBookedFlights(CustomerId customer_id) {
        Set<FlightId> f_ids = CACHE_CUSTOMER_BOOKED_FLIGHTS.get(customer_id);
        if (f_ids == null) {
            f_ids = CACHE_CUSTOMER_BOOKED_FLIGHTS.get(customer_id);
            if (f_ids == null) {
                f_ids = new HashSet<FlightId>();
                CACHE_CUSTOMER_BOOKED_FLIGHTS.put(customer_id, f_ids);
            }
        }
        return (f_ids);
    }

    @Override
    public String toString() {
        Map<String, Object> m = new ListOrderedMap<String, Object>();
        for (CacheType ctype : CACHE_RESERVATIONS.keySet()) {
            m.put(ctype.name(), CACHE_RESERVATIONS.get(ctype).size());
        } // FOR
        m.put("CACHE_CUSTOMER_BOOKED_FLIGHTS", CACHE_CUSTOMER_BOOKED_FLIGHTS.size());
        m.put("CACHE_BOOKED_SEATS", CACHE_BOOKED_SEATS.size());
        m.put("PROFILE", this.profile);

        return StringUtil.formatMaps(m);
    }

    // -----------------------------------------------------------------
    // ADDITIONAL DATA MEMBERS
    // -----------------------------------------------------------------

    private final SEATSProfile profile;
    private final RandomGenerator rng;
    private final List<Reservation> tmp_reservations = new ArrayList<Reservation>();

    /**
     * When a customer looks for an open seat, they will then attempt to book that seat in
     * a new reservation. Some of them will want to change their seats. This data structure
     * represents a customer that is queued to change their seat. 
     */
    protected static class Reservation {
        public final long id;
        public final FlightId flight_id;
        public final CustomerId customer_id;
        public final int seatnum;

        public Reservation(long id, FlightId flight_id, CustomerId customer_id, int seatnum) {
            this.id = id;
            this.flight_id = flight_id;
            this.customer_id = customer_id;
            this.seatnum = seatnum;
            assert (this.seatnum >= 0) : "Invalid seat number\n" + this;
            assert (this.seatnum < SEATSConstants.FLIGHTS_NUM_SEATS) : "Invalid seat number\n" + this;
        }

        @Override
        public int hashCode() {
            int prime = 7;
            int result = 1;
            result = prime * result + seatnum;
            result = prime * result + flight_id.hashCode();
            result = prime * result + customer_id.hashCode();

            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;

            if (!(obj instanceof SEATSWorker) || obj == null)
                return false;

            Reservation r = (Reservation) obj;
            // Ignore id!
            return (this.seatnum == r.seatnum && this.flight_id.equals(r.flight_id)
                    && this.customer_id.equals(r.customer_id));
        }

        @Override
        public String toString() {
            return String.format("{Id:%d / %s / %s / SeatNum:%d}", this.id, this.flight_id, this.customer_id,
                    this.seatnum);
        }
    } // END CLASS

    // -----------------------------------------------------------------
    // REQUIRED METHODS
    // -----------------------------------------------------------------

    public SEATSWorker(int id, SEATSBenchmark benchmark) {
        super(benchmark, id);

        this.rng = benchmark.getRandomGenerator();
        this.profile = new SEATSProfile(benchmark, rng);
    }

    protected void initialize() {
        try {
            this.profile.loadProfile(this);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
        if (LOG.isTraceEnabled())
            LOG.trace("Airport Max Customer Id:\n" + this.profile.airport_max_customer_id);

        // Make sure we have the information we need in the BenchmarkProfile
        String error_msg = null;
        if (this.profile.getFlightIdCount() == 0) {
            error_msg = "The benchmark profile does not have any flight ids.";
        } else if (this.profile.getCustomerIdCount() == 0) {
            error_msg = "The benchmark profile does not have any customer ids.";
        } else if (this.profile.getFlightStartDate() == null) {
            error_msg = "The benchmark profile does not have a valid flight start date.";
        }
        if (error_msg != null)
            throw new RuntimeException(error_msg);

        // Fire off a FindOpenSeats so that we can prime ourselves
        FindOpenSeats proc = this.getProcedure(FindOpenSeats.class);
        try {
            boolean ret = this.executeFindOpenSeats(proc);
            assert (ret);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }

        if (LOG.isDebugEnabled())
            LOG.debug("Initialized SEATSWorker:\n" + this.toString());
    }

    @Override
    protected TransactionStatus executeWork(TransactionType txnType) throws UserAbortException, SQLException {
        Transaction txn = Transaction.get(txnType.getName());
        assert (txn != null) : "Unexpected " + txnType;

        // Get the Procedure handle
        Procedure proc = this.getProcedure(txnType);
        assert (proc != null) : String.format("Failed to get Procedure handle for %s.%s",
                this.getBenchmarkModule().getBenchmarkName(), txnType);
        if (LOG.isDebugEnabled())
            LOG.debug("Attempting to execute " + proc);
        boolean ret = false;
        switch (txn) {
        case DeleteReservation: {
            ret = this.executeDeleteReservation((DeleteReservation) proc);
            break;
        }
        case FindFlights: {
            ret = this.executeFindFlights((FindFlights) proc);
            break;
        }
        case FindOpenSeats: {
            ret = this.executeFindOpenSeats((FindOpenSeats) proc);
            break;
        }
        case NewReservation: {
            ret = this.executeNewReservation((NewReservation) proc);
            break;
        }
        case UpdateCustomer: {
            ret = this.executeUpdateCustomer((UpdateCustomer) proc);
            break;
        }
        case UpdateReservation: {
            ret = this.executeUpdateReservation((UpdateReservation) proc);
            break;
        }
        default:
            assert (false) : "Unexpected transaction: " + txn;
        } // SWITCH
        if (ret == false) {
            if (LOG.isDebugEnabled())
                LOG.debug("Unable to execute " + proc + " right now");
            return (TransactionStatus.RETRY_DIFFERENT);
        }

        if (ret && LOG.isDebugEnabled())
            LOG.debug("Executed a new invocation of " + txn);
        return (TransactionStatus.SUCCESS);
    }

    /**
     * Take an existing Reservation that we know is legit and randomly decide to 
     * either queue it for a later update or delete transaction 
     * @param r
     */
    protected void requeueReservation(Reservation r) {
        CacheType ctype = null;

        // Queue this motha trucka up for a deletin'
        if (rng.nextBoolean()) {
            ctype = CacheType.PENDING_DELETES;
        } else {
            ctype = CacheType.PENDING_UPDATES;
        }
        assert (ctype != null);

        LinkedList<Reservation> cache = CACHE_RESERVATIONS.get(ctype);
        assert (cache != null);
        cache.add(r);
        if (LOG.isDebugEnabled())
            LOG.debug(String.format("Queued %s for %s [cache=%d]", r, ctype, cache.size()));

        while (cache.size() > ctype.limit) {
            cache.remove();
        }
    }

    // -----------------------------------------------------------------
    // DeleteReservation
    // -----------------------------------------------------------------

    private boolean executeDeleteReservation(DeleteReservation proc) throws SQLException {
        // Pull off the first cached reservation and drop it on the cluster...
        final Reservation r = CACHE_RESERVATIONS.get(CacheType.PENDING_DELETES).poll();
        if (r == null) {
            return (false);
        }
        int rand = rng.number(1, 100);

        // Parameters
        long f_id = r.flight_id.encode();
        Long c_id = null;
        String c_id_str = null;
        String ff_c_id_str = null;
        Long ff_al_id = null;

        // Delete with the Customer's id as a string 
        if (rand <= SEATSConstants.PROB_DELETE_WITH_CUSTOMER_ID_STR) {
            c_id_str = Long.toString(r.customer_id.encode());
        }
        // Delete using their FrequentFlyer information
        else if (rand <= SEATSConstants.PROB_DELETE_WITH_CUSTOMER_ID_STR
                + SEATSConstants.PROB_DELETE_WITH_FREQUENTFLYER_ID_STR) {
            ff_c_id_str = Long.toString(r.customer_id.encode());
            ff_al_id = r.flight_id.getAirlineId();
        }
        // Delete using their Customer id
        else {
            c_id = r.customer_id.encode();
        }

        if (LOG.isTraceEnabled())
            LOG.trace("Calling " + proc);
        proc.run(conn, f_id, c_id, c_id_str, ff_c_id_str, ff_al_id);
        conn.commit();

        // We can remove this from our set of full flights because know that there is now a free seat
        BitSet seats = getSeatsBitSet(r.flight_id);
        seats.set(r.seatnum, false);

        // And then put it up for a pending insert
        if (rng.nextInt(100) < SEATSConstants.PROB_REQUEUE_DELETED_RESERVATION) {
            CACHE_RESERVATIONS.get(CacheType.PENDING_INSERTS).add(r);
        }

        return (true);
    }

    // ----------------------------------------------------------------
    // FindFlights
    // ----------------------------------------------------------------

    /**
     * Execute one of the FindFlight transactions
     * @param txn
     * @throws SQLException
     */
    private boolean executeFindFlights(FindFlights proc) throws SQLException {
        long depart_airport_id;
        long arrive_airport_id;
        Timestamp start_date;
        Timestamp stop_date;

        // Select two random airport ids
        if (rng.nextInt(100) < SEATSConstants.PROB_FIND_FLIGHTS_RANDOM_AIRPORTS) {
            // Does it matter whether the one airport actually flies to the other one?
            depart_airport_id = this.profile.getRandomAirportId();
            arrive_airport_id = this.profile.getRandomOtherAirport(depart_airport_id);

            // Select a random date from our upcoming dates
            start_date = this.profile.getRandomUpcomingDate();
            stop_date = new Timestamp(start_date.getTime() + (SEATSConstants.MILLISECONDS_PER_DAY * 2));
        }

        // Use an existing flight so that we guaranteed to get back results
        else {
            FlightId flight_id = this.profile.getRandomFlightId();
            depart_airport_id = flight_id.getDepartAirportId();
            arrive_airport_id = flight_id.getArriveAirportId();

            Timestamp flightDate = flight_id.getDepartDate(this.profile.getFlightStartDate());
            long range = Math.round(SEATSConstants.MILLISECONDS_PER_DAY * 0.5);
            start_date = new Timestamp(flightDate.getTime() - range);
            stop_date = new Timestamp(flightDate.getTime() + range);

            if (LOG.isDebugEnabled())
                LOG.debug(String.format("Using %s as look up in %s: %d / %s", flight_id, proc, flight_id.encode(),
                        flightDate));
        }

        // If distance is greater than zero, then we will also get flights from nearby airports
        long distance = -1;
        if (rng.nextInt(100) < SEATSConstants.PROB_FIND_FLIGHTS_NEARBY_AIRPORT) {
            distance = SEATSConstants.DISTANCES[rng.nextInt(SEATSConstants.DISTANCES.length)];
        }

        if (LOG.isTraceEnabled())
            LOG.trace("Calling " + proc);
        List<Object[]> results = proc.run(conn, depart_airport_id, arrive_airport_id, start_date, stop_date,
                distance);
        conn.commit();

        if (results.size() > 1) {
            // Convert the data into a FlightIds that other transactions can use
            int ctr = 0;
            for (Object row[] : results) {
                FlightId flight_id = new FlightId((Long) row[0]);
                assert (flight_id != null);
                boolean added = profile.addFlightId(flight_id);
                if (added)
                    ctr++;
            } // WHILE
            if (LOG.isDebugEnabled())
                LOG.debug(String.format("Added %d out of %d FlightIds to local cache", ctr, results.size()));
        }
        return (true);
    }

    // ----------------------------------------------------------------
    // FindOpenSeats
    // ----------------------------------------------------------------

    /**
     * Execute the FindOpenSeat procedure
     * @throws SQLException
     */
    private boolean executeFindOpenSeats(FindOpenSeats proc) throws SQLException {
        final FlightId search_flight = this.profile.getRandomFlightId();
        assert (search_flight != null);
        Long airport_depart_id = search_flight.getDepartAirportId();

        if (LOG.isTraceEnabled())
            LOG.trace("Calling " + proc);
        Object[][] results = proc.run(conn, search_flight.encode());
        conn.commit();

        int rowCount = results.length;
        assert (rowCount <= SEATSConstants.FLIGHTS_NUM_SEATS) : String
                .format("Unexpected %d open seats returned for %s", rowCount, search_flight);

        // there is some tiny probability of an empty flight .. maybe 1/(20**150)
        // if you hit this assert (with valid code), play the lottery!
        if (rowCount == 0)
            return (true);

        LinkedList<Reservation> cache = CACHE_RESERVATIONS.get(CacheType.PENDING_INSERTS);
        assert (cache != null) : "Unexpected " + CacheType.PENDING_INSERTS;

        // Store pending reservations in our queue for a later transaction            
        BitSet seats = getSeatsBitSet(search_flight);
        tmp_reservations.clear();

        for (Object row[] : results) {
            if (row == null)
                continue; //  || rng.nextInt(100) < 75) continue; // HACK
            Integer seatnum = (Integer) row[1];

            // We first try to get a CustomerId based at this departure airport
            if (LOG.isTraceEnabled())
                LOG.trace("Looking for a random customer to fly on " + search_flight);
            CustomerId customer_id = profile.getRandomCustomerId(airport_depart_id);

            // We will go for a random one if:
            //  (1) The Customer is already booked on this Flight
            //  (2) We already made a new Reservation just now for this Customer
            int tries = SEATSConstants.FLIGHTS_NUM_SEATS;
            while (tries-- > 0 && (customer_id == null)) { //  || isCustomerBookedOnFlight(customer_id, flight_id))) {
                customer_id = profile.getRandomCustomerId();
                if (LOG.isTraceEnabled())
                    LOG.trace("RANDOM CUSTOMER: " + customer_id);
            } // WHILE
            assert (customer_id != null) : String.format(
                    "Failed to find a unique Customer to reserve for seat #%d on %s", seatnum, search_flight);

            Reservation r = new Reservation(profile.getNextReservationId(getId()), search_flight, customer_id,
                    seatnum.intValue());
            seats.set(seatnum);
            tmp_reservations.add(r);
            if (LOG.isTraceEnabled())
                LOG.trace(
                        "QUEUED INSERT: " + search_flight + " / " + search_flight.encode() + " -> " + customer_id);
        } // WHILE

        if (tmp_reservations.isEmpty() == false) {
            Collections.shuffle(tmp_reservations);
            cache.addAll(tmp_reservations);
            while (cache.size() > SEATSConstants.CACHE_LIMIT_PENDING_INSERTS) {
                cache.remove();
            } // WHILE
            if (LOG.isDebugEnabled())
                LOG.debug(String.format("Stored %d pending inserts for %s [totalPendingInserts=%d]",
                        tmp_reservations.size(), search_flight, cache.size()));
        }
        return (true);
    }

    // ----------------------------------------------------------------
    // NewReservation
    // ----------------------------------------------------------------

    private boolean executeNewReservation(NewReservation proc) throws SQLException {
        Reservation reservation = null;
        BitSet seats = null;
        LinkedList<Reservation> cache = CACHE_RESERVATIONS.get(CacheType.PENDING_INSERTS);
        assert (cache != null) : "Unexpected " + CacheType.PENDING_INSERTS;

        if (LOG.isDebugEnabled())
            LOG.debug(String.format("Attempting to get a new pending insert Reservation [totalPendingInserts=%d]",
                    cache.size()));
        while (reservation == null) {
            Reservation r = cache.poll();
            if (r == null) {
                if (LOG.isDebugEnabled())
                    LOG.warn("Unable to execute " + proc + " - No available reservations to insert");
                break;
            }

            seats = getSeatsBitSet(r.flight_id);

            if (isFlightFull(seats)) {
                if (LOG.isDebugEnabled())
                    LOG.debug(String.format("%s is full", r.flight_id));
                continue;
            }
            // PAVLO: Not sure why this is always coming back as reserved? 
            //            else if (seats.get(r.seatnum)) {
            //                if (LOG.isDebugEnabled())
            //                    LOG.debug(String.format("Seat #%d on %s is already booked", r.seatnum, r.flight_id));
            //                continue;
            //            }
            else if (isCustomerBookedOnFlight(r.customer_id, r.flight_id)) {
                if (LOG.isDebugEnabled())
                    LOG.debug(String.format("%s is already booked on %s", r.customer_id, r.flight_id));
                continue;
            }
            reservation = r;
        } // WHILE
        if (reservation == null) {
            if (LOG.isDebugEnabled())
                LOG.warn("Failed to find a valid pending insert Reservation\n" + this.toString());
            return (false);
        }

        // Generate a random price for now
        double price = 2.0 * rng.number(SEATSConstants.RESERVATION_PRICE_MIN, SEATSConstants.RESERVATION_PRICE_MAX);

        // Generate random attributes
        long attributes[] = new long[9];
        for (int i = 0; i < attributes.length; i++) {
            attributes[i] = rng.nextLong();
        } // FOR

        if (LOG.isTraceEnabled())
            LOG.trace("Calling " + proc);
        proc.run(conn, reservation.id, reservation.customer_id.encode(), reservation.flight_id.encode(),
                reservation.seatnum, price, attributes);
        conn.commit();

        // Mark this seat as successfully reserved
        seats.set(reservation.seatnum);

        // Set it up so we can play with it later
        this.requeueReservation(reservation);

        return (true);
    }

    // ----------------------------------------------------------------
    // UpdateCustomer
    // ----------------------------------------------------------------

    private boolean executeUpdateCustomer(UpdateCustomer proc) throws SQLException {
        // Pick a random customer and then have at it!
        CustomerId customer_id = this.profile.getRandomCustomerId();

        Long c_id = null;
        String c_id_str = null;
        long attr0 = this.rng.nextLong();
        long attr1 = this.rng.nextLong();
        long update_ff = (rng.number(1, 100) <= SEATSConstants.PROB_UPDATE_FREQUENT_FLYER ? 1 : 0);

        // Update with the Customer's id as a string 
        if (rng.nextInt(100) < SEATSConstants.PROB_UPDATE_WITH_CUSTOMER_ID_STR) {
            c_id_str = Long.toString(customer_id.encode());
        }
        // Update using their Customer id
        else {
            c_id = customer_id.encode();
        }

        if (LOG.isTraceEnabled())
            LOG.trace("Calling " + proc);
        proc.run(conn, c_id, c_id_str, update_ff, attr0, attr1);
        conn.commit();

        return (true);
    }

    // ----------------------------------------------------------------
    // UpdateReservation
    // ----------------------------------------------------------------

    private boolean executeUpdateReservation(UpdateReservation proc) throws SQLException {
        LinkedList<Reservation> cache = CACHE_RESERVATIONS.get(CacheType.PENDING_UPDATES);
        assert (cache != null) : "Unexpected " + CacheType.PENDING_UPDATES;

        if (LOG.isTraceEnabled())
            LOG.trace("Let's look for a Reservation that we can update");

        // Pull off the first pending seat change and throw that ma at the server
        Reservation r = null;
        try {
            r = cache.poll();
        } catch (Throwable ex) {
            // Nothing
        }
        if (r == null) {
            if (LOG.isDebugEnabled())
                LOG.warn(String.format("Failed to find Reservation to update [cache=%d]", cache.size()));
            return (false);
        }
        if (LOG.isTraceEnabled())
            LOG.trace("Ok let's try to update " + r);

        long value = rng.number(1, 1 << 20);
        long attribute_idx = rng.nextInt(UpdateReservation.NUM_UPDATES);
        long seatnum = rng.number(0, SEATSConstants.FLIGHTS_NUM_SEATS - 1);

        if (LOG.isTraceEnabled())
            LOG.trace("Calling " + proc);
        proc.run(conn, r.id, r.flight_id.encode(), r.customer_id.encode(), seatnum, attribute_idx, value);
        conn.commit();

        SEATSWorker.this.requeueReservation(r);
        return (true);
    }

}