Пример #1
0
  /**
   * Human-readable String from context.
   *
   * @param context counter context.
   * @return a human-readable String of the context.
   */
  public String toString(ByteBuffer context) {
    ContextState state = ContextState.wrap(context);
    StringBuilder sb = new StringBuilder();
    sb.append("[");

    while (state.hasRemaining()) {
      if (state.getElementIndex() > 0) sb.append(",");
      sb.append("{");
      sb.append(state.getCounterId()).append(", ");
      sb.append(state.getClock()).append(", ");
      sb.append(state.getCount());
      sb.append("}");
      if (state.isGlobal()) sb.append("$");
      else if (state.isLocal()) sb.append("*");
      state.moveToNext();
    }

    sb.append("]");
    return sb.toString();
  }
Пример #2
0
  /*
   * Compares two shards, returns:
   * - GREATER_THAN if leftState overrides rightState
   * - LESS_THAN if rightState overrides leftState
   * - EQUAL for two equal, non-local, shards
   * - DISJOINT for any two local shards
   */
  private Relationship compare(ContextState leftState, ContextState rightState) {
    long leftClock = leftState.getClock();
    long leftCount = leftState.getCount();
    long rightClock = rightState.getClock();
    long rightCount = rightState.getCount();

    if (leftState.isGlobal() || rightState.isGlobal()) {
      if (leftState.isGlobal() && rightState.isGlobal()) {
        if (leftClock == rightClock) {
          // Can happen if an sstable gets lost and disk failure policy is set to 'best effort'
          if (leftCount != rightCount && CompactionManager.isCompactionManager.get()) {
            logger.warn(
                "invalid global counter shard detected; ({}, {}, {}) and ({}, {}, {}) differ only in "
                    + "count; will pick highest to self-heal on compaction",
                leftState.getCounterId(),
                leftClock,
                leftCount,
                rightState.getCounterId(),
                rightClock,
                rightCount);
          }

          if (leftCount > rightCount) return Relationship.GREATER_THAN;
          else if (leftCount == rightCount) return Relationship.EQUAL;
          else return Relationship.LESS_THAN;
        } else {
          return leftClock > rightClock ? Relationship.GREATER_THAN : Relationship.LESS_THAN;
        }
      } else // only one is global - keep that one
      {
        return leftState.isGlobal() ? Relationship.GREATER_THAN : Relationship.LESS_THAN;
      }
    }

    if (leftState.isLocal() || rightState.isLocal()) {
      // Local id and at least one is a local shard.
      if (leftState.isLocal() && rightState.isLocal()) return Relationship.DISJOINT;
      else // only one is local - keep that one
      return leftState.isLocal() ? Relationship.GREATER_THAN : Relationship.LESS_THAN;
    }

    // both are remote shards
    if (leftClock == rightClock) {
      // We should never see non-local shards w/ same id+clock but different counts. However, if we
      // do
      // we should "heal" the problem by being deterministic in our selection of shard - and
      // log the occurrence so that the operator will know something is wrong.
      if (leftCount != rightCount && CompactionManager.isCompactionManager.get()) {
        logger.warn(
            "invalid remote counter shard detected; ({}, {}, {}) and ({}, {}, {}) differ only in "
                + "count; will pick highest to self-heal on compaction",
            leftState.getCounterId(),
            leftClock,
            leftCount,
            rightState.getCounterId(),
            rightClock,
            rightCount);
      }

      if (leftCount > rightCount) return Relationship.GREATER_THAN;
      else if (leftCount == rightCount) return Relationship.EQUAL;
      else return Relationship.LESS_THAN;
    } else {
      if ((leftClock >= 0 && rightClock > 0 && leftClock >= rightClock)
          || (leftClock < 0 && (rightClock > 0 || leftClock < rightClock)))
        return Relationship.GREATER_THAN;
      else return Relationship.LESS_THAN;
    }
  }
Пример #3
0
  /**
   * Return a context w/ an aggregated count for each counter id.
   *
   * @param left counter context.
   * @param right counter context.
   */
  public ByteBuffer merge(ByteBuffer left, ByteBuffer right) {
    boolean leftIsSuperSet = true;
    boolean rightIsSuperSet = true;

    int globalCount = 0;
    int localCount = 0;
    int remoteCount = 0;

    ContextState leftState = ContextState.wrap(left);
    ContextState rightState = ContextState.wrap(right);

    while (leftState.hasRemaining() && rightState.hasRemaining()) {
      int cmp = leftState.compareIdTo(rightState);
      if (cmp == 0) {
        Relationship rel = compare(leftState, rightState);
        if (rel == Relationship.GREATER_THAN) rightIsSuperSet = false;
        else if (rel == Relationship.LESS_THAN) leftIsSuperSet = false;
        else if (rel == Relationship.DISJOINT) leftIsSuperSet = rightIsSuperSet = false;

        if (leftState.isGlobal() || rightState.isGlobal()) globalCount += 1;
        else if (leftState.isLocal() || rightState.isLocal()) localCount += 1;
        else remoteCount += 1;

        leftState.moveToNext();
        rightState.moveToNext();
      } else if (cmp > 0) {
        leftIsSuperSet = false;

        if (rightState.isGlobal()) globalCount += 1;
        else if (rightState.isLocal()) localCount += 1;
        else remoteCount += 1;

        rightState.moveToNext();
      } else // cmp < 0
      {
        rightIsSuperSet = false;

        if (leftState.isGlobal()) globalCount += 1;
        else if (leftState.isLocal()) localCount += 1;
        else remoteCount += 1;

        leftState.moveToNext();
      }
    }

    if (leftState.hasRemaining()) rightIsSuperSet = false;
    else if (rightState.hasRemaining()) leftIsSuperSet = false;

    // if one of the contexts is a superset, return it early.
    if (leftIsSuperSet) return left;
    else if (rightIsSuperSet) return right;

    while (leftState.hasRemaining()) {
      if (leftState.isGlobal()) globalCount += 1;
      else if (leftState.isLocal()) localCount += 1;
      else remoteCount += 1;

      leftState.moveToNext();
    }

    while (rightState.hasRemaining()) {
      if (rightState.isGlobal()) globalCount += 1;
      else if (rightState.isLocal()) localCount += 1;
      else remoteCount += 1;

      rightState.moveToNext();
    }

    leftState.reset();
    rightState.reset();

    return merge(
        ContextState.allocate(globalCount, localCount, remoteCount), leftState, rightState);
  }