[tarantool-patches] Re: Fwd: [PATCH 1/1] rfc: describe a Tarantool wire protocol
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Thu Apr 5 12:37:50 MSK 2018
05.04.2018 12:01, Алексей Гаджиев (Redacted sender alexey.gadzhiev for
DMARC) пишет:
> Looks cool! Now we can send chunked data!
>
> I have one question:
> Is IPROTO_OK mandatory packet in the response messages sequence?
Yes, IPROTO_OK is mandatory.
> How can I detect if next push/result_set available if previous
> consists only of one chunk without SQL_INFO_HAS_NEXT_CHUNK?
See the state machine picture - next chunk is available, if in the
header the
response type is IPROTO_CHUNK. SQL_INFO_HAS_NEXT_CHUNK just means,
that the next chunk is logically linked with the current.
And I have 2 better ideas instead of the 'has_next_chunk' flag:
1) Lets instead of SQL_INFO_HAS_NEXT_CHUNK, that is placed in body,
introduce a header flag: IPROTO_CHUNK_IS_CHAIN. When a several chunks
are logically linked (for example, they are parts of the same result
set), all
of them, except last, contains this flag. I think, that 'Chain' term is
more easy
to understand, than 'HAS_NEXT_CHUNK'. Moreover, HAS_NEXT_CHUNK can
be false, but a client must read more responses, if the message does not
contain IPROTO_OK - it confuses. Actually IPROTO_CHUNK in the header is
the single sign, that more responses are available.
The sample of responses:
IPROTO_CHUNK
|
IPROTO_CHUNK, IS_CHAIN
|
+--IPROTO_CHUNK, IS_CHAIN
|
+--IPROTO_CHUNK, IS_CHAIN
|
+--IPROTO_CHUNK
|
IPROTO_CHUNK
|
...
|
IPROTO_OK/ERROR
2) The similar idea - instead of IPROTO_CHUNK_IS_CHAIN lest introduce
IPROTO_CHAIN_ID - it is an identifier, that is unique for each chunks
sequence. And I like this variant most, because in a future it can be
extended
to allow mixing different chains.
The sample of responses:
IPROTO_CHUNK
|
IPROTO_CHUNK, CHAIN_ID = 1
|
+--IPROTO_CHUNK, CHAIN_ID = 1
|
+--IPROTO_CHUNK, CHAIN_ID = 1
|
IPROTO_CHUNK
|
IPROTO_CHUNK, CHAIN_ID = 2
|
+--IPROTO_CHUNK, CHAIN_ID = 2
|
+--IPROTO_CHUNK, CHAIN_ID = 2
|
IPROTO_CHUNK
|
...
|
IPROTO_OK/ERROR
>
> With best regards,
> Alex
>
>
> Четверг, 5 апреля 2018, 9:32 +03:00 от Alexey Gadzhiev
> <alg1973 at gmail.com>:
>
>
> ---------- Forwarded message ----------
> From: *Vladislav Shpilevoy* <v.shpilevoy at tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3av.shpilevoy at tarantool.org>>
> Date: Thu, Apr 5, 2018 at 12:22 AM
> Subject: [PATCH 1/1] rfc: describe a Tarantool wire protocol
> To: tarantool-patches at freelists.org
> <//e.mail.ru/compose/?mailto=mailto%3atarantool%2dpatches at freelists.org>
> Cc: kostja at tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3akostja at tarantool.org>,
> alg1973 at gmail.com
> <//e.mail.ru/compose/?mailto=mailto%3aalg1973 at gmail.com>,
> Vladislav Shpilevoy <v.shpilevoy at tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3av.shpilevoy at tarantool.org>>
>
>
> ---
> Original:
> https://github.com/tarantool/tarantool/blob/sql-proto-rfc/doc/RFC/wire_protocol.md
>
> doc/RFC/wire_protocol.md <http://wire_protocol.md> | 214
> +++++++++++++++++++++++++++++++++++++++++
> doc/RFC/wire_protocol_img1.png | Bin 0 -> 48970 bytes
> 2 files changed, 214 insertions(+)
> create mode 100644 doc/RFC/wire_protocol.md <http://wire_protocol.md>
> create mode 100644 doc/RFC/wire_protocol_img1.png
>
> diff --git a/doc/RFC/wire_protocol.md <http://wire_protocol.md>
> b/doc/RFC/wire_protocol.md <http://wire_protocol.md>
> new file mode 100644
> index 000000000..d1fc1329c
> --- /dev/null
> +++ b/doc/RFC/wire_protocol.md <http://wire_protocol.md>
> @@ -0,0 +1,214 @@
> +# Tarantool Wire protocol
> +
> +* **Status**: In progress
> +* **Start date**: 04-04-2018
> +* **Authors**: Vladislav Shpilevoy @Gerold103
> v.shpilevoy at tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3av.shpilevoy at tarantool.org>,
> Konstantin Osipov @kostja kostja at tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3akostja at tarantool.org>,
> Alexey Gadzhiev @alg1973 alg1973 at gmail.com
> <//e.mail.ru/compose/?mailto=mailto%3aalg1973 at gmail.com>
> +* **Issues**:
> [#2677](https://github.com/tarantool/tarantool/issues/2677),
> [#2620](https://github.com/tarantool/tarantool/issues/2620),
> [#2618](https://github.com/tarantool/tarantool/issues/2618)
> +
> +## Summary
> +
> +Tarantool wire protocol is a convention how to encode and send
> results of execution of SQL, Lua and C stored functions, DML (Data
> Manipulation Language), DDL (Data Definition Language), DQL (Data
> Query Language) requests to remote clients via network. The
> protocol is unified for all request types. For a single request
> multiple responses of different types can be sent.
> +
> +## Background and motivation
> +
> +Tarantool wire protocol is called **IProto**, and is used by
> database connectors written on different languages and working on
> remote clients. The protocol describes how to distinguish
> different message types and what data can be stored in each
> message. Tarantool has the following response types:
> +* A response, that finalizes a request, and has just data -
> tuples array, or scalar values, or mixed. It has no any metadata.
> This response type incorporates results of any pure Lua and C
> calls including stored procedures, space and index methods. Such
> response is single per request;
> +* A response with just data, but with no request finalization -
> it is so called push-message. During single request execution
> multiple pushes can be sent, and they do not finalize the request
> - a client must be ready to receive more responses;
> +* A formatted response, that is sent on SQL DQL and does not
> finalize a request. Such response contains metadata with result
> set column names, types, flags etc;
> +* A response with metadata only, that is sent on SQL DDL/DML
> requests, and contains affected row count, last autoincrement
> column value, flags;
> +* A response with error message and code, that finalizes a request.
> +
> +In supporting this responses set 2 main challenges appear:
> +1. How to unify responses;
> +2. How to support multiple messages inside a single request.
> +
> +To understand how a single request can produce multiple
> responses, consider the stored procedure (do not pay attention to
> the syntax - it does not matter here):
> +```SQL
> +FUNCTION my_sql_func(a1, a2, a3, a4) BEGIN
> + SELECT my_lua_func(a1);
> + SELECT * FROM table1;
> + SELECT my_c_func(a2);
> + INSERT INTO table1 VALUES (1, 2, 3);
> + RETURN a4;
> +END
> +```
> +, where `my_lua_func()` is the function, written in Lua and
> sending its own push-messages:
> +```Lua
> +function my_lua_func(arg)
> + box.session.push(arg)
> + return arg
> +end
> +```
> +and `my_c_func()` is the function, written in C and returning
> some raw data:
> +```C
> +int
> +my_c_func(box_function_ctx_t *ctx) {
> + box_tuple_t *tuple;
> + /* Fill a tuple with any data. */
> + return box_return_tuple(ctx, tuple);
> +}
> +```
> +Consider each statement:
> +* `SELECT FROM` can split a big result set in multiple messages;
> +* `SELECT my_lua_func()` produces 2 messages: one is the
> push-message generated in `my_lua_func` and another is the result
> of `SELECT` itself;
> +* `INSERT` creates 1 message with metadata;
> +* `RETURN` creates a final response message.
> +
> +Of course, some of messages, or even all of them can be batched
> and send as a single TCP packet, but it does not matter for the
> wire protocol.
> +
> +In the next section it is described, how the Tarantool wire
> protocol deals with this mess.
> +
> +For the protocol details - code values, all header and body keys
> - see Tarantool [website](tarantool.io <http://tarantool.io>).
> +
> +## Detailed design
> +
> +Tarantool response consists of a body and a header. Header is
> used to store response code and some internal metainfo such as
> schema version, request id (called **sync** in Tarantool). Body is
> used to store result data and request-dependent metainfo.
> +
> +### Header
> +
> +There are 3 response codes in header:
> +* `IPROTO_OK` - the last response in a request, that is finished
> successfully;
> +* `IPROTO_CHUNK` - non-final response. One request can generate
> multuple chunk messages;
> +* `IPROTO_ERROR | error code` - the last response in a request,
> that is finished with an error.
> +
> +`IPROTO_ERROR` response is trivial, and consists just of code and
> message. It is no considered further.
> +`IPROTO_OK` and `IPROTO_CHUNK` have the same body format. The
> only exception between them is that `IPROTO_OK` finalizes the
> request. In the next subsection the body format is presented.
> +
> +### Body
> +
> +The common body structure:
> +```
> ++----------------------------------------------+
> +| IPROTO_BODY: { |
> +| IPROTO_METADATA: [ |
> +| { |
> +| IPROTO_FIELD_NAME: string, |
> +| IPROTO_FIELD_TYPE: number, |
> +| IPROTO_FIELD_FLAGS: number, |
> +| }, |
> +| ... |
> +| ], |
> +| |
> +| IPROTO_SQL_INFO: { |
> +| SQL_INFO_ROW_COUNT: number, |
> +| SQL_INFO_LAST_ID: number, |
> +| SQL_INFO_FLAGS: number, |
> +| ... |
> +| }, |
> +| |
> +| IPROTO_DATA: [ |
> +| tuple/scalar, |
> +| ... |
> +| ] |
> +| } |
> ++----------------------------------------------+
> +```
> +For a while the single `SQL_INFO_FLAGS` value is available:
> `SQL_INFO_HAS_NEXT_CHUNK` - a response having this flag means,
> that the current result set is not fully read - more responses are
> available. For example, it could be big `SELECT FROM` sent in
> multiple chunks.
> +
> +Consider, how different responses use the body, and how they can
> be distinguished.
> +
> +_A non formatted response_ has only `IPROTO_DATA` key in a body.
> It is the result of Lua and C DML, DDL, DQL, stored procedures
> calls, push messages. Such response is never linked with next or
> previous messages of the same request.
> +
> +_A non formatted response with metadata_ has only
> `IPROTO_SQL_INFO` and it is always result of DDL/DML executed via
> SQL. As well as the previous type, this response is all-independent.
> +
> +_A formatted response_ always has `IPROTO_DATA`, and can have
> both `IPROTO_SQL_INFO` and `IPROTO_METADATA`. It is always result
> of SQL DQL (`SELECT`). The response can be part of a continuous
> sequence of responses. A first message of the sequence always
> contains `IPROTO_METADATA`, while all non-last ones always contain
> `IPROTO_SQL_INFO` with `SQL_INFO_HAS_NEXT_CHUNK` flag. The last
> message has only `IPROTO_DATA` or nothing.
> +
> +On the picture the state machine of the protocol is showed:
> +
> +
> +For the `FUNCTION my_sql_func` call the following responses are sent:
> +```
> +/* Push from my_lua_func(a1). */
> ++----------------------------------------------+
> +| HEADER: IPROTO_CHUNK |
> ++- - - - - - - - - - - - - - - - - - - - - - - +
> +| BODY: { |
> +| IPROTO_DATA: [ a1 ] |
> +| } |
> ++----------------------------------------------+
> +
> +/* Result of SELECT my_lua_func(a1). */
> ++----------------------------------------------+
> +| HEADER: IPROTO_CHUNK |
> ++- - - - - - - - - - - - - - - - - - - - - - - +
> +| BODY: { |
> +| IPROTO_DATA: [ [ a1 ] ], |
> +| IPROTO_METADATA: [ |
> +| { /* field name, type ... */ } |
> +| ] |
> +| } |
> ++----------------------------------------------+
> +
> +/* First chunk of SELECT * FROM table1. */
> ++----------------------------------------------+
> +| HEADER: IPROTO_CHUNK |
> ++- - - - - - - - - - - - - - - - - - - - - - - +
> +| BODY: { |
> +| IPROTO_DATA: [ tuple1, tuple2, ... ] |
> +| IPROTO_METADATA: [ |
> +| { /* field1 name, type ... */ }, |
> +| { /* field2 name, type ... */ }, |
> +| ... |
> +| ], |
> +| IPROTO_SQL_INFO: { |
> +| SQL_INFO_FLAGS: |
> +| SQL_INFO_HAS_NEXT_CHUNK |
> +| } |
> +| } |
> ++----------------------------------------------+
> +
> + /* From second to next to last chunk. */
> + +----------------------------------------------+
> + | HEADER: IPROTO_CHUNK |
> + +- - - - - - - - - - - - - - - - - - - - - - - +
> + | BODY: { |
> + | IPROTO_DATA: [ tuple1, tuple2, ... ], |
> + | IPROTO_SQL_INFO: { |
> + | SQL_INFO_FLAGS: |
> + | SQL_INFO_HAS_NEXT_CHUNK |
> + | } |
> + | } |
> + +----------------------------------------------+
> +
> + /* Last chunk. */
> + +----------------------------------------------+
> + | HEADER: IPROTO_CHUNK |
> + +- - - - - - - - - - - - - - - - - - - - - - - +
> + | BODY: { |
> + | IPROTO_DATA: [ tuple1, tuple2, ... ] |
> + | } |
> + +----------------------------------------------+
> +
> +/* Result of SELECT my_c_func(a2). */
> ++----------------------------------------------+
> +| HEADER: IPROTO_CHUNK |
> ++- - - - - - - - - - - - - - - - - - - - - - - +
> +| BODY: { |
> +| IPROTO_DATA: [ [ tuple ] ], |
> +| IPROTO_METADATA: [ |
> +| { /* field name, type ... */ } |
> +| ] |
> +| } |
> ++----------------------------------------------+
> +
> +/* Result of INSERT INTO table1 VALUES (1, 2, 3). */
> ++----------------------------------------------+
> +| HEADER: IPROTO_CHUNK |
> ++- - - - - - - - - - - - - - - - - - - - - - - +
> +| BODY: { |
> +| IPROTO_SQL_INFO: { |
> +| SQL_INFO_ROW_COUNT: number, |
> +| SQL_INFO_LAST_ID: number, |
> +| } |
> +| } |
> ++----------------------------------------------+
> +
> +/* Result of RETURN a4 */
> ++----------------------------------------------+
> +| HEADER: IPROTO_OK |
> ++- - - - - - - - - - - - - - - - - - - - - - - +
> +| BODY: { |
> +| IPROTO_DATA: [ a4 ] |
> +| } |
> ++----------------------------------------------+
> +```
> --
> 2.14.3 (Apple Git-98)
>
>
>
>
> --
>
More information about the Tarantool-patches
mailing list