2014年11月18日火曜日

MySQL Fabricつらい(Fabricサーバー上のMySQLプロトコルの口でFabricのAPIが呼べる編)

もともとは、mysqlコマンドラインクライアントにHA切り替え機能を実装する編 でいじってたクライアントにもう少しパラメーターを突っ込もうとか Fabricサーバー上でstatusコマンド叩くとmysqlコマンドラインクライアントがクラッシュする件 を直したいとかそんなことを考えてソースを読んでいたときのこと。

mysql-utilities-1.5.3/mysql/fabric/protocols/mysqlrpc.py のMySQLRPCRequestHandler.handleあたりの中で
 925             # Handle CALL
 926             if CHECK_CALL.match(data):
 927                 call_match = PARSE_CALL.match(data)
 928                 if call_match:
 929                     self._handle_call(call_match)
 930                 else:
 931                     self.send_syntax_error()
 932                 continue
 933
 934             # Handle SHOW CREATE PROCEDURE
 935             if CHECK_SHOW_CREATE_PROC.match(data):
 936                 showproc_match = PARSE_SHOW_CREATE_PROC.match(data)
 937                 if showproc_match:
 938                     self._handle_show_create_procedure(showproc_match)
 939                 else:
 940                     self.send_syntax_error()
 941                 continue
 942
 943             # Handle INFORMATION_SCHEMA.ROUTINES
 944             is_routines_match = PARSE_IS_ROUTINES.match(data)
 945             if is_routines_match:
 946                 self._handle_information_schema_routines(is_routines_match)
 947                 continue
 948
 949             # Handle SET
 950             set_match = PARSE_SET.match(data)
 951             if set_match and set_match.group(1).lower() in ('format',):
 952                 format = dequote(set_match.group(2).strip()).strip().lower()
 953                 if format not in ('json',):
 954                     self.send_error(
 955                         errorcode.ER_LOCAL_VARIABLE,
 956                         "Format '{0}' is not supported".format(format),
 957                         "42000"
 958                     )
 959                 else:
 960                     _LOGGER.debug("Format set to %s", format)
 961                     self._format = format
 962
 963             # We accept anything else without doing anything
 964             self.send_packet(self.ok_packet())


なんか特定の文字列が来た時だけ別ハンドルにしているように見える。
さかのぼってこのPARSE_*の中身を覗いてみると、
  57 # Regular expression getting group, command and the arguments as a string
  58 CHECK_CALL = re.compile(
  59     r"""^CALL\s*""", re.IGNORECASE
  60 )
  61 PARSE_CALL = re.compile(
  62     r"""CALL\s+(\w+)\.(\w+)\s*\((.*)\)"""
  63     r"""(?=(?:[^"']*["'][^"']*["'])*[^"']*$)""", re.IGNORECASE
  64 )
  65 PARSE_CALL_ARGS = re.compile(
  66     r""",(?=(?:[^"']*["'][^"']*["'])*[^"']*$)"""
  67 )
  68 PARSE_CALL_KWARG = re.compile(
  69     r"""=(?=(?:[^"']*["'][^"']*["'])*[^"']*$)"""
  70 )
  71
  72 # Regular expression for SHOW CREATE PROCEDURE
  73 CHECK_SHOW_CREATE_PROC = re.compile(
  74     r"""^SHOW\s+CREATE\s+PROCEDURE\s*""", re.IGNORECASE
  75 )
  76 PARSE_SHOW_CREATE_PROC = re.compile(
  77     r"""SHOW\s+CREATE\s+PROCEDURE\s+(\w+).(\w+)$""", re.IGNORECASE
  78 )
  79
  80 # Regular expression for INFORMATION_SCHEMA.ROUTINES
  81 PARSE_IS_ROUTINES = re.compile(
  82     r"""SELECT\s+(.*)\s+FROM\s+INFORMATION_SCHEMA.ROUTINES\s*(.*)""",
  83     re.IGNORECASE
  84 )
  85
  86 # Regular expression getting SET statements
  87 PARSE_SET = re.compile(
  88     r"""SET\s+(\w+)\s*=(.*)""", re.IGNORECASE
  89 )

このあたりのクエリーは無視せずに返してくれるらしい。

$ client/mysql -P 32275 -u admin -p --protocol=tcp
mysql> SELECT * FROM information_schema.routines;
+----------------------------+-----------------+----------------+----------------------+----------------+
| SPECIFIC_NAME              | ROUTINE_CATALOG | ROUTINE_SCHEMA | ROUTINE_NAME         | ROUTINE_TYPE   |
+----------------------------+-----------------+----------------+----------------------+----------------+
| statistics.node            | fabric          | statistics     | node                 | FABRIC_COMMAND |
| statistics.group           | fabric          | statistics     | group                | FABRIC_COMMAND |
| statistics.procedure       | fabric          | statistics     | procedure            | FABRIC_COMMAND |
| group.activate             | fabric          | group          | activate             | FABRIC_COMMAND |
| group.description          | fabric          | group          | description          | FABRIC_COMMAND |
| group.deactivate           | fabric          | group          | deactivate           | FABRIC_COMMAND |
| group.create               | fabric          | group          | create               | FABRIC_COMMAND |
| group.remove               | fabric          | group          | remove               | FABRIC_COMMAND |
| group.add                  | fabric          | group          | add                  | FABRIC_COMMAND |
| group.health               | fabric          | group          | health               | FABRIC_COMMAND |
| group.lookup_servers       | fabric          | group          | lookup_servers       | FABRIC_COMMAND |
| group.destroy              | fabric          | group          | destroy              | FABRIC_COMMAND |
| group.demote               | fabric          | group          | demote               | FABRIC_COMMAND |
| group.promote              | fabric          | group          | promote              | FABRIC_COMMAND |
| group.lookup_groups        | fabric          | group          | lookup_groups        | FABRIC_COMMAND |
| dump.fabric_nodes          | fabric          | dump           | fabric_nodes         | FABRIC_COMMAND |
| dump.shard_index           | fabric          | dump           | shard_index          | FABRIC_COMMAND |
| dump.sharding_information  | fabric          | dump           | sharding_information | FABRIC_COMMAND |
| dump.servers               | fabric          | dump           | servers              | FABRIC_COMMAND |
| dump.shard_tables          | fabric          | dump           | shard_tables         | FABRIC_COMMAND |
| dump.shard_maps            | fabric          | dump           | shard_maps           | FABRIC_COMMAND |
| manage.teardown            | fabric          | manage         | teardown             | FABRIC_COMMAND |
| manage.stop                | fabric          | manage         | stop                 | FABRIC_COMMAND |
| manage.setup               | fabric          | manage         | setup                | FABRIC_COMMAND |
| manage.ping                | fabric          | manage         | ping                 | FABRIC_COMMAND |
| manage.start               | fabric          | manage         | start                | FABRIC_COMMAND |
| manage.logging_level       | fabric          | manage         | logging_level        | FABRIC_COMMAND |
| server.set_mode            | fabric          | server         | set_mode             | FABRIC_COMMAND |
| server.clone               | fabric          | server         | clone                | FABRIC_COMMAND |
| server.list                | fabric          | server         | list                 | FABRIC_COMMAND |
| server.set_weight          | fabric          | server         | set_weight           | FABRIC_COMMAND |
| server.lookup_uuid         | fabric          | server         | lookup_uuid          | FABRIC_COMMAND |
| server.set_status          | fabric          | server         | set_status           | FABRIC_COMMAND |
| server.destroy             | fabric          | server         | destroy              | FABRIC_COMMAND |
| server.create              | fabric          | server         | create               | FABRIC_COMMAND |
| role.list                  | fabric          | role           | list                 | FABRIC_COMMAND |
| user.roles                 | fabric          | user           | roles                | FABRIC_COMMAND |
| user.usercommand           | fabric          | user           | usercommand          | FABRIC_COMMAND |
| user.list                  | fabric          | user           | list                 | FABRIC_COMMAND |
| user.add                   | fabric          | user           | add                  | FABRIC_COMMAND |
| user.password              | fabric          | user           | password             | FABRIC_COMMAND |
| user.delete                | fabric          | user           | delete               | FABRIC_COMMAND |
| threat.report_error        | fabric          | threat         | report_error         | FABRIC_COMMAND |
| threat.report_failure      | fabric          | threat         | report_failure       | FABRIC_COMMAND |
| provider.unregister        | fabric          | provider       | unregister           | FABRIC_COMMAND |
| provider.register          | fabric          | provider       | register             | FABRIC_COMMAND |
| provider.list              | fabric          | provider       | list                 | FABRIC_COMMAND |
| sharding.list_definitions  | fabric          | sharding       | list_definitions     | FABRIC_COMMAND |
| sharding.remove_definition | fabric          | sharding       | remove_definition    | FABRIC_COMMAND |
| sharding.move_shard        | fabric          | sharding       | move_shard           | FABRIC_COMMAND |
| sharding.disable_shard     | fabric          | sharding       | disable_shard        | FABRIC_COMMAND |
| sharding.remove_table      | fabric          | sharding       | remove_table         | FABRIC_COMMAND |
| sharding.split_shard       | fabric          | sharding       | split_shard          | FABRIC_COMMAND |
| sharding.create_definition | fabric          | sharding       | create_definition    | FABRIC_COMMAND |
| sharding.add_shard         | fabric          | sharding       | add_shard            | FABRIC_COMMAND |
| sharding.add_table         | fabric          | sharding       | add_table            | FABRIC_COMMAND |
| sharding.lookup_table      | fabric          | sharding       | lookup_table         | FABRIC_COMMAND |
| sharding.enable_shard      | fabric          | sharding       | enable_shard         | FABRIC_COMMAND |
| sharding.remove_shard      | fabric          | sharding       | remove_shard         | FABRIC_COMMAND |
| sharding.list_tables       | fabric          | sharding       | list_tables          | FABRIC_COMMAND |
| sharding.prune_shard       | fabric          | sharding       | prune_shard          | FABRIC_COMMAND |
| sharding.lookup_servers    | fabric          | sharding       | lookup_servers       | FABRIC_COMMAND |
| snapshot.destroy           | fabric          | snapshot       | destroy              | FABRIC_COMMAND |
| snapshot.create            | fabric          | snapshot       | create               | FABRIC_COMMAND |
| event.trigger              | fabric          | event          | trigger              | FABRIC_COMMAND |
| event.wait_for_procedures  | fabric          | event          | wait_for_procedures  | FABRIC_COMMAND |
+----------------------------+-----------------+----------------+----------------------+----------------+
66 rows in set (0.00 sec)


おお、ホントだ。
これ、バッキングストアやファームのMySQLサーバーの上ではなくFabricサーバーの上なので、たとえば他のテーブルとかは存在しない。というかたぶんクエリーを握りつぶしてる。

mysql> SELECT * FROM information_schema.tables;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW DATABASES;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES;
Query OK, 0 rows affected (0.00 sec)


SHOW CREATE PROCEDUREでプロシージャを見ると、

mysql> SHOW CREATE PROCEDURE group.health\G
*************************** 1. row ***************************
fabric_help: group health group_id

Check if any server within a group has failed and report health
information.

It returns a dictionary where keys are the servers' uuids and the
values are dictionaries which have the following keys:

* is_alive - whether it is possible to access the server or not.     *
status - PRIMARY, SECONDARY, SPARE or FAULTY.     * threads -
Information on the replication threads.
1 row in set (0.00 sec)


簡単なusageが表示される。

mysql> CALL group.health();
+--------------------------------------+-----+-----------------------------------------------+
| fabric_uuid                          | ttl | message                                       |
+--------------------------------------+-----+-----------------------------------------------+
| 5ca1ab1e-a007-feed-f00d-cab3fe13249e |   1 | execute() takes exactly 2 arguments (1 given) |
+--------------------------------------+-----+-----------------------------------------------+
1 row in set (0.01 sec)


ありゃ、引数の数が足りないらしい。や、それこそusageのSHOW CREATE PROCEDUREに書けよ。
取り敢えずたぶんFabric Groupが足りないんだと思うので渡してみようか。

mysql> CALL group.health('my_first_fabric');
+--------------------------------------+-----+---------+
| fabric_uuid                          | ttl | message |
+--------------------------------------+-----+---------+
| 5ca1ab1e-a007-feed-f00d-cab3fe13249e |   1 | NULL    |
+--------------------------------------+-----+---------+
1 row in set (0.14 sec)

+--------------------------------------+----------+-----------+----------------+-------------------+----------------+-----------------+----------+-----------+
| uuid                                 | is_alive | status    | is_not_running | is_not_configured | io_not_running | sql_not_running | io_error | sql_error |
+--------------------------------------+----------+-----------+----------------+-------------------+----------------+-----------------+----------+-----------+
| 47cf54df-63fc-11e4-942e-fa163e020fd0 |        1 | PRIMARY   |              0 |                 0 |              0 |               0 | False    | False     |
| 4b7036a9-63fc-11e4-942e-fa163e020fd0 |        1 | SECONDARY |              0 |                 0 |              0 |               0 | False    | False     |
+--------------------------------------+----------+-----------+----------------+-------------------+----------------+-----------------+----------+-----------+
2 rows in set (0.14 sec)

Empty set (0.14 sec)


うむ。
こんなこともできるんだ、ってことは認識したんだが、

ドキュメントはよ

MySQL Fabricつらい(AWS連携を試したかったけど挫折した編)

MySQL Utilities 1.5.3からMySQL FabricからAWSが叩けるよ! という噂があったので、1週間半くらいがんばっていたメモ。結論先に言うと、1.5.3では使えない気がする。
http://dev.mysql.com/doc/relnotes/mysql-utilities/en/wb-utils-news-1-5-3.html

この記事は2014/11/18現在の情報を元に書かれているので、「ちゃんと情報書いてあるんじゃん」という未来が来るかも知れません。というか来てくれないと困る。ドキュメントちゃんと書いてくれマジで。

まず、"MySQL Fabric AWS"や"MySQL Fabric EC2"でググっても何も出てこない。これがFabricクオリティ。
かろうじてOracle Open World 2014の "Elastic Scalability in MySQL Fabric with OpenStack" の資料に(教えてもらって)辿り着くものの、

Experiments with Amazon AWS
mysqlfabric provider register my_amazon
   AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
   eu­west­1 ­­provider_type=AMAZON
mysqlfabric machine create my_amazon
   ­­image image­id=ami­892fe1fe
   ­­flavor name=t2.micro

Note!
Not in MySQL Fabric 1.5
Will be on MySQL Lab
ノートの部分がリリースノートと矛盾しててなんかどうなのかって感じがするけれど、providerサブコマンドを使うらしい。

$ mysqlfabric help provider

Commands available in group 'provider' are:
    provider unregister provider_id  [--synchronous]
    provider register provider_id username password url  [--tenant=NONE] [--provider_type=OPENSTACK] [--default_image=NONE] [--default_flavor=NONE] [--extra=NONE] [--synchronous]
    provider list  [--provider_id=NONE]


うん、構文だけ教えてもらっても、肝心な「何を指定すればいいのか」が全くわからない。
そんな時はマニュアル嫁ですよねわかりますん。

http://dev.mysql.com/doc/mysql-utilities/1.5/en/fabric-util.html

(  д ) ゚  ゚ 載ってねええええええ! そもそもproviderコマンドが載ってねええええええ!

さすがのFabricクオリティなので手探りでproviderコマンドを叩いてみる。

$ mysqlfabric provider list
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

provider_id type username url tenant default_image default_flavor extra
----------- ---- -------- --- ------ ------------- -------------- -----


$ mysqlfabric provider register my_amazon AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY eu-west-1 --provider_type=AMAZON
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

ProviderError: Provider type (AMAZON) is not supported yet.




あ、ああ、うん、まだサポートされてないのね。うん。さすがFabricクオリティ。リリースノートなんて知ったこっちゃねぇぜ。

( ゚д゚) えっ

しかし更に待ち構える驚きのFabricクオリティ。
じゃあOPENSTACK(暗黙のデフォルト)ならサポートしてるのかっていうと

$ mysqlfabric provider register my_provider provider_user_name provider_passsword provider_url
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

ProviderError: Provider type (OPENSTACK) is not supported yet.



ちょwwwwwおまwwwwwデフォルトくらいサポートしろwwwww

なお、同じスライドの別のコマンドをコピペすると

$ mysqlfabric provider register my_stack mats xyzzy http://example.net:5000/v2.0/ my_project --provider_type=OPENSTACK
Password for admin:
Usage:  provider register provider_id username password url  [--tenant=NONE] [--provider_type=OPENSTACK] [--default_image=NONE] [--default_flavor=NONE] [--extra=NONE] [--synchronous]

mysqlfabric: error: Wrong number of parameters were provided for command 'provider register'.

ちょwwwww引数の数がおかしいってwwwww
まあほらこのスライド、Oracle Open World 2014のセッションのやつですからねー。。アテになんなくても仕方ないかしらFabricクオリティだし。

( ゚д゚) えっ

取り敢えず db tech showcase 2014 東京 に来ていた 日本一MySQL Fabricに詳しそうな人 を捕まえてみたところ、開発チームに問い合わせてくれました。

返ってきた返答は
You need to install the OpenStack libraries;
- python-novaclient for Compute node
- python-neutronclient for Network node
だそうで、


$ sudo pip install python-novaclient python-neutronclient
$ mysqlfabric manage stop
$ mysqlfabric manage start --daemonize

$ mysqlfabric provider register my_provider provider_user_name provider_passsword provider_url
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
dae961a0-4ec2-4e40-b554-88cb284a5a02        1       1      1

state success          when                                                   description
----- ------- ------------- -------------------------------------------------------------
    3       2   1.41628e+09 Triggered by <mysql.fabric.events.Event object at 0x260ebd0>.
    4       2   1.41628e+09                        Executing action (_register_provider).
    5       2   1.41628e+09                         Executed action (_register_provider).


$ mysqlfabric provider list
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

provider_id      type           username          url tenant default_image default_flavor extra
----------- --------- ------------------ ------------ ------ ------------- -------------- -----
my_provider OPENSTACK provider_user_name provider_url   None          None           None  None




取り敢えず、not supported yetだけは回避できた模様。

:(;゙゚'ω゚'): なんかドキュメント書けよ。。せめてエラーに出せよ。。

ここまでで1週間半。

しかしなんかどうも、

$ ll /usr/lib/python2.6/site-packages/mysql/fabric/providers
total 96
-rw-r--r-- 1 root root 10088 Oct 21 20:13 __init__.py
-rw-r--r-- 2 root root 12202 Oct 22 01:32 __init__.pyc
-rw-r--r-- 2 root root 12202 Oct 22 01:32 __init__.pyo
-rw-r--r-- 1 root root  2951 Oct 21 20:13 null.py
-rw-r--r-- 2 root root  3873 Oct 22 01:32 null.pyc
-rw-r--r-- 2 root root  3873 Oct 22 01:32 null.pyo
-rw-r--r-- 1 root root 14317 Oct 21 20:13 openstack.py
-rw-r--r-- 2 root root 13481 Oct 22 01:32 openstack.pyc
-rw-r--r-- 2 root root 13481 Oct 22 01:32 openstack.pyo


ここを見てる限り、OpenStack以外はまだ実装されてないんじゃないかなぁって気がする。。また今度。

2014年11月17日月曜日

topライクにperformance_schemaを眺めるらしいpstopとやら

pstop – a top-like program for MySQL (based on performance_schema) を見て試してみたメモ。

$ git clone https://github.com/sjmudd/pstop.git
$ cd pstop
$ ll
total 52
-rw-rw-r-- 1 ec2-user ec2-user  710 Nov 17 10:03 BUGS
-rw-rw-r-- 1 ec2-user ec2-user  738 Nov 17 10:03 keys.txt
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 lib
-rw-rw-r-- 1 ec2-user ec2-user 1315 Nov 17 10:03 LICENSE
-rw-rw-r-- 1 ec2-user ec2-user 3905 Nov 17 10:03 main.go
-rw-rw-r-- 1 ec2-user ec2-user 1986 Nov 17 10:03 NEW_FEATURES
drwxrwxr-x 6 ec2-user ec2-user 4096 Nov 17 10:03 performance_schema
-rw-rw-r-- 1 ec2-user ec2-user 1041 Nov 17 10:03 README.md
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 screen
-rw-rw-r-- 1 ec2-user ec2-user 5551 Nov 17 10:03 screen_samples.txt
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 state
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 version

おい、こいつgoだぞ…:(;゙゚'ω゚'): go弱な俺には起動の仕方すらわからない。。


$ go build
main.go:15:2: cannot find package "github.com/go-sql-driver/mysql" in any of:
        /usr/lib/golang/src/pkg/github.com/go-sql-driver/mysql (from $GOROOT)
        ($GOPATH not set)
main.go:16:2: cannot find package "github.com/nsf/termbox-go" in any of:
        /usr/lib/golang/src/pkg/github.com/nsf/termbox-go (from $GOROOT)
        ($GOPATH not set)
main.go:18:2: cannot find package "github.com/sjmudd/mysql_defaults_file" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/mysql_defaults_file (from $GOROOT)
        ($GOPATH not set)
main.go:19:2: cannot find package "github.com/sjmudd/pstop/lib" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/pstop/lib (from $GOROOT)
        ($GOPATH not set)
main.go:20:2: cannot find package "github.com/sjmudd/pstop/state" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/pstop/state (from $GOROOT)
        ($GOPATH not set)
main.go:21:2: cannot find package "github.com/sjmudd/pstop/version" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/pstop/version (from $GOROOT)
        ($GOPATH not set)

むーん。


$ export GOPATH=~/gopath
$ go get
go install: no install location for directory /home/ec2-user/pstop outside GOPATH
$ go build

よくわかってないけどこれでビルドできた。


$ ./pstop
2014/11/17 10:06:11 Could not load ini fileopen /home/ec2-user/.my.cnf: no such file or directory

どうも~/.my.cnfを読み込んで接続情報を取るらしい。


$ cat ~/.my.cnf
[client]
user= msandbox
password= msandbox
port= 5621
host= 127.0.0.1

$ ./pstop

起動できた。
TABキーを叩くとtable_io_waits_summary_by_table, table_io_waits_summary_by_table, file_summary_by_instance, table_lock_waits_summary_by_tableのタブを順繰りに表示してくれる。
"t"でp_sが収集し始めてからの累計と、最後に統計情報をリセット(たぶん、pstopの中で。いちども"z"を叩いてなければ、pstopが起動したときから)の累計切り替え。
"+", "-"で表示間隔の調整。"h"でヘルプだけど、ヘルプから戻るのはもう一度"h"。罠い。

今のところは荒削りにこれで機能が全てっぽい。innotopばりに発展していってくれるととてもうれしい。

Mroonga + MySQL 5.6とMariaDB 10.0だとスピードが結構違う?

あんま真面目に調べてないんですがメモ。


mysql> SELECT COUNT(*) FROM t1 WHERE MATCH(text_column) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.03 sec)

MariaDB [d1]> SELECT COUNT(*) FROM t1 WHERE MATCH(text_column) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.06 sec)

なんか倍くらい違う。


MariaDB [d1]> SHOW PROFILE cpu for query 1;
+-------------------------+----------+----------+------------+
| Status                  | Duration | CPU_user | CPU_system |
+-------------------------+----------+----------+------------+
| starting                | 0.000109 | 0.000000 |   0.000000 |
| checking permissions    | 0.000009 | 0.000000 |   0.000000 |
| Opening tables          | 0.000032 | 0.000000 |   0.000000 |
| After opening tables    | 0.000010 | 0.000000 |   0.000000 |
| System lock             | 0.000008 | 0.000000 |   0.000000 |
| Table lock              | 0.000008 | 0.000000 |   0.001000 |
| After table lock        | 0.000019 | 0.000000 |   0.000000 |
| init                    | 0.000045 | 0.000000 |   0.000000 |
| optimizing              | 0.000347 | 0.000000 |   0.000000 |
| statistics              | 0.000200 | 0.000000 |   0.000000 |
| preparing               | 0.000028 | 0.000000 |   0.000000 |
| FULLTEXT initialization | 0.040459 | 0.027995 |   0.014998 |
| executing               | 0.000024 | 0.000000 |   0.000000 |
| Sending data            | 0.020768 | 0.019997 |   0.000000 |
| end                     | 0.000011 | 0.000000 |   0.000000 |
| query end               | 0.000006 | 0.000000 |   0.000000 |
| closing tables          | 0.000839 | 0.000000 |   0.001000 |
| freeing items           | 0.000012 | 0.000000 |   0.000000 |
| updating status         | 0.000025 | 0.000000 |   0.000000 |
| cleaning up             | 0.000008 | 0.000000 |   0.000000 |
+-------------------------+----------+----------+------------+
20 rows in set (0.00 sec)

mysql> SHOW PROFILE cpu for query 2;
+----------------------+----------+----------+------------+
| Status               | Duration | CPU_user | CPU_system |
+----------------------+----------+----------+------------+
| starting             | 0.000057 | 0.000000 |   0.000000 |
| checking permissions | 0.000009 | 0.000000 |   0.000000 |
| Opening tables       | 0.000016 | 0.000000 |   0.000000 |
| init                 | 0.000018 | 0.000000 |   0.000000 |
| System lock          | 0.000007 | 0.000000 |   0.000000 |
| optimizing           | 0.031675 | 0.094985 |   0.011998 |
| executing            | 0.000027 | 0.000000 |   0.000000 |
| end                  | 0.000007 | 0.000000 |   0.000000 |
| query end            | 0.000005 | 0.000000 |   0.000000 |
| closing tables       | 0.000255 | 0.000000 |   0.000000 |
| freeing items        | 0.000344 | 0.002000 |   0.001000 |
| cleaning up          | 0.000005 | 0.000000 |   0.000000 |
+----------------------+----------+----------+------------+
12 rows in set, 1 warning (0.00 sec)

MariaDB 10.0はMySQL 5.5の時と同じような感じで、FULLTEXT initializationとSending Dataがほとんどの時間を占めてる。MySQL 5.6はoptimizingだけに集中している。これは、計上するステージが変わったからなのかなで済むけど、CPU_userを見るとMariaDBでは全て実時間以下のCPU使用時間に対し、MySQL 5.6のoptimizingステージは実時間の3倍くらいのCPU使用時間になっている。

MySQL 5.6、このへんなんかチューニングしてる? それともPROFILINGでやってるから取れてない(MySQL 5.6ではPROFILING非推奨でp_s使えってことになってるから)だけ?


【2014/11/17 16:10】
お客様の中に詳しい方がいらっしゃいました :)




mysql> SET SESSION mroonga_enable_optimization= OFF;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT COUNT(*) FROM t1 WHERE MATCH(text_column) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.04 sec)

確かにMySQL 5.6ではenable_optimization切ってもそんなに変わらない(= Mroongaのoptimizationでなく、MySQLのFTのレイヤーでごにょごにょしてMroongaのAPIを叩いてる?)


MariaDB [d1]> SET SESSION mroonga_enable_optimization= OFF;
Query OK, 0 rows affected (0.00 sec)

MariaDB [d1]> SELECT COUNT(*) FROM t1 WHERE MATCH(search_text) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.22 sec)

MariaDB 10.0はごりっと落ちる。

MySQL Fabricつらい(FABRIC_OPT_MODE = roはスレーブだけを見に行くわけじゃない)

mysqlコマンドラインクライアントにHA切り替え機能を実装する編 でいじったコマンドラインクライアントでちょこちょこいじってたらなんか不思議な挙動に辿り着く。


mysql> \F ro
Current FABRIC_OPT_DEFAULT_MODE is ro

mysql> SELECT @@port, @@read_only;
+--------+-------------+
| @@port | @@read_only |
+--------+-------------+
|  20886 |           0 |
+--------+-------------+
1 row in set (0.00 sec)

mysql> SELECT @@port, @@read_only;
+--------+-------------+
| @@port | @@read_only |
+--------+-------------+
|  20886 |           0 |
+--------+-------------+
1 row in set (0.00 sec)

mysql> SELECT @@port, @@read_only;
+--------+-------------+
| @@port | @@read_only |
+--------+-------------+
|  20887 |           1 |
+--------+-------------+
1 row in set (0.00 sec)

mysql> SELECT @@port, @@read_only;
+--------+-------------+
| @@port | @@read_only |
+--------+-------------+
|  20887 |           1 |
+--------+-------------+
1 row in set (0.00 sec)

mysql> SELECT @@port, @@read_only;
+--------+-------------+
| @@port | @@read_only |
+--------+-------------+
|  20886 |           0 |
+--------+-------------+
1 row in set (0.00 sec)


マスターとスレーブの間で分散してる。

mysql_options(&mysql, FABRIC_OPT_DEFAULT_MODE, "ro")を設定して10回mysql_queryを叩くプログラムで試してみると

$ ./a.out
Using Fabric for MYSQL connection
port: 20887, read_only: 1
port: 20886, read_only: 0
port: 20887, read_only: 1
port: 20887, read_only: 1
port: 20887, read_only: 1
port: 20887, read_only: 1
port: 20886, read_only: 0
port: 20887, read_only: 1
port: 20887, read_only: 1
port: 20887, read_only: 1


やっぱり分散してる。

$ mysqlfabric group lookup_servers my_first_fabric
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886   PRIMARY READ_WRITE    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 SECONDARY  READ_ONLY    1.0




Fabric内部のweightとやらは1:1になっているのでそうなのか(にしては、スレーブに偏りすぎてる気がするんだけど 母数が足りなかっただけで、1000回に増やしたらまあこんなもんだろうって感じだった)

$ ./a.out | sort | uniq -c
    528 port: 20886, read_only: 0
    472 port: 20887, read_only: 1
      1 Using Fabric for MYSQL connection

$ ./a.out | sort | uniq -c
    505 port: 20886, read_only: 0
    495 port: 20887, read_only: 1
      1 Using Fabric for MYSQL connection

$ ./a.out | sort | uniq -c
    499 port: 20886, read_only: 0
    501 port: 20887, read_only: 1
      1 Using Fabric for MYSQL connection


スレーブ側のweightを10000まで上げてみる。

$ mysqlfabric group lookup_servers my_first_fabric
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886   PRIMARY READ_WRITE    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 SECONDARY  READ_ONLY    1.0


$ mysqlfabric server set_weight 4b7036a9-63fc-11e4-942e-fa163e020fd0 10000
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
4ca2ef7d-3770-4beb-b557-55ea025b18bf        1       1      1

state success          when                                                   description
----- ------- ------------- -------------------------------------------------------------
    3       2   1.41619e+09 Triggered by <mysql.fabric.events.Event object at 0x1102610>.
    4       2   1.41619e+09                        Executing action (_set_server_weight).
    5       2   1.41619e+09                         Executed action (_set_server_weight).


$ mysqlfabric group lookup_servers my_first_fabric
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode  weight
------------------------------------ --------------- --------- ---------- -------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886   PRIMARY READ_WRITE     1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 SECONDARY  READ_ONLY 10000.0


$ ./a.out | sort | uniq -c
   1000 port: 20887, read_only: 1
      1 Using Fabric for MYSQL connection

$ ./a.out | sort | uniq -c
   1000 port: 20887, read_only: 1
      1 Using Fabric for MYSQL connection

$ ./a.out | sort | uniq -c
      1 port: 20886, read_only: 0
    999 port: 20887, read_only: 1
      1 Using Fabric for MYSQL connection


うん、よさげ。スレーブのweightを1のままでマスターのweight 0の方がいいのかしらんと思ったけど、weightを0にするのはダメみたい。

$ mysqlfabric server set_weight 47cf54df-63fc-11e4-942e-fa163e020fd0 0
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

ServerError: Cannot set the server's weight (0.0) to a value lower than or equal to 0.0


当たり前ながら、どれだけスレーブ側にweightを寄せてもFABRIC_OPT_MODE = rwなら必ずマスターを見に行く。

$ ./a.out | sort | uniq -c
   1000 port: 20886, read_only: 0
      1 Using Fabric for MYSQL connection


まだまだ検証要りそうだねぇ。。

2014年11月14日金曜日

MySQL Fabricつらい(mysqlコマンドラインクライアントにHA切り替え機能を実装する編)

Connector/CでマスタースレーブHA打ち分け編 の応用で、mysqlコマンドラインクライアントにHA打ち分け機能を追加してみました。

パッチはこんな感じ。MySQL 5.7.5-m15のmysqlコマンドラインクライアントベースだけど、labs版のConnector/Cが5.6をベースにしてるっぽい気配がして、おとなしく5.6のコマンドラインクライアントベースにすればよかったかも。。


*** client/mysql.cc.orig 2014-09-18 22:36:41.000000000 +0900
--- client/mysql.cc 2014-11-14 16:28:49.499593984 +0900
***************
*** 76,82 ****
  using std::min;
  using std::max;
  
! const char *VER= "14.14";
  
  /* Don't try to make a nice table if the data is too big */
  #define MAX_COLUMN_LENGTH      1024
--- 76,82 ----
  using std::min;
  using std::max;
  
! const char *VER= "14.14-fabric";
  
  /* Don't try to make a nice table if the data is too big */
  #define MAX_COLUMN_LENGTH      1024
***************
*** 188,193 ****
--- 188,195 ----
  static char delimiter[16]= DEFAULT_DELIMITER;
  static size_t delimiter_length= 1;
  unsigned short terminal_width= 80;
+ static char *opt_fabric_group= 0, *opt_fabric_user= 0;
+ static char *opt_fabric_password= 0;
  
  #if defined (_WIN32) && !defined (EMBEDDED_LIBRARY)
  static char *shared_memory_base_name=0;
***************
*** 255,261 ****
             com_notee(String *str, char*), com_charset(String *str,char*),
             com_prompt(String *str, char*), com_delimiter(String *str, char*),
       com_warnings(String *str, char*), com_nowarnings(String *str, char*),
!      com_resetconnection(String *str, char*);
  
  #ifdef USE_POPEN
  static int com_nopager(String *str, char*), com_pager(String *str, char*),
--- 257,264 ----
             com_notee(String *str, char*), com_charset(String *str,char*),
             com_prompt(String *str, char*), com_delimiter(String *str, char*),
       com_warnings(String *str, char*), com_nowarnings(String *str, char*),
!      com_resetconnection(String *str, char*),
!      com_fabric(String *str, char*);
  
  #ifdef USE_POPEN
  static int com_nopager(String *str, char*), com_pager(String *str, char*),
***************
*** 322,327 ****
--- 325,332 ----
  #ifdef USE_POPEN
    { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
  #endif
+   // Added by yoku0825.
+   { "fabric", 'F', com_fabric, 1, "Choose fabric opt group." },
    { "ego",    'G', com_ego,    0,
      "Send command to mysql server, display result vertically."},
    { "exit",   'q', com_quit,   0, "Exit mysql. Same as quit."},
***************
*** 1833,1838 ****
--- 1838,1855 ----
     "password sandbox mode.",
     &opt_connect_expired_password, &opt_connect_expired_password, 0, GET_BOOL,
     NO_ARG, 0, 0, 0, 0, 0, 0},
+   // Added by yoku0825.
+   {"fabric-group", 0,
+    "fabric group", &opt_fabric_group, &opt_fabric_group, 0,
+    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+   {"fabric-user", 0,
+    "Real user name which is used to connect through MySQL Fabric",
+    &opt_fabric_user, &opt_fabric_user, 0,
+    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+   {"fabric-password", 0,
+    "Real password which is used to connect through MySQL Fabric",
+    &opt_fabric_password, &opt_fabric_password, 0,
+    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
    { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
  };
  
***************
*** 4883,4888 ****
--- 4900,4909 ----
    }
  #endif
  
+   // Added by yoku0825.
+   if (opt_fabric_group)
+     mysql_options(&mysql, MYSQL_OPT_USE_FABRIC, NULL);
+ 
    if (!mysql_real_connect(&mysql, host, user, password,
                            database, opt_mysql_port, opt_mysql_unix_port,
                            connect_flag | CLIENT_MULTI_STATEMENTS))
***************
*** 4898,4903 ****
--- 4919,4932 ----
      return -1;     // Retryable
    }
  
+   // Added by yoku0825.
+   if (opt_fabric_group)
+   {
+     mysql_options(&mysql, FABRIC_OPT_GROUP, opt_fabric_group);
+     mysql_options4(&mysql, FABRIC_OPT_GROUP_CREDENTIALS, opt_fabric_user, opt_fabric_password);
+     mysql_options(&mysql, FABRIC_OPT_DEFAULT_MODE, "rw");
+   }
+ 
  #ifdef _WIN32
    /* Convert --execute buffer from UTF8MB4 to connection character set */
    if (!execute_buffer_conversion_done++ &&
***************
*** 5763,5765 ****
--- 5792,5812 ----
    }
    return error;
  }
+ 
+ // Added by yoku0825.
+ static int com_fabric(String *buffer __attribute__((unused)),
+                       char *line __attribute__((unused)))
+ {
+   char *ptr=strchr(line, ' ');
+   char *new_fabric_opt_mode= my_strdup(PSI_NOT_INSTRUMENTED,
+                                         ptr ? ptr + 1 : 0, MYF(MY_WME));
+ 
+   if (strcmp(new_fabric_opt_mode, "ro") || strcmp(new_fabric_opt_mode, "rw"))
+   {
+     mysql_options(&mysql, FABRIC_OPT_DEFAULT_MODE, new_fabric_opt_mode);
+     tee_fprintf(stdout, "Current FABRIC_OPT_DEFAULT_MODE is %s\n", new_fabric_opt_mode);
+   }
+   else
+     tee_fprintf(stdout, "Failed to change FABRIC_OPT_DEFAULT_MODE. Only 'ro' and 'rw' are permitted.\n", new_fabric_opt_mode);
+   return 0;
+ }

取り敢えずConnector/CからMySQL Fabricでマスタースレーブの打ち分けをするのに必要なパラメーターはグループ名, 本物のノードに接続するユーザー名とパスワードくらいなので、オプション(--fabric-group, --fabric-user, --fabric-password)で受け取るようにする。

あとはmysql_real_connectを読んでいるsql_real_connectの中で、--fabric-groupが渡されてたらMYSQL_OPT_USE_FABRICを押し込んで、接続後にFABRIC_OPT_GROUP, FABRIC_OPT_GROUP_CREDENTIALS, FABRIC_OPT_DEFAULT_MODEを押し込むという感じ。コンソールの中でrwとroを切り替えるためにcom_fabricも追加。

実際こんな感じ。


$ client/mysql -P32275 -h127.0.0.1 -uadmin -pxxxx --protocol=tcp ## FabricサーバーのMySQLプロトコルの口に、FABRICモードでなく接続
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 53
Server version: 1.5.3-fabric -- Fabricサーバーのバージョンが出てくる

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SELECT @@port; -- Fabricサーバーにつながってるだけなので何も出てこない
Query OK, 0 rows affected (0.00 sec)


$ client/mysql -P32275 -h127.0.0.1 -uadmin -pxxxx --protocol=tcp --fabric-group=my_first_fabric --fabric-user=msandbox --fabric-password=msandbox
mysql: [Warning] Using a password on the command line interface can be insecure.
Using Fabric for MYSQL connection -- FABRIC経由でつながると(今のところ必ず)これが出る。
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 167034
Server version: 5.6.21-log Source distribution -- MySQLサーバーのバージョン

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SELECT @@port; -- デフォルトはrw側
+--------+
| @@port |
+--------+
|  20886 |
+--------+
1 row in set (0.00 sec)

mysql> fabric ro -- "fabric {ro|rw}"で切り替えられるようにした
Current FABRIC_OPT_DEFAULT_MODE is ro

mysql> SELECT @@port; -- こっちがro側
+--------+
| @@port |
+--------+
|  20887 |
+--------+
1 row in set (0.00 sec)

mysql> \F rw -- "\Fはfabricのシノニム"
Current FABRIC_OPT_DEFAULT_MODE is rw

mysql> SELECT @@port;
+--------+
| @@port |
+--------+
|  20886 |
+--------+
1 row in set (0.00 sec)

前回 使ってたFABRIC_OPT_MODEの方は、mysql_optionsしたすぐ次のクエリー(ただし、トランザクションの途中であれば考慮してくれる)でしか有効でないらしいので、FABRIC_OPT_DEFAULT_modeを使うようにしてみた。

ちなみに、Fabricサーバー(mysqlfabric)のMySQLプロトコルの口にログインしただけの状態でstatusとか叩くとコマンドラインクライアントが落ちる。

MySQL Bugs: #74340: Crash on status query when connected to fabric

--fabric-groupや--fabric-user, --fabric-passwordが間違ってて上手くバックエンドにつなぎにいけない場合も(クライアントがコア吐いて)これまた落ちるんだけど、取り敢えず仕様ということで。。

こんな感じで誰か DBD::mysql にMySQL Fabricをポートしてくだしあ。


【2014/11/17 11:33】
FABRIC_OPT_MODEの動きについてこっちに追記。
http://yoku0825.blogspot.jp/2014/11/mysql-fabricfabricoptmode-ro.html

2014年11月10日月曜日

MySQL Fabricつらい(Connector/CでマスタースレーブHA打ち分け編)

わたしのC力は低いので、サンプルプログラムはへちょいです。

MySQL Fabric対応のコネクターというともろもろありますが、俺が使えるコネクター(つまりDBD::mysqlのことだ!)がないので、Connector/Cにしました。labsです。

参考にしたサイトはこちら。
Using Connector/C with Fabric (MySQL Connectors C)
Oracle公式のConnector/C(チーム)のブログなんてあったんだね! というかこの1記事しかn<censored>

まずは labs からConnector/C 6.2.0 を取ってきてmake。なんかlabsからダウンロードする時にちょくちょく接続がリセットされるんだけどこれ俺だけ?


$ wget http://downloads.mysql.com/snapshots/pb/mysql-connector-c-6.2.0-labs/mysql-connector-c-6.2.0-labs-src.tar.gz
$ tar xzf mysql-connector-c-6.2.0-labs-src.tar.gz
$ cd mysql-connector-c-6.2.0-labs-src
$ cmake -DCMAKE_INSTALL_PREFIX=/home/mysql/connector .
$ make
$ make install

インストール場所だけ他とぶつからないように変えました。


$ vim test.cc
  1 #include <iostream>
  2 #include "mysql.h"
  3
  4
  5 int main()
  6 {
  7   MYSQL mysql;
  8
  9   mysql_init(&mysql);
 10   mysql_options(&mysql, MYSQL_OPT_USE_FABRIC, NULL);
 11
 12   if (!(mysql_real_connect(&mysql, "127.0.0.1", "admin", "xxxx", NULL,
 13                            32275, NULL, 0)))
 14     std::cout << mysql_error(&mysql) << std::endl;
 15   else
 16   {
 17     mysql_options(&mysql, FABRIC_OPT_GROUP, "my_first_fabric");
 18     mysql_options4(&mysql, FABRIC_OPT_GROUP_CREDENTIALS, "msandbox", "msandbox");
 19     mysql_options(&mysql, FABRIC_OPT_MODE, "ro");
 20     mysql_query(&mysql, "SELECT @@port");
 21     MYSQL_RES *res_from_ro= mysql_store_result(&mysql);
 22     MYSQL_ROW row_from_ro = mysql_fetch_row(res_from_ro);
 23     std::cout << "Fabric ro port: " << row_from_ro[0] << std::endl;
 24
 25     mysql_options(&mysql, FABRIC_OPT_MODE, "rw");
 26     mysql_query(&mysql, "SELECT @@port");
 27     MYSQL_RES *res_from_rw= mysql_store_result(&mysql);
 28     MYSQL_ROW row_from_rw = mysql_fetch_row(res_from_rw);
 29     std::cout << "Fabric rw port: " << row_from_rw[0] << std::endl;
 30   }
 31
 32 }


Fabricサーバーに接続して、roプロパティとrwプロパティでそれぞれポート番号を調べるだけのヤーツ(mysql_free_resultすらしない手抜きっぷり)

$ export LD_LIBRARY_PATH=/home/mysql/connector/lib
$ gcc -I/home/mysql/connector/include -L/home/mysql/connector/lib -lmysqlclient -g test.cc
/usr/bin/ld: cannot find -lmysqlclient
collect2: ld returned 1 exit status

$ ll /home/mysql/connector/lib
total 28256
-rw-r--r-- 1 mysql mysql 19390738 Nov 10 19:27 libmysqlconc62.a
lrwxrwxrwx 1 mysql mysql       24 Nov 10 19:27 libmysqlconc62.so -> libmysqlconc62.so.19.0.0
-rw-r--r-- 1 mysql mysql  9537551 Nov 10 19:27 libmysqlconc62.so.19.0.0
lrwxrwxrwx 1 mysql mysql       16 Nov 10 19:27 libmysqlconc.a -> libmysqlconc62.a
lrwxrwxrwx 1 mysql mysql       17 Nov 10 19:27 libmysqlconc.so -> libmysqlconc62.so
lrwxrwxrwx 1 mysql mysql       24 Nov 10 19:27 libmysqlconc.so.19 -> libmysqlconc62.so.19.0.0

$ gcc -I/home/mysql/connector/include -L/home/mysql/connector/lib -lmysqlconc -g test.cc

$ ldd a.out
        linux-vdso.so.1 =>  (0x00007fff415ff000)
        libmysqlconc.so.19 => /home/mysql/connector/lib/libmysqlconc.so.19 (0x00007f05dcc37000)
        libc.so.6 => /lib64/libc.so.6 (0x00000039a8000000)
        libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x000000321aa00000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00000039a8400000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00000039a7c00000)
        librt.so.1 => /lib64/librt.so.1 (0x00000039a9000000)
        libm.so.6 => /lib64/libm.so.6 (0x00000039a8c00000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x000000321a600000)
        /lib64/ld-linux-x86-64.so.2 (0x00000039a7800000)


むむ、なんかlibmysqlclientからlibmysqlconcに名前が変わってた。

$ mysqlfabric group lookup_servers my_first_fabric
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886   PRIMARY READ_WRITE    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 SECONDARY  READ_ONLY    1.0


$ ./a.out
Using Fabric for MYSQL connection
Fabric ro port: 20887
Fabric rw port: 20886
ちゃんとmysql_optionsで"ro"を指定したのはSECONDARYに、"rw"を指定したのはPRIMARYに行ってる。
$ ./sandboxes/rsandbox_5_6_21/master/stop

$ mysqlfabric group lookup_servers my_first_fabric
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address  status       mode weight
------------------------------------ --------------- ------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886  FAULTY READ_WRITE    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 PRIMARY READ_WRITE    1.0


$ ./a.out
Using Fabric for MYSQL connection
Fabric ro port: 20887
Fabric rw port: 20887


マスターを停止して(MySQL Fabric上での)切り替わりが起こると、roもrwも両方新しいPRIMARYの方に向いた。
$ ./sandboxes/rsandbox_5_6_21/master/start
. sandbox server started

$ mysqlfabric server set_status 47cf54df-63fc-11e4-942e-fa163e020fd0 SPARE
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
79142ce2-bc61-4be9-8837-3b6d782344c0        1       1      1

state success          when                                                   description
----- ------- ------------- -------------------------------------------------------------
    3       2   1.41562e+09 Triggered by <mysql .fabric.events.event="" 0x22ae450="" at="" object="">.
    4       2   1.41562e+09                        Executing action (_set_server_status).
    5       2   1.41562e+09                         Executed action (_set_server_status).

$ mysqlfabric group lookup_servers my_first_fabric
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address  status       mode weight
------------------------------------ --------------- ------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886   SPARE    OFFLINE    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 PRIMARY READ_WRITE    1.0


$ ./a.out
Using Fabric for MYSQL connection
Fabric ro port: 20887
Fabric rw port: 20887


起動してSPARE状態に戻してやると、mode: OFFLINEになってた。相変わらずConnector/Cからは両方PRIMARYを向いている。
$ mysqlfabric server set_status 47cf54df-63fc-11e4-942e-fa163e020fd0 SECONDARY
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
b946e459-f80e-4a9f-8f99-a43174de4832        1       1      1

state success          when                                                   description
----- ------- ------------- -------------------------------------------------------------
    3       2   1.41562e+09 Triggered by <mysql .fabric.events.event="" 0x22ae450="" at="" object="">.
    4       2   1.41562e+09                        Executing action (_set_server_status).
    5       2   1.41562e+09                         Executed action (_set_server_status).


$ mysqlfabric group lookup_servers my_first_fabric
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886 SECONDARY  READ_ONLY    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887   PRIMARY READ_WRITE    1.0


$ ./a.out
Using Fabric for MYSQL connection
Fabric ro port: 20887
Fabric rw port: 20887

んん、status: SECONDARYに戻してもConnector/Cは戻ってない。コネクター側でキャッシュを持つとか聞いてるやつだろうか。
とはいえ、3分くらい待ってみても、Fabricサーバーを再起動しても、test.ccをコンパイルしなおしてもずっと20887に寄ったままだった。なんじゃこれ。

ちなみに、DBD::mysqlなんかはlibmysqlclientをそのまま使ってるのでDBI->connectのところでFabricのオプションを突っ込むことになるだろうし(それがmysql_optionsにマッピングされているはず), Connector/JやConnector/PythonはそもそもMySQLプロトコルじゃなくてXML-RPCでしゃべっているのでこれとは違ったアレになるはず。ちょっと微妙かな。。