MySQLユーザ会会 in 長野 を開催しました

5/13(土)に「MySQLユーザ会会 in 長野」を開催しました。

nseg.connpass.com

「会」が2つあるのは仕様です。「MySQLユーザ会」の会合で「MySQLユーザ会会」です。

開催経緯はこんなかんじです。

NSEG(長野ソフトウェア技術者グループ)というのがあって、毎月何かしらやってるんですが、その87回目イベントとして開催しました。


昼前に長野駅について、駅ビルのGitHubにめっちゃコミットしてるMen↓の近くでボーっとしてたら

@RKajiyama さんに遭遇したので、一緒に昼飯を食うことにしました。

他県から長野に来た人とお昼を食べるならやっぱり蕎麦かな…と思ったのですが、あまり長野駅近辺の蕎麦屋を知らなかったので、前に美味しいという噂を聞いたことがあった「ぼっち」という蕎麦屋に行きました。 満席でキューに並んでる途中で @mamy1326 さんも合流。 席につくまでも席についてからも結構待たされたのですが、美味しかったです。

主催者なのに遅れ気味に会場についたら、ほとんどの人はもう来ていました。 ドタキャンなし無断欠席なしで、さすが地方開催イベント(というか事前登録無しで来た人もいた)。発表者含めて参加者18名でした。

ツイートを Togetterにまとめました。


MySQLとは - @sakaik

RDBMSとは? MySQLとは? MySQLのバージョン、歴史等について話していただきました。

AWSでサクッとMySQL環境を立ち上げるデモもありました。

MySQL 8 - @RKajiyama

MySQLの中の人として、MySQLの次バージョンである MySQL 8 について話していただきました。

CTEとかWindow関数とかが実装されて、RDBMSとしてどんどんマトモになっていきますね。MySQLなのに。

MySQLの14年物のバグ #199 が直るらしいです。

yoku0825を支える技術 - @yoku0825

yoku0825とは「GMOペパボの福利厚生」で「はてなの外部API」で「bot」だそうです。

GTID, Multi Thread Slave, Defer Table Index, HANDLER ステートメント, PMM 等についての話がありました。

Bug #2 の紹介がありました。 Closed になってるのに 8.0 でバグが再発したらしいです。

Transactd PHP ORM - @bizstationcorp

TransactdはMySQLのHanderインタフェースを直接叩くことができるAPIを提供するプラグインで、Transactd PHP ORMはそのAPIを使用したPHPのORMです。

既存のORMのAPIとほぼ同じで、省メモリで高速とのこと。

Transactd も PHP ORM もオープンソースでGitHubにあります。

RubyにもTransactd APIがあるので、頑張ればORMを作れそうです。

MySQLの文字コード事情 - @tmtms

2月のMySQL Casualの発表の使いまわしです。MySQL 8 の話も少し追加しました。


懇親会は「酔来処」14名参加でした。

参加者のみなさま、発表者のみなさま、ありがとうございました。

1年に1回くらいは長野で開催したいですね。 次は松本でやるのもいいかなぁと思ったりしてます。自分は土地勘ないので誰か仕切ってくれる人がいれば…。

ZIP中のファイル名の文字化け(Ruby編)

tmtms.hatenablog.com

という記事を書きましたが、今回はRubyでZIPファイルを作る時の話を。

RubyでZIPファイルを作るには、rubyzip というライブラリを使います。

% gem install rubyzip

次のようにしてZIPにファイルを追加できます。

require 'zip'

Zip::File.open('hoge.zip', Zip::File::CREATE) do |zip|
  zip.add('いろはにほへと.txt', '/path/to/いろはにほへと.txt')
end

Zip::File#add の第一引数はZIP内に記録されるファイル名、第二引数は実際のファイルのパスです。この二つのファイル名は同じである必要はありません。

ただし、この場合はUTF-8フラグがセットされません。つまりWindowsの標準機能で開くと文字化けしてしまいます。

UTF-8フラグを立てるには次のように Zip.unicode_names = true を指定します。

require 'zip'

Zip.unicode_names = true
Zip::File.open('hoge.zip', Zip::File::CREATE) do |zip|
  zip.add('いろはにほへと.txt', '/path/to/いろはにほへと.txt')
end

このように作成されたZIPファイルは最近のWindowsで(それとたぶんMacでも)開くことができます。

ただし、パッチがあたってない Windows 7 や、もう世界のどこでも動いていないはずの古いバージョンの Windows は UTF-8 に対応していないようなので、文字化けしてしまいます。

その場合はUTF-8フラグを立てずにファイル名をシフトJIS(CP932)に変換してやればいいです。

require 'zip'

Zip::File.open('hoge.zip', Zip::File::CREATE) do |zip|
  zip.add('いろはにほへと.txt'.encode('cp932'), '/path/to/いろはにほへと.txt')
end

なお、これはMacでは文字化けします(たぶん)。Macでも文字化けしないようです。

あらかじめZIPファイルを渡す相手の環境がわかっているのであれば、それに合わせて変更するのもいいでしょう。

Webアプリであれば User-Agent を見て動きを変えるのもいいかも知れませんね。

ZIP中のファイル名の文字化け

こんな記事がありました。

gihyo.jp

これはMacユーザー用の書籍の宣伝記事らしいのですが、「Windowsを使ってる人のためにMac側がひと手間かけてあげよう」なんて殊勝なことをマカーが言うとは時代も変わったもんです。([追記] はてブのコメントを見たらさすがマカーという意見が並んでて安心しました)

まあ私はWindowsユーザーでもMacユーザーでもないのでどうでもいいのですが、文字化けなネタなので食いついてみます。

記事中に、「付物出稿.zip」というファイルを開いた時の画像が載ってます。

文字の並びからして、UTF-8文字列をシフトJIS(CP932)とみなして表示してしまった文字列でしょう(「繧ォ繝上y繝シ繝輔か繝ォ繧ソ繧・」の元の文字は「カバーフォルダ」で、「蟶ッ繝輔か繝ォ繧ソ繧・」は「帯フォルダ」)。

つまり、Macはファイル名をUTF-8でZIPに書き込み、WindowsはそれをシフトJIS(CP932)と思って開いているということです。

ZIPフォーマット中のファイル名は文字コード情報を持っていません。なので、MacとWindowsのようにファイル名の文字コードが異なるシステム同士でZIPをやりとりすると、このように文字化けしてしまうのです。

手元にMacが無いので確かめてはいないのですが、どうやらWindowsで作ったZIPファイルをMacで開く分には問題ないらしいです。 Macのファイル名の文字コードは昔はシフトJISだったようなので、互換性を考慮してUTF-8で文字化けする場合はシフトJISとみなして開いてくれるのかもしれません。

2007年にZIPフォーマットは拡張されてファイル名がUTF-8であることを示すフラグが追加されました。 ということは、現代においてはZIP作成時にファイル名の文字コードをUTF-8にしてこのフラグを立てれば文字化けはしないはずなのです。

実はWindowsはZIPを作成するときにはCP932でファイル名を書くのですが、ZIPを開くときにはちゃんとこのフラグを見てくれます(Windows 8 あたりから)。 なのにMacで作られたZIPのUTF-8のファイル名が文字化けするということは、MacのZIPはこのフラグを立ててないということなのでしょう。

WindowsがZIP作成時にUTF-8にしないのはおそらく過去の互換性を重視しているからでしょう。 MacはせっかくUTF-8なんだからフラグを立ててくれればいいんですけどね。惜しい。

まあ、それぞれ自分のロケールに従った文字コードでZIPを書いてるという点ではお互い様です。

ちなみにUbuntuはZIP作成時にちゃんとUTF-8フラグを立ててUTF-8で作ります。なのでWindowsでも問題なく開けますし、Macはこのフラグを見てるかどうかは知りませんが、文字コードがUTF-8だからおそらく問題なく開けるでしょう。

UbuntuでのZIP展開時には、UTF-8フラグが立っていたらそのままUTF-8のファイル名として扱い、フラグが立っていない場合は、現在のロケール(LC_ALL, LANG環境変数等)を見て文字コード変換を行います。 jaロケールの場合はCP932からUTF-8への変換を行います。

ですので、UbuntuとWindowsの間では問題になることはないでしょう。

ということでMacをやめてUbuntu使えば解決ですね!

大江戸Ruby会議06

大江戸Ruby会議06 #oedo06 に行ってきたので雑感など。

会場

@sora_h さん生誕20周年記念ということで、会場も「ソラシティカンファレンスセンター」という場所でした。

Docker時代の分散RSpec環境の作り方

speakerdeck.com

RSpecの実行に時間が掛かってたのをAmazon ECSを使って分散実行することで時間とコストが削減されたという話。

RSpec の結果を任意のストレージに出力できる rspec-storage gem は便利そう。

自分の今のプロジェクトも自動テストの時間がどんどん伸びているのでどうにかしたいと思ってます。

しかし、開発にAWSを使える環境はうらやましい。

Text Editing in Ruby

github.com

Ruby製のEmacsライクなテキストエディタを作ったという話でした。名前が中二病っぽい。

冒頭で何のエディタを使っているのか挙手によるアンケートを取ったのですが、会場のほとんどの人がEmacs使いでした(ただし @shugomaeda さんの観測による)。

プレゼン自体このエディタ上のツール(textbringer-presentation)で動いていたとのことでエディタの完成度が伺えます。

Ruby考古学II 1993-1997

まつもとさんがRubyを作るきっかけになった @keiju_ishitsukaさんが、メールとかメモとか記憶を頼りに1993年から1997年当時のRubyについて語りました。

自分がRubyを使い始めたのがちょうど1997年 Ruby 1.1b の頃だったので、それまでの歴史がわかったのは良かったです。

まつもとさんがRubyを作り始めたころはPerlを使ってなかったとか、rescue のスペルミスがかなり長い間残っていたとか、面白かったです。

多相型、推論、Ruby

speakerdeck.com

型を書かずにうまくやろうとすると、現在のRubyをいろいろいじらないといけなくて、それは許容できないだろう。最終的には「型は絶対書きたくない」と言っているまつもとさんをどうにかしないといけないのでは。みたいな話でした。

フルタイムコミッター大戦

コミッター4人が登壇して問題を出し合う早押しクイズ。ぐだぐだでした。

それよりも早押しシステムの方が気になりました。Nintendo Switch のコントローラが Bluetooth なのは知ってたのですが、それをどうやってウェブで制御しているのかと。

懇親会でシステム担当の @yancya さんに聞いてみたら、HTML5 に Gamepad API があって結構簡単にできるとのことでした。Gamepad API の存在は知らなかったので、今度調べてみます。

最初に少しトラブってたのは、会場に Bluetooth デバイスがたくさんあったせいでコントローラとのペアリングがうまくいかなかったそうで、会場の外でペアリングしてうまくいったとのことでした。

upec.jp

如何にして若き天才コミッタは生まれるのか

@rosylillyさんによる、@sora_hさんの本人も覚えてないような過去の話と、後進をどのように育成するかという話。 私もそうなんですが、自分がそうだっただけに若者は勝手に育つと思ってるんだけど、それじゃあダメだと。

Keynote

speakerdeck.com

@sora_hさんのスキルセットがものすごい。うちの上の子と同じ年(誕生日もほぼ同じ)なんだけど、技術的には自分はとっくに追い越されてる。

「インターネットやりたい」って何を言ってるのかと思ったら、ISPのようなことをやりたいとのことで、もうAS番号も取ったとのこと。

「めざせフルスタック」と言ってましたが、もう十分フルスタックエンジニア。

その他

  • Asakusa.rbには行きたいと思いつつ行けてません。ずっと前に一度だけ行ったことありますが、またいつか行ってみたいです。

  • パンダさんかわいかった。

  • 金をケチって高速バスを使ったんだけど、日帰りで往復とも高速バスはさすがに疲れました。もう若くないし…。

Ruby製のEmacsライクなテキストエディタTextbringer

大江戸Ruby会議06で前田さんがRubyでEmacsライクなエディタTextbringerを作ったという発表をしていました。

最初はEmacs上の何かでプレゼンしてたと思ってたのですが、なんとTextbringerでプレゼンしてたとのこと。完成度高い。

Textbringer上で動くMUAを作るのが目標とのことで、Emacs使いで、MUAを作ることがRubyを始めたきっかけの私としては非常に共感しました。

ということで試してみました。

インストール

% gem install textbringer

起動

% textbringer

ファイル読み込みとか保存とかカーソル移動とか基本的な操作は普通にEmacsのキーバインドで使えます。 機能的にはまだまだ足りないようですが、Rubyで拡張できるということで夢が膨らみますね。

おまけ

最初は日本語が入力できなかったのですが、libncursesw5-dev deb をインストールしたらうまくいきました(Ubuntuの場合)。

Sequelのトランザクション内でタイムアウトするとCOMMITされてしまう

ちょっと前にハマったのでメモ。

Sequelでトランザクションを使う時は次のように transaction メソッドにブロックを渡します。

require 'sequel'
require 'logger'

db = Sequel.connect('mysql2://user:passwd@localhost/test')
db.loggers = [Logger.new($stdout)]
db.transaction do
  db[:test].insert(id: 123)
end
I, [2017-03-12T22:34:51.946849 #27932]  INFO -- : (0.000119s) SET @@wait_timeout = 2147483
I, [2017-03-12T22:34:51.947047 #27932]  INFO -- : (0.000133s) SET SQL_AUTO_IS_NULL=0
I, [2017-03-12T22:34:51.947182 #27932]  INFO -- : (0.000039s) BEGIN
I, [2017-03-12T22:34:51.947517 #27932]  INFO -- : (0.000149s) INSERT INTO `test` (`id`) VALUES (123)
I, [2017-03-12T22:34:51.955289 #27932]  INFO -- : (0.007675s) COMMIT

ブロックを抜ける時に自動的にCOMMITされます。

ブロック内で例外が発生した場合は、ROLLBACKされます。

require 'sequel'
require 'logger'

db = Sequel.connect('mysql2://user:passwd@localhost/test')
db.loggers = [Logger.new($stdout)]
db.transaction do
  db[:test].insert(id: 123)
  raise 'hoge'
end
I, [2017-03-12T22:36:10.121107 #27942]  INFO -- : (0.000191s) SET @@wait_timeout = 2147483
I, [2017-03-12T22:36:10.121317 #27942]  INFO -- : (0.000110s) SET SQL_AUTO_IS_NULL=0
I, [2017-03-12T22:36:10.121454 #27942]  INFO -- : (0.000043s) BEGIN
I, [2017-03-12T22:36:10.121834 #27942]  INFO -- : (0.000149s) INSERT INTO `test` (`id`) VALUES (123)
I, [2017-03-12T22:36:10.152997 #27942]  INFO -- : (0.031052s) ROLLBACK
test-2.rb:8:in `block in <main>': hoge (RuntimeError)

ですが、Timeout を使っていてトランザクション中でタイムアウトが発生した場合は

require 'timeout'
require 'sequel'
require 'logger'

db = Sequel.connect('mysql2://user:passwd@localhost/test')
db.loggers = [Logger.new($stdout)]
Timeout.timeout(1) do
  db.transaction do
    db[:test].insert(id: 123)
    sleep 2
  end
end
I, [2017-03-12T22:37:57.026693 #27953]  INFO -- : (0.000084s) SET @@wait_timeout = 2147483
I, [2017-03-12T22:37:57.026803 #27953]  INFO -- : (0.000047s) SET SQL_AUTO_IS_NULL=0
I, [2017-03-12T22:37:57.026924 #27953]  INFO -- : (0.000041s) BEGIN
I, [2017-03-12T22:37:57.027223 #27953]  INFO -- : (0.000119s) INSERT INTO `test` (`id`) VALUES (123)
I, [2017-03-12T22:37:58.034292 #27953]  INFO -- : (0.007787s) COMMIT
test-3.rb:10:in `sleep': execution expired (Timeout::Error)

なんとCOMMITされてしまいます!こわい!

ちなみに、タイムアウト時に発生する例外を指定した場合はちゃんとROLLBACKされます。

require 'timeout'
require 'sequel'
require 'logger'

db = Sequel.connect('mysql2://user:passwd@localhost/test')
db.loggers = [Logger.new($stdout)]
Timeout.timeout(1, Timeout::Error) do
  db.transaction do
    db[:test].insert(id: 123)
    sleep 2
  end
end
I, [2017-03-12T22:40:50.531245 #27979]  INFO -- : (0.000166s) SET @@wait_timeout = 2147483
I, [2017-03-12T22:40:50.531455 #27979]  INFO -- : (0.000106s) SET SQL_AUTO_IS_NULL=0
I, [2017-03-12T22:40:50.531820 #27979]  INFO -- : (0.000222s) BEGIN
I, [2017-03-12T22:40:50.532302 #27979]  INFO -- : (0.000240s) INSERT INTO `test` (`id`) VALUES (123)
I, [2017-03-12T22:40:51.540196 #27979]  INFO -- : (0.009461s) ROLLBACK
test-4.rb:10:in `sleep': execution expired (Timeout::Error)

さっきの例と発生する例外も発生箇所も同じなのに、Sequelの振る舞いが異なっています。

これは、Timeout.timeout の引数で例外を指定しない場合は、実際には例外ではなく throw - catch が使われているためです。 この辺の仕組みは前に書きました。

tmtms.hatenablog.com

Sequel の transaction はブロックを例外で抜けた時はROLLBACKし、そうでないとCOMMITするようになっているため、例外ではなく throw で抜けた場合には COMMIT されてしまうのでした。

例外だけでなく throw でもブロックを抜け得ることを考慮していない Sequel の問題なような気もするのですが、とりあえず Sequel を使用している場合は Timeout を使用しない方が無難なようです。

追記

Sequelの開発者的には、SequelのバグではなくてTimeoutのバグって見解のようです。

github.com

ThinkPad T460s Ubuntu でトラックパッドを無効化

5ヶ月ほど ThinkPad T460s を Ubuntu(Xubuntu) で使ってます。

ちょっと前に さよならMac | めがねをかけるんだ という ThinkPad をdisった記事が話題になりましたが、自分はあんまり不満はありません。 ノートPCでLinuxを使う時の鬼門だった無線LANやサスペンドもまったく問題ありません。 当然トラックパッドは無効にしています。

唯一の不満がこのトラックパッド無効化です。

トラックパッドを無効にしようとBIOSで無効に設定しても無効になりません。

しょうがないので、Xubuntu の設定の「マウスとタッチパッド」でタッチパッドの設定を無効化します。

f:id:tmtms:20170218135347p:plain

これでトラックパッドが無効になってめでたしめでたし。

…と思ったのですが、しばらく使ってると、時折トラックポイントのボタンをクリックしても効かないことがあります。

どうやらトラックパッドに触れているとボタンのクリックイベントを取りこぼすようでした。 トラックパッドを無効にしていたので、それが原因だとなかなか気がつきませんでした。

トラックパッドに二本以上の指が触れているとかなりの確率でボタンのクリックイベントを取りこぼしてしまいます。

どうやら psmouse ドライバのインストール時に proto=bare を指定するとトラックパッドを完全に無効化できるようです。 というより proto=bare ではトラックパッドを扱えないというだけな気がします。

# modprobe -r psmouse
# modprobe psmouse proto=bare

これでトラックパッドに触れていてもボタンクリックを取りこぼすことはなくなりました。

…が、もうひとつ問題が。

proto=bare ではトラックポイントの感度と速度の調整ができません。

proto=bare を指定しない時は、次のようにして感度と速度を調整できました。

# echo 200 > /sys/devices/platform/i8042/serio*/serio*/sensitivity
# echo 200 > /sys/devices/platform/i8042/serio*/serio*/speed

proto=bare で psmouse をインストールした時は、この sensitivity, speed ファイル自体ありません。

色々試行錯誤したところ、proto=bare をつけずに psmouse をインストールして、感度と速度を調整した後に proto=bare をつけて psmouse を再インストールすると感度と速度が保持されたままになることがわかりました。

とりあえず、簡単なシェルスクリプトを作って、電源ON時やサスペンドからの復帰時に走らせてます。

#!/bin/bash
DIR=/sys/devices/platform/i8042
SPEED=200
SENSITIVITY=200

# echo が刺さることがあるので1秒でタイムアウトさせる
sub() {
    (echo $1 > $2) &
    pid=$!
    (sleep 1; kill $pid) &
    pid2=$!
    wait $pid
    kill $pid2
}

modprobe -r psmouse
modprobe psmouse proto=any
while :; do
    sleep 0.3
    speed=$DIR/serio*/serio*/speed
    test -f $speed || continue
    sens=$(dirname $speed)/sensitivity
    test -f $sens || continue
    sub $SPEED $speed
    test "$(cat $speed)" -eq $SPEED || continue
    sub $SENSITIVITY $sens
    test "$(cat $sens)" -eq $SENSITIVITY || continue
    break
done
modprobe -r psmouse
modprobe psmouse proto=bare