Hatena::ブログ(Diary)

アルミ缶の上にアルフォート このページをアンテナに追加 RSSフィード Twitter

2015-01-11

とあるMySQLとチョコレートの話

何回もログを仕込んでようやく原因がわかったバグを潰したメモです。


1ユーザーにつき1個だけチョコレートを作りたいとします。

※今回はDBのunique制約でどうこうするとかいうレベルではなく、チョコレートが2回作られる時点でアウトとします。

以下の挙動をもつAPIをつくります。

リクエストユーザー挙動
1まだチョコレートをもってないユーザーチョコレートを作ってあげて、作ったチョコレートを返す
2チョコレートをもっているユーザーチョコレートを返す

というわけで、以下のかんじで作ってみました。

function get_or_create_choco(user_id) {
  var choco = select_choco(user_id); //DB(master)からチョコレートをとる
  if (choco) {
    return choco;
  }
  else {
    choco = create_choco(user_id); //チョコレートを作ってDBにinsert
    return choco;
  }
}

これはだめぽよです。

リクエストが並列で2回きた場合、create_chocoが2回呼ばれてしまうのです。

というわけで、例えばmemcachedでロックを貼ってみました。


function get_or_create_choco(user_id) {
  var choco = select_choco(user_id); //DB(master)からチョコレートをとる
  if (choco) {
    return choco;
  }
  else {
    var lock = create_choco_lock(user_id) //memcachedで特定のキーでaddしてみる
    if (lock) { //ロック取得成功時
      choco = create_choco(user_id); //チョコレートを作ってDBにinsert
      return choco;
    }
    else { //ロック取得失敗時
   sleep 0.2;
      choco = select_choco(user_id); //DB(master)からチョコレートをとる
      return choco;
    }
  }
}


こうすれば、短期間にきた2回目のリクエストはロックを取得できないので、

0.2秒くらい休んだあとにDBを見に行けば、既に1回目のリクエストでチョコレートは作られているはずなので、

無事に作られたチョコレートを返せるはずです。


もう一工夫。これはかなり頻繁に叩かれる処理なので、ちょっとmemcachedを効かせてみます。


function get_or_create_choco(user_id) {
  var choco = select_choco(user_id); // memdからチョコを取る。なかったらDB(master)からチョコを取る。DBにあったらmemdにセットしてあげる。
  if (choco) {
    return choco;
  }
  else {
    var lock = create_choco_lock(user_id) //memcachedで特定のキーでaddしてみる
    if (lock) { //ロック取得成功時
      choco = create_choco(user_id); //チョコレートを作ってDBにinsert
      return choco;
    }
    else { //ロック取得失敗時
   sleep 0.2;
      choco = select_choco(user_id); // memdからチョコを取る。なかったらDB(master)からチョコを取る。DBにあったらmemdにセットしてあげる。
      return choco;
    }
  }
}

これで完成。

しかし低確率で、なぜかチョコレートを返せないときがありました。

もろもろログを増やしたところ、どうもロック取得失敗時に、select_chocoしてもチョコが取れないようなのです。つまり、2回目のリクエストにおいて、memdにもDBにもチョコがないと言われている。


色々原因を考えました。

・sleepが足りない?

 ・休む時間を伸ばしてあげてもダメ

・1回目のリクエストにおいて、memdやmysqlでのエラーはおきてる?

 ・おきてない

・チョコのmemdへのsetが失敗している?

・memdは複数台あるので、同じkeyなのに別のmemdを見に行ってる?

 ・memdになかったらDBにfallbackするし関係なさそう

・memd evictionしてる?

 ・してない

・memdに「チョコなかったよー」ってネガティブキャッシュしてる?

 ・してない


答えをいうと、REPEATABLE READとAutoCommit=0によるものでした。

AutoCommit=0でmasterに接続しており、コネクションを使いまわしていたので、2回のselect_chocoでトランザクションが継続していました ><

問題となるケースは、一番頭でselect_chocoをしていて空振っています。で、同じconnectionを使いまわしているので、それ以降は他のプロセスがいくらchocoを作っていても、いくらsleepしても「このユーザーはチョコがない」ことが確定してしまっています。シュレーディンガーの猫ですね。

そもそもcreate_chocoでmemdにセットすべきなのにしていないので、(短期間における)2回目のリクエストでは必ずDBを覗きにきてしまい、チョコがないことが確定しているのであぼーんという話。

基本はmaster DBからはちゃんとcommitして抜けているのですが、select_chocoのときだけ単なるselectじゃーんということで、commitしておりませんでした ><

あとはcommitとかコネクション使い回しとかがライブラリでいいかんじに隠蔽されてたのであんまり強く意識してなかったのも敗因です。。


最終的には、

(1)select_chocoではスコープから抜けるときにちゃんとcommitしてトランザクションを完結させる

(2)create_chocoでちゃんとチョコをmemdにもセットする

かんじで修正しました。


皆さんもシュレーディンガーの猫には気をつけましょう。単なるselectもcommitしないと罠になるかもよ。というお話なのでした。

2013-10-03

IRC_name_resolver です。

リファクタあわせて3時間くらい。

https://github.com/mosasiru/irc_name_resolver

2013/10/03追記

IRCで、/who

でいけましたね…ちゃんとrealname登録してろよ!って話でした。(完

使い方

登録

register {nickname} {realname} です。

register Ben Benjamin
# inr_bot "registerd: nick(Ben) => real(Benjamin)"

register Benny Benjamin
# inr_bot "registerd: nick(Benny) => real(Benjamin)"

複数のニックネームが登録できます。


正引き・逆引き

nickname から realname を知る

nick? Benjamin
# inr_bot: "Benjamin's nickname: Ben,Benny"

realname から nickname を知る

real? Ben
# inr_bot: "Ben's realname: Benjamin"
削除

ニックネームの対応付けを削除できます。

unregister Ben Benjamin
# inr_bot: "unregistered: nick(Ben) => real(Benjamin)"
その他

pingbotにおくったり、使い方を確認したり。

ping
# inr_bot: "pong"

how
# show usage.

社内用に作りましたが、使えそうならみなさんガンガン使ってpull-reqなげてください><

2013-10-02 IRCのnicknameと本名を対応付けるbot作った(IRC_name_resolver) このエントリーを含むブックマーク このエントリーのブックマークコメント

IRC_name_resolver です。

リファクタあわせて3時間くらい。

https://github.com/mosasiru/irc_name_resolver


使い方

登録

register {nickname} {realname} です。

register Ben Benjamin
# inr_bot "registerd: nick(Ben) => real(Benjamin)"

register Benny Benjamin
# inr_bot "registerd: nick(Benny) => real(Benjamin)"

複数のニックネームが登録できます。


正引き・逆引き

nickname から realname を知る

nick? Benjamin
# inr_bot: "Benjamin's nickname: Ben,Benny"

realname から nickname を知る

real? Ben
# inr_bot: "Ben's realname: Benjamin"
削除

ニックネームの対応付けを削除できます。

unregister Ben Benjamin
# inr_bot: "unregistered: nick(Ben) => real(Benjamin)"
その他

pingbotにおくったり、使い方を確認したり。

ping
# inr_bot: "pong"

how
# show usage.

社内用に作りましたが、使えそうならみなさんガンガン使ってpull-reqなげてください><

2013-09-23 Limechatのthemeいじった このエントリーを含むブックマーク このエントリーのブックマークコメント

https://github.com/mosasiru/Limelight_custom

デフォルトのLimelightいじっただけです。

yamlcssいじるだけなので、皆もいじりましょう^q^

2013-09-19

Perlなんて大嫌いですし、YAPC楽しみです

Perlなんて大嫌いです。



モジュールの最後の「1;」のダサさったらないし、$@とか諸々の省略形に吐き気がするし、オブジェクト指向は完全に後付けで「bless」でまず脱落しそうになったし、クラスメソッドインスタンスメソッドの区別もないし、アンスコ始まりをprivate methodとみなす紳士協定だし、インスタンス変数は外からいじり放題だし、$@がグローバル変数なのも罠だし、Test::Moreなどのモジュールはsub{}で頑張ってDSLっぽくしようとしているのがなんだか涙ぐましいし、ググり方次第でインターネット黎明期のしょうもないCGI記事がわんさか出てくるし、そもそもPerlスローガンTMTOWTDI(やり方は1つじゃない)」とか大嫌いだし(初心者は酷い書き方ができてしまうし、ベストなやり方を模索する必要があるし、それでいて他人と開発するときは全てのやり方を把握していないと読めない)、会社で使っていなければ習うこともなかったでしょう。



何様って話ですので、名乗ります

僕は大したエンジニアじゃないです。Perlは始めて半年もたってないし、ちゃんとプログラミング勉強したのはここ2年弱。RailsとかCoffeeScriptとかDevise(gem)とか、わかりやすいレールに乗って調子こいてウェブサービスを2,3個リリースした。それでいてライブラリとStackOverFlowの過去ログ頼みだったから全然地力がついていなかったことに入社後気付いた、そんな人間です。低レイヤーの話は全然わからないし、ライブラリも外に公開したことはありません。

RPGで例えるなら装備を充実させるばかりでまったくレベルがあがっていなかったようなかんじ。

たぶん最近の駆け出しウェブエンジニアの典型的な例じゃないでしょうか。強みがあるとすれば、アルゴリズムについてちょこっと詳しいくらいでしょうか。



そんな大した技術力もない人間がこういった文句を言ったら「言語にとやかく言う奴は結局どの言語もまともにできやしない」「CPANで全部解決できるし、できないならCPAN作れ」とか言う人がわらわらと出てくるでしょうし、真っ当な意見だとは思います。




Perlの現状

ただ、実際Perlの現状は結構悲しいものがあります。

QiitaとStackOverflowで記事数をちょっと調べてみたんですけど、他の言語との流行りっぷりの比較は、ちょっと血の気が引きます。


f:id:mosa_siru:20130920033209p:image

f:id:mosa_siru:20130920033210p:image


そんな中で、僕の言うことを素人の戯言として一蹴するのは簡単ですが、「おいおいそんなアグラ書いてていいのかよ」と思ってしまいます。なんだかそういうコミュニティの閉鎖感があるとしたら、すごい勿体無いなと。



YAPC

で、YAPC::Asia Tokyo 2013が始まるわけです。前夜祭も含めればもう始まってますね。Perlの世界的カンファレンスです。初めて参加させていただきます。


USTないの?って言われるたびに「ありません」と答えていたわけですが

その理由は「会場のネットワークが逼迫するから」「会場の運営に集中したいから」とか

色々ありますが、たった1つ明確なのは「会場に来てYAPCの一員となってほしいから」です。

YAPC::Asia にライブ映像配信がないたった1つの理由 | YAPC::Asia Tokyo 2013


とか言ってる場合なのかな、って思うのですよ。

もっと、開いた場にしようよ!って、純粋に思ってしまいます。




でもね

冒頭では言いたいこと言いましたが、実をいうと、なんだかんだでPerlに愛着湧いているわけです。なんだかんだで最早一番慣れた言語になっているし、CPANモジュールもpull-req送ろうと手を加えているし、YAPCでいろんな話が聞きたいし、コミュニティも実際どんなもんかと覗いてみたい。あわよくば混ざりたい。

ただ、もしアグラを書いているところがあるなら、それはどうなの、本当に大丈夫なの、って言いたい。

結局お前何が言いたかったんだよと思うかもしれませんが、YAPC期待していますし、Perlとそのエコシステムが、閉じないでどんどん洗練されていけばいいなと思います!

Connection: close