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

ボクココ

熱海で開発するブログ

RedisはRDBの次に学ぶべきDBかもしれない

ざっくりとではあるが、以下の本を一読した。

Redis入門 インメモリKVSによる高速データ管理

Redis入門 インメモリKVSによる高速データ管理

日本語でRedisについて詳しく書かれた本はこの本くらいしかないかと思うが、それでも次のレベルを目指すWebエンジニアにはお勧めしたい本であった。

ただ、注意していただきたいのは本書は全く入門向けではないということ。確かに前半はコマンド一覧のような説明はあるが、後半からはがっつりと実戦的な内容が書かれている。本書はRedisの説明ではなく、Redisを使って一般的な問題をどう解決するか、ということに主眼が置かれている。

そういった意味で私も一回読んだだけでは到底全てを理解することはできなかった。時間をかけて他の文献も参考にしながらもう一度じっくり読みたいと考えている。

近年のサービスはRedis主体で作られる

もっとも有名な例はLINEだ。あれはペタバイトクラスのRedisを立ててメッセージをやりとりしている。本書にはチャットシステムの構築のヒントも書かれており、メッセージングシステムを構築するのにRedisが優れている点が説明されていた。

そのほかにも、転職システムやTwitterタイムライン、広告配信システム、ログ集計など最近のスタートアップが好んで扱う分野についてサンプルコードを含んで丁寧に説明されていた。それらスタートアップのほとんどは本書を参考にシステムを作ったのではないか、とも感じた。インフラの技術が進めば作れるサービスも変わる、ということを実感できた。そしてRedisが登場したからこそ今までできなかった大規模データ処理サービスが実現できるようになったのだとも言える。

RedisはSQLを書き換える?

Redisは本当にいろいろなことができると知った。今まではちょっと高機能なKVS(Key-Value Store)くらいにしか思っていなかったのだけど、そんなことはなくリレーショナルデータベース(RDB)並みの機能をRedisだけで実現できてしまうのだ!

ただReidsだけで全てを作るのはおすすめできない。結局はKVSの拡張であって設計が大変難しいのと、実際に後からメンテナンスするときもRedisだけで作ったシステムはとても複雑なものになるからだ。例えばキー名を"user:124"にして、中身をハッシュにする。といった設計でやっていく訳だがこれだけでもちょっとトリッキーな感じがしてしまう。

ではどういうときにRedisを使うべきなのか。それは高負荷がかかりそうな部分を補助するときに使う、という立ち位置だと考える。 大規模データを素早く処理したいときにRedisは威力を発揮する。

てことでそこまで負荷のかからない初期のスタートアップはぶっちゃけRDBだけでなんとかなると思う。それでサービスがうまくいったときに負荷のかかる処理をRedisに移行する、というやり方でいいような気もする。

それくらいRedisでデータ設計をするのは難しく失敗しやすい。

だからこそ、RDBの次に学ぶべきものとしてのRedisの立ち位置がもっとも良いのではないか、と思った。

現在のRedisの利用

今運用しているCallConnectでは、Redisを利用している。といってもログインセッションをRedisに置いているくらいだ。ただこの用途だけでもCookieに保存していたRailsのセッションをセキュアに管理できるようになるので大変有用だと考えている。今後バックグラウンドジョブのキューイング処理にまたRedisを利用したいと考えている。

それと同時に今後負荷のかかりそうなデータ処理を扱う際にはRedisの利用を検討していく。

今さらながら Papertrail がアツい

今回はHerokuの定番アドオン Papertrail について。

簡単に言えば、ログを収集してくれるサービスなんだけど、それだけじゃない。

まずデフォルトのHerokuログだとタイムスタンプがUSになっていて、時差の対応ができないためPapertrailで見る。そしてPapertrailで溜まったログを集計できる。しかも無料から始められるので、これを導入しない理由がないくらいのアドオン。

ただ、Papertrailはそんなログ収集だけのツールではない。 ログの発生イベントを通知してくれる機能、これを使えばコードに手を加えなくてもカスタムイベントを生成することができる

ウチでは例えば新規ユーザーが登録してきた際にSlackに通知するといったことがコードレスで実装できる。

Papertrail のイベント設定

f:id:cevid_cpp:20150722191211p:plain

Papertrailアドオンのここにフィルタリングするワードを記述する。例えば POST "registers/complete", "Error" AND "app/web" AND -"NewRelic"など。

これにSlackと連携すれば、このログが発生したタイミングでSlackに通知することができる。

今まではこういうのコードにメール送る処理書いたり、何かしら通知する処理を書くみたいな対応をしていたと思うけど、そんなのはこれからは必要ない。

ログの有効活用は大事ですな。

バケモノの子を見て

たまには雑談エントリー。

キミとなら強くなれる。

見てきました、バケモノの子。 注意 以下解釈のためにちょっとだけストーリーを説明しちゃってます。

http://img.cinematoday.jp/res/T0/01/97/v1428994049/T0019739p.jpg

現代社会の抱える物足りなさや悩み

この映画の舞台は、あの渋谷。渋谷はよく通っていたこともあり何度も通ったことのある景色がアニメ中にいくつも描画されているのが何か不思議な気分になりました。

本作ではバケモノの世界と、人間の世界(渋谷)が分け隔てられていました。渋谷はたくさん人が溢れかえっているけども、どこか心の闇を抱えた場所。 人間の世界は今の日本の状況、先行き不安な日々に対しての表現だったような気がします。

その表現として、バケモノ達は, "人間は最終的に闇を抱えて問題を起こす"と言ってバケモノの世界に迷い込んだ人間を追い出そうとします。

そんなバケモノの世界に迷い込んだ少年が、最後には人間の世界で逞しく生きられるようになった成長物語でした。

感じたこと

本作では師匠と弟子の関係が強く描かれています。それでも師匠は何も教えない。ただそれを後ろで学ぼうとする少年の姿。

今の世の中はなんでも教えてもらおうとしがいがちです。なんでもマニュアルがないとできないような気がします。最初、本作の主人公(レン君)もそんな感じでコツを教えろとかそんなことを言っていました。それが次第に師匠の真似をして練習して強くなっていきます。レン君は勉強においても、自分から学ぼうとする姿勢を崩しませんでした。主体的に行動するこの大切さ、そして何かを突き詰めて努力することの重要性を教えてくれています。

それこそが、今のこの時代を強く生きるための方法ではないでしょうか。受け身にならず自分で信じたものをやり通してみること。胸の剣で切り裂くのです。

終盤には師弟関係の涙ぐましい関係が。弟子が困難に陥ったときは師匠が助け、師匠が困ったときは弟子が助ける。その絆にガチ泣きしましたw

にしても映画館ってのは強力ですね。たぶんテレビで見たらちょい泣きはするかもしれないですがここまで泣くことはなかったと思います。それだけ映画館は作品の中に入り込めるメリットがあるな〜と感じました。

P.S. 告知で出てた進撃の巨人が思ったよりかなりよくできてる感じがしました。これもちょっと見てみたいな。

「コールコネクト」の開発ツールやプロセスについて

本日、誰でも3分で自動電話応答システムを構築できるサービス「コールコネクト」をリリースした。

f:id:cevid_cpp:20150706124754p:plain

今回は、このサービスができるまでの過程を記したいと思う。

開発コンテストで原型を構築

Smart Communication Award 2015 というコンテストで、何か作ろうということでコールコネクトの元となるアイディアを出して、その期間中にコアとなるメイン機能を実装した。

デザイナーの仲間と2人で参加してデモをしたところ、優秀賞をいただいた。これがきっかけでコールコネクトを正式にサービスとして出すプロジェクトがスタートした。

大抵の開発コンテスト(ハッカソン)ってその期間だけ作ったら、後はそのまま放置というパターンが多いと思う。だが今回は会社メンバーと参加してビジネスやマネタイズまでを考えて出した作品であったため、こうしてプロダクトとして世に提供できるまで育てられたのだと思う。

料金プランの構築

プロダクションまで持っていくためには、課金の仕組みの実装が必要だった。これの実装が大変で、テストにも時間がかかった。今回は WebPay を利用した。WebPayを利用すれば、決済の実装を最小限に抑えられるだけでなく、セキュリティ的にも自社でクレカ情報を保存する必要がないので安心してサービスを提供することができる。

4月末に商用申請を送り、6月中旬に全種類のクレジットカードが利用できるようになった。時間はかかったけども、その間サービスの改善に時間を割くことができた。しっかりとした商品説明ページと利用規約特商法などを記述すれば審査には通ると感じた。

今回は月額課金サービスであったため、各種プランに応じて定期的に決済が走る仕組みなど、いかにシンプルに、そして確実に決済ができるのかが勝負だった。課金に関するシステム構築には特に時間がかかった。

追加機能の実装

WebPayで審査を待っている間、ランディングページや管理ページの改善を続けていた。それとは別に、早いうちから無料モニターの形で募集を募り、試していただける方にフィードバックをもらうことにした。

これを実施て本当によかったと感じている。自分らサービス提供者はもう当たり前のようにシステムを利用しているため、初めて利用する側にたってわかりやすいサービスを自分たちだけで構築するのは難しくなってしまっていた。

初めて利用する方の側について、その人がどういう発言をしたか、どういうアクションをサイト内でとったかなどを細かくメモし、それらフィードバックを元にシステムを劇的に変更していった。この改善の時間がなければ、このシステムは誰もが3分で構築できるシステムにはならなかったと思う。

スピードを重視しすぎて機能が不完全のまま出すか、しっかりと用意してサービスを出すか。このどちらもやって出すというのが結局のところだと思う。

フィードバックの内容は基本的に全て Trelloで管理した。ラベルやチェックリストなどをうまく利用して、今何をしなければならないか、今何をしているかがメンバー内で可視化することができた。

Trello では、 「Suggestion」「Task」「Doing」「Check」「Checked」「Done」で振り分けた。まだやるかどうかわからないけどメモ書き程度に Suggestion(提案) があり、実際にやる予定のものがTaskに入る。今やっているものをDoingに入れ、それが終わったらタスクを作った人にCheckでアサインを変更。チェックが完了次第Checkedに入れ、リリースが終わったらDoneへ移る。このボード振り分けでうまく機能していた。

メンバーとの関わり

開発に集中するときは、リモートワークで開発をしていた。弊社には熱海のサテライトオフィスもあり、そこで開発を行っている時期もあった。リモートワークで仕事をする際は、Sqwiggleを利用している。これは確かに1対1で話すときは便利だが、集団で話す場合は料金がかかるので、毎日朝と夕方は appear.in で会議することにした。

ただやっていくうちに、相談が必要なフェーズ(初期のシステム設計やリリース直前など)はできるだけメンバー全員が集まってすぐ結論ができるような環境にしないといけないことに気づいた。リモートワークしてもいい時期、すると効率が悪くなる時期など、そこを見定める必要があると感じた。

テスト

今回はChibinekoというサービスが便利そうだったので、こちらを利用してみた。最もシンプル、というにふさわしい簡素な機能でテスト実施に役立った。

ステータスに応じて(現在のプランや日数など)のテストが多く、時間はかかったけどもそこを妥協せず1週間まるまるテストに時間を割いた。おかげで品質としても満足のいくプロダクトとなった。

終わりに

今回のサービスは3人という少人数でのチーム開発となったが、それぞれがハスラーハッカー・デザイナーの役割を全うでき、良いチームワークができたと感じている。

今後は開発だけでなく、運用フェーズにも入っていくので、そこで得られた知見などもシェアしていきたい。

iOS Swift プッシュ通知を受け取ってからViewControllerに情報を渡す

iOS

久々の iOS のトピック。今回はiOSで初めてプッシュ通知(Push Notification)を利用したので、それのメモ。

プッシュ通知について

サーバーの任意のタイミングで情報を端末に送信する仕組み。これぞアプリのできること、として欠かせない機能だ。何か最新ニュースが出たりとか、リアルタイムで情報を送りたいときによく使われる。てか人気アプリで使ってないところはないくらいな機能だ。

Webアプリでは任意のタイミングでブラウザに情報を渡す仕組みを実装するには、やや特殊なことをしないと難しい。(Web Socket や Polling などを利用するだろう)

設定手順

  1. Apple Developer にて 証明書を作成
  2. Amazon SNSで作成した証明書をアップロード
  3. サーバーにてAmazonSNSの通知を発行
  4. iOSアプリで通知を受け取る

Apple Developer で証明書作成

iOSはコードは簡単だけど、Apple Developer での登録がめんどくさい。まずはPush Notification を利用できるようにするために証明書を作成する必要がある。

AppIDでプッシュ通知したいアプリを作成し、 ここのPush Notifications の項目をオンにして作成。そんで作成したAppIDを選択し、Edit から CreateCertificate を選択。キーチェーンアクセスで作成したcertSigningRequest ファイルをアップロードし、証明書を作成する。

その証明書をキーチェーンアクセスへドラッグアンドドロップして、それをディスクに書き出す。これがp12形式にして書き出す。これに合わせてプロビジョニングファイルも作成しておこう。

Amazon SNS でのプッシュ

Amazon SNS を使えば、 iOS, Androidの区別をほとんどきにすることなく、プッシュ通知を送ることが可能だ。しかも何万通もプッシュ通知送っても料金は非常に低価格。これは使わない手はない。

http://d2wwfe3odivqm9.cloudfront.net/wp-content/uploads/2014/05/Amazon_SNS-320x320.png

Application で Apple Development を選択し、 p12 ファイルをアップロード。その後Load Credential from file を押すと作成ボタンが押せるようになる。作成したApp Arn をサーバーから参照できるようにしよう。今回はAWS_SNS_APP_ARNとして環境変数に保存する。

サーバーにてAmazon SNSの通知を発行

今回は Ruby を例に、発行方法を記述。bundler にて gem "aws-sdk"sdkをインストール。

config/initializers/aws.rb

Aws.config.update({
  region: ENV['AWS_REGION'],
  credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
})

app/controllers/samples_controller.rb

      sns = Aws::SNS::Client.new

    response = client.create_platform_endpoint(
      platform_application_arn: ENV['AWS_SNS_APP_ARN'],
      token: device_uuid
    )
    target_arn = response[:endpoint_arn]

      message = {
        "APNS" => {
          "aps" => { "content-available" => true },
          "other_data" => obj
        }.to_json,
        "APNS_SANDBOX" => {
          "aps" => { "content-available" => true },
          "other_data" => obj
        }.to_json
      }
      push_parameters = {
        target_arn: target_arn,
        message_structure: "json",
        message: message.to_json
      }
      resp = sns.publish(push_parameters)

objに送りたいデータ、target_arn に送り先のTargetArnを選択しよう。target arn は iOSアプリから送信されたトークンを元に作成する。今回の変数でいうdevice_uuidだ。

iOSアプリで通知を受け取る

これは簡単。 AppDelegate にて任意のメソッドを実装するだけだ。JSON解析のためにSwiftyJSONを利用している。

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        // 通知を許可
        var types: UIUserNotificationType = UIUserNotificationType.Badge |
            UIUserNotificationType.Alert |
            UIUserNotificationType.Sound
        var settings: UIUserNotificationSettings = UIUserNotificationSettings( forTypes: types, categories: nil )
        
        application.registerUserNotificationSettings( settings )
        application.registerForRemoteNotifications()

        return true
    }

    // Device Token を取得
    func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData ) {
        var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
        
        var deviceTokenString: String = ( deviceToken.description as NSString )
            .stringByTrimmingCharactersInSet( characterSet )
            .stringByReplacingOccurrencesOfString( " ", withString: "" ) as String
        
        println( deviceTokenString )
        // このトークンをサーバーに送って保存
    }

    // プッシュを受け取ったとき
  func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
        let json = JSON(userInfo)
        
            let notification = NSNotification(
                name:"my_push",
                object: nil,
                userInfo:[
                    "title": json["other_data", "title"].stringValue,
                ]

            NSNotificationCenter.defaultCenter().postNotification(notification)
        }

        completionHandler(.NoData)
    }
}

NSNotificationCenterからmy_pushという名の通知がアプリ内に飛ぶので、それをViewController側でキャッチしてあげます。

class ViewController: UIViewController {
    override func viewDidAppear(animated: Bool) {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "handlePushNotification:", name: "my_push", object: nil)
        
    }

    
    override func viewDidDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
    // observer fired
    func handlePushNotification(notification: NSNotification) {
       // 通知を受け取ったときの処理
    }

}

終わりに

Androidは設定は簡単だけど、実装が面倒。 iOSは設定が面倒だけど実装が簡単。そんな印象。iOSではデフォルトでAndroidでいうEventBusのような仕組み(NSNotificationCenter)を提供してくれてるのがありがたい。乱用するとカオスになりかねないが、こういうときには役立つ機能だと思う。

ただ通知する中身が空だったり間違ったりすると通知が来なかったりするパターンがあるので、送るデータがちゃんと入っているかを確認したほうがいい。これにかなりつまずいた。

Heroku Postgres にあるデータのダンプとインポート

最近は運用系の記事が多くなってきたな。。メモ書き。

Heroku にあるデータをローカルに持っていきたいとか、テスト環境のpostgresにデータを入れたいとか、そういったことをやってみる。Heroku コマンドの pg:backuppg:restore はテーブル全体なので、個別だとpsql, pg_dump をリモートで接続して対応していくことになる。

接続情報の取得

Heroku Postgres は直接psqlコマンドでデータをいじることが可能だ。まずはその接続情報を取得する。

$ heroku pg:credentials --app APP_NAME HEROKU_POSTGRESQL_****

Connection info string:
   "dbname=****** host=*****.compute-1.amazonaws.com port=5432 user=****** password=**** sslmode=require"
Connection URL:
    postgres://*****:****@****.compute-1.amazonaws.com:5432/****

HEROKU_POSTGRESQL_***の名前はHeroku Postgresのアドオンのページ内に書いてある。

データをダンプ

以下のようなコマンドを叩くことで取得できる。

$ pg_dump --no-acl --no-owner -h [host ip].compute-1.amazonaws.com -U [user name] -t [table name] --data-only [database name] > table.dump

pg_dump: server version: 9.4.1; pg_dump version: 9.3.1 のような警告が出たら、 brew upgrade postgresql でアップデートしたらいけた。

データをインポート

idとか制約入っている場合とかごっそり書き換えるのでまずはテーブル削除

$ echo "truncate [table];" | heroku pg:psql [DATABASE] -app YOUR_APP

そのあとデータをインポートしよう。

$ heroku pg:psql --app YOURAPP DATABASE < table.dump

Heroku の DBバックアップを定期的に行う

最近はHerokuのアドオンがまた増えてきていて、そういう新しいアドオンをチェックするのも一興だ。最近はCIツーリなんかもアドオンで無料でできたりする。無料だと月100件までだけど、本番環境ならそれでも十分な気がする。

今回はアドオンの中でも標準的な Heroku Postgres のお話。基本的にはドキュメントを読めばいいのだが、いかんせん英語なもので苦手な方には中々苦労することだろう。

Heroku Postgres は無料で2つバックアップを保存することができる。2つ以上バックアップした場合は古い順から消えていく仕組み。ただデフォルトでは自分でコマンド叩くなりWebインタフェースからバックアップしたりとマニュアルなことが必要。これを自動化させよう。

これもドキュメントに普通に書いてあるので、その通りにやろう。

heroku pg:backups schedule --at '02:00 Asia/Tokyo' DATABASE_URL

これで毎晩朝2時にバックアップがスケジューリングされた。これで問題が起きた時はバックアップからデータを復旧させられる。

Heroku Postgres のTIPs

Dataclips

面白いこととして、 Dataclips 機能というものがある。これはデータのレポート機能といってもいい。ユーザー数など、定期的に情報を取得したいもので、他の人でも把握したいようなデータの場合、簡単なSQLを書いておくことでURLにアクセスする度に最新データが取ってこれる仕組みだ。

これで例えばビジネス側の人に今ユーザー数どんくらい?って言われた時にDataclipsのURLを渡すだけで最新情報が取ってこれるようになる。

もちろんcountだけじゃなくて実際のデータもインポートできるので、よくあるSQLアクセスの定期的なものは Dataclipに保存しておくと便利。

DaaS

Heroku Postgres は Database as a Service だ。これは別にHerokuアプリじゃなきゃ使えないというわけではなく、例えば EC2やその他のサーバーのアプリのDBをHeroku Postgres にすることも可能であるということだ。てことでHerokuの2つのアプリが1つのPostgres を参照することも可能。

この事実を案外知らなかった人は多いのではないだろうか。これらをうまく使えば運用は劇的に楽になるはずだ。ちなみにHeroku postgres を使えば1万行まで無料で使える!

もう自前でDBを持つ時代ではないことは明らかだ。