oinume journal

Scratchpad of what I learned

Herokuの好きなところ

これはHeroku Advent Calendar 2016の13日目の記事です。個人的にHerokuを使い続けてはや3年。仕事でヘビーに使っているわけではないけど、Herokuの好きなところを挙げてみます。

ディプロイが楽

ディプロイがgit push heroku masterで済むのは本当に楽。一昔前だったらディプロイスクリプトを用意しなくてはいけなかったけど、コマンド一つでディプロイできるようになった。というか今だとGitHubでmasterブランチにマージさえすれば勝手にディプロイされるようにもできる。

アドオン

これも月並みな感想だけど、heroku addons:add でアドオンを追加して、アプリケーションに組み込むことができるのが楽。さらに、heroku addons:open <addon>を実行するとブラウザが立ち上がってログイン状態でそのアドオンの管理画面に行けるのも地味にすごい。

ちなみに自分がどんなアプリケーションでも使っているアドオンはpapertrail。↓のような感じでブラウザからログを検索できて、S3にもアーカイブ保存できたりして便利。

papertrail

無料で使える

1つのWebアプリを動かすのであれば、24時間稼働させても無料枠内でタダで使えるのも個人的には嬉しい。

イマイチなところ

もちろんHerokuも100%完璧ではなくて、イマイチなところもある。個人的に不満なのは以下。

  • 日本リージョンが無料では使えない
    • US-eastかEUしかリージョンが選べないので、日本からだとレイテンシがやっぱり気になる。Heroku Private SpacesにはTokyo regionもあるけど有料なので業務じゃないと使えなさそう。
  • CDN的な機能がない
    • addonで組み込むことはできるけど、これぐらいはデフォルトであると嬉しい

最後に

Herokuを使い続けて思うのは「アプリケーションの開発に集中できる」という点。この目的のためにすごく色んなところを頑張ってるんだろうなぁと思う。PaaSはなかなか収益的に厳しそうだけど、素晴らしいサービスなのでこれからも頑張って欲しい!

WebアプリケーションのE2EテストをGoで書く

これはGo Advent Calendar 2016の18日目の記事です。今回はGoでE2Eテストを行うためのライブラリagoutiについて書きます。

GoでE2Eテストを書く理由

WebアプリケーションのサーバーサイドをGoで書いている場合、GoでE2Eテストを書くメリットとして

  • JavaScriptが得意ではないエンジニア(自分)でもE2Eテストがガリガリかける
  • E2Eテスト実行時のカバレッジが取れる=サーバーのコードのどこを通ったかがわかる

があると思っている。特に2番めの理由が大事で、「E2Eテストを全部回した結果、サーバーのこの部分のコードは通っている」とわかるのはけっこう大きなメリットなのではないかと。どこがテストされている・いないを把握することで、「ここはE2Eテストだと難しいからユニットテストでカバーしよう」というような戦法が取りやすいと思う。

じゃあ具体的にGoでどうやってE2Eテストを書くのか、ということを次に説明する。

Go製WebDriverクライアントagouti

GoにはagoutiというSelenium WebDriverのクライアントがあるので、今回はこれを使う。agouti自体は単にWebブラウザを操作するライブラリなので、あわせてChromeDriverなどをインストールする必要がある。MacにChromeDriverをインストールするのはHomebrewでbrew install chromedriverしてインストールするのが楽で、それ以外のOSではChromeDriverのサイトからダウンロードしてすればOK。

一例として、下記のようなagoutiを使うGoのコードを書いて動かすと、Chromeが起動して https://ifconfig.co/ のページが表示されて5秒スリープする。

 webDriver := agouti.ChromeDriver() // ChromeDriverを使う
    if err := webDriver.Start(); err != nil {
        return err
    }
    defer webDriver.Stop()

    page, err := webDriver.NewPage()
    if err != nil {
        t.Error(err)
    }
    // Navigateで指定したURLにアクセスする
    if err := page.Navigate("https://ifconfig.co/"); err != nil {
        t.Error(err)
    }
    time.Sleep(5 * time.Second)

agoutiでWebアプリケーションをテストする

もう少し実践的な例として、フォームに入力された値を表示する簡単なWebアプリをテストするケースを考える。ディレクトリ構成は

  • cmd: サーバーの実行ファイル用
  • app: Webアプリケーションの本体
  • e2e: E2Eテスト置き場

という感じになっていて、全てのコードはgo-e2e-test-sampleのリポジトリに上げてある。

この、単にフォームで送信された名前を表示するだけのWebアプリケーションのコードはこんな感じ。

そして、E2Eテストのコードはこんな感じ。TestIndex/にアクセスしている。

ポイントとしては

  • testMainでサーバーとChromeDriverを起動
  • TestIndex関数内でwebDriver.NewPageでPageを生成し、Page.Navigateで起動したサーバーのURLにアクセス
  • Page.FindByXPathでHTMLの要素を検索してformをSubmit

というような流れになっている。なお、今回はXPathを使ってHTMLの要素を検索しているが、CSS Selectorを使うFindなんかもある。詳しくはgodocを見れば詳しく書いてある。

そして、このe2e_test.gogo testコマンドで実行すると、Chromeが立ち上がってテストが実行される。

$ go test -v ./e2e

カバレッジを取ってみる

では次にカバレッジを取って、どの部分がE2Eテストによって実行されているのかを調べてみる。go testの引数に-coverpkg-coverprofileをつけて実行する。

$ go test -v -cover -coverpkg=./app -coverprofile=cover.out ./e2e

これでcover.outにカバレッジのためのファイルができるので、go tool coverでHTMLとして表示してみる。

$ go tool cover  -html cover.out

ブラウザが立ち上がって下記のような画面が表示されるので、今回のE2Eテストで実行されているコードがひと目でわかる。

go_tool_cover_html

まとめ

  • agouti便利
  • go testでE2Eテストを実行できるようにしておくとgo tool coverなどのGoエコシステムがそのまま使える

Better Heroku Schedulerを探したらCustom clock processesにたどり着いた

これはHeroku Advent Calendar 2016の11日目の記事です。

HerokuではHeroku Schedulerというcronのようなサービスがあるのですが、使い込むうちに「Daily, Hourly, Every 10 minutesの単位でしかジョブを動かせない」という制限がつらくなってきたので、より良い代替であるCustom clock processesを試してみたという話。

Custom clock processesとは?

clockというプロセスを立ち上げっぱなしにして、そのプロセス内でスケジューリングされたジョブを実行する仕組み。Heroku Schedulerは実行される時だけプロセスが立ち上がるけど、clockの場合は常にプロセスが常駐する。

各言語のScheduler

HerokuのCustom clock processesのドキュメントで触れられているのはJavaとPythonの2種類だけど、各言語ごとにだいたいcron likeなSchedulerが用意されている。今回は自分が慣れているPythonのAPSchedulerを試してみた。

APSchedulerの設定

まずPythonのモジュールのインストールのために、requirements.txtというファイルを用意する。

echo APScheduler > requirements.txt

次にProcfileに以下を追記する。

clock: python clock.py

ここで指定されているclock.pyがジョブの設定が書かれたファイルになる。どのように記述するべきか、詳しくは公式のドキュメントに書かれている。以下は1分毎にhello worldを出力する設定。

from apscheduler.schedulers.blocking import BlockingScheduler
import subprocess

scheduler = BlockingScheduler()

@scheduler.scheduled_job('interval', minutes=1)
def timed_job():
    print("Run notifier")
    subprocess.run("hello world", shell=True, check=True)

scheduler.start()

最後に、APSchedulerを動かすのにはPythonが必要なので、buildpackを追加。

heroku buildpacks:add https://github.com/heroku/heroku-buildpack-python

あとはclock.pyをコミットしてディプロイすればOK。

ローカルで試したい場合

ローカルで試してみたい場合は以下のようにAPSchedulerをインストールして、clock.pyを直接呼び出せばOK。

pip install -r requirements.txt
python ./clock.py

Custom clock processesとFree Dyno Hours

Heroku Schedulerは実行する際にプロセスが起動されて、プロセスが動いていた時間だけ無料分のFree Dyno Hoursを消費するのだけど、custom clock processesはworkerのようにclockプロセスが常駐する形になるので、起動していた時間分だけFree Dyno Hoursを消費するようなので注意が必要。webと併用していると倍速でFree Dyno Hoursを消費するので気をつけましょう。

まとめ

Heroku Schedulerよりきめ細かい時限式のジョブを定義したいときはCustom clock processesを使おう!

mysqldumpで特定のレコードだけエクスポートする

忙しい人向けまとめ

  • mysqldumpの--whereオプションを使うと特定のレコードだけmysqldumpできる
  • --whereにはLIMIT句も指定できる
  • --whereオプションで大量のデータから一部だけをmysqldumpすることが可能

本文

mysqldump、データだけエクスポートしたりCREATE TABLEだけエクスポートできたり細かいところまで気が利いているなぁと思うツール。今日知ったのは --whereでdumpするレコードを限定できるよ、ということ。さらに、--whereにはLIMITも指定できるので、「特定の条件にマッチするレコード100件だけ」みたいなエクスポートもできる。

以下、具体例。

$ mysql -u <user> -p<password> <database>

mysql> CREATE TABLE hoge (
  id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  PRIMARY KEY (id)
);

mysql> INSERT INTO hoge VALUES (1, 'foo'), (2, 'bar'), (3, 'baz');

こんな感じでレコードを作って、--where 'id=1'を指定してmysqldumpしてみる。

$ mysqldump -u<user> -p<password> <database> hoge --where 'id=1'

(snip)
LOCK TABLES `hoge` WRITE;
/*!40000 ALTER TABLE `hoge` DISABLE KEYS */;
INSERT INTO `hoge` VALUES (1,'foo');
/*!40000 ALTER TABLE `hoge` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
(snip)

id=1のレコードだけmysqldumpされる。では次にORDER BYとLIMITをつけてみる。

$ mysqldump -u <user> -p<password> <database> hoge --where 'id>=2 ORDER BY id DESC LIMIT 1'

(snip)
INSERT INTO `hoge` VALUES (3,'baz');
(snip)

となって、idの降順でソートして1件だけmysqldumpができた。

まとめ

プログラムの動作検証のためにデータをmysqldumpしたいけど、本番データだとレコード数が多くて丸ごとmysqldumpするのがつらい時に--whereをつけると幸せになれるはず。

新しいMacで設定している項目 - macOS Sierra編

5年間MacBookPro 13-inch, Early 2011のRetinaじゃないヤツをSSD変えたりして延命しつつ使ってたのだけど、さすがに限界だったので新しいMacBookPro 13inch Retina のTouchBarなしモデルを買った。

久しぶりに自宅のMacをセットアップしたので、やったことをメモしておこうかなと。キーバインドを変えるために使っていたKarabinerがSierraでは使えなくなっていたのがつらかった。

トラックパッドでタップだけでクリックできるようにする

システム環境設定 -> トラックパッド -> タップでクリック

これを有効にするとトラックパッドでクリックしなくてもよくなるので楽。

トラックパッドでの右クリック

システム環境設定 -> トラックパッド -> 副ボタンのクリック(右下隅)

自分は2本指でやるのは苦手なので副ボタンのクリック(右下隅)にしている。ただ、新しいMacBookProだとトラックパッドが大きいので右下隅のタップだとつらいかもしれない。

キーのリピート速度

システム環境設定 -> キーボード

下記の設定のスライダーを一番右にする。

  • キーのリピート速度
  • リピート入力認識までの時間

ただ、これでも遅いのでさらに速くしたい場合はKarabinerまたはSierraの場合はKarabiner Elementsを使って設定する。

Karabiner Elementsでキーリピートをさらに速くする

を使う。

brew cask install karabiner
または
brew cask install karabiner-elements

でインストール可能。自分は下記のようにキーリピートの設定をしている。これでWindows並にカーソル移動や文字の削除が速くなる。

Karabiner-Elements

たまにこれを設定しないでカーソル移動が激遅の人がいるので見つけたら教えてあげるようにしてる。

同じアプリで別ウィンドウになっている場合の切り替えのショートカットをoption + tabにする

Chromeでたくさんウィンドウを開いていたりする場合にWindowsみたいに切り替えられるようにする。

システム環境設定 -> キーボード

ショートカットタブのキーボードを選んで、次のウィンドウを操作対象にするoption + tabにする

Tabで全ての項目を移動できるようにする

ショートカットタブでフルキーボードアクセスをすべてのコントロールにチェックにする。こうしないとSafariとかでTabで移動するときにチェックボックスとかに移動できない。

Dockの設定

最近のPCは横幅が大きいので、下ではなく左に置く。

  • サイズ小さめ
  • 拡大する
  • 画面上の位置:左

コンピュータ名を変える

デフォルトだと変な名前になっていることが多いので変える。

共有 -> コンピュータ名

から変えられる。(なんでこんなわかりづらいところにあるのか...)

Finderのウィンドウタイトルでフルパスを表示する

ターミナルから下記のコマンドで設定できる。

defaults write com.apple.finder _FXShowPosixPathInTitle -boolean true

スクリーンショットの保存先の変更

デフォルトだとデスクトップになっているが、このままだとデスクトップが悲惨なことになるので自分は~/Documents/screenshotというディレクトリを作って保存するようにしている。ターミナルで以下のコマンドで設定できる。

defaults write com.apple.screencapture location ~/Documents/screenshot
# この設定を反映する
killall SystemUIServer

なお、以下のコマンドで設定値を削除することができる。

defaults delete com.apple.screencapture location

最後に

そろそろこの辺の手動設定をどうにかして自動化したい…