読者です 読者をやめる 読者になる 読者になる

Studio3104::BLOG.new

uninitialized constant Studio3104 (NameError)

Sinatra で Controller を分割したくなったら Rack::URLMap を使うとよさそう

こういう感じのアプリケーションがあったとします。

で、この例だとまったくファットじゃないんだけど、Controller がファットになってきてしまった場合に、いい感じに分割したくなることもありますよね。
ということでとりあえずこうしてみた。

各クラスが Sinatra::Base を継承するではなくて、Base を設けてそれを継承するようにしているのは、各クラス内でそれぞれ before を書きたくなかったからです。
が、このように書いてしまうと、リクエストパスによっては 1 回のリクエスト中に before 内の処理が複数回実行されてしまいます。(実際に before に適当に p 123 とか書いて起動して、アクセスしてみるとわかる)

それだと最悪なので、最終的にこうした。

ルーティングを config.ru に書きたくなかったので、定数化して呼び出すようにしています。

参考

Sinatra Pattern 20130415

[Ruby] method の Thread をたてるときに method に引数を渡す

普通にメソッドをスレッド化するとこうなる

#!/usr/bin/env ruby
require 'thread'

def foo
  num = 0
  loop do
    sleep 1
    num += 1
    p num
  end
end

t = Thread.new(&method(:foo))
t.join

引数を取るメソッドをスレッド化したい場合

これは Syntax error

#!/usr/bin/env ruby
require 'thread'

def foo(num)
  loop do
    sleep 1
    num += 1
    p num
  end
end

t = Thread.new(&method(:foo), 0)
t.join
-:12: syntax error, unexpected ',', expecting ')'
t = Thread.new(&method(:foo), 0)
                             ^

shell returned 1

これは ArgumentError

#!/usr/bin/env ruby
require 'thread'

def foo(num)
  loop do
    sleep 1
    num += 1
    p num
  end
end

t = Thread.new(&method(:foo, 0))
t.join
-:12:in `method': wrong number of arguments (2 for 1) (ArgumentError)
  from -:12:in `<main>'

shell returned 1

これが正解

#!/usr/bin/env ruby
require 'thread'

def foo(num)
  loop do
    sleep 1
    num += 1
    p num
  end
end

t = Thread.new(0, &method(:foo))
t.join
とのことです

f:id:studio3104:20140911014653p:plain

YAPC::Asia 2014 Tokyo で 40 分の時間をいただいてお話させてもらった

「ブログを書くまでが YAPC::Asia」とのことですので、このエントリを以って僕の YAPC::Asia 2014 が終わってしまいます。

インフラエンジニアは死んだ

インフラエンジニア(狭義)は死んだ - YAPC::Asia Tokyo 2014

トークに応募したきっかけ

「声を上げない層をターゲットにしよう」ということを、トーク募集期間中に実行委員長の和田裕介さんがブログに書かれていました。

僕が2年連続でベストトーク賞をとれた訳 #yapcasia - ゆーすけべー日記

で、よく観察してみると、グレイトな ライブラリやサービスをつくっているハッカーは
「ただ口も声も大きいから」目立っているだけで、1,000人という規模の来場者からしたら
実はごく少数のマイノリティなんすよね。

ああ、なるほど。と思いました。
なんか、いわゆるそういう声の大きい人たちの評判、反応ばかり気にしてしまって、自分の意見を言いづらいみたいな感じに勝手に自分で決めつけてしてしまっていたのかもなぁ、と気付かされました。
「ああ、なんか勘違いしてたかも」と思わせてもらえた。ので、勇気をひねり出してトークに応募してみました。

話したこと

  • 自分は何を果たすべきか考える
  • 名称に縛られてしまうことで行動を起こさないと死ぬ
  • 必要になったときに出来る準備をしておく必要がある

ということを主張してきました。
僕自身、「インフラエンジニアだしプログラミングとか自分のすべきことの範疇の外のことだからするつもりもないし」と思っていた時期がありました。
しかし、それは誤っているのではないかという思いを持つようになり、紆余曲折を経て実際に今ではコードを書くことで解決できる問題に向き合って取り組む時間が多くなりました。
その課程の話をさせていただきました。

伝わりきらず残念だったこと

「なんかいいこと言ってたっぽいけど、結局は人の言葉の引用だよね」みたいなご意見もちらほらといただきました。
まぁ実際に発表内容にはそういう場面も少なくなく、真っ向から否定は出来ないのですが、そういう印象が真っ先に感想として出てきたということは少し残念でした。

実際に何か自分に取って刺さる内容の主張や意見に出会った時に、「刺さった」とか「いい話だった」と感想を持つことは多いと思います。
多くの場合はそこで終了してしまって、その先の自分に活かすための行いを伴うというところまでいかないのではないかなと。

僕の場合は、色々な方々の意見や教えを自分なりに咀嚼して解釈し、それを自分に活かして現在に至るというところがあります。
今回の話はその集大成という意識がありましたので、それをきちんと伝えられるような話し方が出来なくて非常に歯がゆい思いがございます。

なんかもっとこう、実例というか、もっと具体的にやってきたこととかをお話し出来れば良かったのかなぁと反省しております。
次に活かす。

意外と共感の声が聞こえてきた

嬉しいことに多くの共感の声もいただきました。
懇親会でもたくさんの方に声をかけていただき、トークの内容や境遇などについて大勢の方とお話させていただきました。

開発者のお祭だし、共感はあまり得られないだろうなーと思っていたので、同じような思いを持たれて共感してくださる方が意外と大勢参加されたいたんだなぁということに大変驚きました。
YAPC::Asia の幅の広さ、本当にすごい。

YAPC::Asia Ramen Challenge

これもまわってきて、次にまわしたし、これで本当の本当に僕の YAPC::Asia 2014 は終わったと言える。
数年ぶりに食ったなりたけ、すごくうまかったけど、食い終わった後に激しい胃もたれが襲ってきて老いを感じさせられてつらかった。

#yapcramen YAPC::Asia Ramen Challenge - @bayashi Diary

さいごに

今年で3回めの参加でした。本当に楽しかった。
運営スタッフの皆様、スピーカーの皆様、参加者の皆様、本当に素晴らしい場を提供してくださりありがとうございました。

人格というかコミニュケーションというかそういうぼんやりした話

タイトルの通りの話。
書いてる今めっちゃ酔ってるし意図した通り伝わるかわからないけど。
そして酔ってる帰り道でiPhoneで書いてるので装飾とかまったくしてなくて読みにくいです。

本題。
例えば仕事で何かわからないこととか方向性で悩んでることがあったとします。
そういう場合は1人で悩んでいるよりもまわりの誰かに相談しながら考えたりするほうが良い場合が多いと思います。

なので聞きます。
聞いたら、「あー、それは筋が悪い。」とだけ返ってきてそれで終わりだったとします。
そんなことは現実的にはあまりないかも知れないけど、そういうこともあるでしょう。色々な人がいますから。

そのショートアンサーを出した人にどういう意図があるのかということを考えたりとかしますよね普通。

•とりあえず今忙しすぎて君の相手をしている場合ではないが無視するのはアレなのでいったんショートアンサー
•そんなレベルのことは自力で考えて解決できるようにならねばならないよという期待を持って獅子が我が子を谷に突き落とすみたいな気持ちがある
•「どうしてそう思うんですか」と聞かれないから答えない、または聞かれることを期待している

とか、とかとか。

でもそういうことを考えるのって無駄だと思うのです。
あーこの人はこういう意図でこんなショートアンサー出してるんだってことがわかってればいいわけなので。
じゃあどうすればいいかって言ったら普段からもっと人間らしいコミニュケーションしろよってことになる。

メールとかLINEとかIRCとかエビデンスが残るしコミニュケーションのツールとしては非常に優れたものであるけど、その反面で感情表現が希薄になりがちだし、メーリングリストとかLINEグループとかIRCチャネルに人が多すぎると言いたいこと言いづらいみたいなこと少なくないと思うのです。

テキストベースのコミニュケーションが主体になると対面で言いたいこと言えないとかそういうコミュ症を生むし、効率良く情報を伝達することが正義みたいになって意図してないのになんかすごくへこませたりへこまされたりとかあると思う。
テキストベースのコミニュケーションオンリーではなくて、人間としてもっと熱くぶつかって議論したりとかそういうのがあったほうがいい。
そうやってコンテキストを共有して変な探り合いとかしなくていいようにしたほうが仕事も楽しくなるんじゃないかと思う。

かと言って最近の若者は上司と飲みに行くとか飲みニケーションとか嫌がる傾向があるのはわかってるけど、でも人間としてちゃんとコミニュケーションとれないやつがまともな仕事ができんのかよと思うんだ。

老害っぽいな。俺はまだ20代だぞ。

とはいってもオフィスを出たら友達なのかもしれないけど仕事でそういうお友達と談笑みたいな感じばかりになってしまってはならないとは思うしめっちゃ難しい。

なんか最近あの人とあの人はコミニュケーション難しいからみたいな事情でコンテキストスイッチおじさんすることが少なくなくて参ってたからこんなこと酔った勢いで書いた。
こういうコミニュケーションコストについては個人的には問題と感じていて、でも組織全体で見たらたいした問題じゃなくて、俺みたいなコンテキストスイッチおじさんがサスペンションすればいいだけのことかも知れないし、要するになんかよくわからんけど誰かに吐露したかった。それだけ。支離滅裂だ困った。

増田でやるべきだったか。まぁいいや。

MySQL Casual Talks vol 6.でNata2について発表してきた

MySQLのスロークエリログを一覧したりサマライズしたり出来るNata2というツールを作ったので、MySQL Casual Talks vol.6で発表させてもらった。

id:oranieさんが早速試してみてくれて、書いてくれたブログエントリにたくさんのブクマがついて大変にありがたい限りです。
MySQLのslow query logを可視化するnata2が大変便利そう - iをgに変えるとorangeになることに気づいたoranieの日記

使い方などをもう少し詳しく

発表当時はドキュメントがまったくなくて大変に雑な感じだったり、発表で説明しきれなかった部分もあったので、ここで改めて少し詳しい解説をします。

Nata2とは

パーズされたスロークエリログをHTTP Postで登録し、件数グラフ、スロークエリログの履歴、mysqldumpslow相当のサマライズなどの表示が出来るウェブアプリケーションです。
スロークエリログを登録するサーバと、パーズやPostを行うクライアントの2つのコンポーネントがあり、クライアントライブラリは2種類の実装があります。
サーバ、クライアントライブラリすべて、Ruby 2.0以降で動作します。

Nata2 Server

https://github.com/studio3104/nata2

インストール、データベース準備、起動

git clone
$ git clone https://github.com/studio3104/nata2.git
bundle install
$ cd nata2
$ bundle install
スロークエリログを記録しておくデータベースの準備

データベースは、特に理由のない限りはMySQLをお使いいただくことを推奨します。
テストではsqlite3を使ったりはしていますが、他のRDBMSでは動作の確認をしていません。

  • 設定ファイル

Sequeldburlconfig.tomlに記述します。

$ cat ./nata2/config.toml
dburl = "mysql2://nata:password@localhost/nata2"
  • MySQLNata2用のユーザを作成

任意。上述の設定ファイルの通りに接続をしたい場合は、以下の様な感じで。

mysql> GRANT ALL ON nata2.* TO 'nata'@'%' IDENTIFIED BY 'password';
  • create database

Nata2用のデータベース、nata2を作成します。

$ mysql -uroot -p -e'CREATE DATABASE `nata2`'
  • テーブル作成

テーブル作成スクリプトを実行します。

$ bin/nata2server_init_database
起動

unicornと組み合わせたり、ポートを変えたりなど、環境に合わせて。
以下のコマンドでは、フォアグラウンドで起動し、0.0.0.0:9292で待ち受けます。

$ bundle exec rackup

スロークエリの登録

http://nata2.server/api/1/:sarvice_name/:host_name/:database_nameなURLに、以下のようなパラメタをPostしてあげるだけです。
後述のクライアントライブラリを使うとラクですが、自前でパーズなどしてからPostすることももちろん可能です。

{
  datetime: 1390883951,
  user: 'user',
  host: 'localhost',
  query_time: 2.001227,
  lock_time: 0.0,
  rows_sent: 1,
  rows_examined:0,
  sql: 'SELECT SLEEP(2)'
}
Postパラメタ

userhost以外は必須です。

  • datetime
    • スロークエリログ発生日時
    • エポックタイム
  • user
    • クエリの実行ユーザ
    • 文字列
  • host
    • どのホストからのクエリか
    • 文字列
  • query_time
    • クエリ実行時間
    • 小数
  • lock_time
    • ロック時間
    • 小数
  • rows_sent
    • 送信された行数
    • 整数
  • rows_examined
    • 処理対象になった行数
    • 整数
  • sql
    • SQL
    • 文字列
    • 改行文字を含んでも大丈夫

ビュー

  • データベース一覧

同じサービスの中に同名のデータベースが登録されている場合、自動的にComplexの表示の下に複合ビューへのリンクが生成されます。
Databaseの表示の下に表示されるリンクは、各データベースの個別のリンクです。

f:id:studio3104:20140722222126p:plain

  • スロークエリログ履歴

画像は複合ビューです。同一サービス中の同名データベースがマージされて1つのビュー中に表示されています。
グラフはスロークエリログの件数。

f:id:studio3104:20140722221451p:plain

  • スロークエリログ個別

f:id:studio3104:20140722221738p:plain

  • mysqldumpslow相当のサマライズ

画面右上で選択された期間で登録されているスロークエリログをサマライズし、クエリの種類ごとに、合計回数順合計クエリ実行時間順合計ロック時間順合計フェッチ行数順平均クエリ実行時間順平均ロック時間順平均フェッチ行数順に表示します。
画像は合計回数順

f:id:studio3104:20140722221753p:plain

クライアントの実装について

MySQLのスロークエリログは、出力されたuse句以降は、そのデータベースでのスロークエリログだということがわかるが、どのデータベースのスロークエリだったかということをクエリごとに記録してくれない。
そのため、最後のスロークエリログが発生したデータベースを記録しながらファイルをナメていく実装をしています。
なお、Percona Serverはその限りにあらず、schemaというパラメタをクエリごとに記録してくれる。(設定次第?)

nata2-client

https://github.com/studio3104/nata2-client

  • 2つのクライアントライブラリのうちの1つ
  • SSHmysql clientで情報を取得
  • スロークエリログ収集対象サーバと、SSH、mysqldの接続情報は設定ファイルに記述
  • 処理済み行数と、最後のスロークエリログのデータベース名をsqlite3に記録しながら実行する
  • メモリをギリギリまで使いたいMySQLのサーバでfluentdを使うのが不安な場合に使う
インストール
$ git clone https://github.com/studio3104/nata2-client.git
$ cd nata2-client
$ bundle install
設定

config.tomlに以下のように記述します。

# スロークエリログ収集対象ホストとサービス名を指定
[targets]
service1 = [ "host1", "host2" ]
service2 = [ "host3", "host4" ]

# Nata2 ServerのFQDNと待ち受けポートを指定
[nataserver]
fqdn = "nata2.server"
port = 9292

[default]
# 一度の実行で何行処理するか指定
fetch_lines_limit = 10000

# 個別に指定がないホストのSSH接続情報
[default.ssh]
username = "root"
password = "password"
keys = [ "/home/studio3104/.ssh/id_rsa" ]
passphrase = ""
port = 20022

# 個別に指定がないホストのmysqld接続情報
[default.mysql]
username = "nata"
password = "nata"
port = 13306

# ホストごとの個別のSSH接続情報
# host1の実行時にdefault.sshを上書きする
[host1.ssh]
username = "satoshi"
keys = [ "/home/satoshi/.ssh/id_rsa" ]
passphrase = ""
実行
$ bundle exec bin/nata2-client start

fluent-plugin-nata2

https://github.com/studio3104/fluent-plugin-nata2
http://rubygems.org/gems/fluent-plugin-nata2

  • 2つのクライアントライブラリのうちの1つ
  • fluentdプラグイン
    • 2つのプラグインを内包
      • パーズされたスロークエリログをNata2にPostするout_nata2
      • スロークエリログをtail&パーズして吐き出すin_mysqlslowquery_ex

out_nata2

パーズされたスロークエリログをNata2にポストするプラグインです。
登録API(http://nata2.server/api/1/:sarvice_name/:host_name/:database_name)の:service_name:host_nametagから決定されるので、このプラグインに処理が渡るときにはtagの末尾がservicename.hostnameのようになるように設定して、サービス名とホスト名を指定しておく必要があります。

主な設定項目
  • server
    • 必須
    • Nata2 ServerFQDNを指定します
  • port
    • 必須
    • Nata2 Serverの待ち受けポートを指定します

in_mysqlslowquery_ex

fluent-plugin-mysqlslowqueryというプラグインが既に存在しているが、

  • SQLの連続する空白文字と改行文字を省いてしまう
  • Fluent::TailInputを継承しているため、最近のfluentdのtailプラグインで使える機能が使えない
    • どのデータベースのスロークエリログかわからなくなってしまう問題の対応として、ログを先頭から読むread_from_headを有効にして使いたい

の理由から、Fluent::NewTailInputを継承した別のプラグインを書きました。

主な設定項目
  • pos_file

    • オプション(有効推奨)
    • ログファイルをどこまで読んだか記録しておくファイル
    • last_dbname_fileとは異なるパスを指定
  • last_dbname_file

    • オプション(有効推奨)
    • 最後に出力したスロークエリログが発生したデータベースを記録しておくファイルパスを指定
    • pos_fileとは異なるパスを指定
  • dbname_if_missing_dbname_in_log

    • オプション
    • スロークエリログからも、last_dbname_fileからもどのデータベースでのスロークエリログだったかわからなかった場合に使われるデータベース名
  • format

    • 何を指定しても内部でnoneに書き換えられます
  • read_from_head

    • オプション(有効推奨)
    • ログを先頭から読みこむ
  • path

    • 必須
    • スロークエリログのパス
  • tag

    • 必須
    • out_nataとの組み合わせで使う場合、tagの末尾をservicename.hostnameのようにして、サービス名とホスト名を指定しておく必要がある

設定例

とりあえずこんな感じで設定しておけばNata2にスロークエリログが登録されていきます。
ご利用の環境に合わせて適宜変更してご利用ください。

<source>
  type mysqlslowquery_ex
  read_from_head
  path /path/to/slowquery.log
  tag slowquery.servicename.hostname
  pos_file /tmp/slowquery.log.pos
  last_dbname_file /tmp/slowquery.log.lastdb
</source>

<match slowquery.**>
  type nata2
  remove_tag_prefix slowquery.
  server nata2.server
  port 9292
</match>

今後追加したい機能

  • EXPLAINの取得、登録、参照
  • SHOW CREATE TABLEの取得、登録、参照

など

おわりに

自分がはじめて参加したMySQL Casual Talksはvol3.でした。
登壇されていた皆さんがすごく輝いて見えたのを今でも覚えているし、まさか回を重ねた同会で自分が登壇するだなんて、当時の自分はまったく考えていなかっただろうなーと思います。
機会をくださったid:myfinderさん、会場を提供してくださった日本オラクル様、本当にありがとうございました。

YAPC::Asia Tokyo 2014 で 40 分間話をする権利をいただきました

トーク採択していただきました。インフラエンジニア(狭義)は死んだ
ソーシャルメディアでの拡散などしてくださった皆様有難うございます!!!1

初日の朝イチでだいぶ早いですが、早起きしてお越しいただけると大変うれしいです。
2014-08-29 10:20:00慶應義塾日吉キャンパス内 協生館 2F 多目的教室2 でお待ちしております。よろしくお願い致します。

HRForecast の EMBED でヘッダとラベルを非表示に出来るようになったよ

HRForecast には EMBED の機能があり、iframe で別のページ(別のアプリケーション)にグラフを埋め込むことが出来ます。

f:id:studio3104:20140501141942p:plain

ただ、埋め込む先のアプリケーションによっては、グラフ名と 1週間,1ヶ月,1年 などのレンジ指定、CSVエクスポートが置かれているヘッダ領域グラフ名と値が表示される右側のラベル領域が不要な場合もあるかなと思います。
ので、pull-request して取り込んでもらいました。

suppress graph headers or labels when specified respective options #26

指定するクエリストリングと参考画像

iframe の中の URL のクエリストリングによって表示が変わります。

  • ヘッダもラベルも消したい場合

graphheader=0&graphlabel=0

f:id:studio3104:20140501142944p:plain

  • ヘッダだけ消したい場合

graphheader=0 or graphheader=0&graphlabel=1

f:id:studio3104:20140501143119p:plain

  • ラベルだけ消したい場合

graphlabel=0 or graphheader=1&graphlabel=0

f:id:studio3104:20140501143211p:plain

  • ヘッダもラベルも消さず、従来どおりにしたい場合

graphlabel, graphheader を指定しない or graphheader=1&graphlabel=1

HRForecast についてご存じない方はこちらをどうぞ

HRForecast - もうひとつのデータビジュアライズツール
kazeburo/HRForecast