Studio3104::BLOG.new

uninitialized constant Studio3104 (NameError)

#CROSS2014 で #ぶつかり稽古 やります!

ぶつかり稽古って?

オリジナルは昨年11月に行われたこちらのイベントです。エンジニアとエンジニアとの魂のぶつかり愛。

企画について

オリジナルのほうはペアプロでしたが、今回は趣向を変えて、コードレビューをテーマとしました。
自分はオリジナルのぶつかり稽古の関係者ではないのですが、許可を賜り、今回の企画名に冠しております。

実行委員長の @ さんがご自身のブログで仰っておられますが、今年の CROSS はこのようなテーマのもと各セッションが準備されてきました。

今年はテーマとして「クロスでススム、クロスで変わる」を掲げました。 「交流」「議論」を通して、明日への知識・人脈をひとつでも得て、変わる機会になってくだされば幸いです。

自分がセッションオーナーを務めさせていただくことになったセッションでは、このようなモチベーションのもと企画しました。

  • 普段コードレビュをしてもらっていて、よそのあの人達はどんな感じでやっているのだろうと気になる
  • 普段の勉強会の発表やセッションは最適化された手法を人に話す用にブラッシュアップされたものになっていることが多いので、現場感ライブ感のあるセッションを見たい

どんなセッション?

さて肝心の内容ですが、このような感じでお届けする予定です。

  • コードレビュをテーマとしたパネルディスカッションを軸として進行
    • 気をつけていること
    • 使用しているツールについて
    • など!など!!
  • 実際にレビュー、修正を行いながら進行
    • レビュー、修正は各ペアで予め何往復かしておいてもらっているので、普段の開発の一部分を切り取ってお見せする、という感じ

詳しくは実際にセッションにいらっしゃってご覧くださればと存じます。

各弟子に実装していただいた WEB アプリケーションは事前にデプロイし聴衆の方が見られるようにします。 また、各リポジトリの URL も公表しますので、勇気のあるあなたはレビュアーとしてライブで参加することも出来てしまうかもしれません!!!

課題の WEB アプリケーション

仕様はこんな感じです。
コレを各弟子に実装していただきました。

  • アイドルグループの人気楽曲投票WEBアプリケーション
    • 要件
      • 661曲の候補の中から好きな1曲に投票
      • 1シリアルナンバーにつき1回投票可能
      • 最低でも1,000,000個のシリアルナンバーが発行されている
      • 会員登録は不要
    • 使用するフレームワーク、データストアなどは自由
    • スキーマと楽曲データの定義もお願いします

お待ちしております

会場でお会いしましょう!!!!!!

http://www.cross-party.com/programs/butsukari/

HRForecast に値を投げた時に一緒に複合グラフも作っちゃいたい!

HRForecast の API で値を投げた時に返ってくる JSON に、これまではエラーの有無とエラーメッセージだけでしたが、POST 成功時にはグラフの情報が含まれるようになりました。

{
    :metricses => [
        [0] {
            :section_name => "test_section",
                      :id => "1",
                  :colors => "[\"#99cc33\"]",
              :updated_at => nil,
            :service_name => "test_service",
              :graph_name => "test_graph",
                   :color => "#99cc33",
                    :meta => "{\"color\":\"#99cc33\"}",
              :created_at => nil,
                    :sort => "0"
        }
    ],
        :error => 0
}

他にもいくつかの JSON API が追加され、ますます便利になってしまっています。 https://github.com/kazeburo/HRForecast/commit/a5b28908a0fe53c45f9b576687df6a58af385b17

値を投げた時にグラフの情報が返ってくれると例えば何が嬉しいのか

複合グラフを作成する API 自体はないのですが、これまででも POST してあげれば WEB UI からでなくても複合グラフを作成することは出来ました。 しかし複合グラフを作成するには、複合する対象のグラフの ID を送ってあげる必要があります。 ちょっと変な実装をすればグラフ ID を HTTP リクエストで取得することは出来ますが、トリッキーなことをせずとも複合グラフの作成が出来るようになります。

こんな感じでどうでしょうか

ふたつのグラフ(test_1, test_2)に値を投げて、複合グラフ(test_complex)がなければ作成します。
ちょっと工夫すれば test_complex がすでに作られていて、そこに test_3 をつっこみたい、なんてこともカンタンに出来るでしょう。

require "net/http"
require "json"

class HRForecastRequestError < StandardError; end
class HRForecast
  def initialize(fqdn, bind_port, enable_https = false)
    @base_url = enable_https ? "https://" + fqdn : "http://" + fqdn
    @bind_port = bind_port
  end

  # datetime のサポートするフォーマットはドキュメント参照: @base_url/docs
  def post_value(service_name, section_name, graph_name, post_bodies)
    value = post_bodies[:value]
    datetime = post_bodies[:datetime] ? post_bodies[:datetime] : Time.now

    graph_path = [service_name, section_name, graph_name].join("/")
    result = post_request("/api/" + graph_path, number: value, datetime: datetime)

    # 失敗すると、200 で body にエラーメッセージの入った JSON が返るので成否はそこを見て判断
    if result[:error] == 1
      raise HRForecastRequestError, "could not post value to #{graph_path} (#{result[:messages].to_s.chomp})"
    end

    result[:metricses].first[:id].to_i
  end

  def create_complex_graph(service_name, section_name, graph_name, path_data, graph_options = {})
    description = graph_options[:description] ? graph_options[:description] : ""
    stack = graph_options[:stack] ? graph_options[:stack] : 0
    sort = graph_options[:sort] ? graph_options[:sort] : 19

    result = post_request("/add_complex", {
      service_name: service_name,
      section_name: section_name,
      graph_name: graph_name,
      description: description,
      stack: stack, # 0:積み上げないグラフ, 1:積み上げるグラフ
      sort: sort, # 値の大きいモノ順で list に表示される (0..19)
      :"path-data" => path_data # 複合グラフに突っ込むグラフの ID を配列で渡す
    })

    # 失敗すると、200 で body にエラーメッセージの入った JSON が返るので成否はそこを見て判断
    if result[:error] == 1
      graph_path = [service_name, section_name, graph_name].join("/")
      raise HRForecastRequestError, "could not create complex graph #{graph_path} (#{result[:messages].to_s.chomp})"
    end
  end

  def complex_graph_exist?(service_name, section_name, graph_name)
    url = URI.parse(@base_url + ":" + @bind_port.to_s + ["/json_complex", service_name, section_name, graph_name].join("/"))
    http = Net::HTTP.new(url.host, url.port)
    http.get(url.path).is_a?(Net::HTTPSuccess)
  end

  private
  def post_request(request_path, post_data)
    url = @base_url + ":" + @bind_port.to_s + request_path
    response = Net::HTTP.post_form(URI.parse(url), post_data)

    raise HRForecastRequestError, "post request was not success" unless  response.is_a?(Net::HTTPSuccess)
    JSON.parse(response.body, symbolize_names: true)
  end
end
hf = HRForecast.new("test.hrforecast.com", 5127)
service_name = "test_service"
section_name = "test_section"
graph_name_prefix = "test"

test1_graph_id = hf.post_value(service_name, section_name, graph_name_prefix + "_1", value: rand(100) + 1)
test2_graph_id = hf.post_value(service_name, section_name, graph_name_prefix + "_2", value: rand(100) + 1)

complex_graph_name = graph_name_prefix + "_complex"
if !hf.complex_graph_exist?(service_name, section_name, complex_graph_name) && test1_graph_id && test2_graph_id
  hf.create_complex_graph(
    service_name, section_name, complex_graph_name,
    [ test1_graph_id, test2_graph_id ]
  )
end

余談

前述のとおり、このような変な実装によってグラフ ID を取得することは出来ます。

def get_graph_id(service_name, section_name, graph_name)
  # HTTP リクエストでグラフ ID を取得出来るクチが csv ダウンロードリンクしかなかったぽい
  url = URI.parse(@base_url + ":" + @bind_port.to_s + ["/csv", service_name, section_name, graph_name].join("/"))
  http = Net::HTTP.new(url.host, url.port)

  # "d=1" をつけてリクエストすると content-disposition の csv のファイル名からグラフ ID が取得できる
  response = http.head(url.path + "?d=1")
  return $1.to_i if response["content-disposition"] =~ %r{attachment; filename=\"metrics_(\d+).csv\"}
end

自分で書いてて違和感があったので、「複合グラフを作成するために、値が POSTされたときにグラフ ID を返すようにって出来ますか?」って kazeburo さんに相談したら、数分で実装してくれました。
はじめからお願いすれば良かった感が強いですけど、自分はこの変な実装をするために結構な時間を費やしてしまったのですが HRForecast のコードを読んでみたりだとか色々勉強になったこともあったのでまぁそれはそれで良かったと考えます。前向きに。

Percona Cloud Tools を試した

インストールや設定などは、pt-agent のドキュメントの通りに進めれば難なくこなせるはずですので割愛します。

感想

以下の点などを加味して考慮した結果、思っていたほどいいモノではないような気がしました。
ざっと使ってみただけなので誤っている点などあるかも知れませんが、ご容赦を。
「ここちがうよ!」「こうすればもっといいよ!」みたいなのよろしくお願い申し上げます。

pt-agent というエージェントを root 権限で動かさなくてはならない

pt-agent の設定は、Percona Cloud Tools の WEB 画面からも設定変更を行えます。
例えば以下画像だと、How often to report のところをいじるとサーバ上にある cron のファイルが書き換えられます。

f:id:studio3104:20131107163428p:plain

インターネットから操作するのに root 権限あげちゃうのか、、、って感じ。

[追記]
root じゃなくても動かすことは出来る模様。ドキュメントに書いてあった。
でも「面倒な手順で手動インストールが必要で、わかんなかったら Percona に問い合わせてね!」みたいな感じだ。

pt-agent must be installed and ran as root.
It is possible to run as a non-root user, but this requires a more complicated and manual installation.
Please contact Percona for help if you need to run pt-agent as a non-root user.

pt-agentMySQL に作成する pt_agent なるユーザに SUPER 権限がつく

上の画像を例にすると、Long query time を書き換えて Save した場合に、サーバ上の MySQLSET GLOBAL long_query_time=0.000000 とか実行されます。

インターネットから操作するのに SUPER 権限あげちゃうのか、、、って感じ。

mysql> SHOW GRANTS FOR 'pt_agent'@'localhost';
+-----------------------------------------------------------------------------------------------------------------+
| Grants for pt_agent@localhost                                                                                   |
+-----------------------------------------------------------------------------------------------------------------+
| GRANT SUPER ON *.* TO 'pt_agent'@'localhost' IDENTIFIED BY PASSWORD '*16CED81DAC5917722E82063353A27E11178399F0' |
+-----------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

MySQL サーバに pt-agent を泳がせなければならない

(このへんはあまり深くタッチしてないので、うまいやり方があるのかもしれないです。)

ひとつの pt-agent プロセスが ひとつの mysqld と対になるようなので、サーバごとにエージェントを泳がせなくてはなりません。
そうすると、一旦ログを集約サーバなどに集めておいて、そこからだけ投げるみたいなことがやりにくくなります。
ファイルやクエリごとにタグを付けられるわけではなさそうだし、どのクエリがどのサーバのモノなのかを一意に識別させる手段が、どのエージェントから送られてきたログなのか、というところだけなので。
タスクが JSON (/var/lib/pt-agent/servicesにあるファイル) で定義されているので、それをうまいことなんとかすればもしかしたらどうにか出来るのかも知れません。未検証。でもやれたとしても面倒な感じがする。

あとDB サーバが直接インターネットに繋ぎにいけないような構成をとっている場合には proxy を経由させたりとかしなくてはならないかも。面倒。

上述のエージェントに与える権限についてだけど、サーバごとにエージェントが要るのであれば、WEB から設定変えられるほうがまぁいいとは思うけど、権限過多ではないかという気がする。 サーバ もイントラで動かせるんだったらよいと思うけど、クラウドだしなぁ。

こんな感じのサマリー画面

ひとつの DB サーバ全体

f:id:studio3104:20131107170510p:plain

クエリの種類ごと

f:id:studio3104:20131107170640p:plain

これだったら EXPLAIN までしてから結果を表示して欲しい感じがする。。

f:id:studio3104:20131107170738p:plain

これも。。

f:id:studio3104:20131107170848p:plain

「偽だったら真、真だったら偽」みたいなのわかりにくい

flag ? false : true

みたいなコード見かけて、

!flag ? true : false

わかりにくいからこうしたほうがよさそうだと思ったけど、

!flag

これだけで良いじゃん、って指摘してもらった。確かに。

WeeChat は IRC Bouncer にもなる

テキストベースの IRC クライアントとして有名な WeeChat ですが、IRC Bouncer としても使えることがわかり、使ってみたら大変によさそうであったので共有いたします。

旧環境

最近まではこんな感じで ZNC に IRC サーバに繋ぎっぱなしにさせて、クライアントが繋いできたときは設定された行数をプレイバックするみたいにして使っていた。

+--------------+    IRC     +---------+  IRC   +-------------+
| LimeChat(PC) | ---------> |+-------+| -----> | Company IRC |
+--------------+            ||  ZNC  ||        +-------------+
+------------------+  IRC   |+-------+|  IRC   +----------+
| LimeChat(iPhone) | -----> | Server  | -----> | freenode |
+------------------+        +---------+        +----------+

これだとちょっとダルい問題があって、PC からだとネットワークが切れて再接続されるたびにバッファプレイバックするので、直近で nick 呼ばれてたりするといちいち LimeChat が通知出して来てウザい。
iPhone からだと、細い回線下ではバッファの読み込みに超時間がかかってウザい。
かと言ってバッファを少なくし過ぎたり、バッファを残さないようにするとかだと利便性に欠いてしまうので大変ウザい。

新環境

そこでこのような構成にしてみた。
WeeChat はサーバの tmux 上で動いていて、 SSH してから tmux attach してから普通にクライアントとして使います。
最初は WeeChat のウラに ZNC を残しておいて、WeeChat も iPhone も ZNC に繋ぎに行くようにしようと考えていたけど、WeeChat に IRC Boucer としての機能があるとわかり、ZNC は落とした。

+----+        SSH           +-----------+  IRC   +-------------+
| PC | -------------------> |+---------+| -----> | Company IRC |
+----+                      || WeeChat ||        +-------------+
+------------------+  IRC   |+---------+|  IRC   +----------+
| LimeChat(iPhone) | -----> |   Server  | -----> | freenode |
+------------------+        +-----------+        +----------+

新環境のメリットとデメリット

ごく短期間使っただけの所感です。

  • メリット
    • 未読行だけバッファプレイバックする!!
      • ZNC のように、毎回設定された行を読みに行かない
        • iPhone からの利用で上述のようなイライラがない
      • バッファプレイバックに関しては特に設定不要
        • もちろん設定で制御することは可能
    • 集中力が保つようになった
      • LimeChat とターミナルの行き来は地味にダルい
      • ターミナルに集中出来ると、ダレずに作業出来る
      • マルチモニタにして片方に置いておくとかしなくていい
    • CLI でッターンしてるとなんとなくカッコいい感じがする
  • デメリット
    • NOTICE をプレイバックしない!!!!
      • コレは自分にとって結構致命的
      • ユーザーズガイド読んだけどよくわからんかった
      • 誰か知ってたら是非教えて欲しい
      • まぁでもバッファプレイバックを必要とするのは iPhone だけだし、WeeChat からはちゃんと見られるのでとりあえずはおkかな・・・
iPhone と WeeChat で未読が共有出来るのは本当に便利。

Relay の設定

接続先のサーバごとに待ち受けポートを変えるなど、いくつかやり方があるので、ユーザーズガイドを一読してからお試しになることをオススメいたします。
今回は SSL で接続し、ひとつのポートで待ち受けて、サーバパスワードで接続先サーバを区別する方法を紹介します。

  • 前提
    IRC サーバへの接続など、WeeChat を IRC クライアントとしてひと通り普通に使えるようにしておく必要があります。
    ここでは割愛します。クイックスタートガイドを読みましょう。

  • SSL 鍵作成

$ mkdir -p ~/.weechat/ssl
$ cd ~/.weechat/ssl
$ openssl req -nodes -newkey rsa:2048 -keyout relay.pem -x509 -days 3650 -out relay.pem
weechat: /relay sslcertkey
  • パスワードを設定
weechat: /set relay.network.password **********
  • リレー設定
weechat: /relay add ssl.irc 8000
  • クライアントの設定
    • SSL 有効
    • ポート 8000
    • サーバパスワード servername:password

おまけ

WeeChat に乗り換えてやったことを都度書くようにした。
- WeeChat を入れてやったことまとめ
オススメプラグインや設定などあれば、是非教えて下さい。
Enjoy IRC!!

YAPC::Asia 2013 に参加してまいりました。

昨年に引き続いて、今年も参加してまいりました。

じぶんのちいささをしった

kazeburoさんのトークを拝聴して、大筋では仰っていること、ご説明されていた内容は理解出来ましたが、なんとなくぼんやりした感じになってしまいました。
UNIXプロセスとかシステムコールについてほとんどちゃんと理解していなかったからでした。

ほう、さすがベテランのスーパエンジニアさんや!で片付けてしまうのは簡単です。
そうではなくて、知ってしかるべきことを知らなかったのであればそれを埋める努力をしなくてはならないし、そういうところ甘かったな、と。
そもそものセンスとか、経験値積んだ上で霊力上げないとー、とかではなく、知ってるか知らないかというところで、何か問題に直面したとき、それを解決するために取れる手法、手段の幅が変わっていってしまうのであれば、学ぼうとしない理由がないですね。と思った次第です。

まぁ、現状の問題を分析して、それを解決するために Monoceros のようなものを作れるかとかっていうと別の話ですけども。。*1

というわけでコレ買いました。
「目次見て知らなそうな事が書いてそうなら、買いじゃないっすかね」とかそういう感じなので買いました。
Rubyですけどね。

『Working with Unix Processes』待望の完訳。
並列処理やデーモン、プロセス生成、
そしてシグナルといったUnixの基礎であるプロセスについてRubyで解説する、
「今どきの」開発者に向けた新しいUnixプログラミングの手引きです。

:
:

スピーカーとして参加したほうが楽しい

コレみなさんおっしゃってますよね。
去年はボクもLTしました
去年は孤独に過ごす時間が多かったですが、今年は合間でお話するできるような知ってる人もたくさんいて、技術以外の話でも盛り上がったり、そういった面では大変楽しかったです。
でも、去年のLT前の緊張感とか、終わった後に話しかけてもらって嬉しかったりとか、そういう感じのエモーショナルなイベントがいかに素晴らしかったのかということを感じましたわけです。
牧さん&櫛井さん引退のサプライズ人事発表がありましたが、YAPC::Asia 2014 があるならば何か喋りたい。

おまけ

個人スポンサーしたら作ってもらった提灯を会社の名札のとこに飾りましたー

f:id:studio3104:20130930230301j:plain

*1:そもそもそんなことも知らないで今までよくやってこれたなって思うし、黙って学習しておいてしれっと知ってる組に入るほうがかっこいい感じするけど、YAPCきっかけで学習意欲が芽生えたので、え、そんなことも知らないの、おまえとか言われるのやだし本当は書きたくないけど書いた。