private ByteBuffer merge( ContextState mergedState, ContextState leftState, ContextState rightState) { while (leftState.hasRemaining() && rightState.hasRemaining()) { int cmp = leftState.compareIdTo(rightState); if (cmp == 0) { Relationship rel = compare(leftState, rightState); if (rel == Relationship.DISJOINT) // two local shards mergedState.writeLocal( leftState.getCounterId(), leftState.getClock() + rightState.getClock(), leftState.getCount() + rightState.getCount()); else if (rel == Relationship.GREATER_THAN) leftState.copyTo(mergedState); else // EQUAL or LESS_THAN rightState.copyTo(mergedState); rightState.moveToNext(); leftState.moveToNext(); } else if (cmp > 0) { rightState.copyTo(mergedState); rightState.moveToNext(); } else // cmp < 0 { leftState.copyTo(mergedState); leftState.moveToNext(); } } while (leftState.hasRemaining()) { leftState.copyTo(mergedState); leftState.moveToNext(); } while (rightState.hasRemaining()) { rightState.copyTo(mergedState); rightState.moveToNext(); } return mergedState.context; }
/** * 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); }
/** * Determine the count relationship between two contexts. * * <p>EQUAL: Equal set of nodes and every count is equal. GREATER_THAN: Superset of nodes and * every count is equal or greater than its corollary. LESS_THAN: Subset of nodes and every count * is equal or less than its corollary. DISJOINT: Node sets are not equal and/or counts are not * all greater or less than. * * <p>Strategy: compare node logical clocks (like a version vector). * * @param left counter context. * @param right counter context. * @return the Relationship between the contexts. */ public Relationship diff(ByteBuffer left, ByteBuffer right) { Relationship relationship = Relationship.EQUAL; ContextState leftState = ContextState.wrap(left); ContextState rightState = ContextState.wrap(right); while (leftState.hasRemaining() && rightState.hasRemaining()) { // compare id bytes int compareId = leftState.compareIdTo(rightState); if (compareId == 0) { long leftClock = leftState.getClock(); long rightClock = rightState.getClock(); long leftCount = leftState.getCount(); long rightCount = rightState.getCount(); // advance leftState.moveToNext(); rightState.moveToNext(); // process clock comparisons if (leftClock == rightClock) { if (leftCount != rightCount) { // Inconsistent shard (see the corresponding code in merge()). We return DISJOINT in // this // case so that it will be treated as a difference, allowing read-repair to work. return Relationship.DISJOINT; } } else if ((leftClock >= 0 && rightClock > 0 && leftClock > rightClock) || (leftClock < 0 && (rightClock > 0 || leftClock < rightClock))) { if (relationship == Relationship.EQUAL) relationship = Relationship.GREATER_THAN; else if (relationship == Relationship.LESS_THAN) return Relationship.DISJOINT; // relationship == Relationship.GREATER_THAN } else { if (relationship == Relationship.EQUAL) relationship = Relationship.LESS_THAN; else if (relationship == Relationship.GREATER_THAN) return Relationship.DISJOINT; // relationship == Relationship.LESS_THAN } } else if (compareId > 0) { // only advance the right context rightState.moveToNext(); if (relationship == Relationship.EQUAL) relationship = Relationship.LESS_THAN; else if (relationship == Relationship.GREATER_THAN) return Relationship.DISJOINT; // relationship == Relationship.LESS_THAN } else // compareId < 0 { // only advance the left context leftState.moveToNext(); if (relationship == Relationship.EQUAL) relationship = Relationship.GREATER_THAN; else if (relationship == Relationship.LESS_THAN) return Relationship.DISJOINT; // relationship == Relationship.GREATER_THAN } } // check final lengths if (leftState.hasRemaining()) { if (relationship == Relationship.EQUAL) return Relationship.GREATER_THAN; else if (relationship == Relationship.LESS_THAN) return Relationship.DISJOINT; } if (rightState.hasRemaining()) { if (relationship == Relationship.EQUAL) return Relationship.LESS_THAN; else if (relationship == Relationship.GREATER_THAN) return Relationship.DISJOINT; } return relationship; }