blob: a9020c39176760d1188c2df5d5a92bc5636ee201 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.cache.versions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.geode.annotations.internal.MutableForTesting;
import org.apache.geode.internal.InternalDataSerializer;
import org.apache.geode.internal.VersionedDataSerializable;
import org.apache.geode.internal.serialization.Version;
/**
* RVV exceptions are part of a RegionVersionVector. They are held by RegionVersionHolders.
* <p>
*
* An RVV exception represents a missing range of versions from a member. This is represented as a
* pair of version numbers: the last version we've received from the member and the next version
* we've received from the member. The span of versions between (but not including) these two
* numbers are the versions that are missing.
* <p>
*
* RVVException also tracks the scattering of versions we've received that fall within the range of
* the exception. As these missing versions are received the RVVException coalesces the range. If
* isFilled() returns true after adding a missing version to the RVVException, the hole has been
* filled and the RVVException is no longer needed.
* <p>
*
* New exceptions are common to see as operations arrive from parallel threads operating on the same
* region. These exceptions are soon mended and disappear.
* <p>
*
* Old exceptions indicate that we missed something that we are unlikely to receive without asking
* for it.
* <p>
*
* RVVException currently depends on external synchronization. Typically the RegionVersionHolder
* that holds the exception is locked while accessing its RVVExceptions. This is what is done in
* RegionVersionVector.
*
*/
abstract class RVVException
implements Comparable<RVVException>, Cloneable, VersionedDataSerializable {
private static final long serialVersionUID = 2021977010704105114L;
@MutableForTesting
protected static boolean UseTreeSetsForTesting = false;
/**
* The maximum version span that can be represented by a bitset RVVException. If the span is
* greater than this we use the version of RVVException that collects "received versions" in a
* regular collection.
*/
protected static final long RVV_MAX_BITSET_SPAN = 128 * 8; // 128 bytes gives a span of 1k
// versions
long previousVersion;
long nextVersion;
/**
* Create an exception to represent missing versions
*
* @param previousVersion The previous version before the first missing version
* @param nextVersion The next received version after the last missing version
*/
static RVVException createException(long previousVersion, long nextVersion) {
return createException(previousVersion, nextVersion, 0);
}
/** Use this method to create a new RVVException */
static RVVException createException(long previousVersion, long nextVersion,
long initialExceptionCount) {
// arbitrary cutoff of 100 bytes to use a treeSet instead of bitSet
// But if we are deserializing an exception too many received versions use a
// bitset anyway.
long delta = nextVersion - previousVersion;
if (UseTreeSetsForTesting
|| (delta > RVV_MAX_BITSET_SPAN && initialExceptionCount * 512 < delta)) {
return new RVVExceptionT(previousVersion, nextVersion);
}
return new RVVExceptionB(previousVersion, nextVersion);
}
RVVException(long previousVersion, long nextVersion) {
this.previousVersion = previousVersion;
this.nextVersion = nextVersion;
}
/**
* RegionVersionHolder.fromData() calls this to create an exception
*/
static RVVException createException(DataInput in) throws IOException {
long previousVersion = InternalDataSerializer.readUnsignedVL(in);
int size = (int) InternalDataSerializer.readUnsignedVL(in);
long last = previousVersion;
long[] versions = new long[(int) size];
for (int i = 0; i < size; i++) {
long delta = InternalDataSerializer.readUnsignedVL(in);
long value = delta + last;
versions[i] = value;
last = value;
}
long delta = InternalDataSerializer.readUnsignedVL(in);
long nextVersion = last + delta;
RVVException result = createException(previousVersion, nextVersion, size);
for (int i = 0; i < size; i++) {
result.addReceived(versions[i]);
}
return result;
}
/** has the given version been recorded as having been received? */
public abstract boolean contains(long version);
/** return false if any revisions have been recorded in the range of this exception */
public abstract boolean isEmpty();
/** internal method to add a new version to the received-versions collection */
protected abstract void addReceived(long version);
@Override
public void toData(DataOutput out) throws IOException {
InternalDataSerializer.writeUnsignedVL(this.previousVersion, out);
writeReceived(out);
}
/**
* add a received version
*/
public abstract void add(long receivedVersion);
/**
* returns true if the missing versions that this exception represents have all been received
*/
public boolean isFilled() {
return this.previousVersion + 1 >= this.nextVersion;
}
@Override
public abstract RVVException clone();
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(RVVException o) {
long thisVal = this.previousVersion;
long anotherVal = o.previousVersion;
return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
}
/** Test hook - compare two exceptions */
public boolean sameAs(RVVException other) {
return (this.previousVersion == other.previousVersion)
&& (this.nextVersion == other.nextVersion);
}
@Override
public boolean equals(Object obj) {
throw new UnsupportedOperationException("change the test to use sameAs");
}
@Override
public int hashCode() {
throw new UnsupportedOperationException("this class does not support hashing at this time");
}
protected abstract void writeReceived(DataOutput out) throws IOException;
public abstract ReceivedVersionsReverseIterator receivedVersionsReverseIterator();
public abstract long getHighestReceivedVersion();
public abstract class ReceivedVersionsReverseIterator {
abstract boolean hasNext();
abstract long next();
abstract void remove();
}
@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {}
@Override
public Version[] getSerializationVersions() {
return null;
}
public boolean shouldChangeForm() {
return false;
}
public RVVException changeForm() {
throw new IllegalStateException("Should not be called");
}
}