Topics in this section:
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:
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
While implicitly failing is one thing, there are situations where it isn't that obvious what will happen:
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
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
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()
But this would also skip the close if execute fails. Not what we want. Adding another expect-block handles it:
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()
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:
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()
Or ignore error and continue:
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()
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
- Warning
- The layout of conditions are subject to change:
- not all may be implemented yet
- more conditions may be added
no_error
Fail all messages of the block after the first message returning an error.
Example:
Mysqlx.Expect::Open([+no_error])
Mysqlx.Expect::Close()
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:
Mysqlx.Expect::Open([+schema_version::`schema`.`collection` = 1])
Mysqlx.Expect::Close()
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:
Mysqlx.Expect::Open([+gtid_executed_contains = "..."])
Mysqlx.Expect::Close()
gtid_wait_less_than_ms
Used in combination with gtid_executed_contains to wait that the node caught up. (not implemented)
Example:
Mysqlx.Expect::Open([+gtid_wait_less_than_ms = 1000])
Mysqlx.Expect::Close()
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.