* [tarantool-patches] [PATCH 1/1] rfc: describe a Tarantool wire protocol
@ 2018-04-04 21:22 Vladislav Shpilevoy
2018-04-05 8:25 ` [tarantool-patches] " Konstantin Osipov
[not found] ` <CAFoyxqh0QqNBVr7tuFF_uoUw-CvBKOVA3FCyWvixikberOxP9w@mail.gmail.com>
0 siblings, 2 replies; 7+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-04 21:22 UTC (permalink / raw)
To: tarantool-patches; +Cc: kostja, alg1973, Vladislav Shpilevoy
---
Original: https://github.com/tarantool/tarantool/blob/sql-proto-rfc/doc/RFC/wire_protocol.md
doc/RFC/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
create mode 100644 doc/RFC/wire_protocol_img1.png
diff --git a/doc/RFC/wire_protocol.md b/doc/RFC/wire_protocol.md
new file mode 100644
index 000000000..d1fc1329c
--- /dev/null
+++ b/doc/RFC/wire_protocol.md
@@ -0,0 +1,214 @@
+# Tarantool Wire protocol
+
+* **Status**: In progress
+* **Start date**: 04-04-2018
+* **Authors**: Vladislav Shpilevoy @Gerold103 v.shpilevoy@tarantool.org, Konstantin Osipov @kostja kostja@tarantool.org, Alexey Gadzhiev @alg1973 alg1973@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).
+
+## 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:
+![alt text](https://raw.githubusercontent.com/tarantool/tarantool/sql-proto-rfc/doc/RFC/wire_protocol_img1.png)
+
+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)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [tarantool-patches] Re: [PATCH 1/1] rfc: describe a Tarantool wire protocol
2018-04-04 21:22 [tarantool-patches] [PATCH 1/1] rfc: describe a Tarantool wire protocol Vladislav Shpilevoy
@ 2018-04-05 8:25 ` Konstantin Osipov
2018-04-09 15:31 ` [tarantool-patches] " Vladislav Shpilevoy
[not found] ` <CAFoyxqh0QqNBVr7tuFF_uoUw-CvBKOVA3FCyWvixikberOxP9w@mail.gmail.com>
1 sibling, 1 reply; 7+ messages in thread
From: Konstantin Osipov @ 2018-04-05 8:25 UTC (permalink / raw)
To: Vladislav Shpilevoy; +Cc: tarantool-patches, alg1973
* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/04/05 10:25]:
I think HAS_NEXT_CHUNK should be part of header, not body.
Otherwise the idea looks good to me, apart from the minor details
(e.g. field names and exact format of each map).
Let's get together one more time to finalize the spec after
soliciting input from the community.
> ---
> Original: https://github.com/tarantool/tarantool/blob/sql-proto-rfc/doc/RFC/wire_protocol.md
>
> doc/RFC/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
> create mode 100644 doc/RFC/wire_protocol_img1.png
>
> diff --git a/doc/RFC/wire_protocol.md b/doc/RFC/wire_protocol.md
> new file mode 100644
> index 000000000..d1fc1329c
> --- /dev/null
> +++ b/doc/RFC/wire_protocol.md
> @@ -0,0 +1,214 @@
> +# Tarantool Wire protocol
> +
> +* **Status**: In progress
> +* **Start date**: 04-04-2018
> +* **Authors**: Vladislav Shpilevoy @Gerold103 v.shpilevoy@tarantool.org, Konstantin Osipov @kostja kostja@tarantool.org, Alexey Gadzhiev @alg1973 alg1973@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).
> +
> +## 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:
> +![alt text](https://raw.githubusercontent.com/tarantool/tarantool/sql-proto-rfc/doc/RFC/wire_protocol_img1.png)
> +
> +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)
>
--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov
^ permalink raw reply [flat|nested] 7+ messages in thread
* [tarantool-patches] Re: Fwd: [PATCH 1/1] rfc: describe a Tarantool wire protocol
[not found] ` <CAFoyxqh0QqNBVr7tuFF_uoUw-CvBKOVA3FCyWvixikberOxP9w@mail.gmail.com>
@ 2018-04-05 9:01 ` Алексей Гаджиев
2018-04-05 9:37 ` Vladislav Shpilevoy
2018-04-05 10:03 ` Konstantin Osipov
0 siblings, 2 replies; 7+ messages in thread
From: Алексей Гаджиев @ 2018-04-05 9:01 UTC (permalink / raw)
To: tarantool-patches; +Cc: Konstantin Osipov, Vladislav Shpilevoy
[-- Attachment #1: Type: text/plain, Size: 14565 bytes --]
Looks cool! Now we can send chunked data!
I have one question:
Is IPROTO_OK mandatory packet in the response messages sequence?
How can I detect if next push/result_set available if previous consists only of one chunk without SQL_INFO_HAS_NEXT_CHUNK?
With best regards,
Alex
>Четверг, 5 апреля 2018, 9:32 +03:00 от Alexey Gadzhiev <alg1973@gmail.com>:
>
>
>---------- Forwarded message ----------
>From: Vladislav Shpilevoy < v.shpilevoy@tarantool.org >
>Date: Thu, Apr 5, 2018 at 12:22 AM
>Subject: [PATCH 1/1] rfc: describe a Tarantool wire protocol
>To: tarantool-patches@freelists.org
>Cc: kostja@tarantool.org , alg1973@gmail.com , Vladislav Shpilevoy < v.shpilevoy@tarantool.org >
>
>
>---
>Original: https://github.com/tarantool/tarantool/blob/sql-proto-rfc/doc/RFC/wire_protocol.md
>
> doc/RFC/ 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
> create mode 100644 doc/RFC/wire_protocol_img1.png
>
>diff --git a/doc/RFC/ wire_protocol.md b/doc/RFC/ wire_protocol.md
>new file mode 100644
>index 000000000..d1fc1329c
>--- /dev/null
>+++ b/doc/RFC/ wire_protocol.md
>@@ -0,0 +1,214 @@
>+# Tarantool Wire protocol
>+
>+* **Status**: In progress
>+* **Start date**: 04-04-2018
>+* **Authors**: Vladislav Shpilevoy @Gerold103 v.shpilevoy@tarantool.org , Konstantin Osipov @kostja kostja@tarantool.org , Alexey Gadzhiev @alg1973 alg1973@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 ).
>+
>+## 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:
>+![alt text]( https://raw.githubusercontent.com/tarantool/tarantool/sql-proto-rfc/doc/RFC/wire_protocol_img1.png )
>+
>+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)
>
>
--
[-- Attachment #2: Type: text/html, Size: 23520 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* [tarantool-patches] Re: Fwd: [PATCH 1/1] rfc: describe a Tarantool wire protocol
2018-04-05 9:01 ` [tarantool-patches] Re: Fwd: " Алексей Гаджиев
@ 2018-04-05 9:37 ` Vladislav Shpilevoy
2018-04-05 10:03 ` Konstantin Osipov
1 sibling, 0 replies; 7+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-05 9:37 UTC (permalink / raw)
To: Алексей
Гаджиев,
tarantool-patches
Cc: Konstantin Osipov
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@gmail.com>:
>
>
> ---------- Forwarded message ----------
> From: *Vladislav Shpilevoy* <v.shpilevoy@tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3av.shpilevoy@tarantool.org>>
> Date: Thu, Apr 5, 2018 at 12:22 AM
> Subject: [PATCH 1/1] rfc: describe a Tarantool wire protocol
> To: tarantool-patches@freelists.org
> <//e.mail.ru/compose/?mailto=mailto%3atarantool%2dpatches@freelists.org>
> Cc: kostja@tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3akostja@tarantool.org>,
> alg1973@gmail.com
> <//e.mail.ru/compose/?mailto=mailto%3aalg1973@gmail.com>,
> Vladislav Shpilevoy <v.shpilevoy@tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3av.shpilevoy@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@tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3av.shpilevoy@tarantool.org>,
> Konstantin Osipov @kostja kostja@tarantool.org
> <//e.mail.ru/compose/?mailto=mailto%3akostja@tarantool.org>,
> Alexey Gadzhiev @alg1973 alg1973@gmail.com
> <//e.mail.ru/compose/?mailto=mailto%3aalg1973@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:
> +![alt
> text](https://raw.githubusercontent.com/tarantool/tarantool/sql-proto-rfc/doc/RFC/wire_protocol_img1.png)
> +
> +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)
>
>
>
>
> --
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [tarantool-patches] Re: Fwd: [PATCH 1/1] rfc: describe a Tarantool wire protocol
2018-04-05 9:01 ` [tarantool-patches] Re: Fwd: " Алексей Гаджиев
2018-04-05 9:37 ` Vladislav Shpilevoy
@ 2018-04-05 10:03 ` Konstantin Osipov
1 sibling, 0 replies; 7+ messages in thread
From: Konstantin Osipov @ 2018-04-05 10:03 UTC (permalink / raw)
To: Алексей
Гаджиев
Cc: tarantool-patches, Vladislav Shpilevoy
* Алексей Гаджиев <alexey.gadzhiev@corp.mail.ru> [18/04/05 12:05]:
> Looks cool! Now we can send chunked data!
>
> I have one question:
> Is IPROTO_OK mandatory packet in the response messages sequence?
> How can I detect if next push/result_set available if previous consists only of one chunk without SQL_INFO_HAS_NEXT_CHUNK?
>
Generally, you can't. The server doesn't know it yet itself. For
example, imagine this:
while (condition)
box.session.push()
end
return;
In this case there can be variable number of pushes.
But you know for sure there will be next packet before the
sequence of packets for this request ends - either CHUNK or OK.
--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov
^ permalink raw reply [flat|nested] 7+ messages in thread
* [tarantool-patches] [PATCH 1/1] rfc: describe a Tarantool wire protocol
2018-04-05 8:25 ` [tarantool-patches] " Konstantin Osipov
@ 2018-04-09 15:31 ` Vladislav Shpilevoy
2018-06-28 11:45 ` [tarantool-patches] " Konstantin Osipov
0 siblings, 1 reply; 7+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-09 15:31 UTC (permalink / raw)
To: tarantool-patches; +Cc: kostja
Part of #3328
---
Original: https://github.com/tarantool/tarantool/blob/gh-3328-new-iproto/doc/rfc/3328-wire_protocol.md
doc/rfc/3328-wire_protocol.md | 232 ++++++++++++++++++++++++++++++++++++
doc/rfc/3328-wire_protocol_img1.png | Bin 0 -> 50820 bytes
2 files changed, 232 insertions(+)
create mode 100644 doc/rfc/3328-wire_protocol.md
create mode 100644 doc/rfc/3328-wire_protocol_img1.png
diff --git a/doc/rfc/3328-wire_protocol.md b/doc/rfc/3328-wire_protocol.md
new file mode 100644
index 000000000..f6aab0b16
--- /dev/null
+++ b/doc/rfc/3328-wire_protocol.md
@@ -0,0 +1,232 @@
+# Tarantool Wire protocol
+
+* **Status**: In progress
+* **Start date**: 04-04-2018
+* **Authors**: Vladislav Shpilevoy @Gerold103 v.shpilevoy@tarantool.org, Konstantin Osipov @kostja kostja@tarantool.org, Alexey Gadzhiev @alg1973 alg1973@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).
+
+## 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. But
+1. `IPROTO_OK` finalizes a request;
+2. `IPROTO_CHUNK` can have `IPROTO_CHUNK_ID` field in the header, that allows to build a chain of chunks with the same `ID`. Absense of this field means, that the chunk is not a part of a chain.
+
+### 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, |
+| ... |
+| }, |
+| |
+| IPROTO_DATA: [ |
+| tuple/scalar, |
+| ... |
+| ] |
+| } |
++----------------------------------------------+
+```
+
+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 can be result of SQL DQL (`SELECT`) or SQL DML (`INSERT` with human readable response like `NN rows inserted/updated/deleted`). The response can be part of a continuous sequence of responses. A first message of the sequence always contains `IPROTO_METADATA` in the body and `IPROTO_CHUNK_ID` in the header, if there are multiple responses. All sequence chunks always contain `IPROTO_CHUNK_ID` with the same value.
+
+On the picture the state machine of the protocol is showed:
+![alt text](https://raw.githubusercontent.com/tarantool/tarantool/gh-3328-new-iproto/doc/rfc/3328-wire_protocol_img1.png)
+
+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, IPROTO_CHUNK_ID = <id1>|
++- - - - - - - - - - - - - - - - - - - - - - - +
+| BODY: { |
+| IPROTO_DATA: [ tuple1, tuple2, ... ] |
+| IPROTO_METADATA: [ |
+| { /* field1 name, type ... */ }, |
+| { /* field2 name, type ... */ }, |
+| ... |
+| ] |
+| } |
++----------------------------------------------+
+
+ /* From second to next to last chunk. */
+ +----------------------------------------------+
+ | HEADER: IPROTO_CHUNK, IPROTO_CHUNK_ID = <id1>|
+ +- - - - - - - - - - - - - - - - - - - - - - - +
+ | BODY: { |
+ | IPROTO_DATA: [ tuple1, tuple2, ... ] |
+ | } |
+ +----------------------------------------------+
+
+ /* Last chunk. */
+ +----------------------------------------------+
+ | HEADER: IPROTO_CHUNK, IPROTO_CHUNK_ID = <id1>|
+ +- - - - - - - - - - - - - - - - - - - - - - - +
+ | 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 ] |
+| } |
++----------------------------------------------+
+```
+
+## Rationale and alternatives
+
+Another way to link chunks together exists, replacing `IPROTO_CHUNK_ID`.
+Chunks can be linked via flag in a header: `IPROTO_FLAG_IS_CHAIN`, that would be stored in `IPROTO_FLAGS` header value. When a multiple messages form a chain, all of them except last one contain this flag. For example:
+```
+IPROTO_CHUNK
+ |
+IPROTO_CHUNK, IS_CHAIN
+ |
+ +--IPROTO_CHUNK, IS_CHAIN
+ |
+ +--IPROTO_CHUNK, IS_CHAIN
+ |
+ +--IPROTO_CHUNK
+ |
+IPROTO_CHUNK
+ |
+...
+ |
+IPROTO_OK/ERROR
+```
+
+It is slightly simpler than `CHAIN_ID`, but
+1. Does not enable to mix parts of different chains, if it will be needed sometimes;
+2. The last response does not contain `IS_CHAIN`, but it is actually a part of chain. `IS_CHAIN` can not be stored in the last response, because else it will not be distinguishable from the next chain. This can be solved by renaming `IS_CHAIN` to `HAS_NEXT_CHAIN` or something, but `CHAIN_ID` seems better - it has no these problems, and is more scalable.
diff --git a/doc/rfc/3328-wire_protocol_img1.png b/doc/rfc/3328-wire_protocol_img1.png
new file mode 100644
index 0000000000000000000000000000000000000000..714a06ba10574285f43d9569fb21bd2815006644
GIT binary patch
literal 50820
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
literal 0
HcmV?d00001
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [tarantool-patches] Re: [PATCH 1/1] rfc: describe a Tarantool wire protocol
2018-04-09 15:31 ` [tarantool-patches] " Vladislav Shpilevoy
@ 2018-06-28 11:45 ` Konstantin Osipov
0 siblings, 0 replies; 7+ messages in thread
From: Konstantin Osipov @ 2018-06-28 11:45 UTC (permalink / raw)
To: Vladislav Shpilevoy; +Cc: tarantool-patches
* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/04/09 18:34]:
> Part of #3328
Hi,
All the design decisions are OK.
I made some edits and renames, rebased the branch to the latest
1.10 and pushed.
If you're ok with my edits, I will squash and merge.
I haven't checked the wire diagram, it may need some updates as
well to follow my renames -please check and do.
--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2018-06-28 11:45 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-04 21:22 [tarantool-patches] [PATCH 1/1] rfc: describe a Tarantool wire protocol Vladislav Shpilevoy
2018-04-05 8:25 ` [tarantool-patches] " Konstantin Osipov
2018-04-09 15:31 ` [tarantool-patches] " Vladislav Shpilevoy
2018-06-28 11:45 ` [tarantool-patches] " Konstantin Osipov
[not found] ` <CAFoyxqh0QqNBVr7tuFF_uoUw-CvBKOVA3FCyWvixikberOxP9w@mail.gmail.com>
2018-04-05 9:01 ` [tarantool-patches] Re: Fwd: " Алексей Гаджиев
2018-04-05 9:37 ` Vladislav Shpilevoy
2018-04-05 10:03 ` Konstantin Osipov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox