@Override public String toString() { return "MultiPartitionParticipantTxnState initiator: " + initiatorHSId + " coordinator: " + m_isCoordinator + " in-progress: " + m_hasStartedWork + " txnId: " + TransactionIdManager.toString(txnId); }
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; }
/** * 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; }
@SuppressWarnings("unchecked") public Catalog compileCatalog(final String projectFileURL, final ClusterConfig clusterConfig) { if (!clusterConfig.validate()) { addErr(clusterConfig.getErrorMsg()); return null; } // Compiler instance is reusable. Clear the cache. cachedAddedClasses.clear(); m_currentFilename = new File(projectFileURL).getName(); m_jarBuilder = new JarBuilder(this); if (m_outputStream != null) { m_outputStream.println("\n** BEGIN PROJECT COMPILE: " + m_currentFilename + " **"); } ProjectType project = null; try { JAXBContext jc = JAXBContext.newInstance("org.voltdb.compiler.projectfile"); // This schema shot the sheriff. SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = sf.newSchema(this.getClass().getResource("ProjectFileSchema.xsd")); Unmarshaller unmarshaller = jc.createUnmarshaller(); // But did not shoot unmarshaller! unmarshaller.setSchema(schema); JAXBElement<ProjectType> result = (JAXBElement<ProjectType>) unmarshaller.unmarshal(new File(projectFileURL)); project = result.getValue(); } catch (JAXBException e) { // Convert some linked exceptions to more friendly errors. if (e.getLinkedException() instanceof java.io.FileNotFoundException) { addErr(e.getLinkedException().getMessage()); return null; } if (e.getLinkedException() instanceof org.xml.sax.SAXParseException) { addErr("Error schema validating project.xml file. " + e.getLinkedException().getMessage()); return null; } throw new RuntimeException(e); } catch (SAXException e) { addErr("Error schema validating project.xml file. " + e.getMessage()); return null; } try { compileXMLRootNode(project); } catch (final VoltCompilerException e) { // compilerLog.l7dlog( Level.ERROR, // LogKeys.compiler_VoltCompiler_FailedToCompileXML.name(), null); LOG.error(e.getMessage(), e); // e.printStackTrace(); return null; } assert (m_catalog != null); try { ClusterCompiler.compile(m_catalog, clusterConfig); } catch (RuntimeException e) { addErr(e.getMessage()); return null; } // Optimization: Vertical Partitioning if (m_enableVerticalPartitionOptimizations) { if (m_verticalPartitionPlanner == null) { m_verticalPartitionPlanner = new VerticalPartitionPlanner(CatalogUtil.getDatabase(m_catalog), true); } try { m_verticalPartitionPlanner.optimizeDatabase(); } catch (Exception ex) { LOG.warn("Unexpected error", ex); addErr("Failed to apply vertical partition optimizations"); } } // add epoch info to catalog final int epoch = (int) (TransactionIdManager.getEpoch() / 1000); m_catalog.getClusters().get("cluster").setLocalepoch(epoch); // done handling files m_currentFilename = null; return m_catalog; }