diff --git a/CHANGES.txt b/CHANGES.txt index de5cd3768a..12691ced64 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0 + * Lift MessagingService.minimum_version to 40 (CASSANDRA-18314) * Introduce pluggable crypto providers and default to AmazonCorrettoCryptoProvider (CASSANDRA-18624) * Improved DeletionTime serialization (CASSANDRA-18648) * CEP-7: Storage Attached Indexes (CASSANDRA-16052) diff --git a/src/java/org/apache/cassandra/db/CounterMutation.java b/src/java/org/apache/cassandra/db/CounterMutation.java index deb852eab4..ed64e0aad7 100644 --- a/src/java/org/apache/cassandra/db/CounterMutation.java +++ b/src/java/org/apache/cassandra/db/CounterMutation.java @@ -48,8 +48,6 @@ import org.apache.cassandra.utils.*; import org.apache.cassandra.utils.btree.BTreeSet; import static java.util.concurrent.TimeUnit.*; -import static org.apache.cassandra.net.MessagingService.VERSION_30; -import static org.apache.cassandra.net.MessagingService.VERSION_3014; import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.MessagingService.VERSION_50; import static org.apache.cassandra.utils.Clock.Global.nanoTime; @@ -332,8 +330,6 @@ public class CounterMutation implements IMutation return DatabaseDescriptor.getCounterWriteRpcTimeout(unit); } - private int serializedSize30; - private int serializedSize3014; private int serializedSize40; private int serializedSize50; @@ -341,14 +337,6 @@ public class CounterMutation implements IMutation { switch (version) { - case VERSION_30: - if (serializedSize30 == 0) - serializedSize30 = (int) serializer.serializedSize(this, VERSION_30); - return serializedSize30; - case VERSION_3014: - if (serializedSize3014 == 0) - serializedSize3014 = (int) serializer.serializedSize(this, VERSION_3014); - return serializedSize3014; case VERSION_40: if (serializedSize40 == 0) serializedSize40 = (int) serializer.serializedSize(this, VERSION_40); diff --git a/src/java/org/apache/cassandra/db/Mutation.java b/src/java/org/apache/cassandra/db/Mutation.java index dac4af0fa0..cceb8ea510 100644 --- a/src/java/org/apache/cassandra/db/Mutation.java +++ b/src/java/org/apache/cassandra/db/Mutation.java @@ -48,8 +48,6 @@ import org.apache.cassandra.service.AbstractWriteResponseHandler; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.concurrent.Future; -import static org.apache.cassandra.net.MessagingService.VERSION_30; -import static org.apache.cassandra.net.MessagingService.VERSION_3014; import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.MessagingService.VERSION_50; import static org.apache.cassandra.utils.MonotonicClock.Global.approxTime; @@ -317,8 +315,6 @@ public class Mutation implements IMutation, Supplier return buff.append("])").toString(); } - private int serializedSize30; - private int serializedSize3014; private int serializedSize40; private int serializedSize50; @@ -326,14 +322,6 @@ public class Mutation implements IMutation, Supplier { switch (version) { - case VERSION_30: - if (serializedSize30 == 0) - serializedSize30 = (int) serializer.serializedSize(this, VERSION_30); - return serializedSize30; - case VERSION_3014: - if (serializedSize3014 == 0) - serializedSize3014 = (int) serializer.serializedSize(this, VERSION_3014); - return serializedSize3014; case VERSION_40: if (serializedSize40 == 0) serializedSize40 = (int) serializer.serializedSize(this, VERSION_40); diff --git a/src/java/org/apache/cassandra/db/MutationVerbHandler.java b/src/java/org/apache/cassandra/db/MutationVerbHandler.java index 230ca6345e..1ab6711fdb 100644 --- a/src/java/org/apache/cassandra/db/MutationVerbHandler.java +++ b/src/java/org/apache/cassandra/db/MutationVerbHandler.java @@ -66,14 +66,13 @@ public class MutationVerbHandler implements IVerbHandler .withParam(ParamType.RESPOND_TO, originalMessage.from()) .withoutParam(ParamType.FORWARD_TO); - boolean useSameMessageID = forwardTo.useSameMessageID(originalMessage.id()); // reuse the same Message if all ids are identical (as they will be for 4.0+ node originated messages) - Message message = useSameMessageID ? builder.build() : null; + Message message = builder.build(); forwardTo.forEach((id, target) -> { Tracing.trace("Enqueuing forwarded write to {}", target); - MessagingService.instance().send(useSameMessageID ? message : builder.withId(id).build(), target); + MessagingService.instance().send(message, target); }); } } diff --git a/src/java/org/apache/cassandra/db/ReadResponse.java b/src/java/org/apache/cassandra/db/ReadResponse.java index 9ef9128a36..a9e2cec4a7 100644 --- a/src/java/org/apache/cassandra/db/ReadResponse.java +++ b/src/java/org/apache/cassandra/db/ReadResponse.java @@ -298,6 +298,7 @@ public abstract class ReadResponse { public void serialize(ReadResponse response, DataOutputPlus out, int version) throws IOException { + assert version >= MessagingService.VERSION_40; boolean isDigest = response instanceof DigestResponse; ByteBuffer digest = isDigest ? ((DigestResponse)response).digest : ByteBufferUtil.EMPTY_BYTE_BUFFER; ByteBufferUtil.writeWithVIntLength(digest, out); @@ -312,12 +313,8 @@ public abstract class ReadResponse // repaired sstables were read (but they might be on other replicas). // If the coordinator did not request this info, the response contains an empty digest // and a true for the isConclusive flag. - // If the messaging version is < 4.0, these are omitted altogether. - if (version >= MessagingService.VERSION_40) - { - ByteBufferUtil.writeWithVIntLength(response.repairedDataDigest(), out); - out.writeBoolean(response.isRepairedDigestConclusive()); - } + ByteBufferUtil.writeWithVIntLength(response.repairedDataDigest(), out); + out.writeBoolean(response.isRepairedDigestConclusive()); ByteBuffer data = ((DataResponse)response).data; ByteBufferUtil.writeWithVIntLength(data, out); @@ -326,6 +323,7 @@ public abstract class ReadResponse public ReadResponse deserialize(DataInputPlus in, int version) throws IOException { + assert version >= MessagingService.VERSION_40; ByteBuffer digest = ByteBufferUtil.readWithVIntLength(in); if (digest.hasRemaining()) return new DigestResponse(digest); @@ -334,17 +332,8 @@ public abstract class ReadResponse // that comes from the replica's repaired set, along with a flag indicating // whether or not the digest may be influenced by unrepaired/pending // repaired data - boolean repairedDigestConclusive; - if (version >= MessagingService.VERSION_40) - { - digest = ByteBufferUtil.readWithVIntLength(in); - repairedDigestConclusive = in.readBoolean(); - } - else - { - digest = ByteBufferUtil.EMPTY_BYTE_BUFFER; - repairedDigestConclusive = true; - } + digest = ByteBufferUtil.readWithVIntLength(in); + boolean repairedDigestConclusive = in.readBoolean(); ByteBuffer data = ByteBufferUtil.readWithVIntLength(in); return new RemoteDataResponse(data, digest, repairedDigestConclusive, version); @@ -352,6 +341,7 @@ public abstract class ReadResponse public long serializedSize(ReadResponse response, int version) { + assert version >= MessagingService.VERSION_40; boolean isDigest = response instanceof DigestResponse; ByteBuffer digest = isDigest ? ((DigestResponse)response).digest : ByteBufferUtil.EMPTY_BYTE_BUFFER; long size = ByteBufferUtil.serializedSizeWithVIntLength(digest); @@ -360,16 +350,12 @@ public abstract class ReadResponse { // From 4.0, a coordinator may request an additional info about the repaired data // that makes up the response. - if (version >= MessagingService.VERSION_40) - { - size += ByteBufferUtil.serializedSizeWithVIntLength(response.repairedDataDigest()); - size += 1; - } + size += ByteBufferUtil.serializedSizeWithVIntLength(response.repairedDataDigest()); + size += 1; // In theory, we should deserialize/re-serialize if the version asked is different from the current // version as the content could have a different serialization format. So far though, we haven't made // change to partition iterators serialization since 3.0 so we skip this. - assert version >= MessagingService.VERSION_30; ByteBuffer data = ((DataResponse)response).data; size += ByteBufferUtil.serializedSizeWithVIntLength(data); } diff --git a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java index 48ba7388c7..90fc9f3a11 100644 --- a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java +++ b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java @@ -22,22 +22,17 @@ import java.util.*; import javax.annotation.Nullable; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.SortedSetMultimap; import com.google.common.collect.TreeMultimap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.db.*; import org.apache.cassandra.db.rows.CellPath; -import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; -import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.TableMetadata; -import org.apache.cassandra.utils.CassandraVersion; /** * Represents which (non-PK) columns (and optionally which sub-part of a column for complex columns) are selected @@ -69,7 +64,6 @@ import org.apache.cassandra.utils.CassandraVersion; */ public abstract class ColumnFilter { - private final static Logger logger = LoggerFactory.getLogger(ColumnFilter.class); public static final ColumnFilter NONE = selection(RegularAndStaticColumns.NONE); @@ -87,10 +81,6 @@ public abstract class ColumnFilter * For queries that have no restrictions on the clustering or regular columns, C* will return some data for * the partition even if it does not contains any row as long as one of the static columns contains data. * To be able to ensure those queries all columns need to be fetched.

- * - *

This strategy is also used, instead of the ALL_REGULARS_AND_QUERIED_STATICS_COLUMNS one, in mixed version clusters - * where some nodes have a version lower than 4.0. To ensure backward compatibility with those version that interpret the - * _fetchAll_ serialization flag as a true fetch all request.

*/ ALL_COLUMNS { @@ -182,67 +172,6 @@ public abstract class ColumnFilter abstract RegularAndStaticColumns getFetchedColumns(TableMetadata metadata, RegularAndStaticColumns queried); } - /** - * Returns {@code true} if there are pre-4.0-rc2 nodes in the cluster, {@code false} otherwise. - * - *

ColumnFilters from 4.0 releases before RC2 wrongly assumed that fetching all regular columns and not - * the static columns was enough. That was not the case for queries that needed to return rows for empty partitions. - * See CASSANDRA-16686 for more details.

- */ - private static boolean isUpgradingFromVersionLowerThan40RC2() - { - if (Gossiper.instance.isUpgradingFromVersionLowerThan(CassandraVersion.CASSANDRA_4_0_RC2)) - { - logger.trace("ColumnFilter conversion has been applied so that static columns will not be fetched because there are pre 4.0-rc2 nodes in the cluster"); - return true; - } - return false; - } - - /** - * Returns {@code true} if there are pre-4.0 nodes in the cluster, {@code false} otherwise. - * - *

If there pre-4.0 nodes in the cluster all static columns should be fetched along with all regular columns. - * This is due to the fact that this nodes have a different understanding of the fetchAll serialization flag. - * Pre-4.0 the fetchAll flag meant that all the columns regular AND STATIC should be fetched whereas for 4.0 - * nodes it meant that only the regular columns and the queried static columns should be fetched.

- */ - private static boolean isUpgradingFromVersionLowerThan40() - { - if (Gossiper.instance.isUpgradingFromVersionLowerThan(CassandraVersion.CASSANDRA_4_0)) - { - logger.trace("ColumnFilter conversion has been applied so that all static columns will be fetched because there are pre 4.0 nodes in the cluster"); - return true; - } - return false; - } - - /** - * Returns {@code true} if there are pre-3.4 nodes in the cluster, {@code false} otherwise. - * - * When fetchAll is enabled on pre CASSANDRA-10657 (3.4-), queried columns are not considered at all, and it - * is assumed that all columns are queried. CASSANDRA-10657 (3.4+) brings back skipping values of columns - * which are not in queried set when fetchAll is enabled. That makes exactly the same filter being - * interpreted in a different way on 3.4- and 3.4+. - * - * Moreover, there is no way to convert the filter with fetchAll and queried != null so that it is - * interpreted the same way on 3.4- because that Cassandra version does not support such filtering. - * - * In order to avoid inconsistencies in data read by 3.4- and 3.4+ we need to avoid creation of incompatible - * filters when the cluster contains 3.4- nodes. We need to do that by using a wildcard query. - * - * see CASSANDRA-10657, CASSANDRA-15833, CASSANDRA-16415 - */ - private static boolean isUpgradingFromVersionLowerThan34() - { - if (Gossiper.instance.isUpgradingFromVersionLowerThan(CassandraVersion.CASSANDRA_3_4)) - { - logger.trace("ColumnFilter conversion has been applied so that all columns will be queried because there are pre 3.4 nodes in the cluster"); - return true; - } - return false; - } - /** * A filter that includes all columns for the provided table. */ @@ -267,29 +196,13 @@ public abstract class ColumnFilter * A filter that fetches all columns for the provided table, but returns * only the queried ones. */ + @VisibleForTesting public static ColumnFilter selection(TableMetadata metadata, RegularAndStaticColumns queried, boolean returnStaticContentOnPartitionWithNoRows) { - // pre CASSANDRA-10657 (3.4-), when fetchAll is enabled, queried columns are not considered at all, and it - // is assumed that all columns are queried. - if (isUpgradingFromVersionLowerThan34()) - { - return new WildCardColumnFilter(metadata.regularAndStaticColumns()); - } - - // pre CASSANDRA-12768 (4.0-) all static columns should be fetched along with all regular columns. - if (isUpgradingFromVersionLowerThan40()) - { - return SelectionColumnFilter.newInstance(FetchingStrategy.ALL_COLUMNS, metadata, queried, null); - } - - // pre CASSANDRA-16686 (4.0-RC2-) static columns were not fetched unless queried which led to some wrong - // results for some queries - if (!returnStaticContentOnPartitionWithNoRows || isUpgradingFromVersionLowerThan40RC2()) - { + if (!returnStaticContentOnPartitionWithNoRows) return SelectionColumnFilter.newInstance(FetchingStrategy.ALL_REGULARS_AND_QUERIED_STATICS_COLUMNS, metadata, queried, null); - } return SelectionColumnFilter.newInstance(FetchingStrategy.ALL_COLUMNS, metadata, queried, null); } @@ -546,48 +459,26 @@ public abstract class ColumnFilter public ColumnFilter build() { - boolean isFetchAll = metadata != null; + boolean isFetchAllRegulars = metadata != null; RegularAndStaticColumns queried = queriedBuilder == null ? null : queriedBuilder.build(); - // It's only ok to have queried == null in ColumnFilter if isFetchAll. So deal with the case of a selectionBuilder + // It's only ok to have queried == null in ColumnFilter if isFetchAllRegulars. So deal with the case of a selectionBuilder // with nothing selected (we can at least happen on some backward compatible queries - CASSANDRA-10471). - if (!isFetchAll && queried == null) + if (!isFetchAllRegulars && queried == null) queried = RegularAndStaticColumns.NONE; SortedSetMultimap s = buildSubSelections(); - if (isFetchAll) + if (isFetchAllRegulars) { - // When fetchAll is enabled on pre CASSANDRA-10657 (3.4-), queried columns are not considered at all, and it - // is assumed that all columns are queried. CASSANDRA-10657 (3.4+) brings back skipping values of columns - // which are not in queried set when fetchAll is enabled. That makes exactly the same filter being - // interpreted in a different way on 3.4- and 3.4+. - // - // Moreover, there is no way to convert the filter with fetchAll and queried != null so that it is - // interpreted the same way on 3.4- because that Cassandra version does not support such filtering. - // - // In order to avoid inconsitencies in data read by 3.4- and 3.4+ we need to avoid creation of incompatible - // filters when the cluster contains 3.4- nodes. We do that by forcibly setting queried to null. - // - // see CASSANDRA-10657, CASSANDRA-15833, CASSANDRA-16415 - if (queried == null || isUpgradingFromVersionLowerThan34()) - { + // there is no way to convert the filter with fetchAll and queried != null so all columns are queried + // see CASSANDRA-10657, CASSANDRA-15833, CASSANDRA-16415 + if (queried == null) return new WildCardColumnFilter(metadata.regularAndStaticColumns()); - } - // pre CASSANDRA-12768 (4.0-) all static columns should be fetched along with all regular columns. - if (isUpgradingFromVersionLowerThan40()) - { - return SelectionColumnFilter.newInstance(FetchingStrategy.ALL_COLUMNS, metadata, queried, s); - } - - // pre CASSANDRA-16686 (4.0-RC2-) static columns where not fetched unless queried witch lead to some wrong results - // for some queries - if (!returnStaticContentOnPartitionWithNoRows || isUpgradingFromVersionLowerThan40RC2()) - { + if (!returnStaticContentOnPartitionWithNoRows) return SelectionColumnFilter.newInstance(FetchingStrategy.ALL_REGULARS_AND_QUERIED_STATICS_COLUMNS, metadata, queried, s); - } return SelectionColumnFilter.newInstance(FetchingStrategy.ALL_COLUMNS, metadata, queried, s); } @@ -948,9 +839,8 @@ public abstract class ColumnFilter public static class Serializer { - // Prior to 4.0 the FETCH_ALL flag meant fetch all regular and static columns. From 4.0 onward it meant // fetch all regular columns and queried static columns - private static final int FETCH_ALL_MASK = 0x01; + private static final int FETCH_ALL_REGULARS_MASK = 0x01; private static final int HAS_QUERIED_MASK = 0x02; private static final int HAS_SUB_SELECTIONS_MASK = 0x04; // The FETCH_ALL_STATICS flag was added in CASSANDRA-16686 to allow 4.0 to handle queries that required @@ -959,7 +849,7 @@ public abstract class ColumnFilter private static int makeHeaderByte(ColumnFilter selection) { - return (selection.fetchesAllColumns(false) ? FETCH_ALL_MASK : 0) + return (selection.fetchesAllColumns(false) ? FETCH_ALL_REGULARS_MASK : 0) | (!selection.isWildcard() ? HAS_QUERIED_MASK : 0) | (selection.subSelections() != null ? HAS_SUB_SELECTIONS_MASK : 0) | (selection.fetchesAllColumns(true) ? FETCH_ALL_STATICS_MASK : 0); @@ -969,7 +859,7 @@ public abstract class ColumnFilter { out.writeByte(makeHeaderByte(selection)); - if (version >= MessagingService.VERSION_3014 && selection.fetchesAllColumns(false)) + if (selection.fetchesAllColumns(false)) { serializeRegularAndStaticColumns(selection.fetchedColumns(), out); } @@ -1004,9 +894,7 @@ public abstract class ColumnFilter public ColumnFilter deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException { int header = in.readUnsignedByte(); - // The meaning of isFetchAll is actually different for pre-4.0 versions and for 4.0+ versions - // In 4.0+ it meant is fetch all regulars - boolean isFetchAll = (header & FETCH_ALL_MASK) != 0; + boolean isFetchAllRegulars = (header & FETCH_ALL_REGULARS_MASK) != 0; boolean hasQueried = (header & HAS_QUERIED_MASK) != 0; boolean hasSubSelections = (header & HAS_SUB_SELECTIONS_MASK) != 0; boolean isFetchAllStatics = (header & FETCH_ALL_STATICS_MASK) != 0; @@ -1014,50 +902,23 @@ public abstract class ColumnFilter RegularAndStaticColumns fetched = null; RegularAndStaticColumns queried = null; - if (isFetchAll) - { - if (version >= MessagingService.VERSION_3014) - { - fetched = deserializeRegularAndStaticColumns(in, metadata); - } - else - { - fetched = metadata.regularAndStaticColumns(); - } - } + if (isFetchAllRegulars) + fetched = deserializeRegularAndStaticColumns(in, metadata); if (hasQueried) - { queried = deserializeRegularAndStaticColumns(in, metadata); - } SortedSetMultimap subSelections = null; if (hasSubSelections) - { subSelections = deserializeSubSelection(in, version, metadata); - } - if (isFetchAll) + if (isFetchAllRegulars) { - // pre CASSANDRA-10657 (3.4-), when fetchAll is enabled, queried columns are not considered at all, and it - // is assumed that all columns are queried. - if (!hasQueried || isUpgradingFromVersionLowerThan34()) - { + if (!hasQueried) return new WildCardColumnFilter(fetched); - } - // pre CASSANDRA-12768 (4.0-) all static columns should be fetched along with all regular columns. - if (isUpgradingFromVersionLowerThan40()) - { - return new SelectionColumnFilter(FetchingStrategy.ALL_COLUMNS, queried, fetched, subSelections); - } - - // pre CASSANDRA-16686 (4.0-RC2-) static columns where not fetched unless queried witch lead to some wrong results - // for some queries - if (!isFetchAllStatics || isUpgradingFromVersionLowerThan40RC2()) - { + if (!isFetchAllStatics) return new SelectionColumnFilter(FetchingStrategy.ALL_REGULARS_AND_QUERIED_STATICS_COLUMNS, queried, fetched, subSelections); - } return new SelectionColumnFilter(FetchingStrategy.ALL_COLUMNS, queried, fetched, subSelections); } @@ -1091,7 +952,7 @@ public abstract class ColumnFilter { long size = 1; // header byte - if (version >= MessagingService.VERSION_3014 && selection.fetchesAllColumns(false)) + if (selection.fetchesAllColumns(false)) { size += regularAndStaticColumnsSerializedSize(selection.fetchedColumns()); } diff --git a/src/java/org/apache/cassandra/exceptions/RequestFailureReason.java b/src/java/org/apache/cassandra/exceptions/RequestFailureReason.java index 51415facd5..1703c4e180 100644 --- a/src/java/org/apache/cassandra/exceptions/RequestFailureReason.java +++ b/src/java/org/apache/cassandra/exceptions/RequestFailureReason.java @@ -97,20 +97,20 @@ public enum RequestFailureReason public void serialize(RequestFailureReason reason, DataOutputPlus out, int version) throws IOException { - if (version < VERSION_40) - out.writeShort(reason.code); - else - out.writeUnsignedVInt32(reason.code); + assert version >= VERSION_40; + out.writeUnsignedVInt32(reason.code); } public RequestFailureReason deserialize(DataInputPlus in, int version) throws IOException { - return fromCode(version < VERSION_40 ? in.readUnsignedShort() : in.readUnsignedVInt32()); + assert version >= VERSION_40; + return fromCode(in.readUnsignedVInt32()); } public long serializedSize(RequestFailureReason reason, int version) { - return version < VERSION_40 ? 2 : VIntCoding.computeVIntSize(reason.code); + assert version >= VERSION_40; + return VIntCoding.computeVIntSize(reason.code); } } } diff --git a/src/java/org/apache/cassandra/hints/HintsDescriptor.java b/src/java/org/apache/cassandra/hints/HintsDescriptor.java index 02820bcd04..a1e961e7a1 100644 --- a/src/java/org/apache/cassandra/hints/HintsDescriptor.java +++ b/src/java/org/apache/cassandra/hints/HintsDescriptor.java @@ -66,7 +66,6 @@ final class HintsDescriptor { private static final Logger logger = LoggerFactory.getLogger(HintsDescriptor.class); - static final int VERSION_30 = 1; static final int VERSION_40 = 2; static final int VERSION_50 = 3; static final int CURRENT_VERSION = DatabaseDescriptor.getStorageCompatibilityMode().isBefore(5) ? VERSION_40 : VERSION_50; @@ -231,8 +230,6 @@ final class HintsDescriptor { switch (hintsVersion) { - case VERSION_30: - return MessagingService.VERSION_30; case VERSION_40: return MessagingService.VERSION_40; case VERSION_50: diff --git a/src/java/org/apache/cassandra/locator/InetAddressAndPort.java b/src/java/org/apache/cassandra/locator/InetAddressAndPort.java index 78627e49ca..c954a05b1c 100644 --- a/src/java/org/apache/cassandra/locator/InetAddressAndPort.java +++ b/src/java/org/apache/cassandra/locator/InetAddressAndPort.java @@ -55,7 +55,6 @@ import org.apache.cassandra.utils.FastByteOperations; * need to sometimes return a port and sometimes not. * */ -@SuppressWarnings("UnstableApiUsage") public final class InetAddressAndPort extends InetSocketAddress implements Comparable, Serializable { private static final long serialVersionUID = 0; @@ -319,8 +318,6 @@ public final class InetAddressAndPort extends InetSocketAddress implements Compa /** * As of version 4.0 the endpoint description includes a port number as an unsigned short - * This serializer matches the 3.0 CompactEndpointSerializationHelper, encoding the number of address bytes - * in a single byte before the address itself. */ public static final class Serializer implements IVersionedSerializer { @@ -347,17 +344,10 @@ public final class InetAddressAndPort extends InetSocketAddress implements Compa void serialize(byte[] address, int port, DataOutputPlus out, int version) throws IOException { - if (version >= MessagingService.VERSION_40) - { - out.writeByte(address.length + 2); - out.write(address); - out.writeShort(port); - } - else - { - out.writeByte(address.length); - out.write(address); - } + assert version >= MessagingService.VERSION_40; + out.writeByte(address.length + 2); + out.write(address); + out.writeShort(port); } public InetAddressAndPort deserialize(DataInputPlus in, int version) throws IOException @@ -368,11 +358,7 @@ public final class InetAddressAndPort extends InetSocketAddress implements Compa //The original pre-4.0 serialiation of just an address case 4: case 16: - { - byte[] bytes = new byte[size]; - in.readFully(bytes, 0, bytes.length); - return getByAddress(bytes); - } + throw new AssertionError("pre-4.0 serialization size " + size); //Address and one port case 6: case 18: @@ -395,13 +381,7 @@ public final class InetAddressAndPort extends InetSocketAddress implements Compa public InetAddressAndPort extract(ByteBuffer buf, int position) throws IOException { int size = buf.get(position++) & 0xFF; - if (size == 4 || size == 16) - { - byte[] bytes = new byte[size]; - ByteBufferUtil.copyBytes(buf, position, bytes, 0, size); - return getByAddress(bytes); - } - else if (size == 6 || size == 18) + if (size == 6 || size == 18) { byte[] bytes = new byte[size - 2]; ByteBufferUtil.copyBytes(buf, position, bytes, 0, size - 2); @@ -420,26 +400,15 @@ public final class InetAddressAndPort extends InetSocketAddress implements Compa public long serializedSize(InetSocketAddress from, int version) { - //4.0 includes a port number - if (version >= MessagingService.VERSION_40) - { - if (from.getAddress() instanceof Inet4Address) - return 1 + 4 + 2; - assert from.getAddress() instanceof Inet6Address; - return 1 + 16 + 2; - } - else - { - if (from.getAddress() instanceof Inet4Address) - return 1 + 4; - assert from.getAddress() instanceof Inet6Address; - return 1 + 16; - } + assert version >= MessagingService.VERSION_40; + if (from.getAddress() instanceof Inet4Address) + return 1 + 4 + 2; + assert from.getAddress() instanceof Inet6Address; + return 1 + 16 + 2; } } - /** Serializer for handling FWD_FRM message parameters. Pre-4.0 deserialization is a special - * case in the message + /** Serializer for handling FWD_FRM message parameters. */ public static final class FwdFrmSerializer implements IVersionedSerializer { @@ -448,73 +417,43 @@ public final class InetAddressAndPort extends InetSocketAddress implements Compa public void serialize(InetAddressAndPort endpoint, DataOutputPlus out, int version) throws IOException { + assert version >= MessagingService.VERSION_40; byte[] buf = endpoint.addressBytes; - - if (version >= MessagingService.VERSION_40) - { - out.writeByte(buf.length + 2); - out.write(buf); - out.writeShort(endpoint.getPort()); - } - else - { - out.write(buf); - } + out.writeByte(buf.length + 2); + out.write(buf); + out.writeShort(endpoint.getPort()); } public long serializedSize(InetAddressAndPort from, int version) { - //4.0 includes a port number - if (version >= MessagingService.VERSION_40) - { - if (from.getAddress() instanceof Inet4Address) - return 1 + 4 + 2; - assert from.getAddress() instanceof Inet6Address; - return 1 + 16 + 2; - } - else - { - if (from.getAddress() instanceof Inet4Address) - return 4; - assert from.getAddress() instanceof Inet6Address; - return 16; - } + assert version >= MessagingService.VERSION_40; + if (from.getAddress() instanceof Inet4Address) + return 1 + 4 + 2; + assert from.getAddress() instanceof Inet6Address; + return 1 + 16 + 2; } @Override public InetAddressAndPort deserialize(DataInputPlus in, int version) throws IOException { - if (version >= MessagingService.VERSION_40) + assert version >= MessagingService.VERSION_40 : "FWD_FRM deserializations should be special-cased pre-4.0"; + int size = in.readByte() & 0xFF; + switch (size) { - int size = in.readByte() & 0xFF; - switch (size) + //Address and one port + case 6: + case 18: { - //Address and one port - case 6: - case 18: - { - byte[] bytes = new byte[size - 2]; - in.readFully(bytes); + byte[] bytes = new byte[size - 2]; + in.readFully(bytes); - int port = in.readShort() & 0xFFFF; - return getByAddressOverrideDefaults(InetAddress.getByAddress(bytes), bytes, port); - } - default: - throw new AssertionError("Unexpected size " + size); + int port = in.readShort() & 0xFFFF; + return getByAddressOverrideDefaults(InetAddress.getByAddress(bytes), bytes, port); } - } - else - { - throw new IllegalStateException("FWD_FRM deserializations should be special-cased pre-4.0"); + default: + throw new AssertionError("Unexpected size " + size); } } - public InetAddressAndPort pre40DeserializeWithLength(DataInputPlus in, int version, int length) throws IOException - { - assert length == 4 || length == 16 : "unexpected length " + length; - byte[] from = new byte[length]; - in.readFully(from, 0, length); - return InetAddressAndPort.getByAddress(from); - } } } diff --git a/src/java/org/apache/cassandra/net/ForwardingInfo.java b/src/java/org/apache/cassandra/net/ForwardingInfo.java index 2ee199a249..7a117bd999 100644 --- a/src/java/org/apache/cassandra/net/ForwardingInfo.java +++ b/src/java/org/apache/cassandra/net/ForwardingInfo.java @@ -18,8 +18,6 @@ package org.apache.cassandra.net; import com.google.common.base.Preconditions; -import com.google.common.primitives.Ints; -import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; @@ -39,7 +37,6 @@ import static org.apache.cassandra.utils.vint.VIntCoding.computeUnsignedVIntSize * A container used to store a node -> message_id map for inter-DC write forwarding. * We pick one node in each external DC to forward the message to its local peers. * - * TODO: in the next protocol version only serialize peers, message id will become redundant once 3.0 is out of the picture */ public final class ForwardingInfo implements Serializable { @@ -53,19 +50,6 @@ public final class ForwardingInfo implements Serializable this.messageIds = messageIds; } - /** - * @return {@code true} if all host are to use the same message id, {@code false} otherwise. Starting with 4.0 and - * above, we should be reusing the same id, always, but it won't always be true until 3.0/3.11 are phased out. - */ - public boolean useSameMessageID(long id) - { - for (int i = 0; i < messageIds.length; i++) - if (id != messageIds[i]) - return false; - - return true; - } - /** * Apply the provided consumer to all (host, message_id) pairs. */ @@ -79,37 +63,33 @@ public final class ForwardingInfo implements Serializable { public void serialize(ForwardingInfo forwardTo, DataOutputPlus out, int version) throws IOException { + assert version >= VERSION_40; long[] ids = forwardTo.messageIds; List targets = forwardTo.targets; int count = ids.length; - if (version >= VERSION_40) - out.writeUnsignedVInt32(count); - else - out.writeInt(count); + out.writeUnsignedVInt32(count); for (int i = 0; i < count; i++) { inetAddressAndPortSerializer.serialize(targets.get(i), out, version); - if (version >= VERSION_40) - out.writeUnsignedVInt(ids[i]); - else - out.writeInt(Ints.checkedCast(ids[i])); + out.writeUnsignedVInt(ids[i]); } } public long serializedSize(ForwardingInfo forwardTo, int version) { + assert version >= VERSION_40; long[] ids = forwardTo.messageIds; List targets = forwardTo.targets; int count = ids.length; - long size = version >= VERSION_40 ? computeUnsignedVIntSize(count) : TypeSizes.sizeof(count); + long size = computeUnsignedVIntSize(count); for (int i = 0; i < count; i++) { size += inetAddressAndPortSerializer.serializedSize(targets.get(i), version); - size += version >= VERSION_40 ? computeUnsignedVIntSize(ids[i]) : 4; + size += computeUnsignedVIntSize(ids[i]); } return size; @@ -117,7 +97,8 @@ public final class ForwardingInfo implements Serializable public ForwardingInfo deserialize(DataInputPlus in, int version) throws IOException { - int count = version >= VERSION_40 ? in.readUnsignedVInt32() : in.readInt(); + assert version >= VERSION_40; + int count = in.readUnsignedVInt32(); long[] ids = new long[count]; List targets = new ArrayList<>(count); @@ -125,7 +106,7 @@ public final class ForwardingInfo implements Serializable for (int i = 0; i < count; i++) { targets.add(inetAddressAndPortSerializer.deserialize(in, version)); - ids[i] = version >= VERSION_40 ? in.readUnsignedVInt32() : in.readInt(); + ids[i] = in.readUnsignedVInt32(); } return new ForwardingInfo(targets, ids); diff --git a/src/java/org/apache/cassandra/net/FrameDecoder.java b/src/java/org/apache/cassandra/net/FrameDecoder.java index 4cfbf6d6ed..553973f53b 100644 --- a/src/java/org/apache/cassandra/net/FrameDecoder.java +++ b/src/java/org/apache/cassandra/net/FrameDecoder.java @@ -51,10 +51,6 @@ import static org.apache.cassandra.utils.ByteBufferUtil.copyBytes; LZ4 compression with custom frame format; payload is protected by CRC32 * 3. {@link FrameDecoderUnprotected}: no compression; no integrity protection - * 4. {@link FrameDecoderLegacy}: - no compression; no integrity protection; turns unframed streams of legacy messages (< 4.0) into frames - * 5. {@link FrameDecoderLegacyLZ4} - * LZ4 compression using standard LZ4 frame format; groups legacy messages (< 4.0) into frames */ public abstract class FrameDecoder extends ChannelInboundHandlerAdapter { @@ -276,10 +272,6 @@ public abstract class FrameDecoder extends ChannelInboundHandlerAdapter allocator.putUnusedPortion(buf); channelRead(ShareableBytes.wrap(buf)); } - else if (msg instanceof ShareableBytes) // legacy LZ4 decoder - { - channelRead((ShareableBytes) msg); - } else { throw new IllegalArgumentException(); diff --git a/src/java/org/apache/cassandra/net/FrameDecoderLegacy.java b/src/java/org/apache/cassandra/net/FrameDecoderLegacy.java deleted file mode 100644 index a3d7bc593e..0000000000 --- a/src/java/org/apache/cassandra/net/FrameDecoderLegacy.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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.cassandra.net; - -import java.nio.ByteBuffer; -import java.util.Collection; - -import io.netty.channel.ChannelPipeline; - -import static java.lang.Math.max; -import static org.apache.cassandra.net.OutboundConnections.LARGE_MESSAGE_THRESHOLD; - -/** - * {@link InboundMessageHandler} operates on frames that adhere to a certain contract - * (see {@link FrameDecoder.IntactFrame} and {@link FrameDecoder.CorruptFrame} javadoc). - * - * Legacy (pre-4.0) messaging protocol does not natively support framing, however. The job - * of {@link FrameDecoderLegacy} is turn a raw stream of messages, serialized back to back, - * into a sequence of frames that adhere to 4.0+ conventions. - */ -class FrameDecoderLegacy extends FrameDecoder -{ - private final int messagingVersion; - - private int remainingBytesInLargeMessage = 0; - - FrameDecoderLegacy(BufferPoolAllocator allocator, int messagingVersion) - { - super(allocator); - this.messagingVersion = messagingVersion; - } - - final void decode(Collection into, ShareableBytes newBytes) - { - ByteBuffer in = newBytes.get(); - try - { - if (stash != null) - { - int length = Message.serializer.inferMessageSize(stash, 0, stash.position(), messagingVersion); - while (length < 0) - { - if (!in.hasRemaining()) - return; - - if (stash.position() == stash.capacity()) - stash = ensureCapacity(stash, stash.capacity() * 2); - copyToSize(in, stash, stash.capacity()); - - length = Message.serializer.inferMessageSize(stash, 0, stash.position(), messagingVersion); - if (length >= 0 && length < stash.position()) - { - int excess = stash.position() - length; - in.position(in.position() - excess); - stash.position(length); - } - } - - final boolean isSelfContained; - if (length <= LARGE_MESSAGE_THRESHOLD) - { - isSelfContained = true; - - if (length > stash.capacity()) - stash = ensureCapacity(stash, length); - - stash.limit(length); - allocator.putUnusedPortion(stash); // we may be over capacity from earlier doubling - if (!copyToSize(in, stash, length)) - return; - } - else - { - isSelfContained = false; - remainingBytesInLargeMessage = length - stash.position(); - - stash.limit(stash.position()); - allocator.putUnusedPortion(stash); - } - - stash.flip(); - assert !isSelfContained || stash.limit() == length; - ShareableBytes stashed = ShareableBytes.wrap(stash); - into.add(new IntactFrame(isSelfContained, stashed)); - stash = null; - } - - if (remainingBytesInLargeMessage > 0) - { - if (remainingBytesInLargeMessage >= newBytes.remaining()) - { - remainingBytesInLargeMessage -= newBytes.remaining(); - into.add(new IntactFrame(false, newBytes.sliceAndConsume(newBytes.remaining()))); - return; - } - else - { - Frame frame = new IntactFrame(false, newBytes.sliceAndConsume(remainingBytesInLargeMessage)); - remainingBytesInLargeMessage = 0; - into.add(frame); - } - } - - // we loop incrementing our end pointer until we have no more complete messages, - // at which point we slice the complete messages, and stash the remainder - int begin = in.position(); - int end = begin; - int limit = in.limit(); - - if (begin == limit) - return; - - while (true) - { - int length = Message.serializer.inferMessageSize(in, end, limit, messagingVersion); - - if (length >= 0) - { - if (end + length <= limit) - { - // we have a complete message, so just bump our end pointer - end += length; - - // if we have more bytes, continue to look for another message - if (end < limit) - continue; - - // otherwise reset length, as we have accounted for it in end - length = 0; - } - } - - // we are done; if we have found any complete messages, slice them all into a single frame - if (begin < end) - into.add(new IntactFrame(true, newBytes.slice(begin, end))); - - // now consider stashing anything leftover - if (length < 0) - { - stash(newBytes, max(64, limit - end), end, limit - end); - } - else if (length > LARGE_MESSAGE_THRESHOLD) - { - remainingBytesInLargeMessage = length - (limit - end); - Frame frame = new IntactFrame(false, newBytes.slice(end, limit)); - into.add(frame); - } - else if (length > 0) - { - stash(newBytes, length, end, limit - end); - } - break; - } - } - catch (Message.InvalidLegacyProtocolMagic e) - { - into.add(CorruptFrame.unrecoverable(e.read, Message.PROTOCOL_MAGIC)); - } - finally - { - newBytes.release(); - } - } - - void addLastTo(ChannelPipeline pipeline) - { - pipeline.addLast("frameDecoderNone", this); - } -} diff --git a/src/java/org/apache/cassandra/net/FrameDecoderLegacyLZ4.java b/src/java/org/apache/cassandra/net/FrameDecoderLegacyLZ4.java deleted file mode 100644 index 4c620c7b0d..0000000000 --- a/src/java/org/apache/cassandra/net/FrameDecoderLegacyLZ4.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * 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.cassandra.net; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Deque; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.compression.Lz4FrameDecoder; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.lz4.LZ4SafeDecompressor; -import net.jpountz.xxhash.XXHash32; -import net.jpountz.xxhash.XXHashFactory; -import org.apache.cassandra.utils.memory.BufferPool; -import org.apache.cassandra.utils.memory.BufferPools; - -import static java.lang.Integer.reverseBytes; -import static java.lang.String.format; -import static org.apache.cassandra.net.LegacyLZ4Constants.*; -import static org.apache.cassandra.utils.ByteBufferUtil.copyBytes; - -/** - * A {@link FrameDecoder} consisting of two chained handlers: - * 1. A legacy LZ4 block decoder, described below in the description of {@link LZ4Decoder}, followed by - * 2. An instance of {@link FrameDecoderLegacy} - transforming the raw messages in the uncompressed stream - * into properly formed frames expected by {@link InboundMessageHandler} - */ -class FrameDecoderLegacyLZ4 extends FrameDecoderLegacy -{ - private static final BufferPool bufferPool = BufferPools.forNetworking(); - - FrameDecoderLegacyLZ4(BufferPoolAllocator allocator, int messagingVersion) - { - super(allocator, messagingVersion); - } - - @Override - void addLastTo(ChannelPipeline pipeline) - { - pipeline.addLast("legacyLZ4Decoder", new LZ4Decoder(allocator)); - pipeline.addLast("frameDecoderNone", this); - } - - /** - * An implementation of LZ4 decoder, used for legacy (3.0, 3.11) connections. - * - * Netty's provided implementation - {@link Lz4FrameDecoder} couldn't be reused for - * two reasons: - * 1. It has very poor performance when coupled with xxHash, which we use for legacy connections - - * allocating a single-byte array and making a JNI call for every byte of the payload - * 2. It was tricky to efficiently integrate with upstream {@link FrameDecoder}, and impossible - * to make it play nicely with flow control - Netty's implementation, based on - * {@link io.netty.handler.codec.ByteToMessageDecoder}, would potentially keep triggering - * reads on its own volition for as long as its last read had no completed frames to supply - * - defying our goal to only ever trigger channel reads when explicitly requested - * - * Since the original LZ4 block format does not contains size of compressed block and size of original data - * this encoder uses format like LZ4 Java library - * written by Adrien Grand and approved by Yann Collet (author of original LZ4 library), as implemented by - * Netty's {@link Lz4FrameDecoder}, but adapted for our interaction model. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | | - * + Magic + - * | | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |T| Compressed Length - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Uncompressed Length - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | xxHash32 of Uncompressed Payload - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | | - * +-+ + - * | | - * + Payload + - * | | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - private static class LZ4Decoder extends ChannelInboundHandlerAdapter - { - private static final XXHash32 xxhash = - XXHashFactory.fastestInstance().hash32(); - - private static final LZ4SafeDecompressor decompressor = - LZ4Factory.fastestInstance().safeDecompressor(); - - private final BufferPoolAllocator allocator; - - LZ4Decoder(BufferPoolAllocator allocator) - { - this.allocator = allocator; - } - - private final Deque frames = new ArrayDeque<>(4); - - // total # of frames decoded between two subsequent invocations of channelReadComplete() - private int decodedFrameCount = 0; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws CorruptLZ4Frame - { - assert msg instanceof BufferPoolAllocator.Wrapped; - ByteBuffer buf = ((BufferPoolAllocator.Wrapped) msg).adopt(); - // netty will probably have mis-predicted the space needed - bufferPool.putUnusedPortion(buf); - - CorruptLZ4Frame error = null; - try - { - decode(frames, ShareableBytes.wrap(buf)); - } - catch (CorruptLZ4Frame e) - { - error = e; - } - finally - { - decodedFrameCount += frames.size(); - while (!frames.isEmpty()) - ctx.fireChannelRead(frames.poll()); - } - - if (null != error) - throw error; - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) - { - /* - * If no frames have been decoded from the entire batch of channelRead() calls, - * then we must trigger another channel read explicitly, or else risk stalling - * forever without bytes to complete the current in-flight frame. - */ - if (null != stash && decodedFrameCount == 0 && !ctx.channel().config().isAutoRead()) - ctx.read(); - - decodedFrameCount = 0; - ctx.fireChannelReadComplete(); - } - - private void decode(Collection into, ShareableBytes newBytes) throws CorruptLZ4Frame - { - try - { - doDecode(into, newBytes); - } - finally - { - newBytes.release(); - } - } - - private void doDecode(Collection into, ShareableBytes newBytes) throws CorruptLZ4Frame - { - ByteBuffer in = newBytes.get(); - - if (null != stash) - { - if (!copyToSize(in, stash, HEADER_LENGTH)) - return; - - header.read(stash, 0); - header.validate(); - - int frameLength = header.frameLength(); - stash = ensureCapacity(stash, frameLength); - - if (!copyToSize(in, stash, frameLength)) - return; - - stash.flip(); - ShareableBytes stashed = ShareableBytes.wrap(stash); - stash = null; - - try - { - into.add(decompressFrame(stashed, 0, frameLength, header)); - } - finally - { - stashed.release(); - } - } - - int begin = in.position(); - int limit = in.limit(); - while (begin < limit) - { - int remaining = limit - begin; - if (remaining < HEADER_LENGTH) - { - stash(newBytes, HEADER_LENGTH, begin, remaining); - return; - } - - header.read(in, begin); - header.validate(); - - int frameLength = header.frameLength(); - if (remaining < frameLength) - { - stash(newBytes, frameLength, begin, remaining); - return; - } - - into.add(decompressFrame(newBytes, begin, begin + frameLength, header)); - begin += frameLength; - } - } - - private ShareableBytes decompressFrame(ShareableBytes bytes, int begin, int end, Header header) throws CorruptLZ4Frame - { - ByteBuffer buf = bytes.get(); - - if (header.uncompressedLength == 0) - return bytes.slice(begin + HEADER_LENGTH, end); - - if (!header.isCompressed()) - { - validateChecksum(buf, begin + HEADER_LENGTH, header); - return bytes.slice(begin + HEADER_LENGTH, end); - } - - ByteBuffer out = allocator.get(header.uncompressedLength); - try - { - int sourceLength = end - (begin + HEADER_LENGTH); - decompressor.decompress(buf, begin + HEADER_LENGTH, sourceLength, out, 0, header.uncompressedLength); - validateChecksum(out, 0, header); - return ShareableBytes.wrap(out); - } - catch (Throwable t) - { - bufferPool.put(out); - throw t; - } - } - - private void validateChecksum(ByteBuffer buf, int begin, Header header) throws CorruptLZ4Frame - { - int checksum = xxhash.hash(buf, begin, header.uncompressedLength, XXHASH_SEED) & XXHASH_MASK; - if (checksum != header.checksum) - except("Invalid checksum detected: %d (expected: %d)", checksum, header.checksum); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) - { - if (null != stash) - { - bufferPool.put(stash); - stash = null; - } - - while (!frames.isEmpty()) - frames.poll().release(); - - ctx.fireChannelInactive(); - } - - /* reusable container for deserialized header fields */ - private static final class Header - { - long magicNumber; - byte token; - int compressedLength; - int uncompressedLength; - int checksum; - - int frameLength() - { - return HEADER_LENGTH + compressedLength; - } - - boolean isCompressed() - { - return (token & 0xF0) == 0x20; - } - - int maxUncompressedLength() - { - return 1 << ((token & 0x0F) + 10); - } - - void read(ByteBuffer in, int begin) - { - magicNumber = in.getLong(begin + MAGIC_NUMBER_OFFSET ); - token = in.get (begin + TOKEN_OFFSET ); - compressedLength = reverseBytes(in.getInt (begin + COMPRESSED_LENGTH_OFFSET )); - uncompressedLength = reverseBytes(in.getInt (begin + UNCOMPRESSED_LENGTH_OFFSET)); - checksum = reverseBytes(in.getInt (begin + CHECKSUM_OFFSET )); - } - - void validate() throws CorruptLZ4Frame - { - if (magicNumber != MAGIC_NUMBER) - except("Invalid magic number at the beginning of an LZ4 block: %d", magicNumber); - - int blockType = token & 0xF0; - if (!(blockType == BLOCK_TYPE_COMPRESSED || blockType == BLOCK_TYPE_NON_COMPRESSED)) - except("Invalid block type encountered: %d", blockType); - - if (compressedLength < 0 || compressedLength > MAX_BLOCK_LENGTH) - except("Invalid compressedLength: %d (expected: 0-%d)", compressedLength, MAX_BLOCK_LENGTH); - - if (uncompressedLength < 0 || uncompressedLength > maxUncompressedLength()) - except("Invalid uncompressedLength: %d (expected: 0-%d)", uncompressedLength, maxUncompressedLength()); - - if ( uncompressedLength == 0 && compressedLength != 0 - || uncompressedLength != 0 && compressedLength == 0 - || !isCompressed() && uncompressedLength != compressedLength) - { - except("Stream corrupted: compressedLength(%d) and decompressedLength(%d) mismatch", compressedLength, uncompressedLength); - } - } - } - private final Header header = new Header(); - - /** - * @return {@code in} if has sufficient capacity, otherwise a replacement from {@code BufferPool} that {@code in} is copied into - */ - private ByteBuffer ensureCapacity(ByteBuffer in, int capacity) - { - if (in.capacity() >= capacity) - return in; - - ByteBuffer out = allocator.getAtLeast(capacity); - in.flip(); - out.put(in); - bufferPool.put(in); - return out; - } - - private ByteBuffer stash; - - private void stash(ShareableBytes in, int stashLength, int begin, int length) - { - ByteBuffer out = allocator.getAtLeast(stashLength); - copyBytes(in.get(), begin, out, 0, length); - out.position(length); - stash = out; - } - - static final class CorruptLZ4Frame extends IOException - { - CorruptLZ4Frame(String message) - { - super(message); - } - } - - private static void except(String format, Object... args) throws CorruptLZ4Frame - { - throw new CorruptLZ4Frame(format(format, args)); - } - } -} diff --git a/src/java/org/apache/cassandra/net/FrameEncoderLegacy.java b/src/java/org/apache/cassandra/net/FrameEncoderLegacy.java deleted file mode 100644 index 8bfd2678ad..0000000000 --- a/src/java/org/apache/cassandra/net/FrameEncoderLegacy.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.cassandra.net; - -import java.nio.ByteBuffer; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; - -/** - * A no-op frame encoder: legacy format doesn't support framing. Instead, the byte stream - * contains messages, serialized back to back. - */ -@ChannelHandler.Sharable -class FrameEncoderLegacy extends FrameEncoder -{ - static final FrameEncoderLegacy instance = new FrameEncoderLegacy(); - - ByteBuf encode(boolean isSelfContained, ByteBuffer buffer) - { - return GlobalBufferPoolAllocator.wrap(buffer); - } -} diff --git a/src/java/org/apache/cassandra/net/FrameEncoderLegacyLZ4.java b/src/java/org/apache/cassandra/net/FrameEncoderLegacyLZ4.java deleted file mode 100644 index fd8b36b85e..0000000000 --- a/src/java/org/apache/cassandra/net/FrameEncoderLegacyLZ4.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.cassandra.net; - -import java.nio.ByteBuffer; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import net.jpountz.lz4.LZ4Compressor; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.xxhash.XXHash32; -import net.jpountz.xxhash.XXHashFactory; -import org.apache.cassandra.io.compress.BufferType; -import org.apache.cassandra.utils.ByteBufferUtil; - -import static java.lang.Integer.reverseBytes; -import static java.lang.Math.min; -import static org.apache.cassandra.net.LegacyLZ4Constants.*; - -/** - * LZ4 {@link FrameEncoder} implementation for compressed legacy (3.0, 3.11) connections. - * - * Netty's provided implementation - {@link io.netty.handler.codec.compression.Lz4FrameEncoder} couldn't be reused - * for two reasons: - * 1. It notifies flushes as successful when they may not be, by flushing an empty buffer ahead - * of the compressed buffer - * 2. It has very poor performance when coupled with xxHash, which we use for legacy connections - - * allocating a single-byte array and making a JNI call for every byte of the payload - * - * Please see {@link FrameDecoderLegacyLZ4} for the description of the on-wire format of the LZ4 blocks - * used by this encoder. - */ -@ChannelHandler.Sharable -class FrameEncoderLegacyLZ4 extends FrameEncoder -{ - static final FrameEncoderLegacyLZ4 instance = - new FrameEncoderLegacyLZ4(XXHashFactory.fastestInstance().hash32(), - LZ4Factory.fastestInstance().fastCompressor()); - - private final XXHash32 xxhash; - private final LZ4Compressor compressor; - - private FrameEncoderLegacyLZ4(XXHash32 xxhash, LZ4Compressor compressor) - { - this.xxhash = xxhash; - this.compressor = compressor; - } - - @Override - ByteBuf encode(boolean isSelfContained, ByteBuffer payload) - { - ByteBuffer frame = null; - try - { - frame = bufferPool.getAtLeast(calculateMaxFrameLength(payload), BufferType.OFF_HEAP); - - int frameOffset = 0; - int payloadOffset = 0; - - int payloadLength = payload.remaining(); - while (payloadOffset < payloadLength) - { - int blockLength = min(DEFAULT_BLOCK_LENGTH, payloadLength - payloadOffset); - frameOffset += compressBlock(frame, frameOffset, payload, payloadOffset, blockLength); - payloadOffset += blockLength; - } - - frame.limit(frameOffset); - bufferPool.putUnusedPortion(frame); - - return GlobalBufferPoolAllocator.wrap(frame); - } - catch (Throwable t) - { - if (null != frame) - bufferPool.put(frame); - throw t; - } - finally - { - bufferPool.put(payload); - } - } - - private int compressBlock(ByteBuffer frame, int frameOffset, ByteBuffer payload, int payloadOffset, int blockLength) - { - int frameBytesRemaining = frame.limit() - (frameOffset + HEADER_LENGTH); - int compressedLength = compressor.compress(payload, payloadOffset, blockLength, frame, frameOffset + HEADER_LENGTH, frameBytesRemaining); - if (compressedLength >= blockLength) - { - ByteBufferUtil.copyBytes(payload, payloadOffset, frame, frameOffset + HEADER_LENGTH, blockLength); - compressedLength = blockLength; - } - int checksum = xxhash.hash(payload, payloadOffset, blockLength, XXHASH_SEED) & XXHASH_MASK; - writeHeader(frame, frameOffset, compressedLength, blockLength, checksum); - return HEADER_LENGTH + compressedLength; - } - - private static final byte TOKEN_NON_COMPRESSED = 0x15; - private static final byte TOKEN_COMPRESSED = 0x25; - - private static void writeHeader(ByteBuffer frame, int frameOffset, int compressedLength, int uncompressedLength, int checksum) - { - byte token = compressedLength == uncompressedLength - ? TOKEN_NON_COMPRESSED - : TOKEN_COMPRESSED; - - frame.putLong(frameOffset + MAGIC_NUMBER_OFFSET, MAGIC_NUMBER ); - frame.put (frameOffset + TOKEN_OFFSET, token ); - frame.putInt (frameOffset + COMPRESSED_LENGTH_OFFSET, reverseBytes(compressedLength) ); - frame.putInt (frameOffset + UNCOMPRESSED_LENGTH_OFFSET, reverseBytes(uncompressedLength)); - frame.putInt (frameOffset + CHECKSUM_OFFSET, reverseBytes(checksum) ); - } - - private int calculateMaxFrameLength(ByteBuffer payload) - { - int payloadLength = payload.remaining(); - int blockCount = payloadLength / DEFAULT_BLOCK_LENGTH + (payloadLength % DEFAULT_BLOCK_LENGTH != 0 ? 1 : 0); - return compressor.maxCompressedLength(payloadLength) + HEADER_LENGTH * blockCount; - } -} diff --git a/src/java/org/apache/cassandra/net/HandshakeProtocol.java b/src/java/org/apache/cassandra/net/HandshakeProtocol.java index a82c1152e1..3217aeae8a 100644 --- a/src/java/org/apache/cassandra/net/HandshakeProtocol.java +++ b/src/java/org/apache/cassandra/net/HandshakeProtocol.java @@ -22,21 +22,17 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.Objects; -import com.google.common.annotations.VisibleForTesting; - import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.io.compress.BufferType; import org.apache.cassandra.io.util.DataInputBuffer; -import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputBufferFixed; import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.memory.BufferPools; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.apache.cassandra.locator.InetAddressAndPort.Serializer.inetAddressAndPortSerializer; -import static org.apache.cassandra.net.MessagingService.VERSION_30; import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.Message.validateLegacyProtocolMagic; import static org.apache.cassandra.net.Crc.*; @@ -52,8 +48,7 @@ import static org.apache.cassandra.net.OutboundConnectionSettings.*; * it will simply disconnect and reconnect with a more appropriate version. But if the version is acceptable, the connection * initiator sends the third message of the protocol, after which it considers the connection ready. */ -@VisibleForTesting -public class HandshakeProtocol +class HandshakeProtocol { static final long TIMEOUT_MILLIS = 3 * DatabaseDescriptor.getRpcTimeout(MILLISECONDS); @@ -94,8 +89,6 @@ public class HandshakeProtocol private static final int MIN_LENGTH = 8; private static final int MAX_LENGTH = 12 + InetAddressAndPort.Serializer.MAXIMUM_SIZE; - @Deprecated // this is ignored by post40 nodes, i.e. if maxMessagingVersion is set - final int requestMessagingVersion; // the messagingVersion bounds the sender will accept to initiate a connection; // if the remote peer supports any, the newest supported version will be selected; otherwise the nearest supported version final AcceptVersions acceptVersions; @@ -103,17 +96,15 @@ public class HandshakeProtocol final Framing framing; final InetAddressAndPort from; - Initiate(int requestMessagingVersion, AcceptVersions acceptVersions, ConnectionType type, Framing framing, InetAddressAndPort from) + Initiate(AcceptVersions acceptVersions, ConnectionType type, Framing framing, InetAddressAndPort from) { - this.requestMessagingVersion = requestMessagingVersion; this.acceptVersions = acceptVersions; this.type = type; this.framing = framing; this.from = from; } - @VisibleForTesting - int encodeFlags() + private int encodeFlags() { int flags = 0; if (type.isMessaging()) @@ -123,11 +114,7 @@ public class HandshakeProtocol // framing id is split over 2nd and 4th bits, for backwards compatibility flags |= ((framing.id & 1) << 2) | ((framing.id & 2) << 3); - flags |= (requestMessagingVersion << 8); - - if (requestMessagingVersion < VERSION_40 || acceptVersions.max < VERSION_40) - return flags; // for testing, permit serializing as though we are pre40 - + flags |= (acceptVersions.min << 8); // legacy (pre40) flags |= (acceptVersions.min << 16); flags |= (acceptVersions.max << 24); return flags; @@ -140,12 +127,8 @@ public class HandshakeProtocol { out.writeInt(Message.PROTOCOL_MAGIC); out.writeInt(encodeFlags()); - - if (requestMessagingVersion >= VERSION_40 && acceptVersions.max >= VERSION_40) - { - inetAddressAndPortSerializer.serialize(from, out, requestMessagingVersion); - out.writeInt(computeCrc32(buffer, 0, buffer.position())); - } + inetAddressAndPortSerializer.serialize(from, out, acceptVersions.min); + out.writeInt(computeCrc32(buffer, 0, buffer.position())); buffer.flip(); return GlobalBufferPoolAllocator.wrap(buffer); } @@ -167,9 +150,17 @@ public class HandshakeProtocol validateLegacyProtocolMagic(in.readInt()); int flags = in.readInt(); - int requestedMessagingVersion = getBits(flags, 8, 8); + // legacy pre40 messagingVersion flag + if (getBits(flags, 8, 8) < VERSION_40) + return null; + int minMessagingVersion = getBits(flags, 16, 8); int maxMessagingVersion = getBits(flags, 24, 8); + + // 5.0+ does not support pre40 + if (maxMessagingVersion < MessagingService.VERSION_40) + return null; + int framingBits = getBits(flags, 2, 1) | (getBits(flags, 4, 1) << 1); Framing framing = Framing.forId(framingBits); @@ -179,23 +170,15 @@ public class HandshakeProtocol ? ConnectionType.STREAMING : ConnectionType.fromId(getBits(flags, 0, 2)); - InetAddressAndPort from = null; + InetAddressAndPort from = inetAddressAndPortSerializer.deserialize(in, minMessagingVersion); - if (requestedMessagingVersion >= VERSION_40 && maxMessagingVersion >= MessagingService.VERSION_40) - { - from = inetAddressAndPortSerializer.deserialize(in, requestedMessagingVersion); - - int computed = computeCrc32(nio, start, nio.position()); - int read = in.readInt(); - if (read != computed) - throw new InvalidCrc(read, computed); - } + int computed = computeCrc32(nio, start, nio.position()); + int read = in.readInt(); + if (read != computed) + throw new InvalidCrc(read, computed); buf.skipBytes(nio.position() - start); - return new Initiate(requestedMessagingVersion, - minMessagingVersion == 0 && maxMessagingVersion == 0 - ? null : new AcceptVersions(minMessagingVersion, maxMessagingVersion), - type, framing, from); + return new Initiate(new AcceptVersions(minMessagingVersion, maxMessagingVersion), type, framing, from); } catch (EOFException e) @@ -204,7 +187,6 @@ public class HandshakeProtocol } } - @VisibleForTesting @Override public boolean equals(Object other) { @@ -214,17 +196,15 @@ public class HandshakeProtocol Initiate that = (Initiate)other; return this.type == that.type && this.framing == that.framing - && this.requestMessagingVersion == that.requestMessagingVersion && Objects.equals(this.acceptVersions, that.acceptVersions); } @Override public String toString() { - return String.format("Initiate(request: %d, min: %d, max: %d, type: %s, framing: %b, from: %s)", - requestMessagingVersion, - acceptVersions == null ? requestMessagingVersion : acceptVersions.min, - acceptVersions == null ? requestMessagingVersion : acceptVersions.max, + return String.format("Initiate(min: %d, max: %d, type: %s, framing: %b, from: %s)", + acceptVersions.min, + acceptVersions.max, type, framing, from); } } @@ -238,9 +218,8 @@ public class HandshakeProtocol * 1) the messaging version of the peer sending this message * 2) the negotiated messaging version if one could be accepted by both peers, * or if not the closest version that this peer could support to the ones requested - * 3) a CRC protectingn the integrity of the message + * 3) a CRC protecting the integrity of the message * - * Note that the pre40 equivalent of this message contains ONLY the messaging version of the peer. */ static class Accept { @@ -266,18 +245,7 @@ public class HandshakeProtocol return buffer; } - /** - * Respond to pre40 nodes only with our current messagingVersion - */ - static ByteBuf respondPre40(int messagingVersion, ByteBufAllocator allocator) - { - ByteBuf buffer = allocator.directBuffer(4); - buffer.clear(); - buffer.writeInt(messagingVersion); - return buffer; - } - - static Accept maybeDecode(ByteBuf in, int handshakeMessagingVersion) throws InvalidCrc + static Accept maybeDecode(ByteBuf in) throws InvalidCrc { int readerIndex = in.readerIndex(); if (in.readableBytes() < 4) @@ -285,9 +253,9 @@ public class HandshakeProtocol int maxMessagingVersion = in.readInt(); int useMessagingVersion = 0; - // if the other node is pre-4.0, it will respond only with its maxMessagingVersion - if (maxMessagingVersion < VERSION_40 || handshakeMessagingVersion < VERSION_40) - return new Accept(useMessagingVersion, maxMessagingVersion); + // pre-4.0 not supported, close the connection + if (maxMessagingVersion < VERSION_40) + return null; if (in.readableBytes() < 8) { @@ -305,7 +273,6 @@ public class HandshakeProtocol return new Accept(useMessagingVersion, maxMessagingVersion); } - @VisibleForTesting @Override public boolean equals(Object other) { @@ -321,91 +288,6 @@ public class HandshakeProtocol } } - /** - * The third message of the handshake, sent by pre40 nodes on reception of {@link Accept}. - * This message contains: - * 1) The connection initiator's {@link org.apache.cassandra.net.MessagingService#current_version} (4 bytes). - * This indicates the max messaging version supported by this node. - * 2) The connection initiator's broadcast address as encoded by {@link InetAddressAndPort.Serializer}. - * This can be either 7 bytes for an IPv4 address, or 19 bytes for an IPv6 one, post40. - * This can be either 5 bytes for an IPv4 address, or 17 bytes for an IPv6 one, pre40. - *

- * This message concludes the legacy handshake protocol. - */ - static class ConfirmOutboundPre40 - { - private static final int MAX_LENGTH = 4 + InetAddressAndPort.Serializer.MAXIMUM_SIZE; - - final int maxMessagingVersion; - final InetAddressAndPort from; - - ConfirmOutboundPre40(int maxMessagingVersion, InetAddressAndPort from) - { - this.maxMessagingVersion = maxMessagingVersion; - this.from = from; - } - - ByteBuf encode() - { - ByteBuffer buffer = BufferPools.forNetworking().get(MAX_LENGTH, BufferType.OFF_HEAP); - try (DataOutputBufferFixed out = new DataOutputBufferFixed(buffer)) - { - out.writeInt(maxMessagingVersion); - // pre-4.0 nodes should only receive the address, never port, and it's ok to hardcode VERSION_30 - inetAddressAndPortSerializer.serialize(from, out, VERSION_30); - buffer.flip(); - return GlobalBufferPoolAllocator.wrap(buffer); - } - catch (IOException e) - { - throw new IllegalStateException(e); - } - } - - @SuppressWarnings("resource") - static ConfirmOutboundPre40 maybeDecode(ByteBuf in) - { - ByteBuffer nio = in.nioBuffer(); - int start = nio.position(); - DataInputPlus input = new DataInputBuffer(nio, false); - try - { - int version = input.readInt(); - InetAddressAndPort address = inetAddressAndPortSerializer.deserialize(input, version); - in.skipBytes(nio.position() - start); - return new ConfirmOutboundPre40(version, address); - } - catch (EOFException e) - { - // makes the assumption we didn't have enough bytes to deserialize an IPv6 address, - // as we only check the MIN_LENGTH of the buf. - return null; - } - catch (IOException e) - { - throw new IllegalStateException(e); - } - } - - @VisibleForTesting - @Override - public boolean equals(Object other) - { - if (!(other instanceof ConfirmOutboundPre40)) - return false; - - ConfirmOutboundPre40 that = (ConfirmOutboundPre40) other; - return this.maxMessagingVersion == that.maxMessagingVersion - && Objects.equals(this.from, that.from); - } - - @Override - public String toString() - { - return String.format("ConfirmOutboundPre40(maxMessagingVersion: %d; address: %s)", maxMessagingVersion, from); - } - } - private static int getBits(int packed, int start, int count) { return (packed >>> start) & ~(-1 << count); diff --git a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java index e4de527dfc..b6bd17567c 100644 --- a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java +++ b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java @@ -273,7 +273,6 @@ public class InboundConnectionInitiator private final InboundConnectionSettings settings; private HandshakeProtocol.Initiate initiate; - private HandshakeProtocol.ConfirmOutboundPre40 confirmOutboundPre40; /** * A future the essentially places a timeout on how long we'll wait for the peer @@ -301,7 +300,6 @@ public class InboundConnectionInitiator protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { if (initiate == null) initiate(ctx, in); - else if (initiate.acceptVersions == null && confirmOutboundPre40 == null) confirmPre40(ctx, in); else throw new IllegalStateException("Should no longer be on pipeline"); } @@ -321,78 +319,40 @@ public class InboundConnectionInitiator return; } - if (initiate.acceptVersions != null) + assert initiate.acceptVersions != null; + logger.trace("Connection version {} (min {}) from {}", initiate.acceptVersions.max, initiate.acceptVersions.min, initiate.from); + + final AcceptVersions accept; + + if (initiate.type.isStreaming()) + accept = settings.acceptStreaming; + else + accept = settings.acceptMessaging; + + int useMessagingVersion = max(accept.min, min(accept.max, initiate.acceptVersions.max)); + ByteBuf flush = new HandshakeProtocol.Accept(useMessagingVersion, accept.max).encode(ctx.alloc()); + + AsyncChannelPromise.writeAndFlush(ctx, flush, (ChannelFutureListener) future -> { + if (!future.isSuccess()) + exceptionCaught(future.channel(), future.cause()); + }); + + if (initiate.acceptVersions.min > accept.max) { - logger.trace("Connection version {} (min {}) from {}", initiate.acceptVersions.max, initiate.acceptVersions.min, initiate.from); - - final AcceptVersions accept; - - if (initiate.type.isStreaming()) - accept = settings.acceptStreaming; - else - accept = settings.acceptMessaging; - - int useMessagingVersion = max(accept.min, min(accept.max, initiate.acceptVersions.max)); - ByteBuf flush = new HandshakeProtocol.Accept(useMessagingVersion, accept.max).encode(ctx.alloc()); - - AsyncChannelPromise.writeAndFlush(ctx, flush, (ChannelFutureListener) future -> { - if (!future.isSuccess()) - exceptionCaught(future.channel(), future.cause()); - }); - - if (initiate.acceptVersions.min > accept.max) - { - logger.info("peer {} only supports messaging versions higher ({}) than this node supports ({})", ctx.channel().remoteAddress(), initiate.acceptVersions.min, current_version); - failHandshake(ctx); - return; - } - else if (initiate.acceptVersions.max < accept.min) - { - logger.info("peer {} only supports messaging versions lower ({}) than this node supports ({})", ctx.channel().remoteAddress(), initiate.acceptVersions.max, minimum_version); - failHandshake(ctx); - return; - } - else - { - if (initiate.type.isStreaming()) - setupStreamingPipeline(initiate.from, ctx); - else - setupMessagingPipeline(initiate.from, useMessagingVersion, initiate.acceptVersions.max, ctx.pipeline()); - } + logger.info("peer {} only supports messaging versions higher ({}) than this node supports ({})", ctx.channel().remoteAddress(), initiate.acceptVersions.min, current_version); + failHandshake(ctx); + } + else if (initiate.acceptVersions.max < accept.min) + { + logger.info("peer {} only supports messaging versions lower ({}) than this node supports ({})", ctx.channel().remoteAddress(), initiate.acceptVersions.max, minimum_version); + failHandshake(ctx); } else { - int version = initiate.requestMessagingVersion; - assert version < VERSION_40 && version >= settings.acceptMessaging.min; - logger.trace("Connection version {} from {}", version, ctx.channel().remoteAddress()); - if (initiate.type.isStreaming()) - { - // streaming connections are per-session and have a fixed version. we can't do anything with a wrong-version stream connection, so drop it. - if (version != settings.acceptStreaming.max) - { - logger.warn("Received stream using protocol version {} (my version {}). Terminating connection", version, settings.acceptStreaming.max); - failHandshake(ctx); - return; - } setupStreamingPipeline(initiate.from, ctx); - } else - { - // if this version is < the MS version the other node is trying - // to connect with, the other node will disconnect - ByteBuf response = HandshakeProtocol.Accept.respondPre40(settings.acceptMessaging.max, ctx.alloc()); - AsyncChannelPromise.writeAndFlush(ctx, response, - (ChannelFutureListener) future -> { - if (!future.isSuccess()) - exceptionCaught(future.channel(), future.cause()); - }); - - if (version < VERSION_30) - throw new IOException(String.format("Unable to read obsolete message version %s from %s; The earliest version supported is 3.0.0", version, ctx.channel().remoteAddress())); - - // we don't setup the messaging pipeline here, as the legacy messaging handshake requires one more message to finish - } + setupMessagingPipeline(initiate.from, useMessagingVersion, initiate.acceptVersions.max, ctx.pipeline()); } } @@ -406,21 +366,6 @@ public class InboundConnectionInitiator return ctx.pipeline().get(SslHandler.class) != null; } - /** - * Handles the third (and last) message in the internode messaging handshake protocol for pre40 nodes. - * Grabs the protocol version and IP addr the peer wants to use. - */ - @VisibleForTesting - void confirmPre40(ChannelHandlerContext ctx, ByteBuf in) - { - confirmOutboundPre40 = HandshakeProtocol.ConfirmOutboundPre40.maybeDecode(in); - if (confirmOutboundPre40 == null) - return; - - logger.trace("Received third handshake message from peer {}, message = {}", ctx.channel().remoteAddress(), confirmOutboundPre40); - setupMessagingPipeline(confirmOutboundPre40.from, initiate.requestMessagingVersion, confirmOutboundPre40.maxMessagingVersion, ctx.pipeline()); - } - @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { @@ -494,8 +439,7 @@ public class InboundConnectionInitiator // we can't infer the type of streaming connection at this point, // so we use CONTROL unconditionally; it's ugly but does what we want // (establishes an AsyncStreamingInputPlus) - NettyStreamingChannel streamingChannel = - new NettyStreamingChannel(current_version, channel, StreamingChannel.Kind.CONTROL); + NettyStreamingChannel streamingChannel = new NettyStreamingChannel(channel, StreamingChannel.Kind.CONTROL); pipeline.replace(this, "streamInbound", streamingChannel); executorFactory().startThread(String.format("Stream-Deserializer-%s-%s", from, channel.id()), new StreamDeserializingTask(null, streamingChannel, current_version)); @@ -533,26 +477,17 @@ public class InboundConnectionInitiator { case LZ4: { - if (useMessagingVersion >= VERSION_40) - frameDecoder = FrameDecoderLZ4.fast(allocator); - else - frameDecoder = new FrameDecoderLegacyLZ4(allocator, useMessagingVersion); + frameDecoder = FrameDecoderLZ4.fast(allocator); break; } case CRC: { - if (useMessagingVersion >= VERSION_40) - { - frameDecoder = FrameDecoderCrc.create(allocator); - break; - } + frameDecoder = FrameDecoderCrc.create(allocator); + break; } case UNPROTECTED: { - if (useMessagingVersion >= VERSION_40) - frameDecoder = new FrameDecoderUnprotected(allocator); - else - frameDecoder = new FrameDecoderLegacy(allocator, useMessagingVersion); + frameDecoder = new FrameDecoderUnprotected(allocator); break; } default: diff --git a/src/java/org/apache/cassandra/net/InboundMessageHandler.java b/src/java/org/apache/cassandra/net/InboundMessageHandler.java index e12fcececa..5fb7fdcb93 100644 --- a/src/java/org/apache/cassandra/net/InboundMessageHandler.java +++ b/src/java/org/apache/cassandra/net/InboundMessageHandler.java @@ -125,7 +125,7 @@ public class InboundMessageHandler extends AbstractMessageHandler long currentTimeNanos = approxTime.now(); Header header = serializer.extractHeader(buf, peer, currentTimeNanos, version); long timeElapsed = currentTimeNanos - header.createdAtNanos; - int size = serializer.inferMessageSize(buf, buf.position(), buf.limit(), version); + int size = serializer.inferMessageSize(buf, buf.position(), buf.limit()); if (approxTime.isAfter(currentTimeNanos, header.expiresAtNanos)) { @@ -211,7 +211,7 @@ public class InboundMessageHandler extends AbstractMessageHandler long currentTimeNanos = approxTime.now(); Header header = serializer.extractHeader(buf, peer, currentTimeNanos, version); - int size = serializer.inferMessageSize(buf, buf.position(), buf.limit(), version); + int size = serializer.inferMessageSize(buf, buf.position(), buf.limit()); boolean expired = approxTime.isAfter(currentTimeNanos, header.expiresAtNanos); if (!expired && !acquireCapacity(endpointReserve, globalReserve, size, currentTimeNanos, header.expiresAtNanos)) diff --git a/src/java/org/apache/cassandra/net/LegacyFlag.java b/src/java/org/apache/cassandra/net/LegacyFlag.java deleted file mode 100644 index b2781a1fa7..0000000000 --- a/src/java/org/apache/cassandra/net/LegacyFlag.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.cassandra.net; - -import java.io.IOException; - -import com.google.common.base.Preconditions; - -import org.apache.cassandra.io.IVersionedSerializer; -import org.apache.cassandra.io.util.DataInputPlus; -import org.apache.cassandra.io.util.DataOutputPlus; - -/** - * Before 4.0 introduced flags field to {@link Message}, we used to encode flags in params field, - * using a dummy value (single byte set to 0). From now on, {@link MessageFlag} should be extended - * instead. - * - * Once 3.0/3.11 compatibility is phased out, this class should be removed. - */ -@Deprecated -final class LegacyFlag -{ - static final LegacyFlag instance = new LegacyFlag(); - - private LegacyFlag() - { - } - - static IVersionedSerializer serializer = new IVersionedSerializer() - { - public void serialize(LegacyFlag param, DataOutputPlus out, int version) throws IOException - { - Preconditions.checkArgument(param == instance); - out.write(0); - } - - public LegacyFlag deserialize(DataInputPlus in, int version) throws IOException - { - byte b = in.readByte(); - assert b == 0; - return instance; - } - - public long serializedSize(LegacyFlag param, int version) - { - Preconditions.checkArgument(param == instance); - return 1; - } - }; -} diff --git a/src/java/org/apache/cassandra/net/LegacyLZ4Constants.java b/src/java/org/apache/cassandra/net/LegacyLZ4Constants.java deleted file mode 100644 index f4fca446fa..0000000000 --- a/src/java/org/apache/cassandra/net/LegacyLZ4Constants.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.cassandra.net; - -abstract class LegacyLZ4Constants -{ - static final int XXHASH_SEED = 0x9747B28C; - - static final int HEADER_LENGTH = 8 // magic number - + 1 // token - + 4 // compressed length - + 4 // uncompressed length - + 4; // checksum - - static final long MAGIC_NUMBER = (long) 'L' << 56 - | (long) 'Z' << 48 - | (long) '4' << 40 - | (long) 'B' << 32 - | 'l' << 24 - | 'o' << 16 - | 'c' << 8 - | 'k'; - - // offsets of header fields - static final int MAGIC_NUMBER_OFFSET = 0; - static final int TOKEN_OFFSET = 8; - static final int COMPRESSED_LENGTH_OFFSET = 9; - static final int UNCOMPRESSED_LENGTH_OFFSET = 13; - static final int CHECKSUM_OFFSET = 17; - - static final int DEFAULT_BLOCK_LENGTH = 1 << 15; // 32 KiB - static final int MAX_BLOCK_LENGTH = 1 << 25; // 32 MiB - - static final int BLOCK_TYPE_NON_COMPRESSED = 0x10; - static final int BLOCK_TYPE_COMPRESSED = 0x20; - - // xxhash to Checksum adapter discards most significant nibble of value ¯\_(ツ)_/¯ - static final int XXHASH_MASK = 0xFFFFFFF; -} diff --git a/src/java/org/apache/cassandra/net/Message.java b/src/java/org/apache/cassandra/net/Message.java index 1f4a48bb11..da50727d6f 100644 --- a/src/java/org/apache/cassandra/net/Message.java +++ b/src/java/org/apache/cassandra/net/Message.java @@ -50,12 +50,8 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.apache.cassandra.db.TypeSizes.sizeof; import static org.apache.cassandra.db.TypeSizes.sizeofUnsignedVInt; -import static org.apache.cassandra.locator.InetAddressAndPort.Serializer.inetAddressAndPortSerializer; -import static org.apache.cassandra.net.MessagingService.VERSION_3014; -import static org.apache.cassandra.net.MessagingService.VERSION_30; import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.MessagingService.VERSION_50; -import static org.apache.cassandra.net.MessagingService.instance; import static org.apache.cassandra.utils.FBUtilities.getBroadcastAddressAndPort; import static org.apache.cassandra.utils.MonotonicClock.Global.approxTime; import static org.apache.cassandra.utils.vint.VIntCoding.*; @@ -711,15 +707,17 @@ public class Message public void serialize(Message message, DataOutputPlus out, int version) throws IOException { - if (version >= VERSION_40) - serializePost40(message, out, version); - else - serializePre40(message, out, version); + serializeHeader(message.header, out, version); + out.writeUnsignedVInt32(message.payloadSize(version)); + message.verb().serializer().serialize(message.payload, out, version); } public Message deserialize(DataInputPlus in, InetAddressAndPort peer, int version) throws IOException { - return version >= VERSION_40 ? deserializePost40(in, peer, version) : deserializePre40(in, version); + Header header = deserializeHeader(in, peer, version); + skipUnsignedVInt(in); // payload size, not needed by payload deserializer + T payload = (T) header.verb.serializer().deserialize(in, version); + return new Message<>(header, payload); } /** @@ -729,20 +727,64 @@ public class Message */ public Message deserialize(DataInputPlus in, Header header, int version) throws IOException { - return version >= VERSION_40 ? deserializePost40(in, header, version) : deserializePre40(in, header, version); + skipHeader(in); + skipUnsignedVInt(in); // payload size, not needed by payload deserializer + T payload = (T) header.verb.serializer().deserialize(in, version); + return new Message<>(header, payload); } private int serializedSize(Message message, int version) { - return version >= VERSION_40 ? serializedSizePost40(message, version) : serializedSizePre40(message, version); + long size = 0; + size += serializedHeader(message.header, version); + int payloadSize = message.payloadSize(version); + size += sizeofUnsignedVInt(payloadSize) + payloadSize; + return Ints.checkedCast(size); } /** * Size of the next message in the stream. Returns -1 if there aren't sufficient bytes read yet to determine size. */ - int inferMessageSize(ByteBuffer buf, int index, int limit, int version) throws InvalidLegacyProtocolMagic + int inferMessageSize(ByteBuffer buf, int readerIndex, int readerLimit) { - int size = version >= VERSION_40 ? inferMessageSizePost40(buf, index, limit) : inferMessageSizePre40(buf, index, limit); + int index = readerIndex; + + int idSize = computeUnsignedVIntSize(buf, index, readerLimit); + if (idSize < 0) + return -1; // not enough bytes to read id + index += idSize; + + index += CREATION_TIME_SIZE; + if (index > readerLimit) + return -1; + + int expirationSize = computeUnsignedVIntSize(buf, index, readerLimit); + if (expirationSize < 0) + return -1; + index += expirationSize; + + int verbIdSize = computeUnsignedVIntSize(buf, index, readerLimit); + if (verbIdSize < 0) + return -1; + index += verbIdSize; + + int flagsSize = computeUnsignedVIntSize(buf, index, readerLimit); + if (flagsSize < 0) + return -1; + index += flagsSize; + + int paramsSize = extractParamsSize(buf, index, readerLimit); + if (paramsSize < 0) + return -1; + index += paramsSize; + + long payloadSize = getUnsignedVInt(buf, index, readerLimit); + if (payloadSize < 0) + return -1; + index += computeUnsignedVIntSize(payloadSize) + payloadSize; + + int size = index - readerIndex; + if (size > DatabaseDescriptor.getInternodeMaxMessageSizeInBytes()) throw new OversizedMessageException(size); return size; @@ -757,71 +799,6 @@ public class Message * It's assumed that the provided buffer contains all the bytes necessary to deserialize the header fully. */ Header extractHeader(ByteBuffer buf, InetAddressAndPort from, long currentTimeNanos, int version) throws IOException - { - return version >= VERSION_40 - ? extractHeaderPost40(buf, from, currentTimeNanos, version) - : extractHeaderPre40(buf, currentTimeNanos, version); - } - - private static long getExpiresAtNanos(long createdAtNanos, long currentTimeNanos, long expirationPeriodNanos) - { - if (!DatabaseDescriptor.hasCrossNodeTimeout() || createdAtNanos > currentTimeNanos) - createdAtNanos = currentTimeNanos; - return createdAtNanos + expirationPeriodNanos; - } - - /* - * 4.0 ser/deser - */ - - private void serializeHeaderPost40(Header header, DataOutputPlus out, int version) throws IOException - { - out.writeUnsignedVInt(header.id); - // int cast cuts off the high-order half of the timestamp, which we can assume remains - // the same between now and when the recipient reconstructs it. - out.writeInt((int) approxTime.translate().toMillisSinceEpoch(header.createdAtNanos)); - out.writeUnsignedVInt(NANOSECONDS.toMillis(header.expiresAtNanos - header.createdAtNanos)); - out.writeUnsignedVInt32(header.verb.id); - out.writeUnsignedVInt32(header.flags); - serializeParams(header.params, out, version); - } - - private Header deserializeHeaderPost40(DataInputPlus in, InetAddressAndPort peer, int version) throws IOException - { - long id = in.readUnsignedVInt(); - long currentTimeNanos = approxTime.now(); - MonotonicClockTranslation timeSnapshot = approxTime.translate(); - long creationTimeNanos = calculateCreationTimeNanos(in.readInt(), timeSnapshot, currentTimeNanos); - long expiresAtNanos = getExpiresAtNanos(creationTimeNanos, currentTimeNanos, TimeUnit.MILLISECONDS.toNanos(in.readUnsignedVInt())); - Verb verb = Verb.fromId(in.readUnsignedVInt32()); - int flags = in.readUnsignedVInt32(); - Map params = deserializeParams(in, version); - return new Header(id, verb, peer, creationTimeNanos, expiresAtNanos, flags, params); - } - - private void skipHeaderPost40(DataInputPlus in) throws IOException - { - skipUnsignedVInt(in); // id - in.skipBytesFully(4); // createdAt - skipUnsignedVInt(in); // expiresIn - skipUnsignedVInt(in); // verb - skipUnsignedVInt(in); // flags - skipParamsPost40(in); // params - } - - private int serializedHeaderSizePost40(Header header, int version) - { - long size = 0; - size += sizeofUnsignedVInt(header.id); - size += CREATION_TIME_SIZE; - size += sizeofUnsignedVInt(NANOSECONDS.toMillis(header.expiresAtNanos - header.createdAtNanos)); - size += sizeofUnsignedVInt(header.verb.id); - size += sizeofUnsignedVInt(header.flags); - size += serializedParamsSize(header.params, version); - return Ints.checkedCast(size); - } - - private Header extractHeaderPost40(ByteBuffer buf, InetAddressAndPort from, long currentTimeNanos, int version) throws IOException { MonotonicClockTranslation timeSnapshot = approxTime.translate(); @@ -850,292 +827,60 @@ public class Message return new Header(id, verb, from, createdAtNanos, expiresAtNanos, flags, params); } - private void serializePost40(Message message, DataOutputPlus out, int version) throws IOException + private static long getExpiresAtNanos(long createdAtNanos, long currentTimeNanos, long expirationPeriodNanos) { - serializeHeaderPost40(message.header, out, version); - out.writeUnsignedVInt32(message.payloadSize(version)); - message.verb().serializer().serialize(message.payload, out, version); + if (!DatabaseDescriptor.hasCrossNodeTimeout() || createdAtNanos > currentTimeNanos) + createdAtNanos = currentTimeNanos; + return createdAtNanos + expirationPeriodNanos; } - private Message deserializePost40(DataInputPlus in, InetAddressAndPort peer, int version) throws IOException + private void serializeHeader(Header header, DataOutputPlus out, int version) throws IOException { - Header header = deserializeHeaderPost40(in, peer, version); - skipUnsignedVInt(in); // payload size, not needed by payload deserializer - T payload = (T) header.verb.serializer().deserialize(in, version); - return new Message<>(header, payload); - } - - private Message deserializePost40(DataInputPlus in, Header header, int version) throws IOException - { - skipHeaderPost40(in); - skipUnsignedVInt(in); // payload size, not needed by payload deserializer - T payload = (T) header.verb.serializer().deserialize(in, version); - return new Message<>(header, payload); - } - - private int serializedSizePost40(Message message, int version) - { - long size = 0; - size += serializedHeaderSizePost40(message.header, version); - int payloadSize = message.payloadSize(version); - size += sizeofUnsignedVInt(payloadSize) + payloadSize; - return Ints.checkedCast(size); - } - - private int inferMessageSizePost40(ByteBuffer buf, int readerIndex, int readerLimit) - { - int index = readerIndex; - - int idSize = computeUnsignedVIntSize(buf, index, readerLimit); - if (idSize < 0) - return -1; // not enough bytes to read id - index += idSize; - - index += CREATION_TIME_SIZE; - if (index > readerLimit) - return -1; - - int expirationSize = computeUnsignedVIntSize(buf, index, readerLimit); - if (expirationSize < 0) - return -1; - index += expirationSize; - - int verbIdSize = computeUnsignedVIntSize(buf, index, readerLimit); - if (verbIdSize < 0) - return -1; - index += verbIdSize; - - int flagsSize = computeUnsignedVIntSize(buf, index, readerLimit); - if (flagsSize < 0) - return -1; - index += flagsSize; - - int paramsSize = extractParamsSizePost40(buf, index, readerLimit); - if (paramsSize < 0) - return -1; - index += paramsSize; - - long payloadSize = getUnsignedVInt(buf, index, readerLimit); - if (payloadSize < 0) - return -1; - index += computeUnsignedVIntSize(payloadSize) + payloadSize; - - return index - readerIndex; - } - - /* - * legacy ser/deser - */ - - private void serializeHeaderPre40(Header header, DataOutputPlus out, int version) throws IOException - { - out.writeInt(PROTOCOL_MAGIC); - out.writeInt(Ints.checkedCast(header.id)); + out.writeUnsignedVInt(header.id); // int cast cuts off the high-order half of the timestamp, which we can assume remains // the same between now and when the recipient reconstructs it. out.writeInt((int) approxTime.translate().toMillisSinceEpoch(header.createdAtNanos)); - inetAddressAndPortSerializer.serialize(header.from, out, version); - out.writeInt(header.verb.toPre40Verb().id); - serializeParams(addFlagsToLegacyParams(header.params, header.flags), out, version); + out.writeUnsignedVInt(NANOSECONDS.toMillis(header.expiresAtNanos - header.createdAtNanos)); + out.writeUnsignedVInt32(header.verb.id); + out.writeUnsignedVInt32(header.flags); + serializeParams(header.params, out, version); } - private Header deserializeHeaderPre40(DataInputPlus in, int version) throws IOException + private Header deserializeHeader(DataInputPlus in, InetAddressAndPort peer, int version) throws IOException { - validateLegacyProtocolMagic(in.readInt()); - int id = in.readInt(); + long id = in.readUnsignedVInt(); long currentTimeNanos = approxTime.now(); MonotonicClockTranslation timeSnapshot = approxTime.translate(); long creationTimeNanos = calculateCreationTimeNanos(in.readInt(), timeSnapshot, currentTimeNanos); - InetAddressAndPort from = inetAddressAndPortSerializer.deserialize(in, version); - Verb verb = Verb.fromId(in.readInt()); + long expiresAtNanos = getExpiresAtNanos(creationTimeNanos, currentTimeNanos, TimeUnit.MILLISECONDS.toNanos(in.readUnsignedVInt())); + Verb verb = Verb.fromId(in.readUnsignedVInt32()); + int flags = in.readUnsignedVInt32(); Map params = deserializeParams(in, version); - int flags = removeFlagsFromLegacyParams(params); - return new Header(id, verb, from, creationTimeNanos, verb.expiresAtNanos(creationTimeNanos), flags, params); + return new Header(id, verb, peer, creationTimeNanos, expiresAtNanos, flags, params); } - private static final int PRE_40_MESSAGE_PREFIX_SIZE = 12; // protocol magic + id + createdAt - - private void skipHeaderPre40(DataInputPlus in) throws IOException + private void skipHeader(DataInputPlus in) throws IOException { - in.skipBytesFully(PRE_40_MESSAGE_PREFIX_SIZE); // magic, id, createdAt - in.skipBytesFully(in.readByte()); // from - in.skipBytesFully(4); // verb - skipParamsPre40(in); // params + skipUnsignedVInt(in); // id + in.skipBytesFully(4); // createdAt + skipUnsignedVInt(in); // expiresIn + skipUnsignedVInt(in); // verb + skipUnsignedVInt(in); // flags + skipParams(in); // params } - private int serializedHeaderSizePre40(Header header, int version) + private int serializedHeader(Header header, int version) { long size = 0; - size += PRE_40_MESSAGE_PREFIX_SIZE; - size += inetAddressAndPortSerializer.serializedSize(header.from, version); - size += sizeof(header.verb.id); - size += serializedParamsSize(addFlagsToLegacyParams(header.params, header.flags), version); + size += sizeofUnsignedVInt(header.id); + size += CREATION_TIME_SIZE; + size += sizeofUnsignedVInt(NANOSECONDS.toMillis(header.expiresAtNanos - header.createdAtNanos)); + size += sizeofUnsignedVInt(header.verb.id); + size += sizeofUnsignedVInt(header.flags); + size += serializedParamsSize(header.params, version); return Ints.checkedCast(size); } - private Header extractHeaderPre40(ByteBuffer buf, long currentTimeNanos, int version) throws IOException - { - MonotonicClockTranslation timeSnapshot = approxTime.translate(); - - int index = buf.position(); - - index += 4; // protocol magic - - long id = buf.getInt(index); - index += 4; - - int createdAtMillis = buf.getInt(index); - index += 4; - - InetAddressAndPort from = inetAddressAndPortSerializer.extract(buf, index); - index += 1 + buf.get(index); - - Verb verb = Verb.fromId(buf.getInt(index)); - index += 4; - - Map params = extractParams(buf, index, version); - int flags = removeFlagsFromLegacyParams(params); - - long createdAtNanos = calculateCreationTimeNanos(createdAtMillis, timeSnapshot, currentTimeNanos); - long expiresAtNanos = verb.expiresAtNanos(createdAtNanos); - - return new Header(id, verb, from, createdAtNanos, expiresAtNanos, flags, params); - } - - private void serializePre40(Message message, DataOutputPlus out, int version) throws IOException - { - if (message.isFailureResponse()) - message = toPre40FailureResponse(message); - - serializeHeaderPre40(message.header, out, version); - - if (message.payload != null && message.payload != NoPayload.noPayload) - { - int payloadSize = message.payloadSize(version); - out.writeInt(payloadSize); - message.getPayloadSerializer().serialize(message.payload, out, version); - } - else - { - out.writeInt(0); - } - } - - private Message deserializePre40(DataInputPlus in, int version) throws IOException - { - Header header = deserializeHeaderPre40(in, version); - return deserializePre40(in, header, false, version); - } - - private Message deserializePre40(DataInputPlus in, Header header, int version) throws IOException - { - return deserializePre40(in, header, true, version); - } - - private Message deserializePre40(DataInputPlus in, Header header, boolean skipHeader, int version) throws IOException - { - if (skipHeader) - skipHeaderPre40(in); - - int payloadSize = in.readInt(); - T payload = deserializePayloadPre40(in, version, getPayloadSerializer(header.verb, header.id, header.from), payloadSize); - - Message message = new Message<>(header, payload); - - return header.params.containsKey(ParamType.FAILURE_RESPONSE) - ? (Message) toPost40FailureResponse(message) - : message; - } - - private T deserializePayloadPre40(DataInputPlus in, int version, IVersionedAsymmetricSerializer serializer, int payloadSize) throws IOException - { - if (payloadSize == 0 || serializer == null) - { - // if there's no deserializer for the verb, skip the payload bytes to leave - // the stream in a clean state (for the next message) - in.skipBytesFully(payloadSize); - return null; - } - - return serializer.deserialize(in, version); - } - - private int serializedSizePre40(Message message, int version) - { - if (message.isFailureResponse()) - message = toPre40FailureResponse(message); - - long size = 0; - size += serializedHeaderSizePre40(message.header, version); - int payloadSize = message.payloadSize(version); - size += sizeof(payloadSize); - size += payloadSize; - return Ints.checkedCast(size); - } - - private int inferMessageSizePre40(ByteBuffer buf, int readerIndex, int readerLimit) throws InvalidLegacyProtocolMagic - { - int index = readerIndex; - // protocol magic - index += 4; - if (index > readerLimit) - return -1; - validateLegacyProtocolMagic(buf.getInt(index - 4)); - - // rest of prefix - index += PRE_40_MESSAGE_PREFIX_SIZE - 4; - // ip address - index += 1; - if (index > readerLimit) - return -1; - index += buf.get(index - 1); - // verb - index += 4; - if (index > readerLimit) - return -1; - - int paramsSize = extractParamsSizePre40(buf, index, readerLimit); - if (paramsSize < 0) - return -1; - index += paramsSize; - - // payload - index += 4; - - if (index > readerLimit) - return -1; - index += buf.getInt(index - 4); - - return index - readerIndex; - } - - private Message toPre40FailureResponse(Message post40) - { - Map params = new EnumMap<>(ParamType.class); - params.putAll(post40.header.params); - - params.put(ParamType.FAILURE_RESPONSE, LegacyFlag.instance); - params.put(ParamType.FAILURE_REASON, post40.payload); - - Header header = new Header(post40.id(), post40.verb().toPre40Verb(), post40.from(), post40.createdAtNanos(), post40.expiresAtNanos(), 0, params); - return new Message<>(header, NoPayload.noPayload); - } - - private Message toPost40FailureResponse(Message pre40) - { - Map params = new EnumMap<>(ParamType.class); - params.putAll(pre40.header.params); - - params.remove(ParamType.FAILURE_RESPONSE); - - RequestFailureReason reason = (RequestFailureReason) params.remove(ParamType.FAILURE_REASON); - if (null == reason) - reason = RequestFailureReason.UNKNOWN; - - Header header = new Header(pre40.id(), Verb.FAILURE_RSP, pre40.from(), pre40.createdAtNanos(), pre40.expiresAtNanos(), pre40.header.flags, params); - return new Message<>(header, reason); - } - /* * created at + cross-node */ @@ -1184,59 +929,20 @@ public class Message * param ser/deser */ - private Map addFlagsToLegacyParams(Map params, int flags) - { - if (flags == 0) - return params; - - Map extended = new EnumMap<>(ParamType.class); - extended.putAll(params); - - if (MessageFlag.CALL_BACK_ON_FAILURE.isIn(flags)) - extended.put(ParamType.FAILURE_CALLBACK, LegacyFlag.instance); - - if (MessageFlag.TRACK_REPAIRED_DATA.isIn(flags)) - extended.put(ParamType.TRACK_REPAIRED_DATA, LegacyFlag.instance); - - return extended; - } - - private int removeFlagsFromLegacyParams(Map params) - { - int flags = 0; - - if (null != params.remove(ParamType.FAILURE_CALLBACK)) - flags = MessageFlag.CALL_BACK_ON_FAILURE.addTo(flags); - - if (null != params.remove(ParamType.TRACK_REPAIRED_DATA)) - flags = MessageFlag.TRACK_REPAIRED_DATA.addTo(flags); - - return flags; - } - private void serializeParams(Map params, DataOutputPlus out, int version) throws IOException { - if (version >= VERSION_40) - out.writeUnsignedVInt32(params.size()); - else - out.writeInt(params.size()); + out.writeUnsignedVInt32(params.size()); for (Map.Entry kv : params.entrySet()) { ParamType type = kv.getKey(); - if (version >= VERSION_40) - out.writeUnsignedVInt32(type.id); - else - out.writeUTF(type.legacyAlias); + out.writeUnsignedVInt32(type.id); IVersionedSerializer serializer = type.serializer; Object value = kv.getValue(); int length = Ints.checkedCast(serializer.serializedSize(value, version)); - if (version >= VERSION_40) - out.writeUnsignedVInt32(length); - else - out.writeInt(length); + out.writeUnsignedVInt32(length); serializer.serialize(value, out, version); } @@ -1244,7 +950,7 @@ public class Message private Map deserializeParams(DataInputPlus in, int version) throws IOException { - int count = version >= VERSION_40 ? in.readUnsignedVInt32() : in.readInt(); + int count = in.readUnsignedVInt32(); if (count == 0) return NO_PARAMS; @@ -1253,25 +959,13 @@ public class Message for (int i = 0; i < count; i++) { - ParamType type = version >= VERSION_40 - ? ParamType.lookUpById(in.readUnsignedVInt32()) - : ParamType.lookUpByAlias(in.readUTF()); + ParamType type = ParamType.lookUpById(in.readUnsignedVInt32()); - int length = version >= VERSION_40 - ? in.readUnsignedVInt32() - : in.readInt(); + int length = in.readUnsignedVInt32(); if (null != type) { - // Have to special case deserializer as pre-4.0 needs length to decode correctly - if (version < VERSION_40 && type == ParamType.RESPOND_TO) - { - params.put(type, InetAddressAndPort.FwdFrmSerializer.fwdFrmSerializer.pre40DeserializeWithLength(in, version, length)); - } - else - { - params.put(type, type.serializer.deserialize(in, version)); - } + params.put(type, type.serializer.deserialize(in, version)); } else { @@ -1282,12 +976,9 @@ public class Message return params; } - /* - * Extract post-4.0 params map from a ByteBuffer without modifying it. - */ private Map extractParams(ByteBuffer buf, int readerIndex, int version) throws IOException { - long count = version >= VERSION_40 ? getUnsignedVInt(buf, readerIndex) : buf.getInt(readerIndex); + long count = getUnsignedVInt(buf, readerIndex); if (count == 0) return NO_PARAMS; @@ -1305,7 +996,7 @@ public class Message } } - private void skipParamsPost40(DataInputPlus in) throws IOException + private void skipParams(DataInputPlus in) throws IOException { int count = in.readUnsignedVInt32(); @@ -1316,22 +1007,9 @@ public class Message } } - private void skipParamsPre40(DataInputPlus in) throws IOException - { - int count = in.readInt(); - - for (int i = 0; i < count; i++) - { - in.skipBytesFully(in.readShort()); - in.skipBytesFully(in.readInt()); - } - } - private long serializedParamsSize(Map params, int version) { - long size = version >= VERSION_40 - ? computeUnsignedVIntSize(params.size()) - : sizeof(params.size()); + long size = computeUnsignedVIntSize(params.size()); for (Map.Entry kv : params.entrySet()) { @@ -1340,10 +1018,7 @@ public class Message long valueLength = type.serializer.serializedSize(value, version); - if (version >= VERSION_40) - size += sizeofUnsignedVInt(type.id) + sizeofUnsignedVInt(valueLength); - else - size += sizeof(type.legacyAlias) + 4; + size += sizeofUnsignedVInt(type.id) + sizeofUnsignedVInt(valueLength); size += valueLength; } @@ -1351,7 +1026,7 @@ public class Message return size; } - private int extractParamsSizePost40(ByteBuffer buf, int readerIndex, int readerLimit) + private int extractParamsSize(ByteBuffer buf, int readerIndex, int readerLimit) { int index = readerIndex; @@ -1376,33 +1051,6 @@ public class Message return index - readerIndex; } - private int extractParamsSizePre40(ByteBuffer buf, int readerIndex, int readerLimit) - { - int index = readerIndex; - - index += 4; - if (index > readerLimit) - return -1; - int paramsCount = buf.getInt(index - 4); - - for (int i = 0; i < paramsCount; i++) - { - // try to read length and skip to the end of the param name - index += 2; - - if (index > readerLimit) - return -1; - index += buf.getShort(index - 2); - // try to read length and skip to the end of the param value - index += 4; - if (index > readerLimit) - return -1; - index += buf.getInt(index - 4); - } - - return index - readerIndex; - } - private int payloadSize(Message message, int version) { long payloadSize = message.payload != null && message.payload != NoPayload.noPayload @@ -1414,20 +1062,9 @@ public class Message private IVersionedAsymmetricSerializer getPayloadSerializer() { - return getPayloadSerializer(verb(), id(), from()); + return verb().serializer(); } - // Verb#serializer() is null for legacy response messages. Once all Verbs with null handlers - // are removed in a future major, this method can be replaced with a call to verb.serializer. - private static IVersionedAsymmetricSerializer getPayloadSerializer(Verb verb, long id, InetAddressAndPort from) - { - return null != verb.serializer() - ? verb.serializer() - : instance().callbacks.responseSerializer(id, from); - } - - private int serializedSize30; - private int serializedSize3014; private int serializedSize40; private int serializedSize50; @@ -1438,14 +1075,6 @@ public class Message { switch (version) { - case VERSION_30: - if (serializedSize30 == 0) - serializedSize30 = serializer.serializedSize(this, VERSION_30); - return serializedSize30; - case VERSION_3014: - if (serializedSize3014 == 0) - serializedSize3014 = serializer.serializedSize(this, VERSION_3014); - return serializedSize3014; case VERSION_40: if (serializedSize40 == 0) serializedSize40 = serializer.serializedSize(this, VERSION_40); @@ -1459,8 +1088,6 @@ public class Message } } - private int payloadSize30 = -1; - private int payloadSize3014 = -1; private int payloadSize40 = -1; private int payloadSize50 = -1; @@ -1468,14 +1095,6 @@ public class Message { switch (version) { - case VERSION_30: - if (payloadSize30 < 0) - payloadSize30 = serializer.payloadSize(this, VERSION_30); - return payloadSize30; - case VERSION_3014: - if (payloadSize3014 < 0) - payloadSize3014 = serializer.payloadSize(this, VERSION_3014); - return payloadSize3014; case VERSION_40: if (payloadSize40 < 0) payloadSize40 = serializer.payloadSize(this, VERSION_40); diff --git a/src/java/org/apache/cassandra/net/MessagingService.java b/src/java/org/apache/cassandra/net/MessagingService.java index adca6d1588..a3ae7d8d09 100644 --- a/src/java/org/apache/cassandra/net/MessagingService.java +++ b/src/java/org/apache/cassandra/net/MessagingService.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -29,13 +30,11 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import com.google.common.annotations.VisibleForTesting; - -import org.apache.cassandra.utils.concurrent.AsyncPromise; -import org.apache.cassandra.utils.concurrent.FutureCombiner; +import com.google.common.collect.Lists; +import io.netty.util.concurrent.Future; //checkstyle: permit this import import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.netty.util.concurrent.Future; //checkstyle: permit this import import org.apache.cassandra.concurrent.ScheduledExecutors; import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.config.DatabaseDescriptor; @@ -47,9 +46,12 @@ import org.apache.cassandra.metrics.MessagingMetrics; import org.apache.cassandra.service.AbstractWriteResponseHandler; import org.apache.cassandra.utils.ExecutorUtils; import org.apache.cassandra.utils.FBUtilities; +import org.apache.cassandra.utils.concurrent.AsyncPromise; +import org.apache.cassandra.utils.concurrent.FutureCombiner; import static java.util.Collections.synchronizedList; import static java.util.concurrent.TimeUnit.MINUTES; + import static org.apache.cassandra.concurrent.Stage.MUTATION; import static org.apache.cassandra.config.CassandraRelevantProperties.NON_GRACEFUL_SHUTDOWN; import static org.apache.cassandra.utils.Clock.Global.nanoTime; @@ -208,11 +210,13 @@ public class MessagingService extends MessagingServiceMBeanImpl private static final Logger logger = LoggerFactory.getLogger(MessagingService.class); // 8 bits version, so don't waste versions + @Deprecated public static final int VERSION_30 = 10; + @Deprecated public static final int VERSION_3014 = 11; public static final int VERSION_40 = 12; public static final int VERSION_50 = 13; // c14227 TTL overflow, 'uint' timestamps - public static final int minimum_version = VERSION_30; + public static final int minimum_version = VERSION_40; public static final int current_version = DatabaseDescriptor.getStorageCompatibilityMode().isBefore(5) ? VERSION_40 : VERSION_50; static AcceptVersions accept_messaging = new AcceptVersions(minimum_version, current_version); static AcceptVersions accept_streaming = new AcceptVersions(current_version, current_version); @@ -236,7 +240,9 @@ public class MessagingService extends MessagingServiceMBeanImpl public enum Version { + @Deprecated VERSION_30(10), + @Deprecated VERSION_3014(11), VERSION_40(12), VERSION_50(13); @@ -247,6 +253,16 @@ public class MessagingService extends MessagingServiceMBeanImpl { this.value = value; } + + public static List supportedVersions() + { + List versions = Lists.newArrayList(); + for (Version version : values()) + if (minimum_version <= version.value) + versions.add(version); + + return Collections.unmodifiableList(versions); + } } private static class MSHandle diff --git a/src/java/org/apache/cassandra/net/OutboundConnection.java b/src/java/org/apache/cassandra/net/OutboundConnection.java index 19c74756b1..ee023821f3 100644 --- a/src/java/org/apache/cassandra/net/OutboundConnection.java +++ b/src/java/org/apache/cassandra/net/OutboundConnection.java @@ -1215,9 +1215,6 @@ public class OutboundConnection if (messagingVersion > settings.acceptVersions.max) messagingVersion = settings.acceptVersions.max; - // ensure we connect to the correct SSL port - settings = settings.withLegacyPortIfNecessary(messagingVersion); - // In mixed mode operation, some nodes might be configured to use SSL for internode connections and // others might be configured to not use SSL. When a node is configured in optional SSL mode, It should // be able to handle SSL and Non-SSL internode connections. We take care of this when accepting NON-SSL @@ -1231,7 +1228,7 @@ public class OutboundConnection { logger.info("ConnectionId {} is falling back to {} reconnect strategy for retry", id(), fallBackSslFallbackConnectionTypes[index]); } - initiateMessaging(eventLoop, type, fallBackSslFallbackConnectionTypes[index], settings, messagingVersion, result) + initiateMessaging(eventLoop, type, fallBackSslFallbackConnectionTypes[index], settings, result) .addListener(future -> { if (future.isCancelled()) return; diff --git a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java index ebd30f5406..1f1ef6478a 100644 --- a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java +++ b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java @@ -68,12 +68,10 @@ import static org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnect import static org.apache.cassandra.net.InternodeConnectionUtils.DISCARD_HANDLER_NAME; import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_HANDLER_NAME; import static org.apache.cassandra.net.InternodeConnectionUtils.certificates; -import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.HandshakeProtocol.*; import static org.apache.cassandra.net.ConnectionType.STREAMING; import static org.apache.cassandra.net.OutboundConnectionInitiator.Result.incompatible; import static org.apache.cassandra.net.OutboundConnectionInitiator.Result.messagingSuccess; -import static org.apache.cassandra.net.OutboundConnectionInitiator.Result.retry; import static org.apache.cassandra.net.OutboundConnectionInitiator.Result.streamingSuccess; import static org.apache.cassandra.net.SocketFactory.*; @@ -96,16 +94,15 @@ public class OutboundConnectionInitiator> resultPromise; private boolean isClosed; private OutboundConnectionInitiator(ConnectionType type, SslFallbackConnectionType sslConnectionType, OutboundConnectionSettings settings, - int requestMessagingVersion, Promise> resultPromise) + Promise> resultPromise) { this.type = type; this.sslConnectionType = sslConnectionType; - this.requestMessagingVersion = requestMessagingVersion; + this.settings = settings; this.resultPromise = resultPromise; } @@ -118,9 +115,9 @@ public class OutboundConnectionInitiator> initiateStreaming(EventLoop eventLoop, OutboundConnectionSettings settings, - SslFallbackConnectionType sslConnectionType, int requestMessagingVersion) + SslFallbackConnectionType sslConnectionType) { - return new OutboundConnectionInitiator(STREAMING, sslConnectionType, settings, requestMessagingVersion, AsyncPromise.withExecutor(eventLoop)) + return new OutboundConnectionInitiator(STREAMING, sslConnectionType, settings, AsyncPromise.withExecutor(eventLoop)) .initiate(eventLoop); } @@ -132,16 +129,16 @@ public class OutboundConnectionInitiator> initiateMessaging(EventLoop eventLoop, ConnectionType type, SslFallbackConnectionType sslConnectionType, - OutboundConnectionSettings settings, int requestMessagingVersion, Promise> result) + OutboundConnectionSettings settings, Promise> result) { - return new OutboundConnectionInitiator<>(type, sslConnectionType, settings, requestMessagingVersion, result) + return new OutboundConnectionInitiator<>(type, sslConnectionType, settings, result) .initiate(eventLoop); } private Future> initiate(EventLoop eventLoop) { if (logger.isTraceEnabled()) - logger.trace("creating outbound bootstrap to {}, requestVersion: {}", settings, requestMessagingVersion); + logger.trace("creating outbound bootstrap to {}", settings); if (!settings.authenticator.authenticate(settings.to.getAddress(), settings.to.getPort(), null, OUTBOUND_PRECONNECT)) { @@ -306,15 +303,13 @@ public class OutboundConnectionInitiator { if (!future.isSuccess()) exceptionCaught(ctx, future.cause()); }); - if (type.isStreaming() && requestMessagingVersion < VERSION_40) - ctx.pipeline().remove(this); + AsyncChannelPromise.writeAndFlush(ctx, msg.encode(), + future -> { if (!future.isSuccess()) exceptionCaught(ctx, future.cause()); }); ctx.fireChannelActive(); } @@ -335,12 +330,13 @@ public class OutboundConnectionInitiator out) { try { - Accept msg = Accept.maybeDecode(in, requestMessagingVersion); + Accept msg = Accept.maybeDecode(in); if (msg == null) return; @@ -350,68 +346,35 @@ public class OutboundConnectionInitiator result; - if (useMessagingVersion > 0) - { - if (useMessagingVersion < settings.acceptVersions.min || useMessagingVersion > settings.acceptVersions.max) - { - result = incompatible(useMessagingVersion, peerMessagingVersion); - } - else - { - // This is a bit ugly - if (type.isMessaging()) - { - switch (settings.framing) - { - case LZ4: - frameEncoder = FrameEncoderLZ4.fastInstance; - break; - case CRC: - frameEncoder = FrameEncoderCrc.instance; - break; - case UNPROTECTED: - frameEncoder = FrameEncoderUnprotected.instance; - break; - } + assert useMessagingVersion > 0; - result = (Result) messagingSuccess(ctx.channel(), useMessagingVersion, frameEncoder.allocator()); - } - else - { - result = (Result) streamingSuccess(ctx.channel(), useMessagingVersion); - } - } + if (useMessagingVersion < settings.acceptVersions.min || useMessagingVersion > settings.acceptVersions.max) + { + result = incompatible(useMessagingVersion, peerMessagingVersion); } else { - assert type.isMessaging(); - - // pre40 handshake responses only (can be a post40 node) - if (peerMessagingVersion == requestMessagingVersion - || peerMessagingVersion > settings.acceptVersions.max) // this clause is for impersonating 3.0 node in testing only + // This is a bit ugly + if (type.isMessaging()) { switch (settings.framing) { - case CRC: - case UNPROTECTED: - frameEncoder = FrameEncoderLegacy.instance; - break; case LZ4: - frameEncoder = FrameEncoderLegacyLZ4.instance; + frameEncoder = FrameEncoderLZ4.fastInstance; + break; + case CRC: + frameEncoder = FrameEncoderCrc.instance; + break; + case UNPROTECTED: + frameEncoder = FrameEncoderUnprotected.instance; break; } - result = (Result) messagingSuccess(ctx.channel(), requestMessagingVersion, frameEncoder.allocator()); + result = (Result) messagingSuccess(ctx.channel(), useMessagingVersion, frameEncoder.allocator()); } - else if (peerMessagingVersion < settings.acceptVersions.min) - result = incompatible(-1, peerMessagingVersion); else - result = retry(peerMessagingVersion); - - if (result.isSuccess()) { - ConfirmOutboundPre40 message = new ConfirmOutboundPre40(settings.acceptVersions.max, settings.from); - AsyncChannelPromise.writeAndFlush(ctx, message.encode()); + result = (Result) streamingSuccess(ctx.channel(), useMessagingVersion); } } diff --git a/src/java/org/apache/cassandra/net/OutboundConnectionSettings.java b/src/java/org/apache/cassandra/net/OutboundConnectionSettings.java index 6a2543f783..8a83a52aa6 100644 --- a/src/java/org/apache/cassandra/net/OutboundConnectionSettings.java +++ b/src/java/org/apache/cassandra/net/OutboundConnectionSettings.java @@ -33,7 +33,6 @@ import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.FBUtilities; import static org.apache.cassandra.config.DatabaseDescriptor.getEndpointSnitch; -import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.MessagingService.instance; import static org.apache.cassandra.utils.FBUtilities.getBroadcastAddressAndPort; @@ -50,14 +49,11 @@ public class OutboundConnectionSettings public enum Framing { - // for < VERSION_40, implies no framing - // for >= VERSION_40, uses simple unprotected frames with header crc but no payload protection + // uses simple unprotected frames with header crc but no payload protection UNPROTECTED(0), - // for < VERSION_40, uses the jpountz framing format - // for >= VERSION_40, uses our framing format with header crc24 + // uses our framing format with header crc24 LZ4(1), - // for < VERSION_40, implies UNPROTECTED - // for >= VERSION_40, uses simple frames with separate header and payload crc + // uses simple frames with separate header and payload crc CRC(2); public static Framing forId(int id) @@ -133,6 +129,7 @@ public class OutboundConnectionSettings Preconditions.checkArgument(applicationSendQueueCapacityInBytes == null || applicationSendQueueCapacityInBytes >= 1 << 10, "illegal application send queue capacity: " + applicationSendQueueCapacityInBytes); Preconditions.checkArgument(tcpUserTimeoutInMS == null || tcpUserTimeoutInMS >= 0, "tcp user timeout must be non negative: " + tcpUserTimeoutInMS); Preconditions.checkArgument(tcpConnectTimeoutInMS == null || tcpConnectTimeoutInMS > 0, "tcp connect timeout must be positive: " + tcpConnectTimeoutInMS); + Preconditions.checkArgument(acceptVersions == null || acceptVersions.min >= MessagingService.minimum_version, "acceptVersions.min must be minimum_version or higher: " + (acceptVersions == null ? null : acceptVersions.min)); this.authenticator = authenticator; this.to = to; @@ -437,11 +434,6 @@ public class OutboundConnectionSettings : MessagingService.accept_messaging; } - public OutboundConnectionSettings withLegacyPortIfNecessary(int messagingVersion) - { - return withConnectTo(maybeWithSecurePort(connectTo(), messagingVersion, withEncryption())); - } - public InetAddressAndPort connectTo() { InetAddressAndPort connectTo = this.connectTo; @@ -508,20 +500,4 @@ public class OutboundConnectionSettings || ((DatabaseDescriptor.internodeCompression() == Config.InternodeCompression.dc) && !isInLocalDC(snitch, localHost, remoteHost)); } - private static InetAddressAndPort maybeWithSecurePort(InetAddressAndPort address, int messagingVersion, boolean isEncrypted) - { - if (!isEncrypted || messagingVersion >= VERSION_40) - return address; - - // if we don't know the version of the peer, assume it is 4.0 (or higher) as the only time is would be lower - // (as in a 3.x version) is during a cluster upgrade (from 3.x to 4.0). In that case the outbound connection will - // unfortunately fail - however the peer should connect to this node (at some point), and once we learn it's version, it'll be - // in versions map. thus, when we attempt to reconnect to that node, we'll have the version and we can get the correct port. - // we will be able to remove this logic at 5.0. - // Also as of 4.0 we will propagate the "regular" port (which will support both SSL and non-SSL) via gossip so - // for SSL and version 4.0 always connect to the gossiped port because if SSL is enabled it should ALWAYS - // listen for SSL on the "regular" port. - return address.withPort(DatabaseDescriptor.getSSLStoragePort()); - } - } diff --git a/src/java/org/apache/cassandra/net/OutboundConnections.java b/src/java/org/apache/cassandra/net/OutboundConnections.java index 0729194a19..aacc2b4473 100644 --- a/src/java/org/apache/cassandra/net/OutboundConnections.java +++ b/src/java/org/apache/cassandra/net/OutboundConnections.java @@ -43,7 +43,6 @@ import static java.lang.Math.max; import static org.apache.cassandra.config.CassandraRelevantProperties.OTCP_LARGE_MESSAGE_THRESHOLD; import static org.apache.cassandra.gms.Gossiper.instance; import static org.apache.cassandra.net.FrameEncoderCrc.HEADER_AND_TRAILER_LENGTH; -import static org.apache.cassandra.net.LegacyLZ4Constants.HEADER_LENGTH; import static org.apache.cassandra.net.MessagingService.current_version; import static org.apache.cassandra.net.ConnectionType.URGENT_MESSAGES; import static org.apache.cassandra.net.ConnectionType.LARGE_MESSAGES; @@ -60,6 +59,12 @@ public class OutboundConnections { private static final Logger logger = LoggerFactory.getLogger(OutboundConnections.class); + private static final int HEADER_LENGTH = 8 // magic number + + 1 // token + + 4 // compressed length + + 4 // uncompressed length + + 4; // checksum + @VisibleForTesting public static final int LARGE_MESSAGE_THRESHOLD = OTCP_LARGE_MESSAGE_THRESHOLD.getInt() - max(max(HEADER_LENGTH, HEADER_AND_TRAILER_LENGTH), FrameEncoderLZ4.HEADER_AND_TRAILER_LENGTH); diff --git a/src/java/org/apache/cassandra/net/ParamType.java b/src/java/org/apache/cassandra/net/ParamType.java index 6cb8fb4467..a540f9c499 100644 --- a/src/java/org/apache/cassandra/net/ParamType.java +++ b/src/java/org/apache/cassandra/net/ParamType.java @@ -17,11 +17,8 @@ */ package org.apache.cassandra.net; -import java.util.HashMap; -import java.util.Map; import javax.annotation.Nullable; -import org.apache.cassandra.exceptions.RequestFailureReason; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.utils.Int32Serializer; @@ -40,50 +37,39 @@ import static org.apache.cassandra.locator.InetAddressAndPort.FwdFrmSerializer.f * will skip over any params it doesn't recognise. * * Please don't add boolean params here. Extend and use {@link MessageFlag} instead. + * + * Do not re-use old, nor fill gaps in the sequence of, ids. New IDs must be higher. */ public enum ParamType { - FORWARD_TO (0, "FWD_TO", ForwardingInfo.serializer), - RESPOND_TO (1, "FWD_FRM", fwdFrmSerializer), + FORWARD_TO (0, ForwardingInfo.serializer), + RESPOND_TO (1, fwdFrmSerializer), - @Deprecated - FAILURE_RESPONSE (2, "FAIL", LegacyFlag.serializer), - @Deprecated - FAILURE_REASON (3, "FAIL_REASON", RequestFailureReason.serializer), - @Deprecated - FAILURE_CALLBACK (4, "CAL_BAC", LegacyFlag.serializer), + TRACE_SESSION (5, TimeUUID.Serializer.instance), + TRACE_TYPE (6, Tracing.traceTypeSerializer), - TRACE_SESSION (5, "TraceSession", TimeUUID.Serializer.instance), - TRACE_TYPE (6, "TraceType", Tracing.traceTypeSerializer), - - @Deprecated - TRACK_REPAIRED_DATA (7, "TrackRepaired", LegacyFlag.serializer), - - TOMBSTONE_FAIL (8, "TSF", Int32Serializer.serializer), - TOMBSTONE_WARNING (9, "TSW", Int32Serializer.serializer), - LOCAL_READ_SIZE_FAIL (10, "LRSF", Int64Serializer.serializer), - LOCAL_READ_SIZE_WARN (11, "LRSW", Int64Serializer.serializer), - ROW_INDEX_READ_SIZE_FAIL (12, "RIRSF", Int64Serializer.serializer), - ROW_INDEX_READ_SIZE_WARN (13, "RIRSW", Int64Serializer.serializer), - CUSTOM_MAP (14, "CUSTOM", CustomParamsSerializer.serializer), - SNAPSHOT_RANGES (15, "SNAPSHOT_RANGES", RangesSerializer.serializer); + TOMBSTONE_FAIL (8, Int32Serializer.serializer), + TOMBSTONE_WARNING (9, Int32Serializer.serializer), + LOCAL_READ_SIZE_FAIL (10, Int64Serializer.serializer), + LOCAL_READ_SIZE_WARN (11, Int64Serializer.serializer), + ROW_INDEX_READ_SIZE_FAIL (12, Int64Serializer.serializer), + ROW_INDEX_READ_SIZE_WARN (13, Int64Serializer.serializer), + CUSTOM_MAP (14, CustomParamsSerializer.serializer), + SNAPSHOT_RANGES (15, RangesSerializer.serializer); final int id; - @Deprecated final String legacyAlias; // pre-4.0 we used to serialize entire param name string final IVersionedSerializer serializer; - ParamType(int id, String legacyAlias, IVersionedSerializer serializer) + ParamType(int id, IVersionedSerializer serializer) { if (id < 0) throw new IllegalArgumentException("ParamType id must be non-negative"); this.id = id; - this.legacyAlias = legacyAlias; this.serializer = serializer; } private static final ParamType[] idToTypeMap; - private static final Map aliasToTypeMap; static { @@ -94,7 +80,6 @@ public enum ParamType max = max(t.id, max); ParamType[] idMap = new ParamType[max + 1]; - Map aliasMap = new HashMap<>(); for (ParamType type : types) { @@ -102,12 +87,9 @@ public enum ParamType throw new RuntimeException("Two ParamType-s that map to the same id: " + type.id); idMap[type.id] = type; - if (aliasMap.put(type.legacyAlias, type) != null) - throw new RuntimeException("Two ParamType-s that map to the same legacy alias: " + type.legacyAlias); } idToTypeMap = idMap; - aliasToTypeMap = aliasMap; } @Nullable @@ -119,9 +101,4 @@ public enum ParamType return id < idToTypeMap.length ? idToTypeMap[id] : null; } - @Nullable - static ParamType lookUpByAlias(String alias) - { - return aliasToTypeMap.get(alias); - } } diff --git a/src/java/org/apache/cassandra/net/RequestCallbacks.java b/src/java/org/apache/cassandra/net/RequestCallbacks.java index 663126ff02..ee63c5a3e6 100644 --- a/src/java/org/apache/cassandra/net/RequestCallbacks.java +++ b/src/java/org/apache/cassandra/net/RequestCallbacks.java @@ -32,7 +32,6 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.exceptions.RequestFailureReason; -import org.apache.cassandra.io.IVersionedAsymmetricSerializer; import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.Replica; import org.apache.cassandra.metrics.InternodeOutboundMetrics; @@ -109,12 +108,6 @@ public class RequestCallbacks implements OutboundMessageCallbacks assert previous == null : format("Callback already exists for id %d/%s! (%s)", message.id(), to.endpoint(), previous); } - IVersionedAsymmetricSerializer responseSerializer(long id, InetAddressAndPort peer) - { - CallbackInfo info = get(id, peer); - return info == null ? null : info.responseVerb.serializer(); - } - @VisibleForTesting public void removeAndRespond(long id, InetAddressAndPort peer, Message message) { @@ -243,16 +236,12 @@ public class RequestCallbacks implements OutboundMessageCallbacks final InetAddressAndPort peer; public final RequestCallback callback; - @Deprecated // for 3.0 compatibility purposes only - public final Verb responseVerb; - private CallbackInfo(Message message, InetAddressAndPort peer, RequestCallback callback) { this.createdAtNanos = message.createdAtNanos(); this.expiresAtNanos = message.expiresAtNanos(); this.peer = peer; this.callback = callback; - this.responseVerb = message.verb().responseVerb; } public long timeout() diff --git a/src/java/org/apache/cassandra/net/Verb.java b/src/java/org/apache/cassandra/net/Verb.java index d52f14c7ac..e5b4983d77 100644 --- a/src/java/org/apache/cassandra/net/Verb.java +++ b/src/java/org/apache/cassandra/net/Verb.java @@ -337,15 +337,6 @@ public enum Verb return handler.get() == ResponseVerbHandler.instance; } - Verb toPre40Verb() - { - if (!isResponse()) - return this; - if (priority == P0) - return INTERNAL_RSP; - return REQUEST_RSP; - } - @VisibleForTesting Supplier> unsafeSetHandler(Supplier> handler) throws NoSuchFieldException, IllegalAccessException { diff --git a/src/java/org/apache/cassandra/schema/CompressionParams.java b/src/java/org/apache/cassandra/schema/CompressionParams.java index be79ec6674..d826acc28b 100644 --- a/src/java/org/apache/cassandra/schema/CompressionParams.java +++ b/src/java/org/apache/cassandra/schema/CompressionParams.java @@ -46,7 +46,6 @@ import org.apache.cassandra.net.MessagingService; import static java.lang.String.format; -@SuppressWarnings("deprecation") public final class CompressionParams { private static final Logger logger = LoggerFactory.getLogger(CompressionParams.class); @@ -605,6 +604,7 @@ public final class CompressionParams { public void serialize(CompressionParams parameters, DataOutputPlus out, int version) throws IOException { + assert version >= MessagingService.VERSION_40; out.writeUTF(parameters.sstableCompressor.getClass().getSimpleName()); out.writeInt(parameters.otherOptions.size()); for (Map.Entry entry : parameters.otherOptions.entrySet()) @@ -613,15 +613,12 @@ public final class CompressionParams out.writeUTF(entry.getValue()); } out.writeInt(parameters.chunkLength()); - if (version >= MessagingService.VERSION_40) - out.writeInt(parameters.maxCompressedLength); - else - if (parameters.maxCompressedLength != Integer.MAX_VALUE) - throw new UnsupportedOperationException("Cannot stream SSTables with uncompressed chunks to pre-4.0 nodes."); + out.writeInt(parameters.maxCompressedLength); } public CompressionParams deserialize(DataInputPlus in, int version) throws IOException { + assert version >= MessagingService.VERSION_40; String compressorName = in.readUTF(); int optionCount = in.readInt(); Map options = new HashMap<>(); @@ -632,9 +629,7 @@ public final class CompressionParams options.put(key, value); } int chunkLength = in.readInt(); - int minCompressRatio = Integer.MAX_VALUE; // Earlier Cassandra cannot use uncompressed chunks. - if (version >= MessagingService.VERSION_40) - minCompressRatio = in.readInt(); + int minCompressRatio = in.readInt(); CompressionParams parameters; try @@ -650,6 +645,7 @@ public final class CompressionParams public long serializedSize(CompressionParams parameters, int version) { + assert version >= MessagingService.VERSION_40; long size = TypeSizes.sizeof(parameters.sstableCompressor.getClass().getSimpleName()); size += TypeSizes.sizeof(parameters.otherOptions.size()); for (Map.Entry entry : parameters.otherOptions.entrySet()) @@ -658,8 +654,7 @@ public final class CompressionParams size += TypeSizes.sizeof(entry.getValue()); } size += TypeSizes.sizeof(parameters.chunkLength()); - if (version >= MessagingService.VERSION_40) - size += TypeSizes.sizeof(parameters.maxCompressedLength()); + size += TypeSizes.sizeof(parameters.maxCompressedLength()); return size; } } diff --git a/src/java/org/apache/cassandra/service/pager/PagingState.java b/src/java/org/apache/cassandra/service/pager/PagingState.java index 3691d148cf..b207d0b769 100644 --- a/src/java/org/apache/cassandra/service/pager/PagingState.java +++ b/src/java/org/apache/cassandra/service/pager/PagingState.java @@ -400,9 +400,9 @@ public class PagingState } else { - // We froze the serialization version to 3.0 as we need to make this this doesn't change (that is, it has to be - // fix for a given version of the protocol). - mark = Clustering.serializer.serialize(row.clustering(), MessagingService.VERSION_30, makeClusteringTypes(metadata)); + // We froze the serialization version to 3.0 as we need to make sure this this doesn't change + // It got bumped to 4.0 when 3.0 got dropped, knowing it didn't change + mark = Clustering.serializer.serialize(row.clustering(), MessagingService.VERSION_40, makeClusteringTypes(metadata)); } return new RowMark(mark, protocolVersion); } @@ -414,7 +414,7 @@ public class PagingState return protocolVersion.isSmallerOrEqualTo(ProtocolVersion.V3) ? decodeClustering(metadata, mark) - : Clustering.serializer.deserialize(mark, MessagingService.VERSION_30, makeClusteringTypes(metadata)); + : Clustering.serializer.deserialize(mark, MessagingService.VERSION_40, makeClusteringTypes(metadata)); } // Old (pre-3.0) encoding of cells. We need that for the protocol v3 as that is how things where encoded diff --git a/src/java/org/apache/cassandra/service/paxos/uncommitted/PaxosRows.java b/src/java/org/apache/cassandra/service/paxos/uncommitted/PaxosRows.java index 8bdbdf7524..ee5c762a1f 100644 --- a/src/java/org/apache/cassandra/service/paxos/uncommitted/PaxosRows.java +++ b/src/java/org/apache/cassandra/service/paxos/uncommitted/PaxosRows.java @@ -40,7 +40,6 @@ import org.apache.cassandra.db.rows.Cell; import org.apache.cassandra.db.rows.DeserializationHelper; import org.apache.cassandra.db.rows.Row; import org.apache.cassandra.db.rows.UnfilteredRowIterator; -import org.apache.cassandra.exceptions.UnknownTableException; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.SchemaConstants; @@ -96,7 +95,7 @@ public class PaxosRows return null; Ballot ballot = ballotCell.accessor().toBallot(ballotCell.value()); - int version = getInt(row, PROPOSAL_VERSION, MessagingService.VERSION_30); + int version = getInt(row, PROPOSAL_VERSION, MessagingService.VERSION_40); PartitionUpdate update = getUpdate(row, PROPOSAL_UPDATE, version); return ballotCell.isExpiring() ? new AcceptedWithTTL(ballot, update, ballotCell.localDeletionTime()) @@ -110,7 +109,7 @@ public class PaxosRows return Committed.none(partitionKey, metadata); Ballot ballot = ballotCell.accessor().toBallot(ballotCell.value()); - int version = getInt(row, COMMIT_VERSION, MessagingService.VERSION_30); + int version = getInt(row, COMMIT_VERSION, MessagingService.VERSION_40); PartitionUpdate update = getUpdate(row, COMMIT_UPDATE, version); return ballotCell.isExpiring() ? new CommittedWithTTL(ballot, update, ballotCell.localDeletionTime()) @@ -140,22 +139,8 @@ public class PaxosRows Cell cell = row.getCell(cmeta); if (cell == null) throw new IllegalStateException(); - try - { - return PartitionUpdate.fromBytes(cell.buffer(), version); - } - catch (RuntimeException e) - { - // the legacy behaviors of not deleting proposal_version along with proposal and proposal_ballot on commit, - // and accepting proposals younger than the most recent commit combined with the right sequence of tombstone - // purging and retention over a few compactions can result in 3.x format proposals without a proposal version - // value, causing deserialization to fail when looking up the table. So here we detect that and attempt to - // deserialize with the current version - if (e.getCause() instanceof UnknownTableException && version == MessagingService.VERSION_30) - return PartitionUpdate.fromBytes(cell.buffer(), MessagingService.current_version); - throw e; - } + return PartitionUpdate.fromBytes(cell.buffer(), version); } private static Ballot getBallot(Row row, ColumnMetadata cmeta) diff --git a/src/java/org/apache/cassandra/streaming/async/NettyStreamingChannel.java b/src/java/org/apache/cassandra/streaming/async/NettyStreamingChannel.java index 70b1c37b88..8515ad6089 100644 --- a/src/java/org/apache/cassandra/streaming/async/NettyStreamingChannel.java +++ b/src/java/org/apache/cassandra/streaming/async/NettyStreamingChannel.java @@ -59,7 +59,6 @@ public class NettyStreamingChannel extends ChannelInboundHandlerAdapter implemen @VisibleForTesting static final AttributeKey TRANSFERRING_FILE_ATTR = valueOf("transferringFile"); - final int messagingVersion; final Channel channel; /** @@ -75,9 +74,8 @@ public class NettyStreamingChannel extends ChannelInboundHandlerAdapter implemen private volatile boolean closed; - public NettyStreamingChannel(int messagingVersion, Channel channel, Kind kind) + public NettyStreamingChannel(Channel channel, Kind kind) { - this.messagingVersion = messagingVersion; this.channel = channel; channel.attr(TRANSFERRING_FILE_ATTR).set(FALSE); if (kind == Kind.CONTROL) diff --git a/src/java/org/apache/cassandra/streaming/async/NettyStreamingConnectionFactory.java b/src/java/org/apache/cassandra/streaming/async/NettyStreamingConnectionFactory.java index 529b396367..4002f9b61f 100644 --- a/src/java/org/apache/cassandra/streaming/async/NettyStreamingConnectionFactory.java +++ b/src/java/org/apache/cassandra/streaming/async/NettyStreamingConnectionFactory.java @@ -61,12 +61,12 @@ public class NettyStreamingConnectionFactory implements StreamingChannel.Factory { for (int i = 0; i < MAX_CONNECT_ATTEMPTS; i++) { - Future> result = initiateStreaming(eventLoop, settings, sslFallbackConnectionType, messagingVersion); + Future> result = initiateStreaming(eventLoop, settings, sslFallbackConnectionType); result.awaitUninterruptibly(); // initiate has its own timeout, so this is "guaranteed" to return relatively promptly if (result.isSuccess()) { Channel channel = result.getNow().success().channel; - NettyStreamingChannel streamingChannel = new NettyStreamingChannel(messagingVersion, channel, kind); + NettyStreamingChannel streamingChannel = new NettyStreamingChannel(channel, kind); if (kind == StreamingChannel.Kind.CONTROL) { ChannelPipeline pipeline = channel.pipeline(); diff --git a/src/java/org/apache/cassandra/streaming/async/StreamingMultiplexedChannel.java b/src/java/org/apache/cassandra/streaming/async/StreamingMultiplexedChannel.java index 4ebb3913cd..88e11b147c 100644 --- a/src/java/org/apache/cassandra/streaming/async/StreamingMultiplexedChannel.java +++ b/src/java/org/apache/cassandra/streaming/async/StreamingMultiplexedChannel.java @@ -53,12 +53,14 @@ import org.apache.cassandra.utils.concurrent.Semaphore; import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException; import static com.google.common.base.Throwables.getRootCause; + import static java.lang.String.format; import static java.lang.Thread.currentThread; import static java.util.concurrent.TimeUnit.*; import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory; import static org.apache.cassandra.config.CassandraRelevantProperties.STREAMING_SESSION_PARALLELTRANSFERS; +import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.streaming.StreamSession.createLogTag; import static org.apache.cassandra.streaming.messages.StreamMessage.serialize; import static org.apache.cassandra.streaming.messages.StreamMessage.serializedSize; @@ -122,6 +124,7 @@ public class StreamingMultiplexedChannel this.session = session; this.factory = factory; this.to = to; + assert messagingVersion >= VERSION_40; this.messagingVersion = messagingVersion; this.controlChannel = controlChannel; diff --git a/test/burn/org/apache/cassandra/net/ConnectionBurnTest.java b/test/burn/org/apache/cassandra/net/ConnectionBurnTest.java index 6b0f5f59db..abacb6e034 100644 --- a/test/burn/org/apache/cassandra/net/ConnectionBurnTest.java +++ b/test/burn/org/apache/cassandra/net/ConnectionBurnTest.java @@ -163,7 +163,7 @@ public class ConnectionBurnTest public long serializedSize(byte[] payload, int version) { - return MessageGenerator.serializedSize(payload, version); + return MessageGenerator.serializedSize(payload); } }; diff --git a/test/burn/org/apache/cassandra/net/MessageGenerator.java b/test/burn/org/apache/cassandra/net/MessageGenerator.java index 80e647c42e..945315747a 100644 --- a/test/burn/org/apache/cassandra/net/MessageGenerator.java +++ b/test/burn/org/apache/cassandra/net/MessageGenerator.java @@ -147,9 +147,8 @@ abstract class MessageGenerator static Header readHeader(DataInputPlus in, int messagingVersion) throws IOException { - int length = messagingVersion < VERSION_40 - ? in.readInt() - : in.readUnsignedVInt32(); + assert messagingVersion >= VERSION_40; + int length = in.readUnsignedVInt32(); long id = in.readLong(); if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) id = Long.reverseBytes(id); @@ -159,15 +158,13 @@ abstract class MessageGenerator static void writeLength(byte[] payload, DataOutputPlus out, int messagingVersion) throws IOException { - if (messagingVersion < VERSION_40) - out.writeInt(payload.length); - else - out.writeUnsignedVInt32(payload.length); + assert messagingVersion >= VERSION_40; + out.writeUnsignedVInt32(payload.length); } - static long serializedSize(byte[] payload, int messagingVersion) + static long serializedSize(byte[] payload) { - return payload.length + (messagingVersion < VERSION_40 ? 4 : VIntCoding.computeUnsignedVIntSize(payload.length)); + return payload.length + VIntCoding.computeUnsignedVIntSize(payload.length); } private static final Unsafe unsafe; diff --git a/test/burn/org/apache/cassandra/net/Verifier.java b/test/burn/org/apache/cassandra/net/Verifier.java index cd6bdc3275..d1022b3121 100644 --- a/test/burn/org/apache/cassandra/net/Verifier.java +++ b/test/burn/org/apache/cassandra/net/Verifier.java @@ -765,11 +765,12 @@ public class Verifier assert m.is(ENQUEUE); m.serialize = e.at; m.messagingVersion = e.messagingVersion; + assert m.messagingVersion >= VERSION_40; if (current_version != e.messagingVersion) controller.adjust(m.message.serializedSize(current_version), m.message.serializedSize(e.messagingVersion)); m.processOnEventLoop = willProcessOnEventLoop(outbound.type(), m.message, e.messagingVersion); - m.expiresAtNanos = expiresAtNanos(m.message, e.messagingVersion); + m.expiresAtNanos = expiresAtNanos(m.message); int mi = enqueueing.indexOf(m); for (int i = 0 ; i < mi ; ++i) { @@ -1621,18 +1622,15 @@ public class Verifier private static boolean willProcessOnEventLoop(ConnectionType type, Message message, int messagingVersion) { int size = message.serializedSize(messagingVersion); - if (type == ConnectionType.SMALL_MESSAGES && messagingVersion >= VERSION_40) + if (type == ConnectionType.SMALL_MESSAGES) return size <= LARGE_MESSAGE_THRESHOLD; - else if (messagingVersion >= VERSION_40) - return size <= DEFAULT_BUFFER_SIZE; else - return size <= LARGE_MESSAGE_THRESHOLD; + return size <= DEFAULT_BUFFER_SIZE; } - private static long expiresAtNanos(Message message, int messagingVersion) + private static long expiresAtNanos(Message message) { - return messagingVersion < VERSION_40 ? message.verb().expiresAtNanos(message.createdAtNanos()) - : message.expiresAtNanos(); + return message.expiresAtNanos(); } } diff --git a/test/microbench/org/apache/cassandra/test/microbench/MessageOutBench.java b/test/microbench/org/apache/cassandra/test/microbench/MessageOutBench.java index 907047364a..201ce9ae1b 100644 --- a/test/microbench/org/apache/cassandra/test/microbench/MessageOutBench.java +++ b/test/microbench/org/apache/cassandra/test/microbench/MessageOutBench.java @@ -104,10 +104,4 @@ public class MessageOutBench return msgOut.serializedSize(messagingVersion); } } - - @Benchmark - public int serializePre40() throws Exception - { - return serialize(MessagingService.VERSION_30); - } } diff --git a/test/unit/org/apache/cassandra/db/ReadResponseTest.java b/test/unit/org/apache/cassandra/db/ReadResponseTest.java index edfba6cb19..7241d89e61 100644 --- a/test/unit/org/apache/cassandra/db/ReadResponseTest.java +++ b/test/unit/org/apache/cassandra/db/ReadResponseTest.java @@ -162,12 +162,9 @@ public class ReadResponseTest } private void verifySerDe(ReadResponse response) { - // check that roundtripping through ReadResponse.serializer behaves as expected. - // ReadResponses from pre-4.0 nodes will never contain repaired data digest - // or pending session info, but we run all messages through both pre/post 4.0 - // serde to check that the defaults are correctly applied - roundTripSerialization(response, MessagingService.current_version); - roundTripSerialization(response, MessagingService.VERSION_30); + // check that roundtripping through ReadResponse.serializer behaves as expected + for (MessagingService.Version version : MessagingService.Version.supportedVersions()) + roundTripSerialization(response, version.value); } @@ -180,19 +177,10 @@ public class ReadResponseTest DataInputBuffer in = new DataInputBuffer(out.buffer(), false); ReadResponse deser = ReadResponse.serializer.deserialize(in, version); - if (version < MessagingService.VERSION_40) - { - assertFalse(deser.mayIncludeRepairedDigest()); - // even though that means they should never be used, verify that the default values are present - assertEquals(ByteBufferUtil.EMPTY_BYTE_BUFFER, deser.repairedDataDigest()); - assertTrue(deser.isRepairedDigestConclusive()); - } - else - { - assertTrue(deser.mayIncludeRepairedDigest()); - assertEquals(response.repairedDataDigest(), deser.repairedDataDigest()); - assertEquals(response.isRepairedDigestConclusive(), deser.isRepairedDigestConclusive()); - } + assertTrue(version >= MessagingService.VERSION_40); + assertTrue(deser.mayIncludeRepairedDigest()); + assertEquals(response.repairedDataDigest(), deser.repairedDataDigest()); + assertEquals(response.isRepairedDigestConclusive(), deser.isRepairedDigestConclusive()); } catch (IOException e) { diff --git a/test/unit/org/apache/cassandra/db/SinglePartitionSliceCommandTest.java b/test/unit/org/apache/cassandra/db/SinglePartitionSliceCommandTest.java index 8792039904..c65258c347 100644 --- a/test/unit/org/apache/cassandra/db/SinglePartitionSliceCommandTest.java +++ b/test/unit/org/apache/cassandra/db/SinglePartitionSliceCommandTest.java @@ -269,10 +269,10 @@ public class SinglePartitionSliceCommandTest response = ReadResponse.createDataResponse(pi, cmd, executionController.getRepairedDataInfo()); } - out = new DataOutputBuffer((int) ReadResponse.serializer.serializedSize(response, MessagingService.VERSION_30)); - ReadResponse.serializer.serialize(response, out, MessagingService.VERSION_30); + out = new DataOutputBuffer((int) ReadResponse.serializer.serializedSize(response, MessagingService.VERSION_40)); + ReadResponse.serializer.serialize(response, out, MessagingService.VERSION_40); in = new DataInputBuffer(out.buffer(), true); - dst = ReadResponse.serializer.deserialize(in, MessagingService.VERSION_30); + dst = ReadResponse.serializer.deserialize(in, MessagingService.VERSION_40); try (UnfilteredPartitionIterator pi = dst.makeIterator(cmd)) { checkForS(pi); @@ -284,10 +284,10 @@ public class SinglePartitionSliceCommandTest { response = ReadResponse.createDataResponse(pi, cmd, executionController.getRepairedDataInfo()); } - out = new DataOutputBuffer((int) ReadResponse.serializer.serializedSize(response, MessagingService.VERSION_30)); - ReadResponse.serializer.serialize(response, out, MessagingService.VERSION_30); + out = new DataOutputBuffer((int) ReadResponse.serializer.serializedSize(response, MessagingService.VERSION_40)); + ReadResponse.serializer.serialize(response, out, MessagingService.VERSION_40); in = new DataInputBuffer(out.buffer(), true); - dst = ReadResponse.serializer.deserialize(in, MessagingService.VERSION_30); + dst = ReadResponse.serializer.deserialize(in, MessagingService.VERSION_40); try (UnfilteredPartitionIterator pi = dst.makeIterator(cmd)) { checkForS(pi); diff --git a/test/unit/org/apache/cassandra/db/filter/ColumnFilterTest.java b/test/unit/org/apache/cassandra/db/filter/ColumnFilterTest.java index b23aa0731f..3cf7c59e9f 100644 --- a/test/unit/org/apache/cassandra/db/filter/ColumnFilterTest.java +++ b/test/unit/org/apache/cassandra/db/filter/ColumnFilterTest.java @@ -20,15 +20,12 @@ package org.apache.cassandra.db.filter; import java.io.IOException; import java.util.Arrays; -import java.util.Collection; import java.util.function.Consumer; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; import org.apache.cassandra.Util; import org.apache.cassandra.config.DatabaseDescriptor; @@ -52,14 +49,10 @@ import org.apache.cassandra.utils.Throwables; import static org.junit.Assert.assertEquals; -@RunWith(Parameterized.class) public class ColumnFilterTest { private static final ColumnFilter.Serializer serializer = new ColumnFilter.Serializer(); - @Parameterized.Parameter - public String clusterMinVersion; - private final TableMetadata metadata = TableMetadata.builder("ks", "table") .partitioner(Murmur3Partitioner.instance) .addPartitionKeyColumn("pk", Int32Type.instance) @@ -82,13 +75,6 @@ public class ColumnFilterTest private final CellPath path3 = CellPath.create(ByteBufferUtil.bytes(3)); private final CellPath path4 = CellPath.create(ByteBufferUtil.bytes(4)); - - @Parameterized.Parameters(name = "{index}: clusterMinVersion={0}") - public static Collection data() - { - return Arrays.asList(new Object[]{ "3.0" }, new Object[]{ "3.11" }, new Object[]{ "4.0-rc1" }, new Object[]{ "4.0" }); - } - @BeforeClass public static void beforeClass() { @@ -104,7 +90,7 @@ public class ColumnFilterTest @Before public void before() { - Util.setUpgradeFromVersion(clusterMinVersion); + Util.setUpgradeFromVersion("4.0"); } // Select all @@ -341,15 +327,7 @@ public class ColumnFilterTest Consumer check = filter -> { testRoundTrips(filter); assertFetchedQueried(true, true, filter, v1); - if ("3.0".equals(clusterMinVersion)) - { - assertEquals("*/*", filter.toString()); - assertEquals("*", filter.toCQLString()); - assertFetchedQueried(true, true, filter, s1, s2, v2); - assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4); - assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4); - } - else if ("3.11".equals(clusterMinVersion) || (returnStaticContentOnPartitionWithNoRows && "4.0".equals(clusterMinVersion))) + if (returnStaticContentOnPartitionWithNoRows) { assertEquals("*/[v1]", filter.toString()); assertEquals("v1", filter.toCQLString()); @@ -389,15 +367,7 @@ public class ColumnFilterTest Consumer check = filter -> { testRoundTrips(filter); assertFetchedQueried(true, true, filter, s1); - if ("3.0".equals(clusterMinVersion)) - { - assertEquals("*/*", filter.toString()); - assertEquals("*", filter.toCQLString()); - assertFetchedQueried(true, true, filter, v1, v2, s2); - assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4); - assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4); - } - else if ("3.11".equals(clusterMinVersion) || (returnStaticContentOnPartitionWithNoRows && "4.0".equals(clusterMinVersion))) + if (returnStaticContentOnPartitionWithNoRows) { assertEquals("*/[s1]", filter.toString()); assertEquals("s1", filter.toCQLString()); @@ -439,15 +409,7 @@ public class ColumnFilterTest .build(); testRoundTrips(filter); assertFetchedQueried(true, true, filter, v2); - if ("3.0".equals(clusterMinVersion)) - { - assertEquals("*/*", filter.toString()); - assertEquals("*", filter.toCQLString()); - assertFetchedQueried(true, true, filter, s1, s2, v1); - assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4); - assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4); - } - else if ("3.11".equals(clusterMinVersion) || (returnStaticContentOnPartitionWithNoRows && "4.0".equals(clusterMinVersion))) + if (returnStaticContentOnPartitionWithNoRows) { assertEquals("*/[v2[1]]", filter.toString()); assertEquals("v2[1]", filter.toCQLString()); @@ -487,15 +449,7 @@ public class ColumnFilterTest .build(); testRoundTrips(filter); assertFetchedQueried(true, true, filter, s2); - if ("3.0".equals(clusterMinVersion)) - { - assertEquals("*/*", filter.toString()); - assertEquals("*", filter.toCQLString()); - assertFetchedQueried(true, true, filter, v1, v2, s1); - assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4); - assertCellFetchedQueried(true, true, filter, s2, path1, path0, path2, path3, path4); - } - else if ("3.11".equals(clusterMinVersion) || (returnStaticContentOnPartitionWithNoRows && "4.0".equals(clusterMinVersion))) + if (returnStaticContentOnPartitionWithNoRows) { assertEquals("*/[s2[1]]", filter.toString()); assertEquals("s2[1]", filter.toCQLString()); @@ -518,8 +472,6 @@ public class ColumnFilterTest private void testRoundTrips(ColumnFilter cf) { - testRoundTrip(cf, MessagingService.VERSION_30); - testRoundTrip(cf, MessagingService.VERSION_3014); testRoundTrip(cf, MessagingService.VERSION_40); } @@ -533,14 +485,7 @@ public class ColumnFilterTest DataInputPlus input = new DataInputBuffer(output.buffer(), false); ColumnFilter deserialized = serializer.deserialize(input, version, metadata); - if (version == MessagingService.VERSION_30 && columnFilter.fetchesAllColumns(false)) - { - Assert.assertEquals(metadata.regularAndStaticColumns(), deserialized.fetchedColumns()); - } - else - { - Assert.assertEquals(deserialized, columnFilter); - } + Assert.assertEquals(deserialized, columnFilter); } catch (IOException e) { diff --git a/test/unit/org/apache/cassandra/locator/InetAddressAndPortSerializerTest.java b/test/unit/org/apache/cassandra/locator/InetAddressAndPortSerializerTest.java index 78ecfb5aad..01f1a201fd 100644 --- a/test/unit/org/apache/cassandra/locator/InetAddressAndPortSerializerTest.java +++ b/test/unit/org/apache/cassandra/locator/InetAddressAndPortSerializerTest.java @@ -37,10 +37,11 @@ public class InetAddressAndPortSerializerTest InetAddressAndPort ipv4 = InetAddressAndPort.getByName("127.0.0.1:42"); InetAddressAndPort ipv6 = InetAddressAndPort.getByName("[2001:db8:0:0:0:ff00:42:8329]:42"); - testAddress(ipv4, MessagingService.VERSION_30); - testAddress(ipv6, MessagingService.VERSION_30); - testAddress(ipv4, MessagingService.current_version); - testAddress(ipv6, MessagingService.current_version); + for (MessagingService.Version version : MessagingService.Version.supportedVersions()) + { + testAddress(ipv4, version.value); + testAddress(ipv6, version.value); + } } private void testAddress(InetAddressAndPort address, int version) throws Exception diff --git a/test/unit/org/apache/cassandra/net/ConnectionTest.java b/test/unit/org/apache/cassandra/net/ConnectionTest.java index 53bc7c5d97..778dc41fc8 100644 --- a/test/unit/org/apache/cassandra/net/ConnectionTest.java +++ b/test/unit/org/apache/cassandra/net/ConnectionTest.java @@ -58,7 +58,6 @@ import io.netty.channel.ChannelPromise; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions; import org.apache.cassandra.db.commitlog.CommitLog; -import org.apache.cassandra.distributed.shared.WithProperties; import org.apache.cassandra.exceptions.RequestFailureReason; import org.apache.cassandra.exceptions.UnknownColumnException; import org.apache.cassandra.io.IVersionedAsymmetricSerializer; @@ -71,15 +70,12 @@ import org.apache.cassandra.utils.FBUtilities; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.apache.cassandra.config.CassandraRelevantProperties.SSL_STORAGE_PORT; -import static org.apache.cassandra.net.MessagingService.VERSION_30; -import static org.apache.cassandra.net.MessagingService.VERSION_3014; import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.NoPayload.noPayload; import static org.apache.cassandra.net.MessagingService.current_version; -import static org.apache.cassandra.net.ConnectionUtils.*; import static org.apache.cassandra.net.ConnectionType.LARGE_MESSAGES; import static org.apache.cassandra.net.ConnectionType.SMALL_MESSAGES; +import static org.apache.cassandra.net.ConnectionUtils.*; import static org.apache.cassandra.net.OutboundConnectionSettings.Framing.LZ4; import static org.apache.cassandra.net.OutboundConnections.LARGE_MESSAGE_THRESHOLD; import static org.apache.cassandra.utils.Clock.Global.nanoTime; @@ -104,11 +100,6 @@ public class ConnectionTest handlers.putIfAbsent(verb, verb.unsafeSetHandler(supplier)); } - private void unsafeSetExpiration(Verb verb, ToLongFunction expiration) throws Throwable - { - timeouts.putIfAbsent(verb, verb.unsafeSetExpiration(expiration)); - } - @After public void resetVerbs() throws Throwable { @@ -192,11 +183,7 @@ public class ConnectionTest .withRequireClientAuth(false) .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA"); - static final AcceptVersions legacy = new AcceptVersions(VERSION_30, VERSION_30); - static final List> MODIFIERS = ImmutableList.of( - settings -> settings.outbound(outbound -> outbound.withAcceptVersions(legacy)) - .inbound(inbound -> inbound.withAcceptMessaging(legacy)), settings -> settings.outbound(outbound -> outbound.withEncryption(encryptionOptions)) .inbound(inbound -> inbound.withEncryption(encryptionOptions)), settings -> settings.outbound(outbound -> outbound.withFraming(LZ4)) @@ -545,115 +532,6 @@ public class ConnectionTest }); } - @Test - public void testPre40() throws Throwable - { - MessagingService.instance().versions.set(FBUtilities.getBroadcastAddressAndPort(), - MessagingService.VERSION_30); - - try - { - test((inbound, outbound, endpoint) -> { - CountDownLatch done = new CountDownLatch(1); - unsafeSetHandler(Verb._TEST_1, - () -> (msg) -> done.countDown()); - - Message message = Message.out(Verb._TEST_1, noPayload); - outbound.enqueue(message); - Assert.assertTrue(done.await(1, MINUTES)); - Assert.assertTrue(outbound.isConnected()); - }); - } - finally - { - MessagingService.instance().versions.set(FBUtilities.getBroadcastAddressAndPort(), - current_version); - } - } - - @Test - public void testPendingOutboundConnectionUpdatesMessageVersionOnReconnectAttempt() throws Throwable - { - try (WithProperties properties = new WithProperties().set(SSL_STORAGE_PORT, 7011)) - { - // Set up an inbound connection listening *only* on the SSL storage port to - // replicate a 3.x node. Force the messaging version to be incorrectly set to 4.0 - // before the outbound connection attempt. - final Settings settings = Settings.LARGE; - final InetAddressAndPort endpoint = FBUtilities.getBroadcastAddressAndPort(); - - MessagingService.instance().versions.set(FBUtilities.getBroadcastAddressAndPort(), - MessagingService.VERSION_40); - - final InetAddressAndPort legacySSLAddrsAndPort = endpoint.withPort(DatabaseDescriptor.getSSLStoragePort()); - InboundConnectionSettings inboundSettings = settings.inbound.apply(new InboundConnectionSettings().withEncryption(encryptionOptions)) - .withBindAddress(legacySSLAddrsAndPort) - .withAcceptMessaging(new AcceptVersions(VERSION_30, VERSION_3014)) - .withSocketFactory(factory); - InboundSockets inbound = new InboundSockets(Collections.singletonList(inboundSettings)); - OutboundConnectionSettings outboundTemplate = settings.outbound.apply(new OutboundConnectionSettings(endpoint).withEncryption(encryptionOptions)) - .withDefaultReserveLimits() - .withSocketFactory(factory) - .withDefaults(ConnectionCategory.MESSAGING); - ResourceLimits.EndpointAndGlobal reserveCapacityInBytes = new ResourceLimits.EndpointAndGlobal(new ResourceLimits.Concurrent(outboundTemplate.applicationSendQueueReserveEndpointCapacityInBytes), outboundTemplate.applicationSendQueueReserveGlobalCapacityInBytes); - OutboundConnection outbound = new OutboundConnection(settings.type, outboundTemplate, reserveCapacityInBytes); - try - { - logger.info("Running {} {} -> {}", outbound.messagingVersion(), outbound.settings(), inboundSettings); - inbound.open().sync(); - - CountDownLatch done = new CountDownLatch(1); - unsafeSetHandler(Verb._TEST_1, - () -> (msg) -> done.countDown()); - - // Enqueuing outbound message will initiate an outbound - // connection with pending data in the pipeline - Message message = Message.out(Verb._TEST_1, noPayload); - outbound.enqueue(message); - - // Wait until the first connection attempt has taken place - // before updating the endpoint messaging version so that the - // connection takes place to a 4.0 node. - int attempts = 0; - final long waitForAttemptMillis = TimeUnit.SECONDS.toMillis(15); - while (outbound.connectionAttempts() == 0 && attempts < waitForAttemptMillis / 10) - { - Uninterruptibles.sleepUninterruptibly(10, TimeUnit.MILLISECONDS); - attempts++; - } - - // Now that the connection is being attempted, set the endpoint version so - // that on the reconnect attempt the messaging version is rechecked and the - // legacy ssl logic picks the storage port instead. This should trigger a - // TRACE level log message "Endpoint version changed from 12 to 10 since - // connection initialized, updating." - outbound.settings().endpointToVersion.set(endpoint, VERSION_30); - - // The connection should have successfully connected and delivered the _TEST_1 - // message within the timout. - Assert.assertTrue(done.await(15, SECONDS)); - Assert.assertTrue(outbound.isConnected()); - Assert.assertTrue(String.format("expect less successful connections (%d) than attempts (%d)", - outbound.successfulConnections(), outbound.connectionAttempts()), - outbound.successfulConnections() < outbound.connectionAttempts()); - - } - finally - { - outbound.close(false); - inbound.close().get(30L, SECONDS); - outbound.close(false).get(30L, SECONDS); - resetVerbs(); - MessagingService.instance().messageHandlers.clear(); - } - } - finally - { - MessagingService.instance().versions.set(FBUtilities.getBroadcastAddressAndPort(), - current_version); - } - } - @Test public void testCloseIfEndpointDown() throws Throwable { @@ -750,7 +628,7 @@ public class ConnectionTest unsafeSetHandler(Verb._TEST_1, () -> msg -> done.countDown()); outbound.enqueue(Message.out(Verb._TEST_1, noPayload)); Assert.assertTrue(done.await(10, SECONDS)); - Assert.assertEquals(done.getCount(), 0); + Assert.assertEquals(0, done.getCount()); // Simulate disconnect inbound.close().get(10, SECONDS); @@ -763,7 +641,7 @@ public class ConnectionTest outbound.enqueue(Message.out(Verb._TEST_1, noPayload)); latch2.await(10, SECONDS); - Assert.assertEquals(latch2.getCount(), 0); + Assert.assertEquals(0, latch2.getCount()); } finally { diff --git a/test/unit/org/apache/cassandra/net/ForwardingInfoTest.java b/test/unit/org/apache/cassandra/net/ForwardingInfoTest.java index 6e0d11a887..403bbcfd58 100644 --- a/test/unit/org/apache/cassandra/net/ForwardingInfoTest.java +++ b/test/unit/org/apache/cassandra/net/ForwardingInfoTest.java @@ -35,16 +35,12 @@ import static org.junit.Assert.assertTrue; public class ForwardingInfoTest { - @Test - public void testCurrent() throws Exception - { - testVersion(MessagingService.current_version); - } @Test - public void test30() throws Exception + public void testSupportedVersions() throws Exception { - testVersion(MessagingService.VERSION_30); + for (MessagingService.Version version : MessagingService.Version.supportedVersions()) + testVersion(version.value); } private void testVersion(int version) throws Exception diff --git a/test/unit/org/apache/cassandra/net/FramingTest.java b/test/unit/org/apache/cassandra/net/FramingTest.java index 18e31795f8..6695012b8c 100644 --- a/test/unit/org/apache/cassandra/net/FramingTest.java +++ b/test/unit/org/apache/cassandra/net/FramingTest.java @@ -48,11 +48,6 @@ import org.apache.cassandra.utils.memory.BufferPools; import org.apache.cassandra.utils.vint.VIntCoding; import static java.lang.Math.*; -import static org.apache.cassandra.net.MessagingService.VERSION_30; -import static org.apache.cassandra.net.MessagingService.VERSION_3014; -import static org.apache.cassandra.net.MessagingService.current_version; -import static org.apache.cassandra.net.MessagingService.minimum_version; -import static org.apache.cassandra.net.OutboundConnections.LARGE_MESSAGE_THRESHOLD; import static org.apache.cassandra.net.ShareableBytes.wrap; // TODO: test corruption @@ -210,12 +205,6 @@ public class FramingTest return new SequenceOfFrames(uncompressed, cumulativeCompressedLength, frames); } - @Test - public void burnRandomLegacy() - { - burnRandomLegacy(1000); - } - @Test public void testSerializeSizeMatchesEdgeCases() // See CASSANDRA-16103 { @@ -247,212 +236,6 @@ public class FramingTest subTest.accept(1L << 14 - 1); } - private void burnRandomLegacy(int count) - { - SecureRandom seed = new SecureRandom(); - Random random = new Random(); - for (int i = 0 ; i < count ; ++i) - { - long innerSeed = seed.nextLong(); - float ratio = seed.nextFloat(); - int version = minimum_version + random.nextInt(1 + current_version - minimum_version); - logger.debug("seed: {}, ratio: {}, version: {}", innerSeed, ratio, version); - random.setSeed(innerSeed); - testRandomSequenceOfMessages(random, ratio, version, new FrameDecoderLegacy(GlobalBufferPoolAllocator.instance, version)); - } - } - - @Test - public void testRandomLegacy() - { - testRandomLegacy(250); - } - - private void testRandomLegacy(int count) - { - SecureRandom seeds = new SecureRandom(); - for (int messagingVersion : new int[] { VERSION_30, VERSION_3014, current_version}) - { - FrameDecoder decoder = new FrameDecoderLegacy(GlobalBufferPoolAllocator.instance, messagingVersion); - testSomeMessages(seeds.nextLong(), count, 0.0f, messagingVersion, decoder); - testSomeMessages(seeds.nextLong(), count, 0.1f, messagingVersion, decoder); - testSomeMessages(seeds.nextLong(), count, 0.95f, messagingVersion, decoder); - testSomeMessages(seeds.nextLong(), count, 1.0f, messagingVersion, decoder); - } - } - - private void testSomeMessages(long seed, int count, float largeRatio, int messagingVersion, FrameDecoder decoder) - { - logger.info("seed: {}, iterations: {}, largeRatio: {}, messagingVersion: {}, decoder: {}", seed, count, largeRatio, messagingVersion, decoder.getClass().getSimpleName()); - Random random = new Random(seed); - for (int i = 0 ; i < count ; ++i) - { - long innerSeed = random.nextLong(); - logger.debug("inner seed: {}, iteration: {}", innerSeed, i); - random.setSeed(innerSeed); - testRandomSequenceOfMessages(random, largeRatio, messagingVersion, decoder); - } - } - - private void testRandomSequenceOfMessages(Random random, float largeRatio, int messagingVersion, FrameDecoder decoder) - { - SequenceOfFrames sequenceOfMessages = sequenceOfMessages(random, largeRatio, messagingVersion); - - List messages = sequenceOfMessages.original; - ShareableBytes stream = sequenceOfMessages.frames; - - int end = stream.get().limit(); - List out = new ArrayList<>(); - - int messageStart = 0; - int messageIndex = 0; - for (int i = 0 ; i < end ; ) - { - int limit = i + random.nextInt(1 + end - i); - decoder.decode(out, stream.slice(i, limit)); - - int outIndex = 0; - byte[] message = messages.get(messageIndex); - if (i > messageStart) - { - int start; - if (message.length <= LARGE_MESSAGE_THRESHOLD) - { - start = 0; - } - else if (!lengthIsReadable(message, i - messageStart, messagingVersion)) - { - // we should have an initial frame containing only some prefix of the message (probably 64 bytes) - // that was stashed only to decide how big the message was - FrameDecoder.IntactFrame frame = (FrameDecoder.IntactFrame) out.get(outIndex++); - Assert.assertFalse(frame.isSelfContained); - start = frame.contents.remaining(); - verify(message, 0, frame.contents.remaining(), frame.contents); - } - else - { - start = i - messageStart; - } - - if (limit >= message.length + messageStart) - { - FrameDecoder.IntactFrame frame = (FrameDecoder.IntactFrame) out.get(outIndex++); - Assert.assertEquals(start == 0, frame.isSelfContained); - // verify remainder of a large message, or a single fully stashed small message - verify(message, start, message.length, frame.contents); - - messageStart += message.length; - if (++messageIndex < messages.size()) - message = messages.get(messageIndex); - } - else if (message.length > LARGE_MESSAGE_THRESHOLD) - { - FrameDecoder.IntactFrame frame = (FrameDecoder.IntactFrame) out.get(outIndex++); - Assert.assertFalse(frame.isSelfContained); - // verify next portion of a large message - verify(message, start, limit - messageStart, frame.contents); - - Assert.assertEquals(outIndex, out.size()); - for (FrameDecoder.Frame f : out) - f.release(); - out.clear(); - i = limit; - continue; - } - } - - // message is fresh - int beginFrameIndex = messageIndex; - while (messageStart + message.length <= limit) - { - messageStart += message.length; - if (++messageIndex < messages.size()) - message = messages.get(messageIndex); - } - - if (beginFrameIndex < messageIndex) - { - FrameDecoder.IntactFrame frame = (FrameDecoder.IntactFrame) out.get(outIndex++); - Assert.assertTrue(frame.isSelfContained); - while (beginFrameIndex < messageIndex) - { - byte[] m = messages.get(beginFrameIndex); - ShareableBytes bytesToVerify = frame.contents.sliceAndConsume(m.length); - verify(m, bytesToVerify); - bytesToVerify.release(); - ++beginFrameIndex; - } - Assert.assertFalse(frame.contents.hasRemaining()); - } - - if (limit > messageStart - && message.length > LARGE_MESSAGE_THRESHOLD - && lengthIsReadable(message, limit - messageStart, messagingVersion)) - { - FrameDecoder.IntactFrame frame = (FrameDecoder.IntactFrame) out.get(outIndex++); - Assert.assertFalse(frame.isSelfContained); - verify(message, 0, limit - messageStart, frame.contents); - } - - Assert.assertEquals(outIndex, out.size()); - for (FrameDecoder.Frame frame : out) - frame.release(); - out.clear(); - - i = limit; - } - stream.release(); - Assert.assertTrue(stream.isReleased()); - Assert.assertNull(decoder.stash); - Assert.assertTrue(decoder.frames.isEmpty()); - } - - private static boolean lengthIsReadable(byte[] message, int limit, int messagingVersion) - { - try - { - return Message.serializer.inferMessageSize(ByteBuffer.wrap(message), 0, limit, messagingVersion) >= 0; - } - catch (Message.InvalidLegacyProtocolMagic e) - { - throw new IllegalStateException(e); - } - } - - private static SequenceOfFrames sequenceOfMessages(Random random, float largeRatio, int messagingVersion) - { - int messageCount = 1 + random.nextInt(63); - List messages = new ArrayList<>(); - int[] cumulativeLength = new int[messageCount]; - for (int i = 0 ; i < messageCount ; ++i) - { - byte[] payload; - if (random.nextFloat() < largeRatio) payload = randomishBytes(random, 1 << 16, 1 << 17); - else payload = randomishBytes(random, 1, 1 << 16); - Message messageObj = Message.out(Verb._TEST_1, payload); - - byte[] message; - try (DataOutputBuffer out = new DataOutputBuffer(messageObj.serializedSize(messagingVersion))) - { - Message.serializer.serialize(messageObj, out, messagingVersion); - message = out.toByteArray(); - } - catch (IOException e) - { - throw new IllegalStateException(e); - } - messages.add(message); - - cumulativeLength[i] = (i == 0 ? 0 : cumulativeLength[i - 1]) + message.length; - } - - ByteBuffer frames = BufferPools.forNetworking().getAtLeast(cumulativeLength[messageCount - 1], BufferType.OFF_HEAP); - for (byte[] buffer : messages) - frames.put(buffer); - frames.flip(); - return new SequenceOfFrames(messages, cumulativeLength, frames); - } - public static byte[] randomishBytes(Random random, int minLength, int maxLength) { byte[] bytes = new byte[minLength + random.nextInt(Math.max(1, maxLength - minLength))]; diff --git a/test/unit/org/apache/cassandra/net/HandshakeTest.java b/test/unit/org/apache/cassandra/net/HandshakeTest.java index 36439f5fdf..7c536f4baf 100644 --- a/test/unit/org/apache/cassandra/net/HandshakeTest.java +++ b/test/unit/org/apache/cassandra/net/HandshakeTest.java @@ -22,36 +22,34 @@ import java.nio.channels.ClosedChannelException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import com.google.common.net.InetAddresses; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions; import org.apache.cassandra.config.ParameterizedClass; +import org.apache.cassandra.db.commitlog.CommitLog; import org.apache.cassandra.gms.GossipDigestSyn; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.net.OutboundConnectionInitiator.Result.MessagingSuccess; import org.apache.cassandra.security.DefaultSslContextFactory; import org.apache.cassandra.utils.concurrent.AsyncPromise; + import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; - import io.netty.channel.EventLoop; import io.netty.util.concurrent.Future; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.db.commitlog.CommitLog; -import org.apache.cassandra.locator.InetAddressAndPort; -import org.apache.cassandra.net.OutboundConnectionInitiator.Result.MessagingSuccess; -import static org.apache.cassandra.net.MessagingService.VERSION_30; -import static org.apache.cassandra.net.MessagingService.VERSION_3014; import static org.apache.cassandra.net.MessagingService.current_version; import static org.apache.cassandra.net.MessagingService.minimum_version; import static org.apache.cassandra.net.ConnectionType.SMALL_MESSAGES; import static org.apache.cassandra.net.OutboundConnectionInitiator.*; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -75,15 +73,15 @@ public class HandshakeTest factory.shutdownNow(); } - private Result handshake(int req, int outMin, int outMax) throws ExecutionException, InterruptedException + private Result handshake(int outMin, int outMax) throws ExecutionException, InterruptedException { - return handshake(req, new AcceptVersions(outMin, outMax), null); + return handshake(new AcceptVersions(outMin, outMax), null); } - private Result handshake(int req, int outMin, int outMax, int inMin, int inMax) throws ExecutionException, InterruptedException + private Result handshake(int outMin, int outMax, int inMin, int inMax) throws ExecutionException, InterruptedException { - return handshake(req, new AcceptVersions(outMin, outMax), new AcceptVersions(inMin, inMax)); + return handshake(new AcceptVersions(outMin, outMax), new AcceptVersions(inMin, inMax)); } - private Result handshake(int req, AcceptVersions acceptOutbound, AcceptVersions acceptInbound) throws ExecutionException, InterruptedException + private Result handshake(AcceptVersions acceptOutbound, AcceptVersions acceptInbound) throws ExecutionException, InterruptedException { InboundSockets inbound = new InboundSockets(new InboundConnectionSettings().withAcceptMessaging(acceptInbound)); try @@ -98,7 +96,7 @@ public class HandshakeTest new OutboundConnectionSettings(endpoint) .withAcceptVersions(acceptOutbound) .withDefaults(ConnectionCategory.MESSAGING), - req, AsyncPromise.withExecutor(eventLoop)); + AsyncPromise.withExecutor(eventLoop)); return future.get(); } finally @@ -111,7 +109,7 @@ public class HandshakeTest @Test public void testBothCurrentVersion() throws InterruptedException, ExecutionException { - Result result = handshake(current_version, minimum_version, current_version); + Result result = handshake(minimum_version, current_version); Assert.assertEquals(Result.Outcome.SUCCESS, result.outcome); result.success().channel.close(); } @@ -119,7 +117,7 @@ public class HandshakeTest @Test public void testSendCompatibleOldVersion() throws InterruptedException, ExecutionException { - Result result = handshake(current_version, current_version, current_version + 1, current_version +1, current_version + 2); + Result result = handshake(current_version, current_version + 1, current_version +1, current_version + 2); Assert.assertEquals(Result.Outcome.SUCCESS, result.outcome); Assert.assertEquals(current_version + 1, result.success().messagingVersion); result.success().channel.close(); @@ -128,7 +126,7 @@ public class HandshakeTest @Test public void testSendCompatibleFutureVersion() throws InterruptedException, ExecutionException { - Result result = handshake(current_version + 1, current_version - 1, current_version + 1); + Result result = handshake(MessagingService.minimum_version, current_version + 1); Assert.assertEquals(Result.Outcome.SUCCESS, result.outcome); Assert.assertEquals(current_version, result.success().messagingVersion); result.success().channel.close(); @@ -137,7 +135,7 @@ public class HandshakeTest @Test public void testSendIncompatibleFutureVersion() throws InterruptedException, ExecutionException { - Result result = handshake(current_version + 1, current_version + 1, current_version + 1); + Result result = handshake(current_version + 1, current_version + 1); Assert.assertEquals(Result.Outcome.INCOMPATIBLE, result.outcome); Assert.assertEquals(current_version, result.incompatible().closestSupportedVersion); Assert.assertEquals(current_version, result.incompatible().maxMessagingVersion); @@ -146,94 +144,38 @@ public class HandshakeTest @Test public void testSendIncompatibleOldVersion() throws InterruptedException, ExecutionException { - Result result = handshake(current_version + 1, current_version + 1, current_version + 1, current_version + 2, current_version + 3); + Result result = handshake(current_version + 1, current_version + 1, current_version + 2, current_version + 3); Assert.assertEquals(Result.Outcome.INCOMPATIBLE, result.outcome); Assert.assertEquals(current_version + 2, result.incompatible().closestSupportedVersion); Assert.assertEquals(current_version + 3, result.incompatible().maxMessagingVersion); } @Test - public void testSendCompatibleMaxVersionPre40() throws InterruptedException, ExecutionException + public void testSendAllSupported() throws InterruptedException, ExecutionException { - Result result = handshake(VERSION_3014, VERSION_30, VERSION_3014, VERSION_30, VERSION_3014); - Assert.assertEquals(Result.Outcome.SUCCESS, result.outcome); - Assert.assertEquals(VERSION_3014, result.success().messagingVersion); - result.success().channel.close(); - } + List supportedVersions = MessagingService.Version.supportedVersions(); + for (MessagingService.Version outMin : supportedVersions) + for (MessagingService.Version outMax : supportedVersions) + if (outMin.value <= outMax.value) + for (MessagingService.Version inMin : supportedVersions) + for (MessagingService.Version inMax : supportedVersions) + if (inMin.value <= inMax.value) + { + Result result = handshake(outMin.value, outMax.value, inMin.value, inMax.value); + // expect success if out and in have a version in common + boolean expectSuccess = outMin.value <= inMax.value && inMin.value <= outMax.value; - @Test - public void testSendCompatibleFutureVersionPre40() throws InterruptedException, ExecutionException - { - Result result = handshake(VERSION_3014, VERSION_30, VERSION_3014, VERSION_30, VERSION_30); - Assert.assertEquals(Result.Outcome.RETRY, result.outcome); - Assert.assertEquals(VERSION_30, result.retry().withMessagingVersion); - } + Assert.assertEquals(String.format("wrong result outcome for outMin %s outMax %s inMin %s inMax %s", outMin.value, outMax.value, inMin.value, inMax.value), + expectSuccess ? Result.Outcome.SUCCESS : Result.Outcome.INCOMPATIBLE, result.outcome); - @Test - public void testSendIncompatibleFutureVersionPre40() throws InterruptedException, ExecutionException - { - Result result = handshake(VERSION_3014, VERSION_3014, VERSION_3014, VERSION_30, VERSION_30); - Assert.assertEquals(Result.Outcome.INCOMPATIBLE, result.outcome); - Assert.assertEquals(-1, result.incompatible().closestSupportedVersion); - Assert.assertEquals(VERSION_30, result.incompatible().maxMessagingVersion); - } - - @Test - public void testSendCompatibleOldVersionPre40() throws InterruptedException - { - try - { - handshake(VERSION_30, VERSION_30, VERSION_3014, VERSION_3014, VERSION_3014); - Assert.fail("Should have thrown"); + if (expectSuccess) + { + Assert.assertEquals(String.format("wrong agreed messagingVersion for outMin %s outMax %s inMin %s inMax %s", outMin.value, outMax.value, inMin.value, inMax.value), + Math.min(outMax.value, inMax.value), result.success().messagingVersion); + result.success().channel.close(); + } + } } - catch (ExecutionException e) - { - assertTrue(e.getCause() instanceof ClosedChannelException); - } - } - - @Test - public void testSendIncompatibleOldVersionPre40() throws InterruptedException - { - try - { - handshake(VERSION_30, VERSION_30, VERSION_30, VERSION_3014, VERSION_3014); - Assert.fail("Should have thrown"); - } - catch (ExecutionException e) - { - assertTrue(e.getCause() instanceof ClosedChannelException); - } - } - - @Test - public void testSendCompatibleOldVersion40() throws InterruptedException, ExecutionException - { - Result result = handshake(VERSION_30, VERSION_30, VERSION_30, VERSION_30, current_version); - Assert.assertEquals(Result.Outcome.SUCCESS, result.outcome); - Assert.assertEquals(VERSION_30, result.success().messagingVersion); - } - - @Test - public void testSendIncompatibleOldVersion40() throws InterruptedException - { - try - { - Assert.fail(Objects.toString(handshake(VERSION_30, VERSION_30, VERSION_30, current_version, current_version))); - } - catch (ExecutionException e) - { - assertTrue(e.getCause() instanceof ClosedChannelException); - } - } - - @Test // fairly contrived case, but since we introduced logic for testing we need to be careful it doesn't make us worse - public void testSendToFuturePost40BelievedToBePre40() throws InterruptedException, ExecutionException - { - Result result = handshake(VERSION_30, VERSION_30, current_version, VERSION_30, current_version + 1); - Assert.assertEquals(Result.Outcome.SUCCESS, result.outcome); - Assert.assertEquals(VERSION_30, result.success().messagingVersion); - } @Test public void testOutboundConnectionfFallbackDuringUpgrades() throws ClosedChannelException, InterruptedException diff --git a/test/unit/org/apache/cassandra/net/MessageSerializationPropertyTest.java b/test/unit/org/apache/cassandra/net/MessageSerializationPropertyTest.java index b30390d4a1..88a45af510 100644 --- a/test/unit/org/apache/cassandra/net/MessageSerializationPropertyTest.java +++ b/test/unit/org/apache/cassandra/net/MessageSerializationPropertyTest.java @@ -70,7 +70,7 @@ public class MessageSerializationPropertyTest implements Serializable try (DataOutputBuffer out = new DataOutputBuffer(1024)) { qt().withShrinkCycles(0).forAll(MESSAGE_GEN).checkAssert(orFail(message -> { - for (MessagingService.Version version : MessagingService.Version.values()) + for (MessagingService.Version version : MessagingService.Version.supportedVersions()) { out.clear(); serializer.serialize(message, out, version.value); @@ -99,7 +99,7 @@ public class MessageSerializationPropertyTest implements Serializable { qt().withShrinkCycles(0).forAll(MESSAGE_GEN).checkAssert(orFail(message -> { withTable(schema, message, orFail(ignore -> { - for (MessagingService.Version version : MessagingService.Version.values()) + for (MessagingService.Version version : MessagingService.Version.supportedVersions()) { first.clear(); second.clear(); diff --git a/test/unit/org/apache/cassandra/net/MessageTest.java b/test/unit/org/apache/cassandra/net/MessageTest.java index 3850bc4f03..0228608ea4 100644 --- a/test/unit/org/apache/cassandra/net/MessageTest.java +++ b/test/unit/org/apache/cassandra/net/MessageTest.java @@ -43,8 +43,6 @@ import org.apache.cassandra.utils.FreeRunningClock; import org.apache.cassandra.utils.TimeUUID; import static org.apache.cassandra.net.Message.serializer; -import static org.apache.cassandra.net.MessagingService.VERSION_3014; -import static org.apache.cassandra.net.MessagingService.VERSION_30; import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.NoPayload.noPayload; import static org.apache.cassandra.net.ParamType.RESPOND_TO; @@ -103,8 +101,6 @@ public class MessageTest .withParam(TRACE_SESSION, nextTimeUUID()) .build(); - testInferMessageSize(msg, VERSION_30); - testInferMessageSize(msg, VERSION_3014); testInferMessageSize(msg, VERSION_40); } @@ -122,11 +118,11 @@ public class MessageTest // should return -1 - fail to infer size - for all lengths of buffer until payload length can be read for (int limit = 0; limit < serializedSize - payloadSize; limit++) - assertEquals(-1, serializer.inferMessageSize(buffer, 0, limit, version)); + assertEquals(-1, serializer.inferMessageSize(buffer, 0, limit)); // once payload size can be read, should correctly infer message size for (int limit = serializedSize - payloadSize; limit < serializedSize; limit++) - assertEquals(serializedSize, serializer.inferMessageSize(buffer, 0, limit, version)); + assertEquals(serializedSize, serializer.inferMessageSize(buffer, 0, limit)); } } @@ -264,8 +260,6 @@ public class MessageTest private void testCycle(Message msg) throws IOException { - testCycle(msg, VERSION_30); - testCycle(msg, VERSION_3014); testCycle(msg, VERSION_40); } diff --git a/test/unit/org/apache/cassandra/net/ProxyHandlerConnectionsTest.java b/test/unit/org/apache/cassandra/net/ProxyHandlerConnectionsTest.java index 34aab911e5..b21de607b3 100644 --- a/test/unit/org/apache/cassandra/net/ProxyHandlerConnectionsTest.java +++ b/test/unit/org/apache/cassandra/net/ProxyHandlerConnectionsTest.java @@ -199,18 +199,11 @@ public class ProxyHandlerConnectionsTest boolean expire = i % 2 == 0; Message.Builder builder = Message.builder(Verb._TEST_1, 1L); - if (settings.right.acceptVersions == ConnectionTest.legacy) - { - // backdate messages; leave 500 milliseconds to leave outbound path - builder.withCreatedAt(nanoTime - (expire ? 0 : MILLISECONDS.toNanos(1500))); - } - else - { - // Give messages 500 milliseconds to leave outbound path - builder.withCreatedAt(nanoTime) - .withExpiresAt(nanoTime + (expire ? MILLISECONDS.toNanos(500) : MILLISECONDS.toNanos(3000))); - } - outbound.enqueue(builder.build()); + // Give messages 500 milliseconds to leave outbound path + builder.withCreatedAt(nanoTime) + .withExpiresAt(nanoTime + (expire ? MILLISECONDS.toNanos(500) : MILLISECONDS.toNanos(3000))); + + outbound.enqueue(builder.build()); } enqueueDone.countDown(); diff --git a/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java b/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java index 462bbe849b..debb03e705 100644 --- a/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java +++ b/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java @@ -67,7 +67,7 @@ public class StreamTransferTaskTest @Override public NettyStreamingChannel create(InetSocketAddress to, int messagingVersion, StreamingChannel.Kind kind) { - return new NettyStreamingChannel(messagingVersion, new TestChannel(), kind); + return new NettyStreamingChannel(new TestChannel(), kind); } }; diff --git a/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java b/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java index b156ab7454..03d6aa7fd3 100644 --- a/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java +++ b/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java @@ -56,7 +56,6 @@ import static org.apache.cassandra.net.TestChannel.REMOTE_ADDR; public class StreamingInboundHandlerTest { - private static final int VERSION = MessagingService.current_version; private NettyStreamingChannel streamingChannel; private EmbeddedChannel channel; @@ -72,7 +71,7 @@ public class StreamingInboundHandlerTest public void setup() { channel = new TestChannel(); - streamingChannel = new NettyStreamingChannel(VERSION, channel, StreamingChannel.Kind.CONTROL); + streamingChannel = new NettyStreamingChannel(channel, StreamingChannel.Kind.CONTROL); channel.pipeline().addLast("stream", streamingChannel); } @@ -100,7 +99,7 @@ public class StreamingInboundHandlerTest public void StreamDeserializingTask_deriveSession_StreamInitMessage() { StreamInitMessage msg = new StreamInitMessage(REMOTE_ADDR, 0, nextTimeUUID(), StreamOperation.REPAIR, nextTimeUUID(), PreviewKind.ALL); - StreamDeserializingTask task = new StreamDeserializingTask(null, streamingChannel, streamingChannel.messagingVersion); + StreamDeserializingTask task = new StreamDeserializingTask(null, streamingChannel, MessagingService.current_version); StreamSession session = task.deriveSession(msg); Assert.assertNotNull(session); } @@ -109,7 +108,7 @@ public class StreamingInboundHandlerTest public void StreamDeserializingTask_deriveSession_NoSession() { CompleteMessage msg = new CompleteMessage(); - StreamDeserializingTask task = new StreamDeserializingTask(null, streamingChannel, streamingChannel.messagingVersion); + StreamDeserializingTask task = new StreamDeserializingTask(null, streamingChannel, MessagingService.current_version); task.deriveSession(msg); } @@ -133,7 +132,7 @@ public class StreamingInboundHandlerTest public void StreamDeserializingTask_deserialize_ISM_HasSession() { TimeUUID planId = nextTimeUUID(); - StreamResultFuture future = StreamResultFuture.createFollower(0, planId, StreamOperation.REPAIR, REMOTE_ADDR, streamingChannel, streamingChannel.messagingVersion, nextTimeUUID(), PreviewKind.ALL); + StreamResultFuture future = StreamResultFuture.createFollower(0, planId, StreamOperation.REPAIR, REMOTE_ADDR, streamingChannel, MessagingService.current_version, nextTimeUUID(), PreviewKind.ALL); StreamManager.instance.registerFollower(future); StreamMessageHeader header = new StreamMessageHeader(TableId.generate(), REMOTE_ADDR, planId, false, 0, 0, 0, nextTimeUUID()); diff --git a/test/unit/org/apache/cassandra/streaming/async/StreamingMultiplexedChannelTest.java b/test/unit/org/apache/cassandra/streaming/async/StreamingMultiplexedChannelTest.java index d43f6d5138..48a95ea602 100644 --- a/test/unit/org/apache/cassandra/streaming/async/StreamingMultiplexedChannelTest.java +++ b/test/unit/org/apache/cassandra/streaming/async/StreamingMultiplexedChannelTest.java @@ -62,7 +62,7 @@ public class StreamingMultiplexedChannelTest public void setUp() { channel = new TestChannel(); - streamingChannel = new NettyStreamingChannel(current_version, channel, StreamingChannel.Kind.CONTROL); + streamingChannel = new NettyStreamingChannel(channel, StreamingChannel.Kind.CONTROL); TimeUUID pendingRepair = nextTimeUUID(); session = new StreamSession(StreamOperation.BOOTSTRAP, REMOTE_ADDR, new NettyStreamingConnectionFactory(), streamingChannel, current_version, true, 0, pendingRepair, PreviewKind.ALL); StreamResultFuture future = StreamResultFuture.createFollower(0, nextTimeUUID(), StreamOperation.REPAIR, REMOTE_ADDR, streamingChannel, current_version, pendingRepair, session.getPreviewKind());