Example #1
0
  /**
   * Finds range of server deltas needed to transform against, then transforms all client ops
   * against the server ops.
   */
  private VersionedWaveletDelta transformSubmittedDelta(
      WaveletDelta submittedDelta, HashedVersion appliedVersion)
      throws OperationException, InvalidHashException {

    NavigableSet<VersionedWaveletDelta> serverDeltas =
        deserializedTransformedDeltas.tailSet(
            deserializedTransformedDeltas.floor(
                emptyDeserializedDeltaAtVersion(appliedVersion.getVersion())),
            true);

    if (serverDeltas.size() == 0) {
      LOG.warning("Got empty server set, but not sumbitting to head! " + submittedDelta);
      // Not strictly an invalid hash, but it's a related issue
      throw new InvalidHashException("Cannot submit to head");
    }

    // Confirm that the target version/hash of this delta is valid.
    if (!serverDeltas.first().version.equals(appliedVersion)) {
      LOG.warning(
          "Mismatched hashes: expected: "
              + serverDeltas.first().version
              + " got: "
              + appliedVersion);
      // Don't leak the hash to the client in the error message.
      throw new InvalidHashException("Mismatched hashes at version " + appliedVersion.getVersion());
    }

    ParticipantId clientAuthor = submittedDelta.getAuthor();
    List<WaveletOperation> clientOps = submittedDelta.getOperations();
    for (VersionedWaveletDelta d : serverDeltas) {
      // If the client delta transforms to nothing before we've traversed all the server
      // deltas, return the version at which the delta was obliterated (rather than the
      // current version) to ensure that delta submission is idempotent.
      if (clientOps.isEmpty()) {
        return new VersionedWaveletDelta(new WaveletDelta(clientAuthor, clientOps), d.version);
      }
      ParticipantId serverAuthor = d.delta.getAuthor();
      List<WaveletOperation> serverOps = d.delta.getOperations();
      if (clientAuthor.equals(serverAuthor) && clientOps.equals(serverOps)) {
        return d;
      }
      clientOps = transformOps(clientOps, clientAuthor, serverOps, serverAuthor);
    }
    return new VersionedWaveletDelta(new WaveletDelta(clientAuthor, clientOps), currentVersion);
  }
Example #2
0
 /**
  * Transform a wavelet delta if it has been submitted against a different head (currentVersion).
  * Must be called with write lock held.
  *
  * @param delta to possibly transform
  * @param appliedVersion that the delta is applied to
  * @return the transformed delta and the version it was applied at (the version is the current
  *     version of the wavelet, unless the delta is a duplicate in which case it is the version at
  *     which it was originally applied)
  * @throws InvalidHashException if submitting against same version but different hash
  * @throws OperationException if transformation fails
  */
 protected VersionedWaveletDelta maybeTransformSubmittedDelta(
     WaveletDelta delta, HashedVersion appliedVersion)
     throws InvalidHashException, OperationException {
   if (appliedVersion.equals(currentVersion)) {
     // Applied version is the same, we're submitting against head, don't need to do OT
     return new VersionedWaveletDelta(delta, appliedVersion);
   } else {
     // Not submitting against head, we need to do OT, but check the versions really are different
     if (appliedVersion.getVersion() == currentVersion.getVersion()) {
       LOG.warning(
           "Same version ("
               + currentVersion.getVersion()
               + ") but different hashes ("
               + appliedVersion
               + "/"
               + currentVersion
               + ")");
       throw new InvalidHashException(
           "Different hash, same version: " + currentVersion.getVersion());
     } else {
       return transformSubmittedDelta(delta, appliedVersion);
     }
   }
 }
Example #3
0
 /**
  * Returns the applied delta that was applied at a given hashed version.
  *
  * @param version the version to look up
  * @return the applied delta applied at the specified hashed version
  */
 protected ByteStringMessage<ProtocolAppliedWaveletDelta> lookupAppliedDelta(
     HashedVersion version) {
   return appliedDeltas.floor(emptyAppliedDeltaAtVersion(version.getVersion()));
 }
Example #4
0
 /**
  * Return a dummy VersionedWaveletDelta instance used as a range boundary for use in searches
  * within a NavigableSet of deltas.
  *
  * @param version the version with which to return the versioned delta
  * @return a dummy versioned delta with a null delta
  */
 private static VersionedWaveletDelta emptyDeserializedDeltaAtVersion(final long version) {
   return new VersionedWaveletDelta(null, HashedVersion.unsigned(version));
 }
Example #5
0
 /**
  * Return a dummy ProtocolWaveletDelta instance used as a range boundary for use in searches
  * within a NavigableSet of deltas.
  *
  * @param version the version to return the delta applied at
  * @return the generated dummy delta
  */
 private static ProtocolWaveletDelta emptyDeltaAtVersion(final long version) {
   return ProtocolWaveletDelta.newBuilder()
       .setAuthor("dummy")
       .setHashedVersion(WaveletOperationSerializer.serialize(HashedVersion.unsigned(version)))
       .build();
 }