怖話Andyというユーザー名で登録しようとするとエラー。

Mysql2::Error - Duplicate entry 'Andy' for key 'index_users_on_name'

多分小文字のandyとぶつかってるんだろうな。

validates :name, uniqueness: trueしてるのに何故だろう。mysqlのcollationが変なのになってるのかなと確認してみるも問題無し。

uniquenessを確認している部分のSQLクエリをみてみると・・・

SELECT 1 AS one FROM `users` WHERE `users`.`name` = BINARY 'Andy' LIMIT 1

BINARY・・・だと・・・!?

deviseの設定でcase_insensitive_keysを設定しないとBINARYで確認しにいくようです。

# config/initializers/devise.rb:
config.case_insensitive_keys = [ :email, :name ]

ここにnameも追加してOK。configのコメントぐらいちゃんと読んどけってことですな。

starseeker

「usefulなライブラリを求めてるのにstarseekerを使ってない?」

「ふざけるな!」

「本日より貴様をスノーボール二等兵と呼ぶ」

「いい名前だろ、気に入ったか?」

成人男性の98%が利用してるというstarseeker.soに関して、残念ながら残りの2%に該当してしまった人(もしいるとすれば)にご説明させて頂きます。

starseekerとは

githubで自分がfollowしている人がstarを付けたrepositoryを毎朝メールしてくれるサービスです。

ただ僕はRSSもTwitterも見ない人のものは全く見ない代わりに見ると決めた人はひとつも漏らさず見るスタイル(情報収集スタイルについて - komagata)なので、starseekerもRSSフィードで見てます。

例えば、

moroさんがstarしてるreposは何か実用的でちょこっと便利なやつなんだろうなーとか

junoさんがstarしたのは、俺が知らないなんかおしゃれなgemとかなんだろう、とか

kenchanさんはすごくエッジなライブラリを見つけててすごいな、とか

全く知らなかったclojureを始めた時も、clojureの有名な人をfollowしとけばそこから直ぐに良い人が見つかって広がりました。

ビジネスマンが日経読んでないと上司に怒られる・・・みたいな。これが無い生活は考えられません。

あと、starseeker.so自体もruby2.0.0-p0, rails4beta1, pumaでソースがとても綺麗で参考になりました。

まあ、控えめに言って 神サービス ・・・ ですよね。

#263 Client Side Validations - RailsCasts

modelに定義したvalidationルールを使ってclient sideでもjsでvalidateしてくれるclient_side_validationsは糞便利なんですが、hiddenな要素はvalidateできない。

例えば、user_idがhiddenであって、特定のユーザーをブロックしたい時にはhidden要素もclient_side_validationsでvalidateしたい。

jsを2行書けば済むことなんですが、今後もありそうなのでgemにしました。

komagata/client_side_validations-with_hidden · GitHub
# Gemfile:
gem 'client_side_validations-with_hidden'
// app/assets/javascripts/application.js:
//= require rails.validations
//= require rails.validations.with_hidden

productionのデータ変更処理をmigrationに書くとrails_best_practicesに怒られる。

Rails Best Practices | Isolating Seed Data

怖話のコードで言えば下記の様なもの。

# encoding: utf-8                                                                        
class InsertSeedToSound < ActiveRecord::Migration
  def up
    Sound.create!(id: 1, name: "鳥の鳴き声")
    Sound.create!(id: 2, name: "犬の鳴き声")
    Sound.create!(id: 3, name: "水滴")
    Sound.create!(id: 4, name: "カエルの鳴き声")
    Sound.create!(id: 5, name: "ラジオのチューニング")
  end
    
  def down
    Sound.delete_all
  end
end

僕はmigrationに書いちゃってるけどみんなさんはどうやってますか?

seed.rbに書けっていうけどrake db:seedってリエントラントに書くもの?最初の一回だけじゃないの?

それとも、productionの状態に合わせて過去のmigrationをまとめてseed.rbをそれに合わせて書きなおすみたいなことするのかな?

ちょっと周りのrubyist2名ぐらいに聞いた感じ結論が出なかったので「ふつうこうだよ」っていうのを知ってる方がいたら教えていただけるとありがたいです!(@komagata

Githubも使ってたというAWSベースのPaaSの老舗Engine Yard Cloudを試してみました。

Engine Yard CloudはAWSの東京リージョンもサポートし、最初の500時間無料とのことで今後複数台構成を考えている怖話の移行先として動くかどうかデプロイしてみます。

Application作成。githubのreposを直接入れることができるのは嬉しい。

deployのためのキーが発行されます。これをgithubや自分のgit repos serverに登録してやれば勝手に取ってきてdeployしてくれると。

怖話のgithub reposのDeploy Keysにさっきのキーを登録。

Environmentの作成。Application Serverが選べるのか。怖話はunicornなので選択。rubyもgemもデフォルトでOK。

無料お試し中は東京リージョンは選べないそうです。残念。MySQLのバージョンを選んで、へーDBバックアップやインスタンスのスナップショットもデフォルトで取ってくれるのか。

一番気になるインスタンス構成の選択画面。

Single Instance
1台でアプリもDBも賄う。
Staging Configuration
appサーバー2台とDBサーバー1台。
Production Configuration
appサーバー3台とDBサーバー2台(master and slave)
Custom Configuration
カスタム。(お試し中は派手にできないらしい)

appサーバーは良い感じにbalanceしてくれるらしい。しかしDBサーバーはどうなってるんだろう。やっぱりアプリ内で明示的にmaster, slave分けてアクセスするんだよね?

とりあえずStaging configurationで。

後はただ起動処理を眺めます。ログを見るとchefで色々ガーっとやってくれてるみたいです。

rake db:migrateのところをrake db:seedにして怖話のseedを実行。そして割り当てられた仮のドメインにアクセスしてみると。

おお、特に何もいじってないのに怖話のproductionが動いた!凄いぞEYC!

鬼サポート

実は初めてdeployした時は500番が出て動きませんでした。

右下にあるサポートチャットに、

"I deployed an app. But dosen't work."

とか物凄い抽象的な助けを求めたら、

「railsアプリにエラーがあるのかもしれない。ここからsshでログインできるから"/path-to/production.log"にあるログを見てみたらどうだい」

みたいなこと言われたので見てみたら、Engin Yard Cloudとか関係無く、普通に怖話のproduction(のseedのコード)がバグってただけでした。修正してアップし直したら動きました。

多分あっちは昼間なんだろうけど、夜の2時頃、お酒を飲みながら適当にインフラを弄ってた俺としては助けを求めた瞬間に的確に帰ってきたサポート体制に感動しました。(飲酒も手伝って)

logentriesのサポートチャットを使った時も同じような体験をしましたが、技術的にちゃんとわかってるサポートが瞬時に答えてくれるってのは今時のサービスでは当たり前なんでしょうか?ありがたすぎるぞ。

感想

VPSで動かしてた怖話のproductionが何の変更も無しに動いたのはびっくり。あと金額も俺の超概算だとAWS直に10〜20%ぐらい載っけるぐらいの思ってたより安い価格設定。

自分の用途としてはある程度規模のサービスで、しっかりした会社からの受託開発を受けたら本気で検討する価値ありだなと思いました。

なんだかんだ行っても複数台構成には色々と面倒がついてきますし(デプロイ、監視、ロギング、バックアップ等)、何よりもAWSで作りこんだら引き継ぎが超面倒そう。リアルに想像すると「EC2で作りこんであの会社に引き継いだら運用できるのかな・・・」って思う時がありますが、EYCなら引き継げそう。特に鬼サポートはちゃんとした会社にとってはありがたいと思います。ググレカスとか言われないですし。

小さいrailsサービスあるある

railsで何かサービスを作ったとする。ExceptionNotificationも入れた。NewRelicで5分毎に死活監視もしてる。なのに「落ちてるみたいです」とTwitterで言われる。

nginx + unicornの組み合わせでアクセスが増えた時、大抵真っ先に起こるのがunicornのtime out。

2013/02/08 18:36:10 [error] 20932#0: *3506622 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 〜ってやつ。

重いDBのクエリとかでアクセスをさばくスピードより接続数が増えてきて起こる。railsアプリまで処理が行かないのでExceptionは起こらない。NewRelicで監視してるURL以外で起きたり時々起きたりしてるからこちらが気付かない。

Nginxのエラーログを監視する

要はNginxのエラーログも監視しないといけない。syslog(rsyslog)でもplainなfileを監視したりalertメール飛ばしたり、複数台のログを1台に集約したりプラグインでできるけど、不必要なエラーログをフィルタリングしたりの設定が少し面倒くさい。

nagios or ganglia or monit入れる?この程度のサービスでfluentd使うのも大袈裟だしなあ。

logentriesを使う

logentriesLogglyとか沢山あるログ収集系のWebサービスです。多分これ系のどのサービス使っても同じ事できますが、LogglyだとAlertingは別サービスになってたりしてメンドイ。logentriesはWeb UIはイマイチですが設定は簡単だしalertメール飛ばすだけなら十分便利です。

logをlogentriesに送る方法は色々ありますが、専用agentを入れるのが一番簡単です。



$ su -
# echo 'deb http://rep.logentries.com/ squeeze main' >/etc/apt/sources.list.d/logentries.list
# gpg --keyserver pgp.mit.edu --recv-keys C43C79AD && gpg -a --export C43C79AD | apt-key add -
# apt-get update
# apt-get install python-setproctitle logentries
# le register
# apt-get install logentries-daemon

NginxのErrorログを監視。

# le follow /var/log/nginx/error.log --name Nginx-Error

しばらくするとこんな感じでログが集まってくる。

nginxのerror logは普通に404とかのエラーもあるので全部をalertメールしてたらウザいのでパターンにタグをつけます。

そしてそのタグに対してAlerts設定でメールを飛ばせばOK。「1時間に10回以上起きたら」とか「1時間に1回しか送りません」とかもWebから設定できるので簡単。

これで

「お客さんのエラー体験 = 俺らにAlertメール」

の図式に(常識的なメール数の中で)なるのでみんなで頑張ってエラー減らしまっしょい!という雰囲気になって良いです。

iOSアプリ、Androidアプリのダウンロード数などの情報を集約してくれてメール通知などもしてくれるDISTIMOがAPIを提供してたのでmixpanelの時と同じようにTeam Dashboardに出してみました。

アプリ版ももっと頑張ろうというモチベーションが湧いて来ました。

Team DashboardはHTTP Proxyという機能があって、ブラウザ上からJSONのデータを引っ張ってきてパスを指定すればそれだけでウィジェットに表示できるので、合計数は怖話本体にJSON APIを作り、PVのGoogle Analyticsと収益のnendはそれぞれ引っ張ってきて(nendはmechanizeでスクレイプ)、heroku上のsinatraで自作のJSONを返しています。

グラフだけはganglia互換みたいなDatapointsを自分で用意する必要があります。でもdemoコードがついてるので実装はパクれば簡単。レンジ指定などは端折ってとりあえずMixpanelから日別の怖い話投稿数・サインアップ数・シェア数を取ってきて出すようにしてみました。

# app/models/sources/datapoints/mixpanel.rb:
module Sources
  module Datapoints
    class Mixpanel < Sources::Datapoints::Base
      def get(targets, from, to, options = {})
        client = ::Mixpanel::Client.new(
          api_key: 'xxxxxxxxxxxxxxxxxxxxxxx',
          api_secret: 'xxxxxxxxxxxxxxxxxxxxxxxx'
        )

        data = client.request('events', {
          event:     '["Share","Signed up","Posted story"]',
          type:      'general',
          unit:      'day',    
          interval:   30,      
        })

        results = []           
        targets.each do |target|        
          datapoints = []
          data["data"]["series"].each do |date|
            datapoints << [data["data"]["values"][target][date], Time.parse(date).to_i]
          end
          results << {'target' => target, 'datapoints' => datapoints}
        end                    

        results                
      end                      

      def available_targets(options = {}) 
        ["Share", "Posted story", "Signed up"]
      end

      def supports_target_browsing?
        true
      end
    end
  end
end

怖話 - Team Dashboard


SNSログイン(Twitterでログインとか)をリリースしてから明らかにサインアップ数が増えてることが分かって嬉しい。

あとはWebとiPhone版とAndroid版の比率とか出したいので円グラフを出したいところです。

昨日Team Dashboardを設定して今日見てみたら…

もう、オマエら勝手に弄るなよなぁw

そもそもイントラ向けっぽいのでデータ変更系メソッドだけにBASIC認証かけときました。

# app/controllers/api/base_controller.rb: 
module Api
  class BaseController < ApplicationController
    http_basic_authenticate_with name: 'admin', password: 'foo', only: [:create, :update, :destroy]
    # some codes
  end
end

これでよし。

追記:


Created with Gifboom

弊社オフィスのダッシュボードは現Engine Yard@yandoさんから貰ったEeePCで動かしています。@yandoさんとても助かっています。ありがとうございます。みんなEngine Yard使おう!(東京リージョンもあるよ!)

オフィスでGoogle AnalyticsとかMixpanelとかlogentriesとかディスプレイに表示させてたんですが、適当じゃなくてそろそろちゃんとした指標(ユーザー増加率とか)を出して共有したいなと思いました。自分で作るのは面倒なので探していたらTeam Dashboardがrails + backborne.jsで出来てて手頃そう。(qnypさんのブログで知りました)

$ git clone git://github.com/fdietz/team_dashboard.git
$ bundle
$ cp config/database.example.yml config/database.yml
$ rake db:create && rake db:migrate
$ rake populate
$ rails s

デフォルトはmysqlなのでherokuで動かしたい場合はpostgresql-adapterを追加。

# Gemfile:
group :production do
  gem "activerecord-postgresql-adapter"
end

怖話では毎月PV・収益の報告をしてますが、これでリアルタイムで見れるようになりました。

怖話 - Team Dashboard