Skip to content

Commit

Permalink
Pipeline API
Browse files Browse the repository at this point in the history
Removed sansio_algorithm base class

close #75
  • Loading branch information
anarthal authored Jun 8, 2024
1 parent 22eddec commit 9ba4f9d
Show file tree
Hide file tree
Showing 101 changed files with 6,257 additions and 1,548 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeIsMainRegex: "([-_](test|unittest))?$"
IncludeIsMainRegex: null
IncludeIsMainSourceRegex: ""
IndentGotoLabels: true
IndentPPDirectives: None
Expand Down
2 changes: 1 addition & 1 deletion .drone.star
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,9 @@ def main(ctx):
linux_b2('Linux B2 clang-14-arm64', _image('build-clang14'), toolset='clang-14', cxxstd='20', arch='arm64'),
linux_b2('Linux B2 clang-16-sanit', _image('build-clang16'), toolset='clang-16', cxxstd='20', address_sanitizer=1, undefined_sanitizer=1),
linux_b2('Linux B2 clang-16-i386-sanit', _image('build-clang16-i386'), toolset='clang-16', cxxstd='20', address_model=32, address_sanitizer=1, undefined_sanitizer=1),
linux_b2('Linux B2 gcc-5', _image('build-gcc5'), toolset='gcc-5', cxxstd='11'), # gcc-5 C++14 doesn't like my constexpr field_view
linux_b2('Linux B2 clang-17', _image('build-clang17'), toolset='clang-17', cxxstd='20'),
linux_b2('Linux B2 clang-18', _image('build-clang18'), toolset='clang-18', cxxstd='23'),
linux_b2('Linux B2 gcc-5', _image('build-gcc5'), toolset='gcc-5', cxxstd='11'), # gcc-5 C++14 doesn't like my constexpr field_view
linux_b2('Linux B2 gcc-5-ts-executor', _image('build-gcc5'), toolset='gcc-5', cxxstd='11', use_ts_executor=1),
linux_b2('Linux B2 gcc-6', _image('build-gcc6'), toolset='gcc-6', cxxstd='14,17'),
linux_b2('Linux B2 gcc-10', _image('build-gcc10'), toolset='gcc-10', cxxstd='17,20'),
Expand Down
4 changes: 4 additions & 0 deletions doc/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ docca.reference reference.qbk
\"BOOST_MYSQL_OUTPUT_STRING=class\" \\
\"BOOST_MYSQL_FORMATTABLE=class\" \\
\"BOOST_MYSQL_STATIC_ROW=class\" \\
\"BOOST_MYSQL_PIPELINE_REQUEST_TYPE=class\" \\
\"BOOST_MYSQL_PIPELINE_STAGE_TYPE=class\" \\
\"BOOST_MYSQL_WRITABLE_FIELD=class\" \\
\"BOOST_MYSQL_DECL=\" \\
\"BOOST_MYSQL_HAS_LOCAL_TIME=\" \\
\"BOOST_NO_CXX17_DEDUCTION_GUIDES=\" \\
"
<doxygen:param>SKIP_FUNCTION_MACROS=NO
<doxygen:param>OUTPUT_LANGUAGE=English
Expand Down
7 changes: 7 additions & 0 deletions doc/qbk/00_main.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,13 @@
[def __FieldViewFwdIterator__ [reflink2 FieldViewFwdIterator ['FieldViewFwdIterator]]]
[def __Formattable__ [reflink2 Formattable ['Formattable]]]
[def __OutputString__ [reflink2 OutputString ['OutputString]]]
[def __PipelineRequestType__ [reflink2 PipelineRequestType ['PipelineRequestType]]]
[def __PipelineStageType__ [reflink2 PipelineStageType ['PipelineStageType]]]
[def __ResultsType__ [reflink2 ResultsType ['ResultsType]]]
[def __SocketStream__ [reflink2 SocketStream ['SocketStream]]]
[def __StaticRow__ [reflink2 StaticRow ['StaticRow]]]
[def __Stream__ [reflink2 Stream ['Stream]]]
[def __WritableField__ [reflink2 WritableFieldTuple ['WritableField]]]
[def __WritableFieldTuple__ [reflink2 WritableFieldTuple ['WritableFieldTuple]]]


Expand Down Expand Up @@ -136,6 +139,7 @@ END
[import ../../test/integration/test/snippets/any_connection.cpp]
[import ../../test/integration/test/snippets/connection_pool.cpp]
[import ../../test/integration/test/snippets/sql_formatting.cpp]
[import ../../test/integration/test/snippets/pipeline.cpp]

[include 01_intro.qbk]
[include 02_integrating.qbk]
Expand All @@ -160,6 +164,7 @@ END
[include 21_connection_pool.qbk]
[include 22_sql_formatting.qbk]
[include 23_sql_formatting_advanced.qbk]
[include 24_pipeline.qbk]
[include 24_examples.qbk]
[include 25_tests.qbk]

Expand All @@ -173,6 +178,8 @@ END
[include helpers/FieldViewFwdIterator.qbk]
[include helpers/Formattable.qbk]
[include helpers/OutputString.qbk]
[include helpers/PipelineRequestType.qbk]
[include helpers/PipelineStageType.qbk]
[include helpers/ResultsType.qbk]
[include helpers/SocketStream.qbk]
[include helpers/StaticRow.qbk]
Expand Down
24 changes: 24 additions & 0 deletions doc/qbk/24_examples.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Here is a list of available examples:
# [link mysql.examples.batch_inserts_generic (Experimental) Generic batch inserts with Boost.Describe: extending format_sql]
# [link mysql.examples.dynamic_filters (Experimental) Implements a query with several dynamic filters using client-side query formatting]
# [link mysql.examples.patch_updates (Experimental) Implements a dynamic UPDATE query for PATCH-like update semantics using client-side query formatting]
# [link mysql.examples.pipeline (Experimental) Prepares, executes and closes statements in batch using the pipeline API]
# [link mysql.examples.connection_pool (Experimental) A REST API server that uses connection pooling]
# [@https://github.com/anarthal/servertech-chat The BoostServerTech chat project uses Boost.MySQL and Boost.Redis to implement a chat server]

Expand Down Expand Up @@ -399,6 +400,29 @@ __assume_setup__





[section:pipeline (Experimental) Pipelines]

This example demonstrates how use [link mysql.pipeline the pipeline API]
to prepare, execute and close statements in batch.

The example employs async functions with C++20 coroutines.

__assume_setup__

[import ../../example/pipeline.cpp]
[example_pipeline]

[endsect]








[section:connection_pool (Experimental) Connection pools]

This example demonstrates how to use [reflink connection_pool].
Expand Down
201 changes: 201 additions & 0 deletions doc/qbk/24_pipeline.qbk
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
[/
Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)

Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]

[section:pipeline (Experimental) Pipelines]
[nochunk]

Functions like [refmemunq any_connection execute], [refmemunq any_connection prepare_statement]
and their async counterparts are half-duplex:
they write a single request to the server and wait for its response.
In contrast, pipelines can increase efficiency by coalescing several requests into a single message, saving round-trips to the server.

[warning
The MySQL client/server protocol doesn't have explicit support for pipelines. [*From the server's point of view,
a pipeline is just a sequence of unrelated requests]. The server will try to execute all stages
in each pipeline, regardless of the result of previous stages. Pipelines are considered
an [*advanced feature]. Please read [link mysql.pipeline.pitfalls the pitfalls section] for more info.
]

[note
This feature is experimental. Its API may change in subsequent releases.
]

[heading Use cases]

You should use pipelines for lightweight operations, dominated by round-trip time. Typical examples include:

* Running connection setup code, involving operations like [refmemunq any_connection reset_connection],
[refmemunq any_connection set_character_set] or preparing statements. [reflink connection_pool] uses
pipelines to clean up connections for re-use.
* Preparing several statements, in batch.
* Executing and closing a statement in a single round-trip.


You should [*avoid] pipelines for the following cases:

* When you can achieve the same functionality using semicolon-separated queries
(thus using [link mysql.multi_resultset.multi_queries multi-queries] and [link mysql.sql_formatting client-side SQL formatting]).
Multi-queries will stop after the first error, which is usually what you want. See [link mysql.pipeline.pitfalls this section] for more info.
* When running heavyweight queries, where the gains in round-trip time are not significant.
* When there are dependencies between stages in the pipeline. Lack of protocol support makes this use case impossible.

If you're not sure, don't use this feature.






[heading Pipeline requests and responses]

There are two interfaces to pipelines: dynamic ([reflink pipeline_request]) and static
([reflink static_pipeline_request]). Both are C++11 compatible.

To run a dynamic pipeline, create a request object describing what should the pipeline do:

[pipeline_dynamic_request]

Use [refmem pipeline_request add] with any of the [link mysql.pipeline.reference available stage types]
to add stages your pipeline.

To actually run the pipeline, create a response object and call
[refmem any_connection run_pipeline] or [refmemunq any_connection async_run_pipeline]:

[pipeline_dynamic_run]

Finally, you can access the statements using:

[pipeline_dynamic_results]




[heading Static pipelines]

If the type and number of stages in your pipeline is known at compile-time, you can use
static pipelines, instead. The mechanics are similar, except that some checks are moved
to compile-time. The pipeline in the previous example can be rewritten as:

[pipeline_static]









[heading:error Error handling]

If any of the pipeline stages result in an error, the entire [refmemunq any_connection run_pipeline] operation
is considered failed. This means that [*if `run_pipipeline` completed successfully, all stages succeeded]. Recall that
[*all stages are always run, regardless of the outcome of previous stages].

If `run_pipipeline` fails, you can check which stages succeeded and failed by inspecting responses.
Response types like [refmem prepare_statement_stage response_type] are aliases for
[link mysql.error_handling.system_result `boost::system::result`], a vocabulary type that
can contain either a value or an error. For instance:

[pipeline_errors]

The module uses [reflink errcode_with_diagnostics] as the error type,
which contains an `error_code` and a [reflink diagnostics] object.




[heading:pitfalls Potential pitfalls]

All requests in the pipeline are always run, regardless of the outcome of previous requests. As a result, some pipelines can behave non-intuitively:

[pipeline_pitfalls_bad]

Pipelines aren't the best fit here. Instead, you can express the same logic using semicolon-separated queries:

[pipeline_pitfalls_good]

Pipeline stages are run sequentially by the server. If any of the stages involves a heavyweight query,
the server won't process subsequent stages until the query completes.



[heading:reference Pipeline stage reference]

In the table below, the following variables are assumed:

* `req` is a [reflink pipeline_request].
* `stmt` is a valid [reflink statement].
* `result` is a [reflink results] object.
* `conn` is an [reflink any_connection] object.

[table:reference
[
[Stage type]
[Example]
[When run, equivalent to...]
[Response type]
]
[
[
[reflink execute_stage][br][br]
Behaves like [refmem any_connection execute]
]
[[pipeline_reference_execute]]
[[pipeline_reference_execute_equivalent]]
[
[reflink results] or [reflink errcode_with_diagnostics]
]
]
[
[
[reflink prepare_statement_stage][br][br]
Behaves like [refmem any_connection prepare_statement]
]
[[pipeline_reference_prepare_statement]]
[[pipeline_reference_prepare_statement_equivalent]]
[
[reflink statement] or [reflink errcode_with_diagnostics]
]
]
[
[
[reflink close_statement_stage][br][br]
Behaves like [refmem any_connection close_statement]
]
[[pipeline_reference_close_statement]]
[[pipeline_reference_close_statement_equivalent]]
[
Possibly empty [reflink errcode_with_diagnostics]
]
]
[
[
[reflink reset_connection_stage][br][br]
Behaves like [refmem any_connection reset_connection]
]
[[pipeline_reference_reset_connection]]
[[pipeline_reference_reset_connection_equivalent]]
[
Possibly empty [reflink errcode_with_diagnostics]
]
]
[
[
[reflink set_character_set_stage][br][br]
Behaves like [refmem any_connection set_character_set]
]
[[pipeline_reference_set_character_set]]
[[pipeline_reference_set_character_set_equivalent]]
[
Possibly empty [reflink errcode_with_diagnostics]
]
]
]


[endsect]
15 changes: 15 additions & 0 deletions doc/qbk/helpers/PipelineRequestType.qbk
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[/
Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)

Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]

[section:boost__mysql__PipelineRequestType PipelineRequestType concept]

A type `T` satisfies `PipelineRequestType` if either:

* It's exactly the [reflink pipeline_request] class.
* It's an instantiation of the [reflink static_pipeline_request] template class.

[endsect]
18 changes: 18 additions & 0 deletions doc/qbk/helpers/PipelineStageType.qbk
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[/
Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)

Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]

[section:boost__mysql__PipelineStageType PipelineStageType concept]

A type `T` satisfies `PipelineStageType` if it's exactly one of these types:

* [reflink execute_stage]
* [reflink prepare_statement_stage]
* [reflink close_statement_stage]
* [reflink reset_connection_stage]
* [reflink set_character_set_stage]

[endsect]
Loading

0 comments on commit 9ba4f9d

Please sign in to comment.