@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;
  }
Пример #3
0
  /**
   * 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;
  }
Пример #4
0
  @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;
  }