Project: Percona Server for MySQL (App Definition and Development / Database)
File: mysql_server_mock.md

The MySQL Server Mock speaks (a subset of) the MySQL Client/Server protocol knows how to authenticate respond to statements send by the client with resultsets status (Ok, last-insert-id, ...) Error It reads the protocol trace from a JSON file which contains: sequence of statements to expect responses to send extra context information like execution-times Testing MySQL InnoDB Cluster components like MySQL Shell MySQL Router Connectors involves: initialize a MySQL Server via the MySQL Shell `dba.deploySandboxInstance` which runs `mysqld --initialize` use the MySQL Shell to builds a cluster with ``dba.createCluster`` add more nodes with ``cluster.addInstance`` (10-20 seconds). execute the statements to test the right behaviour (100ms) shut down the whole setup again (1s) The setup and shutdown costs (20s) outscale the actual test runtime (100ms) by a great margin. On the other side all the components speak to the MySQL Server over the MySQL %Protocol only. Statements are sent, responses are received. What the MySQL Server did inbetween to generate the response isn't visible from the outside. There is no difference from the point of view of the clients if the response comes from a genuine MySQL Server or from another program that returns the same bytes over the wire. A simple trace-file: ```{.json} {"stmts": [ { "stmt": "select @@version_comment limit 1", "result": { "columns": [{ "type": "STRING", "name": "@@version_comment" }], "rows": [ ["ImAMock"] ]} }, { "stmt": "select USER()", "result": { "columns": [{ "type": "STRING", "name": "USER()" }], "rows": [ ["mock"] ] } }] } ``` Starting the mock: ``` $ ./mysqlservermock ./simple.json 5500 ``` Use a client to talk to the mock: ``` $ mysql --port=5500 Server version: 8.0.0-mock ImAMock [...] mock@localhost:5500 (none)> ``` When using a MySQL Server the execution time of a statement is driven by the system that is used (storage-io, network-io, cpu speed, ...). In cases where the execution time doesn't affect the behaviour (timeout testing, ...) the test setup could pretend the system has zero latency (storage in RAM, super fast CPUs, local networks). It would be required to: capture the statement exchanged between client and server provide a MySQL Server mock which speaks the MySQL %Protocol listens on a TCP port returns the predefined results depending on the statement as a MySQL Server would. By using the Server Mock it is easier to create error-cases that are hard to create in real setups: disk full server dead nodes out of sync ... By using the MySQL Server Mock in the Router tests, the tests don't need the rely on installing MySQL Shell MySQL Server which involves: ensure the right version check they actually work on the target using the right paths To bootstrap a router, the router expects to talk to a MySQL Server. It: logs in with `root` and some password executes 15 statements writes ``mysqlrouter.conf`` Start the "MySQL Server" mock on port ``5050`` with the ``bootstrapper.yaml`` $ ./mysqlservermock --mysqld-port=5050 --stmt-file=./bootstrapper.yaml Let the router do the bootstrap $ mysqlrouter --bootstrap localhost:5050 -d router-conf Bootstrapping MySQL Router instance at

MySQL Router has now been configured for the InnoDB cluster 'test'. The following connection information can be used to connect to the cluster. Classic MySQL protocol connections to cluster 'test': Read/Write Connections: localhost:6446 Read/Only Connections: localhost:6447 X protocol connections to cluster 'test': Read/Write Connections: localhost:64460 Read/Only Connections: localhost:64470 The mock will log: 2017-01-24 12:24:11,551 MAIN INFO: listening on 5050 2017-01-24 12:24:23,357 MAIN INFO: accepted connection from ('127.0.0.1', 38684) 2017-01-24 12:24:23,358 MAIN DEBUG: received: SELECT * FROM mysqlinnodbclustermetadata.schemaversion 2017-01-24 12:24:23,358 MAIN DEBUG: sending Result 2017-01-24 12:24:23,395 MAIN DEBUG: received: SELECT ((SELECT count(*) FROM ... After the bootstrap is finished the bootstrap-mysql-server-mock can be stopped. Using the bootstrapped config: the metadata store is expected on port ``5500`` the data store is expected on port ``5100`` Start the metadata-store $ ./mysqlservermock --mysqld-port=5500 --stmt-file=./metadata-store.yaml Start the data-store $ ./mysqlservermock --mysqld-port=5100 --stmt-file=./group-replication.yaml Start the router $ mysqlrouter --config router-conf/mysqlrouter.conf The router log file says: 2017-01-24 12:28:30 INFO [7fc57d7eb700] [routing:testdefaultro] started: listening on 0.0.0.0:6447; read-only 2017-01-24 12:28:30 INFO [7fc57dfec700] Starting Metadata Cache 2017-01-24 12:28:30 INFO [7fc57cfea700] [routing:testdefaultrw] started: listening on 0.0.0.0:6446; read-write 2017-01-24 12:28:30 INFO [7fc577fff700] [routing:testdefaultx_rw] started: listening on 0.0.0.0:64460; read-write 2017-01-24 12:28:30 INFO [7fc5747e9700] [routing:testdefaultx_ro] started: listening on 0.0.0.0:64470; read-only 2017-01-24 12:28:30 INFO [7fc57dfec700] Connected with metadata server running on 127.0.0.1:5500 2017-01-24 12:28:30 INFO [7fc57dfec700] Changes detected in cluster 'test' after metadata refresh 2017-01-24 12:28:30 INFO [7fc57dfec700] Metadata for cluster 'test' has 1 replicasets: 2017-01-24 12:28:30 INFO [7fc57dfec700] 'default' (3 members, single-primary) 2017-01-24 12:28:30 INFO [7fc57dfec700] localhost:5100 / 50000 - role=HA mode=RW 2017-01-24 12:28:30 INFO [7fc57dfec700] localhost:5110 / 50100 - role=HA mode=RO 2017-01-24 12:28:30 INFO [7fc57dfec700] localhost:5120 / 50200 - role=HA mode=RO 2017-01-24 12:28:30 INFO [7fc5757fa700] Connected with metadata server running on 127.0.0.1:5500 The ``metadata-store`` mock outputs: 2017-01-24 12:28:10,322 MAIN INFO: listening on 5500 2017-01-24 12:28:20,774 MAIN INFO: accepted connection from ('127.0.0.1', 53278) 2017-01-24 12:28:20,775 MAIN DEBUG: received: SELECT R.replicasetname, I.mysqlserveruuid, H.location, I.addresses->>'$.mysqlClassic', I.addresses->>'$.mysqlX' FROM mysqlinnodbclustermetadata.clusters AS F JOIN mysqlinnodbclustermetadata.replicasets AS R ON F.clusterid = R.clusterid JOIN mysqlinnodbclustermetadata.instances AS I ON R.replicasetid = I.replicasetid JOIN mysqlinnodbclustermetadata.hosts AS H ON I.hostid = H.hostid WHERE F.clustername = 'test'; 2017-01-24 12:28:20,775 MAIN DEBUG: sending Result The ``group-replication`` mock outputs: 2017-01-24 12:28:27,851 MAIN INFO: listening on 5100 2017-01-24 12:28:30,755 MAIN INFO: accepted connection from ('127.0.0.1', 41520) 2017-01-24 12:28:30,755 MAIN DEBUG: received: show status like 'groupreplicationprimary_member' 2017-01-24 12:28:30,755 MAIN DEBUG: sending Result 2017-01-24 12:28:30,795 MAIN DEBUG: received: SELECT memberid, memberhost, memberport, memberstate, @@groupreplicationsingleprimarymode FROM performanceschema.replicationgroupmembers WHERE channelname = 'groupreplicationapplier' 2017-01-24 12:28:30,795 MAIN DEBUG: sending Result 2017-01-24 12:28:30,835 MAIN INFO: closed connection to ('127.0.0.1', 41520) The behaviour of the ``mysqlservermock`` is driven by JSON Tracefiles Javascript files They describe what the mock shall respond with when it receives: a handshake a sequence of statements The structure of the JSON Tracefiles is defined in JSON-schema and can be seen in the source at src/mockserver/src/mysqlservermockschema.js The Javascript backed files for the mock are more powerful then the static JSON Tracefiles as it: allows to interface with the REST API of the ``mysqlservermock`` allows "stmts" to be handled at runtime via javascript functions