2011/04/05  |  Written by  |  under Blog

無事にRubyKaigi 2011 CFPに当選したので、今年もjpmobileの話をします。

で、内容はというと、今年はどちらかというとチュートリアル的なお話になります。時間が限られているのでエッセンス的な話になると思うのですが、「 1.0.0以降では、ここさえ押さえておけばいいよ」と言う内容にしようと思ってます。

 
このエントリーを含むはてなブックマークはてなブックマーク - RubyKaigi 2011でjpmobileの話をします この記事をクリップ!Livedoorクリップ - RubyKaigi 2011でjpmobileの話をします Googleブックマークに追加 Digg This
Tags: , ,
2010/12/07  |  Written by  |  under Blog


Rubyのテスト文化とツール 2010 by @hsbt さん

  • Asakusa.rb & 永和の人
  • 去年と着ているものが同じ
    • 「テストを書かないとRubyistではない!」と言う話し

永和の文化

  • テストを書くと言う文化がある
  • in World Conference
    • Ruby を使うとどんどん書ける
    • Ruby を使うとどんどん書ける
      • 書けるが不安になるので、テストを書く
  • metric_fu
    • 静的解析ツールを一度に実行してくれる
  • のレイヤーごとに使っているツール
    • mock/stub
      • stub -> RSpec の stub
      • mock -> RSpec の should_receive
      • rr
      • webmock
        • stub な Web API 呼び出し
    • fixture replacement
      • factory_girl
      • Machinist
    • intergration test
      • Capybara
        • Selenium Webdriver
    • 動かなにテストを放置しない
      • Hudson による CI
        • 完全テストは CI にまかせている
      • parallel_test
    • バグを再現するテストを書いてから直す
      • テストの実行が遅いのをなんとかする
      • Spork
    • DRY
      • 多段ネストを止める
      • Custom Matchers を使う

私的まとめ

  • Hudson による CI は導入してみようかな
  • Custom Matchers を利用することを念頭に置いてみよう

高速な乗除算の実現と性能評価 by @mrkn さん

  • 計算の話し
  • 速さ == ロマン
  • Rational の計算 -> 通分される

私的まとめ

  • あとで直接聞いてみるか

Rubyの未来 未来のRuby by matz

  • 1993
    • Ruby 生誕
    • 利用者 1人
  • 1995
    • fj.sources で公開
  • 2010
    • TIOBEで10位
  • 「いまだに職業としてチームプログラミングの経験がない」
  • 1993
    • 悩めるプログラマ
    • もっと光を
      • List/Smalltalk/Perl のパワー
    • “光あれ”
      • そこに良いものがあるならば、それを届けよう (自分に)
      • なければ自分で作ろう
  • Ruby が届けた光
    • オブジェクト指向
    • エンコーディング
      • IPv6 とか
    • Webプログランミング
      • メタプログラミング
      • モンキーパッチング
    • TDD
      • DSL/BDD
  • 世界を変えよう
    • 自分を変えよう
    • 世界がそれについてくる
      • それが本当に良いものならば
  • 未来のRuby
    • Ruby 2.0
      • Trais/Refinement/Method Combination/Keyword Arguments
  • Rubyの未来
    • HPC
      • 生産性/オブジェクト指向を届ける
    • 組み込み
      • 環境の制約が強い
      • RiteVM
        • LuaっぽいVM
        • Rubyのサブセット
        • インクリメンタルGC
        • 残念なこと
          • 事業の終わりが見える頃までオープンソースになりません
          • 中途からオフィシャルに参加する方法がありません
        • すばらしいこと
          • HPC/組み込みに光が届く
    • いつも人々に届ける「光」であり続ける
    • 人々が「光」を届けるよう動機づける

質疑応答

  • Module はどこから?
    • lisp の mix-in (flavor)
  • Ruby をセールスする良い方法は?
    • 人は実績を見て判断するからそれか、マーケティングが得意な人(DHHとか)を見つける
      • DHHは論争を恐れない(炎上しているところにガソリン持って飛び込んでいく)
  • 北海道の印象は?
    • 体重的に危険な場所
  • Ruby 2.0 features は?
    • trunk で
  • Module を名前空間として使うことは考えていた?
    • わりと初期のころからそう思ってた
  • HPCのは論文が出ると思うが、無料で読めるようにできる?が
    • 相談します

Rubyの教えてくれたこと by 島田さん

  • たのしいRuby
  • 日本Rubyの会 理事
  • ブロックをキーにして、Rubyの楽しさや良さについて
    • 構造を複雑にせずに、柔軟性をあたえられる
    • YAGNI(You Ain’t Gonna Need It)
      • どうせ必要ないって
  • 変化を抱擁する
    • SOLID Principle
    • Open-Closed Principle in オブジェクト倶楽部
    • 変化を受け入れられるようにしておくことが重要
  • 道具を知ること
    • 道具を知り道具を選ぶこと
  • 読めると分かるは違う
    • 分かるコード -> Why がわかる
    • 良いコード -> わかって使える、修正できる

北のRails開発現場から’10 by @tmaeda さん

  • Ruby逆引きレシピ のAmazon レビューを書いてくれる人募集
  • ペアプログラミング導入の経緯など
  • 悩み
    • 技術者間のスキル差が埋まらない
    • 新卒と中途と
      • キャリアパスが見えにくい
      • 時間が解決してくれる?
        • してくれない
    • デスクワークな仕事は仕事っぷりが見えない
      • 見えないからこそ、判断基準がないため、新人さんのキャリアパスや、程度感がわからない
  • ペアプロの効果
    • 考える部分が加速する
      • 普通のプログラミングでのボトルネックは考えること
    • ものすごい集中と良い緊張感
    • コーディングとコードレビューと教育と引き継ぎを一気に
    • 動いたという感動を共有できる
    • 細かいところまで丸みえになる
  • GNU screen / readline などのライブラリなど使う
  • まとめ
    • ペアプロしましょう!
  • 環境は?
    • 環境はドライバー依存
  • ペアプロの難しさ
    • 導入前にはあったが、いまのところは大丈夫
  • 技術力の差は埋まったか?
    • 結構埋まった。しかも仲良くなった。
  • ペアプロしたときの技術力ある人のパフォーマンス低下は?
    • 総合的に見たときには、仕方ない
  • ずっとやってる?
    • 午前と午後に2時間だけやってる
  • ベテラン同士のペアプロは?
    • お互いの刺激になってよい
  • 人月単価とか工数見積りは?
    • コストとの問題は起きていない

(カーリングとRuby)2投目 – Making Ricochet with the Ruby stone into Granites. by はしむかい としかつ(@curler_hashi) さん

  • 妹背牛カーリング協会 強化委員
  • 02で全てをかっさらった、あの人が帰ってきた
  • Geek でも Suits でもない人達のためのUI
    • Markdown + 独自拡張
  • あったらいいなこんなソフト
    • 試合記録どか動画記録込み
      • 日本製のものがない
  • カーリングの石に加速度センサつけて、データを取るといいかも
    • ダメな投擲のときにアラームがあると
  • カーリングをやるのにいくらぐらいかかる?
    • 石に200万円ぐらい
  • 主要なプロダクトは米

Lightning Talk

RVMについて語るときに僕の語ること

  • “ささだメンバー”

文書検索ラングバ

  • “デバッグ力”

Rails 3.1(仮)

  • Isolated Engine

JRubyによるエンタープライズWeb開発

  • “みなさんエンタープライズな方”

HTMLデザインをまったく崩さない、美しいテンプレートエンジンの作り方 パート2

  • 分解して再構築する感じか

たまにはcgi.rbのことでも話してみる

  • “Passenger上でtdiayがそのまま動く”

Perl loves Ruby

  • “Hokkaido.pm”

Rubyでネイティブスマートフォンアプリ開発

  • Rhodesは一回やってみるべきだな

どこでもRubyといっしょ 〜WindowsMobile携帯にRubyを入れて遊んでみた〜

  • “iPhoneではなく、なぜWindowsMobileを選んだか”

Pusherアプリの作り方

  • “会いに行けるプログラマ”

コンサドーレ札幌の情報収集をrubyでやるには

  • “最終節4-0で勝ちました!”

或るWebサービス開発のこれから – “オープンWebサービス”という妄想 -

  • コードも議論も展望も公開する、”オープンWebサービス”

ご自宅用ツール間同期メカニズム兼ストレージのご提案 by @m_seki さん

  • “プロの無職”
  • toRuby
  • 栃木Ruby会議03 at 2011/02/26
  • dRuby本初刷5周年!
  • Drop
    • Rinda の実用的なライブラリ
    • 似ているものは
    • 時刻をキーとするKVS
  • キーの排他制御は?
    • 同じ時刻であれば1単位待って追加する

Railsアプリ開発 野生のカン by 社長(エア的な)

  • マイペースにやりたいがスライドがが
  • コードレビューの前に設計資料を見る
    • rspec は設計資料とは見ない
  • 具体的に文章として書かれているかどうか
    • 機能の紹介やターゲットの記述など
  • 複雑なところに図を書いているかどうか
    • Class/Object
    • ベン図意外といい
    • URL設計がコードの外でされているかどうか
      • コード書くまえに Wiki に書く
      • 最初に書く
      • 目で見て美しいかどうか重要
    • RESTful I/F
      • CRUD
      • “みなさんもっと少女マンガを読まないといけないと思います”
      • ガラスの仮面を読んで REST 脳に
        • 4つの動詞だけで会話できるようになるために
      • 本を借りる
        • 「本を借りること」を作る、と言う考えかた
  • filters on controller
    • 多すぎても少なすぎてもダメ
    • filter には前提条件
    • :except, :only はどっちを使う?
      • :only のみを使う
      • 指定されて「ない」ものは把握しづらい
      • :except だと追加で実装するときにわかりづらい
  • private/protected/public
  • 例外処理
    • Javaから来るとやりすぎる
  • パラメータの加工とか
    • accessor でやれということ
  • transactions
    • コントローラの中で使うことはないはず
    • それコールバックで
  • controller の継承
    • アクション共有は難易度が高い
    • Mix-in とか partial で
  • attr_protected / attr_accessible
    • 前者が保護する、後者が指定した方を通す
    • attr_protected を使う方がいい
  • save/save!
    • save で false を見ないのなら save! で例外出す方がいい
  • validation はまたの機械に
  • routes.rb
    • コントローラ別にまとめよう
  • コメント
    • コードが不自然なところに理由を書く
  • 英語を入れるのは、世界と繋っているということを考えて
  • future
    • 未来に制約を残さない
    • あとから来た人が足を取られないようにしよう

私的まとめ

  • RESTful I/F は勉強になったので、実践してみよう
  • filters には :only を使う
  • ガラスの仮面を読みます
  • attr_protected を使うようにしましょう

私のプロフィール by @takahashim さん

  • 国内外に煽るだけ煽ってしまった感じがある
  • 2010/06/02 株式会社達人出版会
  • オーム社よりも早かった
  • これまでの「征義」の話をしよう
    • 個人的すぎるかもしれないので構成変えました
  • 達人出版会のサービス
    • 著者に原稿を書いてもらい、編集・組版して出版する
      • EPUB/PDF で
    • 近代的な書籍制作環境の構築
  • 執筆支援環境
    • Redmine + git
      • gitosis と Redmine を連携できるプラグインがある
  • 組版システム
    • ReVIEW

There Is No Spoon:Revisited by @kakutani さん

  • オーム社の電子出版サイトの制作している
  • もう感極まってしまったので、何も書けなかった。

クロージング

  • 遅れているのでさっさと懇親会へ

懇親会

  • 名古屋のあれがかなりあれであることがわかったので、名古屋Ruby会議に参加することになりました。
  • いろんな人とお話しできたのがよかった。

二次会

  • 同上
  • もうなんか感極まっ(ry

五丈源

  • matz と深夜ラーメンというまれに見る状況に感極ま(ry)

まとめ

  • いろいろと会場や懇親会のことを伝えたいんだけど、やっぱり参加しないことにはこの雰囲気や熱意は伝わならないのではないかなと思う。この気持ちを味わいたいのなら、是非とも地域Ruby会議に、特に地方のは行くべき。そして懇親会で話をすべき。そうすると自分が何をしたかったのか、何をすべきかとかがわかるかもしれないと思う。少なくとも私はわかったので、これからも参加し続けるつもり。次は西側かな。
  • 二晩ぐらい経ってみて、「一度終わってみるべき10の理由」的なのが思い浮んだが、もうちょい練れるまで表には出さないことにする。
  • 二次会での「月曜から砂漠に戻るんですよ、みんな」的な話が印象に残ったな。それを打破するための RailsDevCon だったりするので、もうちょい頑張りたい。
  • 滞在ホテルがえにしテックの最寄りだったらしい。エアを名乗ってもいいだろうか。
 
このエントリーを含むはてなブックマークはてなブックマーク - 札幌Ruby会議03に参加してきた この記事をクリップ!Livedoorクリップ - 札幌Ruby会議03に参加してきた Googleブックマークに追加 Digg This
Tags: , , ,
2010/11/22  |  Written by  |  under Blog

と言うわけで、去る2010/11/20(土)にRailsDevCon2010を開催してきました。今回は実行委員と言いつつ、当日まであまり何もできなかったので、会場係として、明い緑のパーカーと言う季節もへったくれもない格好でいろいろ動きまわってました。

実は企画自体はRubyKaigi2009の直後ぐらいから @ysakaki さんといろいろ妄想を語ってたのですが、当時のスタッフの都合などもあり、なかなか開催に踏み切れませんでした。そんなとき、RubyWorldConference 2010での @a_matsuda さんの講演を聞いて、「これはやらんといかんな!」と思い立ち、急遽開催することになりました。少ない準備期間でこれだけの資料を準備していただいたスピーカーの方々、また当日の設営と撤収作業を手伝っていただいた当日スタッフの方々、そして事前の準備に奔走したスタッフの面々、そして隣で仕事と共に頑張ってくれた副実行委員長、本当にありがとうございました!このRailsDevConは定期的に開催できればいいなーと思ってるので、その時はまたみなさんご参加ください。

そして次は TokyuRubyKaigi 03 の司会へと続きます。

 
このエントリーを含むはてなブックマークはてなブックマーク - RailsDevCon2010を開催しました! この記事をクリップ!Livedoorクリップ - RailsDevCon2010を開催しました! Googleブックマークに追加 Digg This
Tags: ,
2010/08/31  |  Written by  |  under Blog

  • softbank 回線は絶望的
  • wifi も人数が多いせいか絶望的
  • ここで emobile も絶望的っぽいので、回線はあきらめることにした

2010 2日目

  • 昨日疲れたからか、かなりよく寝れたっぽいのが功を奏している気がした。

会議

  • 参加者の皆さんにより、いい情報交換ができました。
  • 後ほど別にまとめよう

基調講演: Matz

  • 2.0
    • Ruby 1.9.2
      • Ruby 1.9.2 Award
        • 遠藤さん( @mametter )
  • キーノートの苦悩
  • Ruby 2.0 (笑)
  • 完璧に近い言語が、ある種限界
  • Ruby は十分に良い言語
  • つくばくんだり
  • Matz’s Goal: To Make Ruby nearly perfect
  • “珍しくRubyのプログラムが書かれている”
  • 積み残しがある
    • Local variable propagation
      • 使われていたら自動的にブロックの外側へ伝播させる
        • 「でも重箱の隅をつつくようなことは気にしないよ!」と言われる
    • Mix-in defect
      • インクルード時の構造が取り込まれるので、後で追加しても反映されない
    • No private method
    • Global monkey patching
      • Classbox
    • Integer division
      • 5 / 2 #=> 2
      • mathn ?
        • mathn + Classbox
  • Mix-in defect
    • Inheritance : 継承
    • LSP : Liscov Substitution Princible
      • Liscov 置換原理
    • undef があるので LSP じゃない
    • Ruby の継承は方としての整合性などは重要視されていない
      • 多重継承もないし
    • そこで Mix-in
      • 多重継承の使い方の一つの方法
    • Non primary class == Module
    • Problem
      • 名前の衝突を検出できない
        • 解決法もない
      • 継承関係の変更に追従できない、とか
    • 名前の衝突の検出
      • 意図的か?事故なのか?
      • alias を使った解決法は泥臭い
      • alias もコンフリクトする
  • mix
    • Traits-like ( from smalltalk )
    • 本当に”混ぜ”る
    • mix
      • モジュールのメソッドをすべてコピーする
      • 名前が重なるとエラー
      • mix だと定数が取り込まれない
      • mix 時に名前をどうするかを指定できる
      • 明示的に取り込む定数を指定できたりする
      • 定数も被らないように変更することができる
    • include よりも mix が短い
    • まだ真の private method が無いことが問題になる
      • そこで classbox
  • Ruby 2.0
    • もうすぐ始まる
    • 1.9 からはそれほど変更ないかも
    • Traits
    • Classbox
    • キーワード変数
    • 互換性に問題のない些細な問題
  • mix は順番によって違わないことが重要
  • “kind_of? とかくそ食らえ”

KSP

  • つつがなく終了
  • “あなたが想像していたかくたにさんでしたか?”

M-x ruby-and--workshop

  • iknow の zev さん主催
  • 裏番組が JRubyKaigi 2010 と Vim とペアプロという、これまた熾烈な戦い
  • すべて emacs の中で実行しています。
  • emacs 24 では ELPA がデフォルトになるらしい
  • irbsh
  • M-x occur
  • M-x で複数のスペースが一つになる

UNIX 修正主義

  • akr さん
  • ちゃんと聞けてなかったので他の人のを見ることにしよう

Lightning Talks

 
このエントリーを含むはてなブックマークはてなブックマーク - RubyKaigi 2010 2nd day この記事をクリップ!Livedoorクリップ - RubyKaigi 2010 2nd day Googleブックマークに追加 Digg This
Tags: , , , , , ,
 |  Written by  |  under Blog


2010 1日目

Opening

  • Theme
    • Conflicts and Resolutions
  • Good Timing
  • Start!

諸注意

  • 名札忘れないように
    • モバイルルータもダメそうだ
  • フィードバックもしよう
  • 大ホールは飲食禁止
  • 各種諸注意
  • コミュニティ・ナイト
    • 自分の分を持っていろいろ集まれー

Conflicts and Resolutions

  • 漫談形式らしい
    • @takahashim
    • @a_matsuda
    • @wycats
    • @tenderlove
      • translator
        • @matz
        • @lchin

質問など

  • Different 2 and 3
    • Rails 3 は 2 の上位互換
    • いろんなところを書き換えているが、APIはそれほど変わっていない
    • コアな部分を切り離したので、各パーツを独立で使うことができる
  • merb と rails の統合で、当初描いていたゴールにたどり着いた?
    • たどり着いている!見返してみてもその通りだと感じる。
    • AR の代替物を簡単に使えるようになった
  • Ruby 1.9 のいいところと、はまったところを紹介してほしい
    • Python 3 と違って 1.8 と互換性のあるプログラムを書くことができるところ
    • エンコーディングについてが問題だった
      • 良かった点は、エンコードについてちゃんと考えるようになった
    • Rails が依存するライブラリは同時に 1.9 に対応した
      • ライブラリは早急に 1.9 で動くようにするべきだ
    • ruby-trunk でテストしていたので問題なくリリースできた
    • Rails 3.0 は来週リリースされるが、Ruby 1.9.2 で問題なく動くことを信じている
  • Ruby と Rails のコアコミッターになったのはなぜだと思う?
    • たくさん文句を言ったから
    • たくさんバグレポートを出してたくさんパッチを書いたから
      • Rails に関して言えば、熱意を出せばコミッターになれるよ
      • ただしパッチにはテストを書いてくれ
  • tenderlove は Rails 3 についてどう思う?
    • ActiveRecord については独立したコンポーネントとして利用できる状態ではないことが残念な点
      • ActiveRelation(Arel) を入れることで抽象化できるようになった
        • でもまだ作業が必要
    • 心配しないでも Rails 3.1 がでるから
  • Ruby と Rails のコアチームの違いをどう感じる?
    • 日本語と英語の間の壁が大きいようだ
  • 言い残したこと
    • お互いの言語を学ぶのがいいと思う
    • Ruby と Rails が共通の CI をも持てばいろいろいいんじゃないだろうか
    • 1.9 に移行しようぜ!

on Rails 3.0

  • いろいろ疲れました…
  • sub-class 化する方が alias_method_chain するよりも良かったらしい by @wycats
    • Rails 3.0 Release して Mail を理解したら変更してみようかな

Ruby 親方会議 2010

  • 親方会議の紹介
    • 「Ruby を使って仕事をしているエンジニア社長や個人事業者の情報交換をする」を公開で。
  • RubyKaigi 2009 -> Sapporo RubyKaigi #02 -> toRuby Kaigi 02 -> Sendai RubyKaigi 02
  • rake:money があるので、社員を雇用している社長をパネラーに迎えて情報交換
  • なぜ親方会議をやるのか
    • 社長は孤独
      • 仲間がほしい!
      • エンジニア仲間が欲しい
  • オフレコの話もあるので気をつけて!

自己紹介

  • 新井さん
    • もぐらの CTO
    • ビジネスの勉強をする勉強会を開催している
      • プログラムを価値あるものにするために
  • 山崎さん
    • スケールアウト CEO
    • 強力なメンツががっ!
  • 和田さん
    • ホームページがないことで有名な会社「タワーズ・クエスト」の社長です
    • 起業20年目で二代目社長
  • 大場さん
    • 万葉の社長様
    • 17:30 から Hyper Everyleaf Time!

起業したきっかけ

  • 藤岡さん
    • 何かやりたかったので起業してみた
  • 新井さん
    • 言われたものをつくるのではなくて、自分で考えたものを作りたかったので起業した
  • 山崎さん
    • 将来的に窓際っぽくなる気がしたので辞めた
  • 和田さん
    • プログラム書きすぎて留年しました
    • エンジニアとしての知名度が上がったので
  • 大場さん
    • 最初はIMEの大手でキャリアプランが見えなくなったので
  • 最初に採用するのって大変じゃないですか?
    • まあその場のフィーリングっぽい感じで決まったっぽい (万葉)
  • 解雇するのって大変
  • 何を聞きたくてやってきましたか?
    • 社員として企業の利益に貢献できるようになるにはどうすればいいのか?
      • 新井さん
        • 社長と同じ目線で同じ感覚で話をできるといいんじゃないかと思う
        • どうすれば利益が出るのかを考えているのかはいいことです
      • 山崎さん
        • ソフトウェアのコピーはただであるということを、ビジネスに生かせればいいのではないか?
      • 大場さん
        • 利益を出すことと、生きていくことは必ずしも一致しない
      • 山崎さん
        • 会社は4番バッターだけじゃなくて、職人が欲しいときもある
  • 人を獲得していくために取り組んでいること
    • 和田さん
      • どういう人かわからないので採用にはリスクがある
      • 素をさらすことに気をつけている
    • 大場さん
      • 露出を増やす
      • 法律を守る
      • 嫌なことをしない
    • 山崎さん
      • ゲリラ戦法しかない
      • 人と会わないとどういう人か、どういうスキルかわかならない
      • 面白い仕事を準備している
    • 新井さん
      • 勉強会を主催している
      • ハードル高めの内容だといいんじゃないかと思われる
    • 藤岡さん
      • いきなり応募してきたりして困ることがある
      • で応募してくるのが、いまのところあたりっぽい
  • 会社の目標と個人の目標
    • 藤岡さん
      • 会社: 自分がいなくても回る会社にする
      • 個人: 何か違うことをしたい
    • 新井さん
      • 会社: 会社として大きくしたい
      • 個人: 勉強して社会貢献したい
    • 山崎さん
      • 会社: 会社を大きくする
      • 個人: 技術が省力化に繋がっていないのがちょっとあれだなと思う。自分の本当にやりたいことができるようになることを目指している。
    • 和田さん
      • 会社: 会社を大きくする
      • 個人: 自分が書いたソフトウェアで働き過ぎを防げるようになることが大義。ゆとりを作れるようなソフトウェアを作って社会貢献にしたい。
    • 久保さん
      • 会社: 会社を大きくする
      • 個人: 個人の時間を作れるような環境を社員に提供したい
  • GIGAZINE 問題ではどう?
  • 他にもチョイスがあるにもかかわらず、弱小企業を選んでくれた人材にどう報いるのか?
    • 山崎さん
      • ストックオプションなどはないのでどうすればいいのか?いまは給料を高めにすることで報いようとしている。
    • 藤岡さん
      • 地元の雇用を守ること
    • 新井さん
      • 高い給与を出すことで報いようとしている
    • 和田さん
      • 経済的に報いるのは中長期的にやらなければいけないので、やっていく。
      • ソフトウェア産業の儲け方は特殊なので、いろいろ考えていきたい。
      • 人それぞれの報い方をやっていきたい。
  • 起業するときのパートナーはどう?
    • 藤岡さん
      • 欲しかったけどいなかった
    • 新井さん
      • 一人ではいろいろわからないことがあったので、パートナーを見つけて起業した
    • 山崎さん
      • 二人だとそれはコストになる。営業とか
      • アメリカだとスタートアップ資金が入ることがあるが、日本はない
      • 追い込まれると、お金に関していろいろある
    • 新井さん
      • 営業の人だとそれまでフリーでやってもらうとか
      • パートナーにするなら、フリーや起業したことある人の方がいい
    • 和田さん
      • 血縁は入れない方がいい
    • 久保さん
      • 長年の友人で、仕事も共にやっていた
      • 何で言い合おうと思ってやっています。

参考文献

  • ネットワーク経済の法則
  • スティグリッツミクロ経済学
  • Eric Sink on the Business of Software 革新的ソフトウェア企業の作り方 [単行本(ソフトカバー)]
    • http://www.amazon.co.jp/Eric-Sink-Business-Software-%E9%9D%A9%E6%96%B0%E7%9A%84%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E4%BC%81%E6%A5%AD%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9/dp/4798117501
 
このエントリーを含むはてなブックマークはてなブックマーク - RubyKaigi 2010 1st day この記事をクリップ!Livedoorクリップ - RubyKaigi 2010 1st day Googleブックマークに追加 Digg This
Tags: , , , , , , , , ,
2010/06/28  |  Written by  |  under Blog

と言うわけで、Rack化してみました。

darashi's jpmobile at rack – GitHub

Github上のrackブランチがそれにあたります。

with on GAE

以下で実験中です。ちゃんと絵文字と漢字コードが変換されたときはちょっとうれしかったりしました。

http://rust-stnard.appspot.com/

使い方

特に難しいことはなく、以下のような感じで動作させることができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding: utf-8 -*-
require 'rubygems'
require 'sinatra'
$LOAD_PATH << './lib/jpmobile/lib'
require 'jpmobile'
require 'jpmobile/rack'

require 'pp'

use Jpmobile::Rack::MobileCarrier
use Jpmobile::Rack::ParamsFilter
use Jpmobile::Rack::Filter

get '/' do
  erb :index
end
1
2
3
4
5
6
7
8
<html>
  <head>
    <title>そうでもない</title>
  </head>
  <body>
    &#xe63e;言わずもがな&#xe668;
  </body>
</html>

Encoding on 1.9

またパラメータのEncodingも変換しています。Rack::Request#paramsが必ずASCII-8BITになるので、それを無理矢理UTF-8に戻しています。

そして次は

3.0 on Ruby 1.9 で動作する jpmobile と言うのが目標です。と言うかRubyKaigi 2010までには何とかしないとダメなので、7月初旬にはリリースできるようにしたいと思っています。 3.0対応のパッチとか送ってもらっているのですが、Rack化でいろいろ変わったのと、見直したいところも多々あるので、しばしお待ちください。すんません…

 
このエントリーを含むはてなブックマークはてなブックマーク - Sinatra with jpmobile この記事をクリップ!Livedoorクリップ - Sinatra with jpmobile Googleブックマークに追加 Digg This
Tags: , , ,

前々回、前回の続きです。次はMySQLドライバを調べてみます。

ActiveRecordからRuby/MySQLへ

さてアクティブチェックしては、connection_adaptors/mysql_adaptor.rbactive?メソッドでした。

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
      def active?
        if @connection.respond_to?(:stat)
          @connection.stat
        else
          @connection.query 'select 1'
        end

        # mysql-ruby doesn't raise an exception when stat fails.
        if @connection.respond_to?(:errno)
          @connection.errno.zero?
        else
          true
        end
      rescue Mysql::Error
        false
      end

ここでは、@connection.stat@connection.query 'select 1'が呼び出されます。その前に、まず@connectionとはなにかと言うと、親クラスであるAbstractAdapterの38行目にあるinitializeメソッドで作成されるインスタンス変数です。ファイルはconnection_adaptors/abstract_adaptor.rbです。

38
39
40
41
42
43
      def initialize(connection, logger = nil) #:nodoc:
        @connection, @logger = connection, logger
        @runtime = 0
        @last_verification = 0
        @query_cache_enabled = false
      end

このメソッドはconnection_adaptors/mysql_adaptor.rbの200行目で呼び出されるわけです。このクラスは同じファイルの75行目でインスタンスを生成しています。

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
    # Establishes a connection to the database that's used by all Active Record objects.
    def self.mysql_connection(config) # :nodoc:
      config = config.symbolize_keys
      host     = config[:host]
      port     = config[:port]
      socket   = config[:socket]
      username = config[:username] ? config[:username].to_s : 'root'
      password = config[:password].to_s
      database = config[:database]

      # Require the MySQL driver and define Mysql::Result.all_hashes
      unless defined? Mysql
        begin
          require_library_or_gem('mysql')
        rescue LoadError
          $stderr.puts '!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.'
          raise
        end
      end

      MysqlCompat.define_all_hashes_method!

      mysql = Mysql.init
      mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]

      default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0
      options = [host, username, password, database, port, socket, default_flags]
      ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
    end
  end

この引数mysqlはその前のMySQL.initで生成されています。このMySQLはRubyのMySQLドライバであるMySQL/Rubyのことです。

/Rubyを見てみる

さてこのMySQL/Rubyですが、拡張ライブラリなのでC言語で書かれています。また内部的にはMySQL APIを呼び出しているので、適宜両方見ていきます。

まず、statメソッドがあればそれが呼ばれるので、そちらを調べると、ext/mysql_api/mysql.cの1978行目にstatメソッドの定義があります。

1
  rb_define_method(cMysql, "stat", my_stat, 0);

細かいことは省きますが、MySQL#statが呼ばれるとmy_statが実行されます。ではそのmy_statは693行目にありました。

693
694
695
696
697
698
699
700
701
/*  stat()    */
static VALUE my_stat(VALUE obj)
{
  MYSQL* m = GetHandler(obj);
  const char* s = mysql_stat(m);
  if (s == NULL)
    mysql_raise(m);
  return rb_tainted_str_new2(s);
}

ここでGetHandlerはマクロで、構造体の中にある接続ハンドラを取得しています。で、mysql_statという関数を実行して結果を判定しているわけですね。ではこのmysql_statはと言うと、MySQL 5.0のリファレンスマニュアルには「mysqladmin statusと同じ結果を返す」とだけ書かれています。

MySQLのソースを読む

あまりC/C++をやった経験がないのですが、とりあえず調査続行と言うことでMySQL本体のソースを追ってみます。まずはダウンロードして展開すると、libmysql/libmysql.cと言うファイルが見つかります。前述のは、いわゆる共有ライブラリ経由での接続なので、ここを見ればいいと思われます。このファイルの1407行目にmysql_statがありました。

1407
1408
1409
1410
1411
1412
1413
1414
const char * STDCALL
mysql_stat(MYSQL *mysql)
{
  DBUG_ENTER("mysql_stat");
  if (simple_command(mysql,COM_STATISTICS,0,0,0))
    DBUG_RETURN(mysql->net.last_error);
  DBUG_RETURN((*mysql->methods->read_statistics)(mysql));
}

どうやらsimple_commandを呼び出しているだけのようです。simple_commandinclude/mysql.hの852行目に定義されているマクロです。

852
853
854
#define simple_command(mysql, command, arg, length, skip_check) \
  (*(mysql)->methods->advanced_command)(mysql, command, 0,  \
                                        0, arg, length, skip_check, NULL)

mysql経由でadvanced_commandを呼び出しています。mysqlinclude/mysql.hの258行目で定義されている構造体です。

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
typedef struct st_mysql
{
  NET   net;      /* Communication parameters */
  unsigned char *connector_fd;    /* ConnectorFd for SSL */
  char    *host,*user,*passwd,*unix_socket,*server_version,*host_info;
  char          *info, *db;
  struct charset_info_st *charset;
  MYSQL_FIELD *fields;
  MEM_ROOT  field_alloc;
  my_ulonglong affected_rows;
  my_ulonglong insert_id;   /* id if insert on table with NEXTNR */
  my_ulonglong extra_info;    /* Not used */
  unsigned long thread_id;    /* Id for connection in server */
  unsigned long packet_length;
  unsigned int  port;
  unsigned long client_flag,server_capabilities;
  unsigned int  protocol_version;
  unsigned int  field_count;
  unsigned int  server_status;
  unsigned int  server_language;
  unsigned int  warning_count;
  struct st_mysql_options options;
  enum mysql_status status;
  my_bool free_me;    /* If free in mysql_close */
  my_bool reconnect;    /* set to 1 if automatic reconnect */

  /* session-wide random string */
  char          scramble[SCRAMBLE_LENGTH+1];

 /*
   Set if this is the original connection, not a master or a slave we have
   added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave()
 */

  my_bool rpl_pivot;
  /*
    Pointers to the master, and the next slave connections, points to
    itself if lone connection.
  */

  struct st_mysql* master, *next_slave;

  struct st_mysql* last_used_slave; /* needed for round-robin slave pick */
 /* needed for send/read/store/use result to work correctly with replication */
  struct st_mysql* last_used_con;

  LIST  *stmts;                     /* list of all statements */
  const struct st_mysql_methods *methods;
  void *thd;
  /*
    Points to boolean flag in MYSQL_RES  or MYSQL_STMT. We set this flag
    from mysql_stmt_close if close had to cancel result set of this object.
  */

  my_bool *unbuffered_fetch_owner;
  /* needed for embedded server - no net buffer to store the 'info' */
  char *info_buffer;
  void *extension;
} MYSQL;

この中のmethodsst_mysql_methodsと言う構造体で、753行目に定義されています。

753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
typedef struct st_mysql_methods
{
  my_bool (*read_query_result)(MYSQL *mysql);
  my_bool (*advanced_command)(MYSQL *mysql,
            enum enum_server_command command,
            const unsigned char *header,
            unsigned long header_length,
            const unsigned char *arg,
            unsigned long arg_length,
            my_bool skip_check,
                              MYSQL_STMT *stmt);
  MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
         unsigned int fields);
  MYSQL_RES * (*use_result)(MYSQL *mysql);
  void (*fetch_lengths)(unsigned long *to,
      MYSQL_ROW column, unsigned int field_count);
  void (*flush_use_result)(MYSQL *mysql);
#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
  MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
  my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
  int (*stmt_execute)(MYSQL_STMT *stmt);
  int (*read_binary_rows)(MYSQL_STMT *stmt);
  int (*unbuffered_fetch)(MYSQL *mysql, char **row);
  void (*free_embedded_thd)(MYSQL *mysql);
  const char *(*read_statistics)(MYSQL *mysql);
  my_bool (*next_result)(MYSQL *mysql);
  int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
  int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
#endif
} MYSQL_METHODS;

だんだん長くなってきました。さてこれはMYSQL_METHODS型なのですが、定義しかありません。実体はと言うと、sql-common/client.cの1885行目で代入されている、client_methodsです。

1885
1886
1887
1888
1889
  /* Don't give sigpipe errors if the client doesn't want them */
  set_sigpipe(mysql);
  mysql->methods= &client_methods;
  net->vio = 0;       /* If something goes wrong */
  mysql->client_flag=0;     /* For handshake */

で、このclient_methodsは1763行目で定義されています。

1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
static MYSQL_METHODS client_methods=
{
  cli_read_query_result,                       /* read_query_result */
  cli_advanced_command,                        /* advanced_command */
  cli_read_rows,                               /* read_rows */
  cli_use_result,                              /* use_result */
  cli_fetch_lengths,                           /* fetch_lengths */
  cli_flush_use_result                         /* flush_use_result */
#ifndef MYSQL_SERVER
  ,cli_list_fields,                            /* list_fields */
  cli_read_prepare_result,                     /* read_prepare_result */
  cli_stmt_execute,                            /* stmt_execute */
  cli_read_binary_rows,                        /* read_binary_rows */
  cli_unbuffered_fetch,                        /* unbuffered_fetch */
  NULL,                                        /* free_embedded_thd */
  cli_read_statistics,                         /* read_statistics */
  cli_read_query_result,                       /* next_result */
  cli_read_change_user_result,                 /* read_change_user_result */
  cli_read_binary_rows                         /* read_rows_from_cursor */
#endif
};

もうちょい続きそうです。で、ここにあるcli_advanced_commandはと言うと、764行目にありました。

1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
my_bool
cli_advanced_command(MYSQL *mysql, enum enum_server_command command,
         const uchar *header, ulong header_length,
         const uchar *arg, ulong arg_length, my_bool skip_check,
                     MYSQL_STMT *stmt)
{
  NET *net= &mysql->net;
  my_bool result= 1;
  init_sigpipe_variables
  my_bool stmt_skip= stmt ? stmt->state != MYSQL_STMT_INIT_DONE : FALSE;
  DBUG_ENTER("cli_advanced_command");

  /* Don't give sigpipe errors if the client doesn't want them */
  set_sigpipe(mysql);

  if (mysql->net.vio == 0)
  {           /* Do reconnect if possible */
    if (mysql_reconnect(mysql) || stmt_skip)
      DBUG_RETURN(1);
  }
  if (mysql->status != MYSQL_STATUS_READY ||
      mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
  {
    DBUG_PRINT("error",("state: %d", mysql->status));
    set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
    DBUG_RETURN(1);
  }

  net_clear_error(net);
  mysql->info=0;
  mysql->affected_rows= ~(my_ulonglong) 0;
  /*
    We don't want to clear the protocol buffer on COM_QUIT, because if
    the previous command was a shutdown command, we may have the
    response for the COM_QUIT already in the communication buffer
  */

  net_clear(&mysql->net, (command != COM_QUIT));

  if (net_write_command(net,(uchar) command, header, header_length,
      arg, arg_length))
  {
    DBUG_PRINT("error",("Can't send command to server. Error: %d",
      socket_errno));
    if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
    {
      set_mysql_error(mysql, CR_NET_PACKET_TOO_LARGE, unknown_sqlstate);
      goto end;
    }
    end_server(mysql);
    if (mysql_reconnect(mysql) || stmt_skip)
      goto end;
    if (net_write_command(net,(uchar) command, header, header_length,
        arg, arg_length))
    {
      set_mysql_error(mysql, CR_SERVER_GONE_ERROR, unknown_sqlstate);
      goto end;
    }
  }
  result=0;
  if (!skip_check)
    result= ((mysql->packet_length=cli_safe_read(mysql)) == packet_error ?
       1 : 0);
end:
  reset_sigpipe(mysql);
  DBUG_PRINT("exit",("result: %d", result));
  DBUG_RETURN(result);
}

だんだん面倒になってきたので、これぐらいにしておいて上から順に見ていくと、いろいろ初期化して何らかの事情で切断されたことを知っていれば(mysql->net.vio == 0)再接続。続いて準備をしてコマンド(COM_STATISTICS)送信(net_write_command)し、結果を返して終わる。

まとめ

長くなりましたが、MySQL/RubyのstatメソッドではCOM_STATISTICSを送信していると言うことがわかりました。これは統計情報を取得しようとしていると言うことで、件の負荷上昇に原因となります。ただ、じゃあ外せばいいかというと、Rails + MySQL (+ Mongrel?) でDB接続の通信が無い状態が続くとデッドロックする。 – こせきの技術日記と言うこともあるのでそう簡単ではないようです。今回の場合はRubyではTCP接続をしていないのでMongrelの問題は発生しませんが、負荷軽減を目論んでアクティブチェックを外す場合には、フレームワークやドライバまでちゃんと調べた方が良さそうだと感じました。

 
このエントリーを含むはてなブックマークはてなブックマーク - RubyのMySQLドライバを見る &#8211; アクティブチェック問題 この記事をクリップ!Livedoorクリップ - RubyのMySQLドライバを見る &#8211; アクティブチェック問題 Googleブックマークに追加 Digg This
Tags: , , , ,

前回の話を受けて、Railsの場合はどうなのかを調べてみたいと思います。ターゲットはRails 2.3.5です。

ActiveRecordに潜る

RailsのO/R MapperはActiveRecordです。今回はActiveRecordでMySQLの場合について調べます。

まずはアクティブチェックしている部分を探します。ざっと見ると、connection_adaptors/mysql_adaptor.rbの275行目にactive?メソッドがありました。

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
      def active?
        if @connection.respond_to?(:stat)
          @connection.stat
        else
          @connection.query 'select 1'
        end

        # mysql-ruby doesn't raise an exception when stat fails.
        if @connection.respond_to?(:errno)
          @connection.errno.zero?
        else
          true
        end
      rescue Mysql::Error
        false
      end

うぉ、SELECT 1とかそのままじゃないか。とりあえず次はこれを呼び出しているところを探します。するとconnection_adaptors/abstract_adaptor.rbの149行目にverify!(*ignored)メソッドがありました。

147
148
149
150
151
152
      # Checks whether the connection to the database is still active (i.e. not stale).
      # This is done under the hood by calling <tt>active?</tt>. If the connection
      # is no longer active, then this method will reconnect to the database.
      def verify!(*ignored)
        reconnect! unless active?
      end

アクティブでなければ再接続するようです。ではこのメソッドはどこで呼ばれているのかというと、connection_adaptors/abstract/connection_pool.rbの255行目にあるcheckout_and_verifyメソッドです。

255
256
257
258
259
260
      def checkout_and_verify(c)
        c.verify!
        c.run_callbacks :checkout
        @checked_out << c
        c
      end

ここで接続確認して接続を取得、それをインスタンス変数@checked_outに保存しているわけです。まだ続きますが、ではこのメソッドはと言うと、すぐ上にあるchechout_new_connectioncheckout_existing_connectionメソッドで呼び出されています。

244
245
246
247
248
249
250
251
252
253
      def checkout_new_connection
        c = new_connection
        @connections << c
        checkout_and_verify(c)
      end

      def checkout_existing_connection
        c = (@connections - @checked_out).first
        checkout_and_verify(c)
      end

まだ続きそうです。では、これはと言うと同じファイルの181行目にあるcheckoutメソッドで呼び出されているようです。

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
      def checkout
        # Checkout an available connection
        @connection_mutex.synchronize do
          loop do
            conn = if @checked_out.size < @connections.size
                     checkout_existing_connection
                   elsif @connections.size < @size
                     checkout_new_connection
                   end
            return conn if conn
            # No connections available; wait for one
            if @queue.wait(@timeout)
              next
            else
              # try looting dead threads
              clear_stale_cached_connections!
              if @size == @checked_out.size
                raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}.  The max pool size is currently #{@size}; consider increasing it."
              end
            end
          end
        end
      end

さてさて、ではこのメソッドはどこから呼ばれるかというと、同じファイルにあるconnectionwith_connectionメソッドのようです。

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
      # Retrieve the connection associated with the current thread, or call
      # #checkout to obtain one if necessary.
      #
      # #connection can be called any number of times; the connection is
      # held in a hash keyed by the thread id.
      def connection
        if conn = @reserved_connections[current_connection_id]
          conn
        else
          @reserved_connections[current_connection_id] = checkout
        end
      end

      # Signal that the thread is finished with the current connection.
      # #release_connection releases the connection-thread association
      # and returns the connection to the pool.
      def release_connection
        conn = @reserved_connections.delete(current_connection_id)
        checkin conn if conn
      end

      # Reserve a connection, and yield it to a block. Ensure the connection is
      # checked back in when finished.
      def with_connection
        conn = checkout
        yield conn
      ensure
        checkin conn
      end

とまあだいたいこんな感じで、ActiveRecord::Base.connection.execute("SELECT * FROM users")が実行されるときには、active?メソッドが呼び出されているわけです。実際に、例えばp "Active Check!"なんてのを仕込むと毎回表示されます。これはproduction環境でも変わりませんでした。

さて次回はactive?のところでは実際は何が呼び出されるのかを調べるために、MySQLドライバを見てみたいと思います。

 
このエントリーを含むはてなブックマークはてなブックマーク - ActiveRecordでの接続チェックはどこでやっているのか この記事をクリップ!Livedoorクリップ - ActiveRecordでの接続チェックはどこでやっているのか Googleブックマークに追加 Digg This
Tags: , , , ,

例によって気になったので意訳。

Checking for a live database connection considered harmful on Performance Blog

顧客のデータベースでよく見かけて注意するのだが、クエリーを送信する前にデータベース接続がアクティブかどうかをチェックするのは大きなオーバーヘッドになる。これは、次のような擬似コードで書かれるデザインパターンに由来する。

1
2
3
4
5
6
function query_database(connection, sql)
   if !connection.is_alive() and !connection.reconnect() then
      throw exception
   end
   return connection.execute(sql)
end

多くの開発環境やフレームワークで、こういうコードになっている。これには、実際には期待したとおりには動かないと言うことと、大きなパフォーマンスのオーバーヘッドがあると言う2点で間違っている。
2つの間違いがあり

実はちゃんと動かない

このコードはレースコンディション(競合状態)によって動作しない。もしチェックしたときに接続がアクティブだとしても、connection.execute(sql)を実行するときにアクティブだとは保証されない。さらにもし非アクティブで再接続したとしても、同様にアクティブであるとは保証されない。

チェックして実行するのは実用的ではない。代わりに、次のように書き換えるべきだ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function query_database(connection, sql, retries=1)
   while true
      try
         result=connection.execute(sql)
         return result
      catch InactiveConnectionException e
         if retries> 0 then
            retries = retries - 1
            connection.reconnect()
         else
            throw e
         end
      end
   end
end

is_active()が無くなったのに気付いただろうか。接続がアクティブであればクエリーが実行され、そうでなければ失敗して再接続し、再度実行しようとする。

このコードだと、必要に応じてロック待ちのタイムアウトやデッドロックのときに再試行することができるようになっている。私の経験上、多くのアプリケーションで有効である。ほとんどのアプリケーションでは、こう言うときには単純に再試行するだけで、ちゃんと扱おうとはしていない。

パフォーマンスのオーバーヘッド

アクティブチェックには大抵の場合、MySQLのプロトコルレベルのコマンドであるpingstatisticsの呼び出しか、SELECT 1のような自明なクエリーが実行される。前者のコマンドはSHOW GLOBAL STATUSで表示されるCom_admin_commandsをインクリメントし、後者のクエリーは診断を難しくする。これは多くのアプリケーションで非常に高コストとなる。ここには2つのコストがある。1つはネットワーク通信とクエリー実行時間のアプリケーションへのコストで、もう1つはデータベースサーバの負荷上昇。このデータベースサーバへの負荷はかなり大きい。何日か前に、「管理コマンドのstatistics」を使うRuby on Railsアプリケーションを見たが、全クエリー実行時間の40%がこのコマンドだった。この不必要な接続チェックを削除したところ、データベースの負荷が半分程度に削減できた。これは普通じゃない!

アプリケーションのクエリーが長いとき、追加のクエリーはノイズの中で消えてしまう。しかし高トラフィックアプリケーションはクエリー実行時間を短くするのに途方もない努力を費やし、いくつかのチューニングしたアプリケーションでは、クエリーの実行時間がミリ秒より長くならないかドキドキしている。もしデータベースで毎秒20,000クエリー走っているなら、コネクションチェックも毎秒20,000回行われていることになる。これらpingstatisticsと言うクエリーは、アプリケーションで実行すべきクエリーと同じぐらいコストがかかっているんだ。

これはデータベースサーバへの負荷であった。アプリケーション側では、クエリー実行時間の2倍の遅延があるのがわかるだろう。クエリーを実行するときには、そのアプリケーションフレームワークがチェックのためにネットワーク通信をして、さらに別のネットワーク通信でクエリーを実行する。これもやっぱり問題だ。

問題なのは、さっきの擬似コードがレアケースに気を取られて一般的な場合にペナルティーを課しているところ。普通は接続は生きていて、確認したり再接続したりはしなくてもいい。良い方法は、レースコンディションを解決したコードを使うことだ。もし接続が切れていても、クエリーを実行するときに探せばいいだけだ。そのときまでは、全てがOKで、クエリーを実行できる。

問題となるライブラリのアップストリームメンテナが、この問題を見つけ出して解決することを願っている。アプリケーションが成長するときの大きな手助けになるからだ。ラボではうまくいっていて、現場でもそうであったとしても、パフォーマンスはすぐに問題となる。そしてそれはすごく目立つんだ。

追記

この馬鹿げた結果を見て欲しい。

1
2
3
4
5
6
7
8
9
# Rank Query ID           Response time    Calls  R/Call   Item
 # ==== ================== ================ ====== ======== ===============
 #    1 0x5E796D5A4A7D1CA9 10651.0708 73.1% 120487   0.0884 ADMIN STATISTICS
 #    2 0x85FFF5AA78E5FF6A  1090.0772  7.5%  23621   0.0461 BEGIN
 #    3 0x6E85B9A9C9FF813E   868.0335  6.0%   6923   0.1254 UPDATE scores
 #    4 0xA3A0423749EC0E37   851.0152  5.8%   6020   0.1414 UPDATE user_datas
 #    5 0x813031B8BBC3B329   822.0041  5.6%  23299   0.0353 COMMIT
 #    6 0xA873BBC4583C4C85   278.4533  1.9%   6985   0.0399 SELECT users user_devices
That's right, 73% of the server's load is consumed by checking to see if the connection is still alive

まとめ

これが本当だとすると、Rubyで使われているMySQLライブラリや、RailsのMySQLアダプタとかをちゃんと調べないとやばいんじゃないか?と言うわけで調査にはいります。

 
このエントリーを含むはてなブックマークはてなブックマーク - データベースアクティブチェックは負荷を上昇させるだけ この記事をクリップ!Livedoorクリップ - データベースアクティブチェックは負荷を上昇させるだけ Googleブックマークに追加 Digg This
Tags: , , , ,
2010/04/23  |  Written by  |  under Blog

型をt.binaryなどとすると、デフォルトではBLOBとなってしまい、64KiBまでしか保存できなくなってしまいます。そこで:limit => 1.megabyteとオプションで制限を大きめに書いてやると、Railsが勝手に拡張してMEDIUMBLOBにしてくれます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CreateImages < ActiveRecord::Migration
  def self.up
    create_table :images do |t|
      t.binary  :data, :limit => 1.megabyte
      t.integer :stat

      t.timestamps
    end
  end

  def self.down
    drop_table :images
  end
end
1
2
3
4
5
6
7
8
9
10
11
mysql> desc imags;
+----------------+------------+------+-----+---------+----------------+
| Field          | Type       | Null | Key | Default | Extra          |
+----------------+------------+------+-----+---------+----------------+
| id             | int(11)    | NO   | PRI | NULL    | auto_increment |
| data           | mediumblob | YES  |     | NULL    |                |
| stat           | int(11)    | YES  |     | NULL    |                |
| created_at     | datetime   | YES  |     | NULL    |                |
| updated_at     | datetime   | YES  |     | NULL    |                |
+----------------+------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
 
このエントリーを含むはてなブックマークはてなブックマーク - RailsでMySQLのMEDIUMBLOBを使いたいとき この記事をクリップ!Livedoorクリップ - RailsでMySQLのMEDIUMBLOBを使いたいとき Googleブックマークに追加 Digg This
Tags: ,