public synchronized void tick(int counter) { this.tick_counter = counter; if (config.warehouse_debug) { Map<String, Histogram<Integer>> m = new ListOrderedMap<String, Histogram<Integer>>(); m.put( String.format( "LAST ROUND\n - SampleCount=%d", this.lastWarehouseHistory.getSampleCount()), this.lastWarehouseHistory); m.put( String.format("TOTAL\n - SampleCount=%d", this.totalWarehouseHistory.getSampleCount()), this.totalWarehouseHistory); long total = this.totalWarehouseHistory.getSampleCount(); LOG.info( String.format( "ROUND #%02d - Warehouse Temporal Skew - %d / %d [%.2f]\n%s", this.tick_counter, this.temporal_counter, total, (this.temporal_counter / (double) total), StringUtil.formatMaps(m))); LOG.info(StringUtil.SINGLE_LINE); this.lastWarehouseHistory.clearValues(); } }
/** testLoadAirportFlights */ public void testLoadAirportFlights() throws Exception { Map<String, Histogram<String>> histograms = SEATSHistogramUtil.loadAirportFlights(AIRLINE_DATA_DIR); assertNotNull(histograms); assertFalse(histograms.isEmpty()); assert (histograms.size() >= 200); // Just some airports that we expect to be in there String airports[] = {"BWI", "LAX", "JFK", "MDW", "ATL", "SFO", "ORD"}; for (String a : airports) { assertTrue(a, histograms.containsKey(a)); } // FOR System.err.println(StringUtil.formatMaps(histograms)); // We expect ATL to be the max // assertEquals("ATL", histogram.getMaxCountValue()); // Make sure the values are formatted correctly // ListOrderedMap<String, Histogram<String>> m = new ListOrderedMap<String, // Histogram<String>>(); Pattern p = Pattern.compile("[\\d\\w]{3,3}"); for (String s_airport : histograms.keySet()) { assert (p.matcher(s_airport).matches()) : "Invalid source airport: " + s_airport; // m.put(s_airport, histograms.get(s_airport)); for (Object value : histograms.get(s_airport).values()) { assert (p.matcher(value.toString()).matches()) : "Invalid destination airport: " + value; } // FOR } // FOR // System.err.println(StringUtil.formatMaps(m)); }
/** * Called by Java to store dependencies for the EE. Creates a private list of dependencies to be * manipulated by the tracker. Does not copy the table data - references WorkUnit's tables. * * @param dependencies */ public void stashWorkUnitDependencies(final Map<Integer, List<VoltTable>> dependencies) { if (d) LOG.debug( String.format( "Stashing %d InputDependencies:\n%s", dependencies.size(), StringUtil.formatMaps(dependencies))); m_dependencyTracker.trackNewWorkUnit(dependencies); }
private Map<String, Long> getCodeXref(String col_name) { Map<String, Long> m = this.code_id_xref.get(col_name); assert (m != null) : "Invalid code xref mapping column '" + col_name + "'"; assert (m.isEmpty() == false) : "Empty code xref mapping for column '" + col_name + "'\n" + StringUtil.formatMaps(this.code_id_xref); return (m); }
public String debug() { Map<String, Object> m = new LinkedHashMap<String, Object>(); m.put("PartitionId", this.partitionId); m.put("# of Elements", this.size()); m.put("Wait Time", this.waitTime); m.put("Next Time Remaining", Math.max(0, EstTime.currentTimeMillis() - this.blockTime)); m.put("Next Txn", this.nextTxn); m.put("Last Popped Txn", this.lastTxnPopped); m.put("Last Seen Txn", this.lastSeenTxn); return (StringUtil.formatMaps(m)); }
protected QueueState checkQueueState() { QueueState newState = QueueState.UNBLOCKED; AbstractTransaction ts = super.peek(); if (ts == null) { if (t) LOG.trace(String.format("Partition %d :: Queue is empty.", this.partitionId)); newState = QueueState.BLOCKED_EMPTY; } // Check whether can unblock now else if (ts == this.nextTxn && this.state != QueueState.UNBLOCKED) { if (EstTime.currentTimeMillis() < this.blockTime) { newState = QueueState.BLOCKED_SAFETY; } else if (d) { LOG.debug( String.format( "Partition %d :: Wait time for %s has passed. Unblocking...", this.partitionId, this.nextTxn)); } } // This is a new txn and we should wait... else if (this.nextTxn != ts) { long txnTimestamp = TransactionIdManager.getTimestampFromTransactionId(ts.getTransactionId().longValue()); long timestamp = EstTime.currentTimeMillis(); long waitTime = Math.max(0, this.waitTime - (timestamp - txnTimestamp)); newState = (waitTime > 0 ? QueueState.BLOCKED_SAFETY : QueueState.UNBLOCKED); this.blockTime = timestamp + waitTime; this.nextTxn = ts; if (d) { String debug = ""; if (t) { Map<String, Object> m = new LinkedHashMap<String, Object>(); m.put("Txn Init Timestamp", txnTimestamp); m.put("Current Timestamp", timestamp); m.put("Block Time Remaining", (this.blockTime - timestamp)); debug = "\n" + StringUtil.formatMaps(m); } LOG.debug( String.format( "Partition %d :: Blocking %s for %d ms [maxWait=%d]%s", this.partitionId, ts, (this.blockTime - timestamp), this.waitTime, debug)); } } if (newState != this.state) { this.state = newState; if (d) LOG.debug( String.format( "Partition %d :: State:%s / NextTxn:%s", this.partitionId, this.state, this.nextTxn)); } return this.state; }
@Override public String toString() { Map<String, Object> m = new ListOrderedMap<String, Object>(); m.put("Scale Factor", this.scale_factor); m.put("# of Reservations", this.num_reservations); m.put("Flight Start Date", this.flight_start_date); m.put("Flight Upcoming Date", this.flight_upcoming_date); m.put("Flight Past Days", this.flight_past_days); m.put("Flight Future Days", this.flight_future_days); m.put("Num Flights", this.num_flights); m.put("Num Customers", this.num_customers); m.put("Num Reservations", this.num_reservations); return (StringUtil.formatMaps(m)); }
@Override public String toString() { Map<String, Object> m = new ListOrderedMap<String, Object>(); m.put("Warehouses", parameters.warehouses); m.put( "W_ID Range", String.format("[%d, %d]", parameters.starting_warehouse, parameters.last_warehouse)); m.put("Districts per Warehouse", parameters.districtsPerWarehouse); m.put("Custers per District", parameters.customersPerDistrict); m.put("Initial Orders per District", parameters.newOrdersPerDistrict); m.put("Items", parameters.items); m.put("Affine Warehouse", lastAssignedWarehouseId); m.put("Skew Factor", this.skewFactor); if (this.zipf != null && this.zipf.isHistoryEnabled()) { m.put("Skewed Warehouses", this.zipf.getHistory()); } return ("TPCC Simulator Options\n" + StringUtil.formatMaps(m, this.config.debugMap())); }
/** * Store dependency tables for later retrieval by the EE. * * @param workunit */ void trackNewWorkUnit(final Map<Integer, List<VoltTable>> dependencies) { for (final Entry<Integer, List<VoltTable>> e : dependencies.entrySet()) { // could do this optionally - debug only. if (d) verifyDependencySanity(e.getKey(), e.getValue()); // create a new list of references to the workunit's table // to avoid any changes to the WorkUnit's list. But do not // copy the table data. ArrayDeque<VoltTable> deque = m_depsById.get(e.getKey()); if (deque == null) { deque = new ArrayDeque<VoltTable>(); // intentionally overwrite the previous dependency id. // would a lookup and a clear() be faster? m_depsById.put(e.getKey(), deque); } else { deque.clear(); } deque.addAll(e.getValue()); } if (d) LOG.debug("Current InputDepencies:\n" + StringUtil.formatMaps(m_depsById)); }
public String debug() { long timestamp = System.currentTimeMillis(); AbstractTransaction peek = super.peek(); @SuppressWarnings("unchecked") Map<String, Object> m[] = new Map[3]; int i = -1; m[++i] = new LinkedHashMap<String, Object>(); m[i].put("PartitionId", this.partitionId); m[i].put("Current State", this.state); m[i].put("# of Elements", this.size()); m[i].put("# of Popped", this.txnsPopped); m[i].put("Last Popped Txn", this.lastTxnPopped); m[i].put("Last Seen Txn", this.lastSeenTxnId); m[i].put("Last Safe Txn", this.lastSafeTxnId); m[++i] = new LinkedHashMap<String, Object>(); m[i].put("Throttled", super.isThrottled()); m[i].put("Threshold", super.getThrottleThreshold()); m[i].put("Release", super.getThrottleRelease()); m[i].put("Increase Delta", super.getThrottleThresholdIncreaseDelta()); m[i].put("Max Size", super.getThrottleThresholdMaxSize()); m[++i] = new LinkedHashMap<String, Object>(); m[i].put("Peek Txn", (peek == null ? "null" : peek)); m[i].put("Wait Time", this.waitTime + " ms"); m[i].put("Current Time", timestamp); m[i].put( "Blocked Time", (this.blockTimestamp > 0 ? this.blockTimestamp + (this.blockTimestamp < timestamp ? " **PASSED**" : "") : "--")); m[i].put( "Blocked Remaining", (this.blockTimestamp > 0 ? Math.max(0, this.blockTimestamp - timestamp) + " ms" : "--")); return (StringUtil.formatMaps(m)); }
/** * This is the most important method of the queue. This will figure out the next state and how * long we must wait until we can release the next transaction. <B>Note:</B> I believe that this * is the only thing that needs to be synchronized * * @param afterRemoval If this flag is set to true, then it means that who ever is calling this * method just removed something from the queue. That means that we need to go and check * whether the lastSafeTxnId should change. * @return */ private QueueState checkQueueState(boolean afterRemoval) { if (trace.val && super.isEmpty() == false) LOG.trace( String.format( "Partition %d :: checkQueueState(afterPoll=%s) [current=%s]", this.partitionId, afterRemoval, this.state)); QueueState newState = (afterRemoval ? QueueState.BLOCKED_SAFETY : QueueState.UNBLOCKED); long currentTimestamp = -1l; AbstractTransaction ts = super.peek(); // BLOCKING Long txnId = null; if (ts == null) { // if (trace.val) // LOG.trace(String.format("Partition %d :: Queue is empty.", // this.partitionId)); newState = QueueState.BLOCKED_EMPTY; } // Check whether can unblock now else { assert (ts.isInitialized()) : String.format( "Unexpected uninitialized transaction %s [partition=%d]", ts, this.partitionId); txnId = ts.getTransactionId(); // HACK: Ignore null txnIds if (txnId == null) { LOG.warn( String.format( "Partition %d :: Uninitialized transaction handle %s", this.partitionId, ts)); return (this.state); } assert (txnId != null) : "Null transaction id from " + txnId; // If this txnId is greater than the last safe one that we've seen, then we know // that the lastSafeTxnId has been polled. That means that we need to // wait for an appropriate amount of time before we're allow to be executed. if (txnId.compareTo(this.lastSafeTxnId) > 0 && afterRemoval == false) { newState = QueueState.BLOCKED_ORDERING; if (debug.val) LOG.debug( String.format( "Partition %d :: txnId[%d] > lastSafeTxnId[%d]", this.partitionId, txnId, this.lastSafeTxnId)); } // If our current block time is negative, then we know that we're the first txnId // that's been in the system. We'll also want to wait a bit before we're // allowed to be executed. else if (this.blockTimestamp == NULL_BLOCK_TIMESTAMP) { newState = QueueState.BLOCKED_SAFETY; if (debug.val) LOG.debug( String.format( "Partition %d :: txnId[%d] ==> %s (blockTime=%d)", this.partitionId, txnId, newState, this.blockTimestamp)); } // Check whether it's safe to unblock this mofo else if ((currentTimestamp = System.currentTimeMillis()) < this.blockTimestamp) { newState = QueueState.BLOCKED_SAFETY; if (debug.val) LOG.debug( String.format( "Partition %d :: txnId[%d] ==> %s (blockTime[%d] - current[%d] = %d)", this.partitionId, txnId, newState, this.blockTimestamp, currentTimestamp, Math.max(0, this.blockTimestamp - currentTimestamp))); } // We didn't find any reason to block this txn, so it's sail yo for it... else if (debug.val) { LOG.debug( String.format( "Partition %d :: Safe to Execute %d [currentTime=%d]", this.partitionId, txnId, System.currentTimeMillis())); } } if (newState != this.state) { // note if we get non-empty but blocked if ((newState == QueueState.BLOCKED_ORDERING) || (newState == QueueState.BLOCKED_SAFETY)) { if (trace.val) LOG.trace( String.format("Partition %d :: NewState=%s --> %s", this.partitionId, newState, ts)); long txnTimestamp = TransactionIdManager.getTimestampFromTransactionId(txnId.longValue()); if (currentTimestamp == -1) currentTimestamp = System.currentTimeMillis(); // Calculate how long we need to wait before this txn is safe to run // If we're blocking on "safety", then we can use an offset based // on when the txnId was created. If we're blocking for "ordering", // then we'll want to wait for the full wait time. int waitTime = this.waitTime; if (newState == QueueState.BLOCKED_SAFETY) { waitTime = (int) Math.max(0, this.waitTime - (currentTimestamp - txnTimestamp)); } this.blockTimestamp = currentTimestamp + waitTime; if (trace.val) LOG.trace( String.format( "Partition %d :: SET blockTimestamp = %d --> %s", this.partitionId, this.blockTimestamp, ts)); if (this.blockTimestamp <= currentTimestamp) { newState = QueueState.UNBLOCKED; } if (this.profiler != null && this.lastSafeTxnId.equals(txnId) == false) this.profiler.waitTimes.put(newState == QueueState.UNBLOCKED ? 0 : waitTime); if (debug.val) LOG.debug( String.format( "Partition %d :: SET lastSafeTxnId = %d --> %s", this.partitionId, this.lastSafeTxnId, ts)); if (trace.val) { LOG.trace( String.format( "Partition %d :: SET lastSafeTxnId = %d --> %s", this.partitionId, this.lastSafeTxnId, ts)); String debug = ""; if (trace.val) { Map<String, Object> m = new LinkedHashMap<String, Object>(); m.put("Txn Init Timestamp", txnTimestamp); m.put("Current Timestamp", currentTimestamp); m.put("Block Time Remaining", (this.blockTimestamp - currentTimestamp)); debug = "\n" + StringUtil.formatMaps(m); } LOG.trace( String.format( "Partition %d :: Blocking %s for %d ms " + "[maxWait=%d, origState=%s, newState=%s]\n%s%s", this.partitionId, ts, (this.blockTimestamp - currentTimestamp), this.waitTime, this.state, newState, this.debug(), debug)); } } else if (newState == QueueState.UNBLOCKED) { if (currentTimestamp == -1) currentTimestamp = System.currentTimeMillis(); if (this.blockTimestamp > currentTimestamp) { newState = QueueState.BLOCKED_SAFETY; } } } // IF // This txn should always becomes our next safeTxnId. // This is essentially the next txn // that should be executed, but somebody *could* come along and add in // a new txn with a lower id. But that's ok because we've synchronized setting // the id up above. This is actually probably the only part of this entire method // that needs to be protected... if (txnId != null) this.lastSafeTxnId = txnId; // Set the new state if (newState != this.state) { if (trace.val) LOG.trace( String.format( "Partition %d :: ORIG[%s]->NEW[%s] / LastSafeTxn:%d", this.partitionId, this.state, newState, this.lastSafeTxnId)); if (this.profiler != null) { this.profiler.queueStates.get(this.state).stopIfStarted(); this.profiler.queueStates.get(newState).start(); } this.state = newState; // Always poke anybody that is blocking on this queue. // The txn may not be ready to run just yet, but at least they'll be // able to recompute a new sleep time. this.isReady.signal(); } else if (this.profiler != null) { this.profiler.queueStates.get(this.state).restart(); } // Sanity Check if ((this.state == QueueState.BLOCKED_ORDERING) || (this.state == QueueState.BLOCKED_SAFETY)) { assert (this.state != QueueState.BLOCKED_EMPTY); } // Make sure that we're always in a valid state to avoid livelock problems assert (this.state != QueueState.BLOCKED_SAFETY || (this.state == QueueState.BLOCKED_SAFETY && this.blockTimestamp != NULL_BLOCK_TIMESTAMP)) : String.format("Invalid state %s with NULL blocked timestamp", this.state); assert (this.state != QueueState.BLOCKED_ORDERING || (this.state == QueueState.BLOCKED_ORDERING && this.blockTimestamp != NULL_BLOCK_TIMESTAMP)) : String.format("Invalid state %s with NULL blocked timestamp", this.state); return this.state; }
public TPCCSimulation( TPCCSimulation.ProcCaller client, RandomGenerator generator, Clock clock, ScaleParameters parameters, TPCCConfig config, double skewFactor, Catalog catalog) { assert parameters != null; this.client = client; this.generator = generator; this.clock = clock; this.parameters = parameters; this.affineWarehouse = lastAssignedWarehouseId; this.skewFactor = skewFactor; this.config = config; if (config.neworder_skew_warehouse) { if (debug.val) LOG.debug("Enabling W_ID Zipfian Skew: " + skewFactor); this.zipf = new RandomDistribution.Zipf( new Random(), parameters.starting_warehouse, parameters.last_warehouse + 1, Math.max(1.001d, this.skewFactor)); this.custom_skew = new RandomDistribution.HotWarmCold( new Random(), parameters.starting_warehouse + 1, parameters.last_warehouse, TPCCConstants.HOT_DATA_WORKLOAD_SKEW, TPCCConstants.HOT_DATA_SIZE, TPCCConstants.WARM_DATA_WORKLOAD_SKEW, TPCCConstants.WARM_DATA_SIZE); } if (config.warehouse_debug) { LOG.info("Enabling WAREHOUSE debug mode"); } lastAssignedWarehouseId += 1; if (lastAssignedWarehouseId > parameters.last_warehouse) lastAssignedWarehouseId = 1; if (debug.val) { LOG.debug(this.toString()); } if (config.neworder_multip_remote) { synchronized (TPCCSimulation.class) { if (remoteWarehouseIds == null) { remoteWarehouseIds = new HashMap<Integer, List<Integer>>(); HashMap<Integer, Integer> partitionToSite = new HashMap<Integer, Integer>(); Database catalog_db = CatalogUtil.getDatabase(catalog); DefaultHasher hasher = new DefaultHasher(catalog_db); for (Site s : CatalogUtil.getCluster(catalog_db).getSites()) { for (Partition p : s.getPartitions()) partitionToSite.put(p.getId(), s.getId()); } // FOR for (int w_id0 = parameters.starting_warehouse; w_id0 <= parameters.last_warehouse; w_id0++) { final int partition0 = hasher.hash(w_id0); final int site0 = partitionToSite.get(partition0); final List<Integer> rList = new ArrayList<Integer>(); for (int w_id1 = parameters.starting_warehouse; w_id1 <= parameters.last_warehouse; w_id1++) { // Figure out what partition this W_ID maps to int partition1 = hasher.hash(w_id1); // Check whether this partition is on our same local site int site1 = partitionToSite.get(partition1); if (site0 != site1) rList.add(w_id1); } // FOR remoteWarehouseIds.put(w_id0, rList); } // FOR LOG.debug("NewOrder Remote W_ID Mapping\n" + StringUtil.formatMaps(remoteWarehouseIds)); } } // SYNCH } }
public static MaterializedViewInfo addVerticalPartition( final Table catalog_tbl, final Collection<Column> catalog_cols, final boolean createIndex) throws Exception { assert (catalog_cols.isEmpty() == false); Database catalog_db = ((Database) catalog_tbl.getParent()); String viewName = getNextVerticalPartitionName(catalog_tbl, catalog_cols); if (debug.get()) LOG.debug( String.format( "Adding Vertical Partition %s for %s: %s", viewName, catalog_tbl, catalog_cols)); // Create a new virtual table Table virtual_tbl = catalog_db.getTables().get(viewName); if (virtual_tbl == null) { virtual_tbl = catalog_db.getTables().add(viewName); } virtual_tbl.setIsreplicated(true); virtual_tbl.setMaterializer(catalog_tbl); virtual_tbl.setSystable(true); virtual_tbl.getColumns().clear(); // Create MaterializedView and link it to the virtual table MaterializedViewInfo catalog_view = catalog_tbl.getViews().add(viewName); catalog_view.setVerticalpartition(true); catalog_view.setDest(virtual_tbl); List<Column> indexColumns = new ArrayList<Column>(); Column partition_col = catalog_tbl.getPartitioncolumn(); if (partition_col instanceof VerticalPartitionColumn) { partition_col = ((VerticalPartitionColumn) partition_col).getHorizontalColumn(); } if (debug.get()) LOG.debug(catalog_tbl.getName() + " Partition Column: " + partition_col); int i = 0; assert (catalog_cols != null); assert (catalog_cols.isEmpty() == false) : "No vertical partitioning columns for " + catalog_view.fullName(); for (Column catalog_col : catalog_cols) { // MaterializedView ColumnRef ColumnRef catalog_ref = catalog_view.getGroupbycols().add(catalog_col.getName()); catalog_ref.setColumn(catalog_col); catalog_ref.setIndex(i++); // VirtualTable Column Column virtual_col = virtual_tbl.getColumns().add(catalog_col.getName()); virtual_col.setDefaulttype(catalog_col.getDefaulttype()); virtual_col.setDefaultvalue(catalog_col.getDefaultvalue()); virtual_col.setIndex(catalog_col.getIndex()); virtual_col.setNullable(catalog_col.getNullable()); virtual_col.setSize(catalog_col.getSize()); virtual_col.setType(catalog_col.getType()); if (debug.get()) LOG.debug(String.format("Added VerticalPartition column %s", virtual_col.fullName())); // If they want an index, then we'll make one based on every column except for the column // that the table is partitioned on if (createIndex) { boolean include = true; if (partition_col instanceof MultiColumn) { include = (((MultiColumn) partition_col).contains(catalog_col) == false); } else if (catalog_col.equals(partition_col)) { include = false; } if (include) indexColumns.add(virtual_col); } } // FOR if (createIndex) { if (indexColumns.isEmpty()) { Map<String, Object> m = new ListOrderedMap<String, Object>(); m.put("Partition Column", partition_col); m.put("VP Table Columns", virtual_tbl.getColumns()); m.put("Passed-in Columns", CatalogUtil.debug(catalog_cols)); LOG.error("Failed to find index columns\n" + StringUtil.formatMaps(m)); throw new Exception(String.format("No columns selected for index on %s", viewName)); } String idxName = "SYS_IDX_" + viewName; Index virtual_idx = virtual_tbl.getIndexes().get(idxName); if (virtual_idx == null) { virtual_idx = virtual_tbl.getIndexes().add(idxName); } virtual_idx.getColumns().clear(); IndexType idxType = (indexColumns.size() == 1 ? IndexType.HASH_TABLE : IndexType.BALANCED_TREE); virtual_idx.setType(idxType.getValue()); i = 0; for (Column catalog_col : indexColumns) { ColumnRef cref = virtual_idx.getColumns().add(catalog_col.getTypeName()); cref.setColumn(catalog_col); cref.setIndex(i++); } // FOR if (debug.get()) LOG.debug( String.format( "Created %s index '%s' for vertical partition '%s'", idxType, idxName, viewName)); } return (catalog_view); }
/** * MAIN! * * @param vargs * @throws Exception */ public static void main(String[] vargs) throws Exception { ArgumentsParser args = ArgumentsParser.load(vargs); args.require( ArgumentsParser.PARAM_CATALOG, ArgumentsParser.PARAM_WORKLOAD, ArgumentsParser.PARAM_PARTITION_PLAN, ArgumentsParser.PARAM_DESIGNER_INTERVALS // ArgumentsParser.PARAM_DESIGNER_HINTS ); assert (args.workload.getTransactionCount() > 0) : "No transactions were loaded from " + args.workload; if (args.hasParam(ArgumentsParser.PARAM_CATALOG_HOSTS)) { ClusterConfiguration cc = new ClusterConfiguration(args.getParam(ArgumentsParser.PARAM_CATALOG_HOSTS)); args.updateCatalog(FixCatalog.cloneCatalog(args.catalog, cc), null); } // If given a PartitionPlan, then update the catalog File pplan_path = new File(args.getParam(ArgumentsParser.PARAM_PARTITION_PLAN)); if (pplan_path.exists()) { PartitionPlan pplan = new PartitionPlan(); pplan.load(pplan_path, args.catalog_db); if (args.getBooleanParam(ArgumentsParser.PARAM_PARTITION_PLAN_REMOVE_PROCS, false)) { for (Procedure catalog_proc : pplan.proc_entries.keySet()) { pplan.setNullProcParameter(catalog_proc); } // FOR } if (args.getBooleanParam(ArgumentsParser.PARAM_PARTITION_PLAN_RANDOM_PROCS, false)) { for (Procedure catalog_proc : pplan.proc_entries.keySet()) { pplan.setRandomProcParameter(catalog_proc); } // FOR } pplan.apply(args.catalog_db); System.out.println("Applied PartitionPlan '" + pplan_path + "' to catalog\n" + pplan); System.out.print(StringUtil.DOUBLE_LINE); if (args.hasParam(ArgumentsParser.PARAM_PARTITION_PLAN_OUTPUT)) { String output = args.getParam(ArgumentsParser.PARAM_PARTITION_PLAN_OUTPUT); if (output.equals("-")) output = pplan_path.getAbsolutePath(); pplan.save(new File(output)); System.out.println("Saved PartitionPlan to '" + output + "'"); } } else { System.err.println("PartitionPlan file '" + pplan_path + "' does not exist. Ignoring..."); } System.out.flush(); int num_intervals = args.num_intervals; // getIntParam(ArgumentsParser.PARAM_DESIGNER_INTERVALS); TimeIntervalCostModel<SingleSitedCostModel> costmodel = new TimeIntervalCostModel<SingleSitedCostModel>( args.catalogContext, SingleSitedCostModel.class, num_intervals); if (args.hasParam(ArgumentsParser.PARAM_DESIGNER_HINTS)) costmodel.applyDesignerHints(args.designer_hints); double cost = costmodel.estimateWorkloadCost(args.catalogContext, args.workload); Map<String, Object> m = new LinkedHashMap<String, Object>(); m.put("PARTITIONS", CatalogUtil.getNumberOfPartitions(args.catalog_db)); m.put("INTERVALS", args.num_intervals); m.put("EXEC COST", costmodel.last_execution_cost); m.put("SKEW COST", costmodel.last_skew_cost); m.put("TOTAL COST", cost); m.put("PARTITIONS TOUCHED", costmodel.getTxnPartitionAccessHistogram().getSampleCount()); System.out.println(StringUtil.formatMaps(m)); // long total = 0; m.clear(); // for (int i = 0; i < num_intervals; i++) { // SingleSitedCostModel cm = costmodel.getCostModel(i); // Histogram<Integer> h = cm.getTxnPartitionAccessHistogram(); // m.put(String.format("Interval %02d", i), // cm.getTxnPartitionAccessHistogram()); // total += h.getSampleCount(); // h.setKeepZeroEntries(true); // for (Integer partition : // CatalogUtil.getAllPartitionIds(args.catalog_db)) { // if (h.contains(partition) == false) h.put(partition, 0); // } // System.out.println(StringUtil.box("Interval #" + i, "+", 100) + "\n" // + h); // System.out.println(); // } // FOR // System.out.println(StringUtil.formatMaps(m)); // System.err.println("TOTAL: " + total); }
@Override public void process(Pair<TransactionTrace, Integer> p) { assert (p != null); final TransactionTrace txn_trace = p.getFirst(); final int i = p.getSecond(); // Interval final int txn_weight = (use_txn_weights ? txn_trace.getWeight() : 1); final String proc_key = CatalogKey.createKey(CatalogUtil.DEFAULT_DATABASE_NAME, txn_trace.getCatalogItemName()); // Terrible Hack: Assume that we are using the SingleSitedCostModel // and that // it will return fixed values based on whether the txn is // single-partitioned or not SingleSitedCostModel singlesited_cost_model = (SingleSitedCostModel) cost_models[i]; total_interval_txns[i] += txn_weight; total_interval_queries[i] += (txn_trace.getQueryCount() * txn_weight); histogram_procs.put(proc_key, txn_weight); try { singlesited_cost_model.estimateTransactionCost(catalogContext, workload, filter, txn_trace); TransactionCacheEntry txn_entry = singlesited_cost_model.getTransactionCacheEntry(txn_trace); assert (txn_entry != null) : "No txn entry for " + txn_trace; Collection<Integer> partitions = txn_entry.getTouchedPartitions(); // If the txn runs on only one partition, then the cost is // nothing if (txn_entry.isSinglePartitioned()) { singlepartition_ctrs[i] += txn_weight; if (!partitions.isEmpty()) { assert (txn_entry.getAllTouchedPartitionsHistogram().getValueCount() == 1) : txn_entry + " says it was single-partitioned but the partition count says otherwise:\n" + txn_entry.debug(); singlepartition_with_partitions_ctrs[i] += txn_weight; } histogram_sp_procs.put(proc_key, txn_weight); // If the txn runs on multiple partitions, then the cost // is... // XXX 2010-06-28: The number of partitions that the txn // touches divided by the total number of partitions // XXX 2010-07-02: The histogram for the total number of // partitions touched by all of the queries // in the transaction. This ensures that txns with just one // multi-partition query // isn't weighted the same as a txn with many // multi-partition queries } else { assert (!partitions.isEmpty()) : "No touched partitions for " + txn_trace; if (partitions.size() == 1 && txn_entry.getExecutionPartition() != HStoreConstants.NULL_PARTITION_ID) { assert (CollectionUtil.first(partitions) != txn_entry.getExecutionPartition()) : txn_entry.debug(); exec_mismatch_ctrs[i] += txn_weight; partitions_touched[i] += txn_weight; } else { assert (partitions.size() > 1) : String.format( "%s is not marked as single-partition but it only touches one partition\n%s", txn_trace, txn_entry.debug()); } partitions_touched[i] += (partitions.size() * txn_weight); // Txns multipartition_ctrs[i] += txn_weight; histogram_mp_procs.put(proc_key, txn_weight); } Integer base_partition = txn_entry.getExecutionPartition(); if (base_partition != null) { exec_histogram[i].put(base_partition, txn_weight); } else { exec_histogram[i].put(all_partitions, txn_weight); } if (debug.val) { // && // txn_trace.getCatalogItemName().equalsIgnoreCase("DeleteCallForwarding")) // { Procedure catalog_proc = txn_trace.getCatalogItem(catalogContext.database); Map<String, Object> inner = new LinkedHashMap<String, Object>(); for (Statement catalog_stmt : catalog_proc.getStatements()) { inner.put(catalog_stmt.fullName(), CatalogUtil.getReferencedTables(catalog_stmt)); } Map<String, Object> m = new LinkedHashMap<String, Object>(); m.put(txn_trace.toString(), null); m.put("Interval", i); m.put("Single-Partition", txn_entry.isSinglePartitioned()); m.put("Base Partition", base_partition); m.put("Touched Partitions", partitions); m.put(catalog_proc.fullName(), inner); LOG.debug(StringUtil.formatMaps(m)); } // We need to keep a count of the number txns that didn't have // all of its queries estimated // completely so that we can update the access histograms down // below for entropy calculations // Note that this is at the txn level, not the query level. if (!txn_entry.isComplete()) { incomplete_txn_ctrs[i] += txn_weight; tmp_missingPartitions.clear(); tmp_missingPartitions.addAll(all_partitions); tmp_missingPartitions.removeAll(txn_entry.getTouchedPartitions()); // Update the histogram for this interval to keep track of // how many times we need to // increase the partition access histogram incomplete_txn_histogram[i].put(tmp_missingPartitions, txn_weight); if (trace.val) { Map<String, Object> m = new LinkedHashMap<String, Object>(); m.put(String.format("Marking %s as incomplete in interval #%d", txn_trace, i), null); m.put("Examined Queries", txn_entry.getExaminedQueryCount()); m.put("Total Queries", txn_entry.getTotalQueryCount()); m.put("Touched Partitions", txn_entry.getTouchedPartitions()); m.put("Missing Partitions", tmp_missingPartitions); LOG.trace(StringUtil.formatMaps(m)); } } } catch (Exception ex) { CatalogUtil.saveCatalog(catalogContext.catalog, CatalogUtil.CATALOG_FILENAME); throw new RuntimeException( "Failed to estimate cost for " + txn_trace.getCatalogItemName() + " at interval " + i, ex); } }
@Override protected double estimateWorkloadCostImpl( final CatalogContext catalogContext, final Workload workload, final Filter filter, final Double upper_bound) throws Exception { if (debug.val) LOG.debug( "Calculating workload execution cost across " + num_intervals + " intervals for " + num_partitions + " partitions"); // (1) Grab the costs at the different time intervals // Also create the ratios that we will use to weight the interval costs final AtomicLong total_txns = new AtomicLong(0); // final HashSet<Long> trace_ids[] = new HashSet[num_intervals]; for (int i = 0; i < num_intervals; i++) { total_interval_txns[i] = 0; total_interval_queries[i] = 0; singlepartition_ctrs[i] = 0; singlepartition_with_partitions_ctrs[i] = 0; multipartition_ctrs[i] = 0; partitions_touched[i] = 0; incomplete_txn_ctrs[i] = 0; exec_mismatch_ctrs[i] = 0; incomplete_txn_histogram[i].clear(); missing_txn_histogram[i].clear(); exec_histogram[i].clear(); } // FOR // (2) Now go through the workload and estimate the partitions that each txn // will touch for the given catalog setups if (trace.val) { LOG.trace("Total # of Txns in Workload: " + workload.getTransactionCount()); if (filter != null) LOG.trace( "Workload Filter Chain: " + StringUtil.join(" ", "\n", filter.getFilters())); } // QUEUING THREAD tmp_consumers.clear(); Producer<TransactionTrace, Pair<TransactionTrace, Integer>> producer = new Producer<TransactionTrace, Pair<TransactionTrace, Integer>>( CollectionUtil.iterable(workload.iterator(filter))) { @Override public Pair<Consumer<Pair<TransactionTrace, Integer>>, Pair<TransactionTrace, Integer>> transform(TransactionTrace txn_trace) { int i = workload.getTimeInterval(txn_trace, num_intervals); assert (i >= 0) : "Invalid time interval '" + i + "'\n" + txn_trace.debug(catalogContext.database); assert (i < num_intervals) : "Invalid interval: " + i + "\n" + txn_trace.debug(catalogContext.database); total_txns.incrementAndGet(); Pair<TransactionTrace, Integer> p = Pair.of(txn_trace, i); return (Pair.of(tmp_consumers.get(i), p)); } }; // PROCESSING THREADS final int num_threads = ThreadUtil.getMaxGlobalThreads(); int interval_ctr = 0; for (int thread = 0; thread < num_threads; thread++) { // First create a new IntervalProcessor/Consumer IntervalProcessor ip = new IntervalProcessor(catalogContext, workload, filter); // Then assign it to some number of intervals for (int i = 0, cnt = (int) Math.ceil(num_intervals / (double) num_threads); i < cnt; i++) { if (interval_ctr > num_intervals) break; tmp_consumers.put(interval_ctr++, ip); if (trace.val) LOG.trace( String.format("Interval #%02d => IntervalProcessor #%02d", interval_ctr - 1, thread)); } // FOR // And make sure that we queue it up too producer.addConsumer(ip); } // FOR (threads) ThreadUtil.runGlobalPool(producer.getRunnablesList()); // BLOCKING if (debug.val) { int processed = 0; for (Consumer<?> c : producer.getConsumers()) { processed += c.getProcessedCounter(); } // FOR assert (total_txns.get() == processed) : String.format("Expected[%d] != Processed[%d]", total_txns.get(), processed); } // We have to convert all of the costs into the range of [0.0, 1.0] // For each interval, divide the number of partitions touched by the total number // of partitions that the interval could have touched (worst case scenario) final double execution_costs[] = new double[num_intervals]; StringBuilder sb = (this.isDebugEnabled() || debug.get() ? new StringBuilder() : null); Map<String, Object> debug_m = null; if (sb != null) { debug_m = new LinkedHashMap<String, Object>(); } if (debug.val) LOG.debug("Calculating execution cost for " + this.num_intervals + " intervals..."); long total_multipartition_txns = 0; for (int i = 0; i < this.num_intervals; i++) { interval_weights[i] = total_interval_txns[i] / (double) total_txns.get(); long total_txns_in_interval = (long) total_interval_txns[i]; long total_queries_in_interval = (long) total_interval_queries[i]; long num_txns = this.cost_models[i].txn_ctr.get(); long potential_txn_touches = (total_txns_in_interval * num_partitions); // TXNS double penalty = 0.0d; total_multipartition_txns += multipartition_ctrs[i]; // Divide the total number of partitions touched by... // This is the total number of partitions that we could have touched // in this interval // And this is the total number of partitions that we did actually touch if (multipartition_ctrs[i] > 0) { assert (partitions_touched[i] > 0) : "No touched partitions for interval " + i; double cost = (partitions_touched[i] / (double) potential_txn_touches); if (this.use_multitpartition_penalty) { penalty = this.multipartition_penalty * (1.0d + (multipartition_ctrs[i] / (double) total_txns_in_interval)); assert (penalty >= 1.0) : "The multipartition penalty is less than one: " + penalty; cost *= penalty; } execution_costs[i] = Math.min(cost, (double) potential_txn_touches); } // For each txn that wasn't even evaluated, add all of the // partitions to the incomplete histogram if (num_txns < total_txns_in_interval) { if (trace.val) LOG.trace( "Adding " + (total_txns_in_interval - num_txns) + " entries to the incomplete histogram for interval #" + i); for (long ii = num_txns; ii < total_txns_in_interval; ii++) { missing_txn_histogram[i].put(all_partitions); } // WHILE } if (sb != null) { tmp_penalties.add(penalty); tmp_total.add(total_txns_in_interval); tmp_touched.add(partitions_touched[i]); tmp_potential.add(potential_txn_touches); Map<String, Object> inner = new LinkedHashMap<String, Object>(); inner.put("Partitions Touched", partitions_touched[i]); inner.put("Potential Touched", potential_txn_touches); inner.put("Multi-Partition Txns", multipartition_ctrs[i]); inner.put("Total Txns", total_txns_in_interval); inner.put("Total Queries", total_queries_in_interval); inner.put("Missing Txns", (total_txns_in_interval - num_txns)); inner.put("Cost", String.format("%.05f", execution_costs[i])); inner.put("Exec Txns", exec_histogram[i].getSampleCount()); debug_m.put("Interval #" + i, inner); } } // FOR if (sb != null) { Map<String, Object> m0 = new LinkedHashMap<String, Object>(); m0.put("SinglePartition Txns", (total_txns.get() - total_multipartition_txns)); m0.put("MultiPartition Txns", total_multipartition_txns); m0.put( "Total Txns", String.format( "%d [%.06f]", total_txns.get(), (1.0d - (total_multipartition_txns / (double) total_txns.get())))); Map<String, Object> m1 = new LinkedHashMap<String, Object>(); m1.put("Touched Partitions", tmp_touched); m1.put("Potential Partitions", tmp_potential); m1.put("Total Partitions", tmp_total); m1.put("Penalties", tmp_penalties); sb.append(StringUtil.formatMaps(debug_m, m0, m1)); if (debug.val) LOG.debug("**** Execution Cost ****\n" + sb); this.appendDebugMessage(sb); } // LOG.debug("Execution By Intervals:\n" + sb.toString()); // (3) We then need to go through and grab the histograms of partitions were accessed if (sb != null) { if (debug.val) LOG.debug("Calculating skew factor for " + this.num_intervals + " intervals..."); debug_histograms.clear(); sb = new StringBuilder(); } for (int i = 0; i < this.num_intervals; i++) { ObjectHistogram<Integer> histogram_txn = this.cost_models[i].getTxnPartitionAccessHistogram(); ObjectHistogram<Integer> histogram_query = this.cost_models[i].getQueryPartitionAccessHistogram(); this.histogram_query_partitions.put(histogram_query); long num_queries = this.cost_models[i].query_ctr.get(); this.query_ctr.addAndGet(num_queries); // DEBUG SingleSitedCostModel inner_costModel = (SingleSitedCostModel) this.cost_models[i]; boolean is_valid = (partitions_touched[i] + singlepartition_with_partitions_ctrs[i]) == (this.cost_models[i].getTxnPartitionAccessHistogram().getSampleCount() + exec_mismatch_ctrs[i]); if (!is_valid) { LOG.error("Transaction Entries: " + inner_costModel.getTransactionCacheEntries().size()); ObjectHistogram<Integer> check = new ObjectHistogram<Integer>(); for (TransactionCacheEntry tce : inner_costModel.getTransactionCacheEntries()) { check.put(tce.getTouchedPartitions()); // LOG.error(tce.debug() + "\n"); } LOG.error( "Check Touched Partitions: sample=" + check.getSampleCount() + ", values=" + check.getValueCount()); LOG.error( "Cache Touched Partitions: sample=" + this.cost_models[i].getTxnPartitionAccessHistogram().getSampleCount() + ", values=" + this.cost_models[i].getTxnPartitionAccessHistogram().getValueCount()); int qtotal = inner_costModel.getAllQueryCacheEntries().size(); int ctr = 0; int multip = 0; for (QueryCacheEntry qce : inner_costModel.getAllQueryCacheEntries()) { ctr += (qce.getAllPartitions().isEmpty() ? 0 : 1); multip += (qce.getAllPartitions().size() > 1 ? 1 : 0); } // FOR LOG.error("# of QueryCacheEntries with Touched Partitions: " + ctr + " / " + qtotal); LOG.error("# of MultiP QueryCacheEntries: " + multip); } assert (is_valid) : String.format( "Partitions Touched by Txns Mismatch in Interval #%d\n" + "(partitions_touched[%d] + singlepartition_with_partitions_ctrs[%d]) != " + "(histogram_txn[%d] + exec_mismatch_ctrs[%d])", i, partitions_touched[i], singlepartition_with_partitions_ctrs[i], this.cost_models[i].getTxnPartitionAccessHistogram().getSampleCount(), exec_mismatch_ctrs[i]); this.histogram_java_partitions.put(this.cost_models[i].getJavaExecutionHistogram()); this.histogram_txn_partitions.put(histogram_txn); long num_txns = this.cost_models[i].txn_ctr.get(); assert (num_txns >= 0) : "The transaction counter at interval #" + i + " is " + num_txns; this.txn_ctr.addAndGet(num_txns); // Calculate the skew factor at this time interval // XXX: Should the number of txns be the total number of unique txns // that were executed or the total number of times a txn touched the partitions? // XXX: What do we do when the number of elements that we are examining is zero? // I guess the cost just needs to be zero? // XXX: What histogram do we want to use? target_histogram.clear(); target_histogram.put(histogram_txn); // For each txn that we haven't gotten an estimate for at this interval, // we're going mark it as being broadcast to all partitions. That way the access // histogram will look uniform. Then as more information is added, we will // This is an attempt to make sure that the skew cost never decreases but only increases long total_txns_in_interval = (long) total_interval_txns[i]; if (sb != null) { debug_histograms.put("Incomplete Txns", incomplete_txn_histogram[i]); debug_histograms.put("Missing Txns", missing_txn_histogram[i]); debug_histograms.put( "Target Partitions (BEFORE)", new ObjectHistogram<Integer>(target_histogram)); debug_histograms.put("Target Partitions (AFTER)", target_histogram); } // Merge the values from incomplete histogram into the target // histogram target_histogram.put(incomplete_txn_histogram[i]); target_histogram.put(missing_txn_histogram[i]); exec_histogram[i].put(missing_txn_histogram[i]); long num_elements = target_histogram.getSampleCount(); // The number of partition touches should never be greater than our // potential touches assert (num_elements <= (total_txns_in_interval * num_partitions)) : "New Partitions Touched Sample Count [" + num_elements + "] < " + "Maximum Potential Touched Count [" + (total_txns_in_interval * num_partitions) + "]"; if (sb != null) { Map<String, Object> m = new LinkedHashMap<String, Object>(); for (String key : debug_histograms.keySet()) { ObjectHistogram<?> h = debug_histograms.get(key); m.put( key, String.format("[Sample=%d, Value=%d]\n%s", h.getSampleCount(), h.getValueCount(), h)); } // FOR sb.append( String.format( "INTERVAL #%d [total_txns_in_interval=%d, num_txns=%d, incomplete_txns=%d]\n%s", i, total_txns_in_interval, num_txns, incomplete_txn_ctrs[i], StringUtil.formatMaps(m))); } // Txn Skew if (num_elements == 0) { txn_skews[i] = 0.0d; } else { txn_skews[i] = SkewFactorUtil.calculateSkew(num_partitions, num_elements, target_histogram); } // Exec Skew if (exec_histogram[i].getSampleCount() == 0) { exec_skews[i] = 0.0d; } else { exec_skews[i] = SkewFactorUtil.calculateSkew( num_partitions, exec_histogram[i].getSampleCount(), exec_histogram[i]); } total_skews[i] = (0.5 * exec_skews[i]) + (0.5 * txn_skews[i]); if (sb != null) { sb.append("Txn Skew = " + MathUtil.roundToDecimals(txn_skews[i], 6) + "\n"); sb.append("Exec Skew = " + MathUtil.roundToDecimals(exec_skews[i], 6) + "\n"); sb.append("Total Skew = " + MathUtil.roundToDecimals(total_skews[i], 6) + "\n"); sb.append(StringUtil.DOUBLE_LINE); } } // FOR if (sb != null && sb.length() > 0) { if (debug.val) LOG.debug("**** Skew Factor ****\n" + sb); this.appendDebugMessage(sb); } if (trace.val) { for (int i = 0; i < num_intervals; i++) { LOG.trace( "Time Interval #" + i + "\n" + "Total # of Txns: " + this.cost_models[i].txn_ctr.get() + "\n" + "Multi-Partition Txns: " + multipartition_ctrs[i] + "\n" + "Execution Cost: " + execution_costs[i] + "\n" + "ProcHistogram:\n" + this.cost_models[i].getProcedureHistogram().toString() + "\n" + // "TransactionsPerPartitionHistogram:\n" + // this.cost_models[i].getTxnPartitionAccessHistogram() // + "\n" + StringUtil.SINGLE_LINE); } } // (3) We can now calculate the final total estimate cost of this workload as the following // Just take the simple ratio of mp txns / all txns this.last_execution_cost = MathUtil.weightedMean( execution_costs, total_interval_txns); // MathUtil.roundToDecimals(MathUtil.geometricMean(execution_costs, // MathUtil.GEOMETRIC_MEAN_ZERO), // 10); // The final skew cost needs to be weighted by the percentage of txns running in that interval // This will cause the partitions with few txns this.last_skew_cost = MathUtil.weightedMean( total_skews, total_interval_txns); // roundToDecimals(MathUtil.geometricMean(entropies, // MathUtil.GEOMETRIC_MEAN_ZERO), // 10); double new_final_cost = (this.use_execution ? (this.execution_weight * this.last_execution_cost) : 0) + (this.use_skew ? (this.skew_weight * this.last_skew_cost) : 0); if (sb != null) { Map<String, Object> m = new LinkedHashMap<String, Object>(); m.put("Total Txns", total_txns.get()); m.put("Interval Txns", Arrays.toString(total_interval_txns)); m.put("Execution Costs", Arrays.toString(execution_costs)); m.put("Skew Factors", Arrays.toString(total_skews)); m.put("Txn Skew", Arrays.toString(txn_skews)); m.put("Exec Skew", Arrays.toString(exec_skews)); m.put("Interval Weights", Arrays.toString(interval_weights)); m.put( "Final Cost", String.format( "%f = %f + %f", new_final_cost, this.last_execution_cost, this.last_skew_cost)); if (debug.val) LOG.debug(StringUtil.formatMaps(m)); this.appendDebugMessage(StringUtil.formatMaps(m)); } this.last_final_cost = new_final_cost; return (MathUtil.roundToDecimals(this.last_final_cost, 5)); }