polardbxengine/plugin/x/protocol/doc/mysqlx-protocol-expect.dox

259 lines
7.7 KiB
Plaintext

/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is also distributed with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms,
* as designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have included with MySQL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/** @page mysqlx_protocol_expectations Expectations
Topics in this section:
- @ref expectations_Setting_Expectations
- @ref expectations_Behavior
- @ref expectations_Conditions
With the use of pipelining in the X %Protocol (sending messages
without waiting for successful response) only so many messages can be
pipelined without causing havoc if one of the pipelined, dependent
messages fails:
@code{unparsed}
Mysqlx.Crud::PrepareFind(stmt_id=1, ...) // may fail
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // would fail implicitly as stmt_id=1 doesn't exist
Mysqlx.PreparedStmt::Close(stmt_id=1) // would fail implicitly as stmt_id=1 doesn't exist
@endcode
While implicitly failing is one thing, there are situations where it
isn't that obvious what will happen:
@code{unparsed}
Mysqlx.Crud::PrepareInsert(stmt_id=1, ...) // ok
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // ok
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // duplicate key error
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // what now? abort the insert? ignore?
Mysqlx.PreparedStmt::Close(stmt_id=1) // close the stmt_id
@endcode
Setting Expectations {#expectations_Setting_Expectations}
====================
Expectations let statements fail reliably until the end of the block.
Assume the ``PrepareFind`` fails:
- don't execute the ``Execute``
- don't try to close the stmt
@code{unparsed}
Mysqlx.Expect::Open([+no_error])
Mysqlx.Crud::PrepareFind(stmt_id=1, ...) // may fail
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // expectation(no_error) failed
Mysqlx.PreparedStmt::Close(stmt_id=1) // expectation(no_error) failed
Mysqlx.Expect::Close()
@endcode
But this would also skip the close if execute fails. Not what we want.
Adding another expect-block handles it:
@code{unparsed}
Mysqlx.Expect::Open([+no_error])
Mysqlx.Crud::PrepareFind(stmt_id=1, ...) // may fail
Mysqlx.Expect::Open([+no_error])
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // expectation(no_error) failed
Mysqlx.Expect::Close()
Mysqlx.PreparedStmt::Close(stmt_id=1) // expectation(no_error) failed
Mysqlx.Expect::Close()
@endcode
With these expectations pipelined, the server will handle errors in a
consistent, reliable way.
It also allows to express how a streaming insert would behave if one of
the inserts fails (for example: duplicate key error, disk full, and so
on):
Either fail at first error:
@code{unparsed}
Mysqlx.Expect::Open([+no_error])
Mysqlx.Crud::PrepareInsert(stmt_id=1, ...) // ok
Mysqlx.Expect::Open([+no_error])
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // ok
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // duplicate_key error
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // expectation(no_error) failed
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // expectation(no_error) failed
Mysqlx.Expect::Close()
Mysqlx.PreparedStmt::Close(stmt_id=1) // ok
Mysqlx.Expect::Close()
@endcode
Or ignore error and continue:
@code{unparsed}
Mysqlx.Expect::Open([+no_error])
Mysqlx.Crud::PrepareInsert(stmt_id=1, ...) // ok
Mysqlx.Expect::Open([-no_error])
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // ok
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // duplicate_key error
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // ok
Mysqlx.PreparedStmt::Execute(stmt_id=1, ...) // ok
Mysqlx.Expect::Close()
Mysqlx.PreparedStmt::Close(stmt_id=1) // expectation(no_error) failed
Mysqlx.Expect::Close()
@endcode
Behavior {#expectations_Behavior}
========
An Expectation Block:
- encloses client messages
- has a Condition Set
- has a parent Expectation Block
- can inherit a Condition Set from the parent Expectation Block or
start with a empty Condition Set
- fails if one of the Conditions fails while the Block is started or
active
- fails if one of the Conditions isn't recognized or not valid
A Condition Set:
- has a set of Conditions
- allows to set/unset Conditions
A Condition:
- has a key and value
- key is integer
- value format depends on the key
If a Expectation Block fails, all following messages of the Expectation
block are failing with:
- error-msg: ``Expectation failed: %%s``
- error-code: ...
Conditions {#expectations_Conditions}
==========
@warning
The layout of conditions are subject to change:
- not all may be implemented yet
- more conditions may be added
| Condition | Key |
|------------------------------------------|-----|
| @ref expectations_no_error | 1 |
| @ref expectations_schema_version | 2 |
| @ref expectations_gtid_executed_contains | 3 |
| @ref expectations_gtid_wait_less_than_ms | 4 |
## no_error {#expectations_no_error}
Fail all messages of the block after the first message returning an
error.
Example:
@code{unparsed}
Mysqlx.Expect::Open([+no_error])
Mysqlx.Expect::Close()
@endcode
## schema_version {#expectations_schema_version}
Fail all messages of the block if the schema version for the collection
doesn't match. (_not implemented_)
@note
This is a used by the JSON schema support of the server to ensure
client and server are in agreement of what schema version is
*current* as it is currently planned to enforce the checks on the
client-side.
Example:
@code{unparsed}
Mysqlx.Expect::Open([+schema_version::`schema`.`collection` = 1])
Mysqlx.Expect::Close()
@endcode
## gtid_executed_contains {#expectations_gtid_executed_contains}
Fail all messages until the end of the block if the ``@@gtid_executed``
doesn't contain the set GTID. (_not implemented_)
@note
Used by the *read-your-writes* to ensure another node is already up
to date.
Example:
@code{unparsed}
Mysqlx.Expect::Open([+gtid_executed_contains = "..."])
Mysqlx.Expect::Close()
@endcode
## gtid_wait_less_than_ms {#expectations_gtid_wait_less_than_ms}
Used in combination with @ref expectations_gtid_executed_contains
to wait that the node caught up. (_not implemented_)
Example:
@code{unparsed}
Mysqlx.Expect::Open([+gtid_wait_less_than_ms = 1000])
Mysqlx.Expect::Close()
@endcode
## sql_stateless {#expectations_sql_stateless}
Fail any message that executes stateful statements like:
- temporary tables
- user variables
- session variables
- stateful functions (``INSERT_ID()``, ``GET_LOCK()``)
- stateful language features (``SQL_CALC_FOUND_ROWS``)
@note
Depending on the implementation stored procedures may be not allowed
as they may through levels of indirection use stateful SQL features.
*/