Lift MessagingService.minimum_version to 40

patch by Mick Semb Wever; reviewed by Andrés de la Peña García, Maxim Muzafarov for CASSANDRA-18314
This commit is contained in:
Mick Semb Wever 2023-03-09 14:18:44 +01:00
parent 6ab45971fc
commit 4bbfd64fcd
No known key found for this signature in database
GPG Key ID: E91335D77E3E87CB
53 changed files with 432 additions and 2719 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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<Mutation>
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<Mutation>
{
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);

View File

@ -66,14 +66,13 @@ public class MutationVerbHandler implements IVerbHandler<Mutation>
.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<Mutation> message = useSameMessageID ? builder.build() : null;
Message<Mutation> 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);
});
}
}

View File

@ -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);
}

View File

@ -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.</p>
*
* <p>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.</p>
*/
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.
*
* <p>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.</p>
*/
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.
*
* <p>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.</p>
*/
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<ColumnIdentifier, ColumnSubselection> 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<ColumnIdentifier, ColumnSubselection> 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());
}

View File

@ -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);
}
}
}

View File

@ -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:

View File

@ -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<InetAddressAndPort>, 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<InetAddressAndPort>
{
@ -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<InetAddressAndPort>
{
@ -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);
}
}
}

View File

@ -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<InetAddressAndPort> 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<InetAddressAndPort> 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<InetAddressAndPort> 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);

View File

@ -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();

View File

@ -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<Frame> 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);
}
}

View File

@ -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 <em>for every byte of the payload</em>
* 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 <a href="https://github.com/idelpivnitskiy/lz4-java">LZ4 Java</a> 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<ShareableBytes> 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<ShareableBytes> into, ShareableBytes newBytes) throws CorruptLZ4Frame
{
try
{
doDecode(into, newBytes);
}
finally
{
newBytes.release();
}
}
private void doDecode(Collection<ShareableBytes> 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));
}
}
}

View File

@ -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);
}
}

View File

@ -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 <em>for every byte of the payload</em>
*
* 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;
}
}

View File

@ -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.
* <p>
* 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);

View File

@ -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<Object> 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:

View File

@ -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))

View File

@ -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<LegacyFlag> serializer = new IVersionedSerializer<LegacyFlag>()
{
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;
}
};
}

View File

@ -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;
}

View File

@ -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<T>
public <T> void serialize(Message<T> 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 <T> Message<T> 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<T>
*/
public <T> Message<T> 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 <T> int serializedSize(Message<T> 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<T>
* 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<ParamType, Object> 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<T>
return new Header(id, verb, from, createdAtNanos, expiresAtNanos, flags, params);
}
private <T> void serializePost40(Message<T> 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 <T> Message<T> 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 <T> Message<T> 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 <T> int serializedSizePost40(Message<T> 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<ParamType, Object> 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<ParamType, Object> 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 <T> void serializePre40(Message<T> 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 <T> Message<T> deserializePre40(DataInputPlus in, int version) throws IOException
{
Header header = deserializeHeaderPre40(in, version);
return deserializePre40(in, header, false, version);
}
private <T> Message<T> deserializePre40(DataInputPlus in, Header header, int version) throws IOException
{
return deserializePre40(in, header, true, version);
}
private <T> Message<T> 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<T> message = new Message<>(header, payload);
return header.params.containsKey(ParamType.FAILURE_RESPONSE)
? (Message<T>) toPost40FailureResponse(message)
: message;
}
private <T> T deserializePayloadPre40(DataInputPlus in, int version, IVersionedAsymmetricSerializer<?, T> 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 <T> int serializedSizePre40(Message<T> 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<ParamType, Object> 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<RequestFailureReason> toPost40FailureResponse(Message<?> pre40)
{
Map<ParamType, Object> 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<T>
* param ser/deser
*/
private Map<ParamType, Object> addFlagsToLegacyParams(Map<ParamType, Object> params, int flags)
{
if (flags == 0)
return params;
Map<ParamType, Object> 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<ParamType, Object> 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<ParamType, Object> 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<ParamType, Object> 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<T>
private Map<ParamType, Object> 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<T>
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<T>
return params;
}
/*
* Extract post-4.0 params map from a ByteBuffer without modifying it.
*/
private Map<ParamType, Object> 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<T>
}
}
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<T>
}
}
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<ParamType, Object> params, int version)
{
long size = version >= VERSION_40
? computeUnsignedVIntSize(params.size())
: sizeof(params.size());
long size = computeUnsignedVIntSize(params.size());
for (Map.Entry<ParamType, Object> kv : params.entrySet())
{
@ -1340,10 +1018,7 @@ public class Message<T>
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<T>
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<T>
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 <T> int payloadSize(Message<T> message, int version)
{
long payloadSize = message.payload != null && message.payload != NoPayload.noPayload
@ -1414,20 +1062,9 @@ public class Message<T>
private IVersionedAsymmetricSerializer<T, ?> 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 <In,Out> IVersionedAsymmetricSerializer<In, Out> 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<T>
{
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<T>
}
}
private int payloadSize30 = -1;
private int payloadSize3014 = -1;
private int payloadSize40 = -1;
private int payloadSize50 = -1;
@ -1468,14 +1095,6 @@ public class Message<T>
{
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);

View File

@ -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<Version> supportedVersions()
{
List<Version> versions = Lists.newArrayList();
for (Version version : values())
if (minimum_version <= version.value)
versions.add(version);
return Collections.unmodifiableList(versions);
}
}
private static class MSHandle

View File

@ -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;

View File

@ -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<SuccessType extends OutboundConnectionI
private final ConnectionType type;
private final SslFallbackConnectionType sslConnectionType;
private final OutboundConnectionSettings settings;
private final int requestMessagingVersion; // for pre40 nodes
private final Promise<Result<SuccessType>> resultPromise;
private boolean isClosed;
private OutboundConnectionInitiator(ConnectionType type, SslFallbackConnectionType sslConnectionType, OutboundConnectionSettings settings,
int requestMessagingVersion, Promise<Result<SuccessType>> resultPromise)
Promise<Result<SuccessType>> resultPromise)
{
this.type = type;
this.sslConnectionType = sslConnectionType;
this.requestMessagingVersion = requestMessagingVersion;
this.settings = settings;
this.resultPromise = resultPromise;
}
@ -118,9 +115,9 @@ public class OutboundConnectionInitiator<SuccessType extends OutboundConnectionI
* The returned {@code Future} is guaranteed to be completed on the supplied eventLoop.
*/
public static Future<Result<StreamingSuccess>> initiateStreaming(EventLoop eventLoop, OutboundConnectionSettings settings,
SslFallbackConnectionType sslConnectionType, int requestMessagingVersion)
SslFallbackConnectionType sslConnectionType)
{
return new OutboundConnectionInitiator<StreamingSuccess>(STREAMING, sslConnectionType, settings, requestMessagingVersion, AsyncPromise.withExecutor(eventLoop))
return new OutboundConnectionInitiator<StreamingSuccess>(STREAMING, sslConnectionType, settings, AsyncPromise.withExecutor(eventLoop))
.initiate(eventLoop);
}
@ -132,16 +129,16 @@ public class OutboundConnectionInitiator<SuccessType extends OutboundConnectionI
* The returned {@code Future} is guaranteed to be completed on the supplied eventLoop.
*/
static Future<Result<MessagingSuccess>> initiateMessaging(EventLoop eventLoop, ConnectionType type, SslFallbackConnectionType sslConnectionType,
OutboundConnectionSettings settings, int requestMessagingVersion, Promise<Result<MessagingSuccess>> result)
OutboundConnectionSettings settings, Promise<Result<MessagingSuccess>> result)
{
return new OutboundConnectionInitiator<>(type, sslConnectionType, settings, requestMessagingVersion, result)
return new OutboundConnectionInitiator<>(type, sslConnectionType, settings, result)
.initiate(eventLoop);
}
private Future<Result<SuccessType>> 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<SuccessType extends OutboundConnectionI
* containing the streaming protocol version, is all that is required.
*/
@Override
public void channelActive(final ChannelHandlerContext ctx)
public void channelActive(final ChannelHandlerContext ctx) throws Exception
{
Initiate msg = new Initiate(requestMessagingVersion, settings.acceptVersions, type, settings.framing, settings.from);
Initiate msg = new Initiate(settings.acceptVersions, type, settings.framing, settings.from);
logger.trace("starting handshake with peer {}, msg = {}", settings.connectToId(), msg);
AsyncChannelPromise.writeAndFlush(ctx, msg.encode(),
future -> { 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<SuccessType extends OutboundConnectionI
* do *not* send out the third message of the internode messaging handshake.
* We will reconnect on the appropriate protocol version.
*/
@SuppressWarnings("unchecked")
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
{
try
{
Accept msg = Accept.maybeDecode(in, requestMessagingVersion);
Accept msg = Accept.maybeDecode(in);
if (msg == null)
return;
@ -350,68 +346,35 @@ public class OutboundConnectionInitiator<SuccessType extends OutboundConnectionI
FrameEncoder frameEncoder = null;
Result<SuccessType> 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<SuccessType>) messagingSuccess(ctx.channel(), useMessagingVersion, frameEncoder.allocator());
}
else
{
result = (Result<SuccessType>) 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<SuccessType>) messagingSuccess(ctx.channel(), requestMessagingVersion, frameEncoder.allocator());
result = (Result<SuccessType>) 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<SuccessType>) streamingSuccess(ctx.channel(), useMessagingVersion);
}
}

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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<String, ParamType> aliasToTypeMap;
static
{
@ -94,7 +80,6 @@ public enum ParamType
max = max(t.id, max);
ParamType[] idMap = new ParamType[max + 1];
Map<String, ParamType> 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);
}
}

View File

@ -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);
}
<In,Out> IVersionedAsymmetricSerializer<In, Out> 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()

View File

@ -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<? extends IVerbHandler<?>> unsafeSetHandler(Supplier<? extends IVerbHandler<?>> handler) throws NoSuchFieldException, IllegalAccessException
{

View File

@ -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<String, String> 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<String, String> 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<String, String> 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;
}
}

View File

@ -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

View File

@ -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)

View File

@ -59,7 +59,6 @@ public class NettyStreamingChannel extends ChannelInboundHandlerAdapter implemen
@VisibleForTesting
static final AttributeKey<Boolean> 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)

View File

@ -61,12 +61,12 @@ public class NettyStreamingConnectionFactory implements StreamingChannel.Factory
{
for (int i = 0; i < MAX_CONNECT_ATTEMPTS; i++)
{
Future<Result<StreamingSuccess>> result = initiateStreaming(eventLoop, settings, sslFallbackConnectionType, messagingVersion);
Future<Result<StreamingSuccess>> 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();

View File

@ -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;

View File

@ -163,7 +163,7 @@ public class ConnectionBurnTest
public long serializedSize(byte[] payload, int version)
{
return MessageGenerator.serializedSize(payload, version);
return MessageGenerator.serializedSize(payload);
}
};

View File

@ -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;

View File

@ -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();
}
}

View File

@ -104,10 +104,4 @@ public class MessageOutBench
return msgOut.serializedSize(messagingVersion);
}
}
@Benchmark
public int serializePre40() throws Exception
{
return serialize(MessagingService.VERSION_30);
}
}

View File

@ -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)
{

View File

@ -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);

View File

@ -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<Object[]> 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<ColumnFilter> 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<ColumnFilter> 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)
{

View File

@ -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

View File

@ -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<TimeUnit> 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<Function<Settings, Settings>> 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
{

View File

@ -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

View File

@ -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<byte[]> messages = sequenceOfMessages.original;
ShareableBytes stream = sequenceOfMessages.frames;
int end = stream.get().limit();
List<FrameDecoder.Frame> 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<byte[]> 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<byte[]> 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))];

View File

@ -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<MessagingService.Version> 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

View File

@ -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();

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}
};

View File

@ -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());

View File

@ -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());