mirror of https://github.com/apache/cassandra
Support more concurrent requests in native protocol
patch by Aleksey Yeschenko; reviewed by Sylvain Lebresne for CASSANDRA-7231
This commit is contained in:
parent
969a92804d
commit
a4bea74aeb
|
|
@ -1,4 +1,5 @@
|
||||||
2.1.0-rc1
|
2.1.0-rc1
|
||||||
|
* Support more concurrent requests in native protocol (CASSANDRA-7231)
|
||||||
* Add tab-completion to debian nodetool packaging (CASSANDRA-6421)
|
* Add tab-completion to debian nodetool packaging (CASSANDRA-6421)
|
||||||
* Change concurrent_compactors defaults (CASSANDRA-7139)
|
* Change concurrent_compactors defaults (CASSANDRA-7139)
|
||||||
* Add PowerShell Windows launch scripts (CASSANDRA-7001)
|
* Add PowerShell Windows launch scripts (CASSANDRA-7001)
|
||||||
|
|
|
||||||
|
|
@ -48,10 +48,10 @@ Table of Contents
|
||||||
|
|
||||||
The CQL binary protocol is a frame based protocol. Frames are defined as:
|
The CQL binary protocol is a frame based protocol. Frames are defined as:
|
||||||
|
|
||||||
0 8 16 24 32
|
0 8 16 24 32 40
|
||||||
+---------+---------+---------+---------+
|
+---------+---------+---------+---------+---------+
|
||||||
| version | flags | stream | opcode |
|
| version | flags | stream | opcode |
|
||||||
+---------+---------+---------+---------+
|
+---------+---------+---------+---------+---------+
|
||||||
| length |
|
| length |
|
||||||
+---------+---------+---------+---------+
|
+---------+---------+---------+---------+
|
||||||
| |
|
| |
|
||||||
|
|
@ -62,7 +62,7 @@ Table of Contents
|
||||||
|
|
||||||
The protocol is big-endian (network byte order).
|
The protocol is big-endian (network byte order).
|
||||||
|
|
||||||
Each frame contains a fixed size header (8 bytes) followed by a variable size
|
Each frame contains a fixed size header (9 bytes) followed by a variable size
|
||||||
body. The header is described in Section 2. The content of the body depends
|
body. The header is described in Section 2. The content of the body depends
|
||||||
on the header opcode value (the body can in particular be empty for some
|
on the header opcode value (the body can in particular be empty for some
|
||||||
opcode values). The list of allowed opcode is defined Section 2.3 and the
|
opcode values). The list of allowed opcode is defined Section 2.3 and the
|
||||||
|
|
@ -129,8 +129,8 @@ Table of Contents
|
||||||
|
|
||||||
2.3. stream
|
2.3. stream
|
||||||
|
|
||||||
A frame has a stream id (one signed byte). When sending request messages, this
|
A frame has a stream id (a [short] value). When sending request messages, this
|
||||||
stream id must be set by the client to a positive byte (negative stream id
|
stream id must be set by the client to a non-negative value (negative stream id
|
||||||
are reserved for streams initiated by the server; currently all EVENT messages
|
are reserved for streams initiated by the server; currently all EVENT messages
|
||||||
(section 4.2.6) have a streamId of -1). If a client sends a request message
|
(section 4.2.6) have a streamId of -1). If a client sends a request message
|
||||||
with the stream id X, it is guaranteed that the stream id of the response to
|
with the stream id X, it is guaranteed that the stream id of the response to
|
||||||
|
|
@ -142,13 +142,13 @@ Table of Contents
|
||||||
writes REQ_1, REQ_2, REQ_3 on the wire (in that order), the server might
|
writes REQ_1, REQ_2, REQ_3 on the wire (in that order), the server might
|
||||||
respond to REQ_3 (or REQ_2) first. Assigning different stream id to these 3
|
respond to REQ_3 (or REQ_2) first. Assigning different stream id to these 3
|
||||||
requests allows the client to distinguish to which request an received answer
|
requests allows the client to distinguish to which request an received answer
|
||||||
respond to. As there can only be 128 different simultaneous stream, it is up
|
respond to. As there can only be 32768 different simultaneous streams, it is up
|
||||||
to the client to reuse stream id.
|
to the client to reuse stream id.
|
||||||
|
|
||||||
Note that clients are free to use the protocol synchronously (i.e. wait for
|
Note that clients are free to use the protocol synchronously (i.e. wait for
|
||||||
the response to REQ_N before sending REQ_N+1). In that case, the stream id
|
the response to REQ_N before sending REQ_N+1). In that case, the stream id
|
||||||
can be safely set to 0. Clients should also feel free to use only a subset of
|
can be safely set to 0. Clients should also feel free to use only a subset of
|
||||||
the 128 maximum possible stream ids if it is simpler for those
|
the 32768 maximum possible stream ids if it is simpler for those
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
2.4. opcode
|
2.4. opcode
|
||||||
|
|
@ -902,6 +902,7 @@ Table of Contents
|
||||||
bytes] representing the unknown ID.
|
bytes] representing the unknown ID.
|
||||||
|
|
||||||
10. Changes from v2
|
10. Changes from v2
|
||||||
|
* stream id is now 2 bytes long (a [short] value), so the header is now 1 byte longer (9 bytes total).
|
||||||
* BATCH messages now have <flags> (like QUERY and EXECUTE) and a corresponding optional
|
* BATCH messages now have <flags> (like QUERY and EXECUTE) and a corresponding optional
|
||||||
<serial_consistency> parameters (see Section 4.1.7).
|
<serial_consistency> parameters (see Section 4.1.7).
|
||||||
* User Defined Types and tuple types have to added to ResultSet metadata (see 4.2.5.2) and a
|
* User Defined Types and tuple types have to added to ResultSet metadata (see 4.2.5.2) and a
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
|
@ -39,8 +38,19 @@ public class Frame
|
||||||
public final ByteBuf body;
|
public final ByteBuf body;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On-wire frame.
|
* An on-wire frame consists of a header and a body.
|
||||||
* Frames are defined as:
|
*
|
||||||
|
* The header is defined the following way in native protocol version 3 and later:
|
||||||
|
*
|
||||||
|
* 0 8 16 24 32 40
|
||||||
|
* +---------+---------+---------+---------+---------+
|
||||||
|
* | version | flags | stream | opcode |
|
||||||
|
* +---------+---------+---------+---------+---------+
|
||||||
|
* | length |
|
||||||
|
* +---------+---------+---------+---------+
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* In versions 1 and 2 the header has a smaller (1 byte) stream id, and is thus defined the following way:
|
||||||
*
|
*
|
||||||
* 0 8 16 24 32
|
* 0 8 16 24 32
|
||||||
* +---------+---------+---------+---------+
|
* +---------+---------+---------+---------+
|
||||||
|
|
@ -68,9 +78,10 @@ public class Frame
|
||||||
|
|
||||||
public static class Header
|
public static class Header
|
||||||
{
|
{
|
||||||
public static final int LENGTH = 8;
|
// 8 bytes in protocol versions 1 and 2, 8 bytes in protocol version 3 and later
|
||||||
|
public static final int MODERN_LENGTH = 9;
|
||||||
|
public static final int LEGACY_LENGTH = 8;
|
||||||
|
|
||||||
public static final int BODY_LENGTH_OFFSET = 4;
|
|
||||||
public static final int BODY_LENGTH_SIZE = 4;
|
public static final int BODY_LENGTH_SIZE = 4;
|
||||||
|
|
||||||
public final int version;
|
public final int version;
|
||||||
|
|
@ -153,34 +164,52 @@ public class Frame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until we have read at least the header
|
// Wait until we have read at least the short header
|
||||||
if (buffer.readableBytes() < Header.LENGTH)
|
if (buffer.readableBytes() < Header.LEGACY_LENGTH)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int idx = buffer.readerIndex();
|
int idx = buffer.readerIndex();
|
||||||
|
|
||||||
int firstByte = buffer.getByte(idx);
|
int firstByte = buffer.getByte(idx++);
|
||||||
Message.Direction direction = Message.Direction.extractFromVersion(firstByte);
|
Message.Direction direction = Message.Direction.extractFromVersion(firstByte);
|
||||||
int version = firstByte & 0x7F;
|
int version = firstByte & 0x7F;
|
||||||
|
|
||||||
if (version > Server.CURRENT_VERSION)
|
if (version > Server.CURRENT_VERSION)
|
||||||
throw new ProtocolException("Invalid or unsupported protocol version: " + version);
|
throw new ProtocolException("Invalid or unsupported protocol version: " + version);
|
||||||
|
|
||||||
int flags = buffer.getByte(idx + 1);
|
// Wait until we have the complete V3+ header
|
||||||
int streamId = buffer.getByte(idx + 2);
|
if (version >= Server.VERSION_3 && buffer.readableBytes() < Header.MODERN_LENGTH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int flags = buffer.getByte(idx++);
|
||||||
|
|
||||||
|
int streamId, headerLength;
|
||||||
|
if (version >= Server.VERSION_3)
|
||||||
|
{
|
||||||
|
streamId = buffer.getShort(idx);
|
||||||
|
idx += 2;
|
||||||
|
headerLength = Header.MODERN_LENGTH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
streamId = buffer.getByte(idx);
|
||||||
|
idx++;
|
||||||
|
headerLength = Header.LEGACY_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
// This throws a protocol exceptions if the opcode is unknown
|
// This throws a protocol exceptions if the opcode is unknown
|
||||||
Message.Type type = Message.Type.fromOpcode(buffer.getByte(idx + 3), direction);
|
Message.Type type = Message.Type.fromOpcode(buffer.getByte(idx++), direction);
|
||||||
|
|
||||||
long bodyLength = buffer.getUnsignedInt(idx + Header.BODY_LENGTH_OFFSET);
|
long bodyLength = buffer.getUnsignedInt(idx);
|
||||||
|
idx += Header.BODY_LENGTH_SIZE;
|
||||||
|
|
||||||
if (bodyLength < 0)
|
if (bodyLength < 0)
|
||||||
{
|
{
|
||||||
buffer.skipBytes(Header.LENGTH);
|
buffer.skipBytes(headerLength);
|
||||||
throw new ProtocolException("Invalid frame body length: " + bodyLength);
|
throw new ProtocolException("Invalid frame body length: " + bodyLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
long frameLength = bodyLength + Header.LENGTH;
|
long frameLength = bodyLength + headerLength;
|
||||||
if (frameLength > MAX_FRAME_LENGTH)
|
if (frameLength > MAX_FRAME_LENGTH)
|
||||||
{
|
{
|
||||||
// Enter the discard mode and discard everything received so far.
|
// Enter the discard mode and discard everything received so far.
|
||||||
|
|
@ -193,14 +222,13 @@ public class Frame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// never overflows because it's less than the max frame length
|
if (buffer.readableBytes() < frameLength)
|
||||||
int frameLengthInt = (int) frameLength;
|
|
||||||
if (buffer.readableBytes() < frameLengthInt)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// extract body
|
// extract body
|
||||||
ByteBuf body = CBUtil.allocator.buffer((int) bodyLength).writeBytes(buffer.duplicate().slice(idx + Header.LENGTH, (int) bodyLength));
|
ByteBuf body = CBUtil.allocator.buffer((int) bodyLength).writeBytes(buffer.duplicate().slice(idx, (int) bodyLength));
|
||||||
buffer.readerIndex(idx + frameLengthInt);
|
idx += bodyLength;
|
||||||
|
buffer.readerIndex(idx);
|
||||||
|
|
||||||
Connection connection = ctx.channel().attr(Connection.attributeKey).get();
|
Connection connection = ctx.channel().attr(Connection.attributeKey).get();
|
||||||
if (connection == null)
|
if (connection == null)
|
||||||
|
|
@ -239,14 +267,23 @@ public class Frame
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public static class Encoder extends MessageToMessageEncoder<Frame>
|
public static class Encoder extends MessageToMessageEncoder<Frame>
|
||||||
{
|
{
|
||||||
public void encode(ChannelHandlerContext ctx, Frame frame, List results)
|
public void encode(ChannelHandlerContext ctx, Frame frame, List<Object> results)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
ByteBuf header = CBUtil.allocator.buffer(Frame.Header.LENGTH);
|
int headerLength = frame.header.version >= Server.VERSION_3
|
||||||
|
? Header.MODERN_LENGTH
|
||||||
|
: Header.LEGACY_LENGTH;
|
||||||
|
ByteBuf header = CBUtil.allocator.buffer(headerLength);
|
||||||
|
|
||||||
Message.Type type = frame.header.type;
|
Message.Type type = frame.header.type;
|
||||||
header.writeByte(type.direction.addToVersion(frame.header.version));
|
header.writeByte(type.direction.addToVersion(frame.header.version));
|
||||||
header.writeByte(Header.Flag.serialize(frame.header.flags));
|
header.writeByte(Header.Flag.serialize(frame.header.flags));
|
||||||
header.writeByte(frame.header.streamId);
|
|
||||||
|
if (frame.header.version >= Server.VERSION_3)
|
||||||
|
header.writeShort(frame.header.streamId);
|
||||||
|
else
|
||||||
|
header.writeByte(frame.header.streamId);
|
||||||
|
|
||||||
header.writeByte(type.opcode);
|
header.writeByte(type.opcode);
|
||||||
header.writeInt(frame.body.readableBytes());
|
header.writeInt(frame.body.readableBytes());
|
||||||
|
|
||||||
|
|
@ -257,7 +294,7 @@ public class Frame
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public static class Decompressor extends MessageToMessageDecoder<Frame>
|
public static class Decompressor extends MessageToMessageDecoder<Frame>
|
||||||
{
|
{
|
||||||
public void decode(ChannelHandlerContext ctx, Frame frame, List results)
|
public void decode(ChannelHandlerContext ctx, Frame frame, List<Object> results)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Connection connection = ctx.channel().attr(Connection.attributeKey).get();
|
Connection connection = ctx.channel().attr(Connection.attributeKey).get();
|
||||||
|
|
@ -282,7 +319,7 @@ public class Frame
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public static class Compressor extends MessageToMessageEncoder<Frame>
|
public static class Compressor extends MessageToMessageEncoder<Frame>
|
||||||
{
|
{
|
||||||
public void encode(ChannelHandlerContext ctx, Frame frame, List results)
|
public void encode(ChannelHandlerContext ctx, Frame frame, List<Object> results)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Connection connection = ctx.channel().attr(Connection.attributeKey).get();
|
Connection connection = ctx.channel().attr(Connection.attributeKey).get();
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,8 @@ public class Server implements CassandraDaemon.Server
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Server.class);
|
private static final Logger logger = LoggerFactory.getLogger(Server.class);
|
||||||
|
|
||||||
/** current version of the native protocol we support */
|
public static final int VERSION_3 = 3;
|
||||||
public static final int CURRENT_VERSION = 2;
|
public static final int CURRENT_VERSION = VERSION_3;
|
||||||
|
|
||||||
private final ConnectionTracker connectionTracker = new ConnectionTracker();
|
private final ConnectionTracker connectionTracker = new ConnectionTracker();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue