kakakakakku blog

Weekly Tech Blog: Keep on Learning!

知っておくと便利な git clone --depth と git diff --indent-heuristic

コミット履歴が無駄に多く,黒歴史のある Git リポジトリで開発をする場合,初回の git clone が非常に遅いという問題がある.コミット数に依存せずに素早く落とせる方法を探していて,最近(今さら...!) git clone--depth オプションのことを知った.用途によっては非常に便利なので,まとめておこうと思う.

前提

現時点で公開されている最新バージョンの Git 2.12.1 を前提にしている.Git は今もまだ機能が増えているため,定期的にバージョンアップしておくと良いと思う.Mac なら brew upgrade git でサクッと最新バージョンになる.

$ git --version
git version 2.12.1

git clone --depth とは

git clone--depth オプションを使うと,指定したコミット数で刈り取ることができる.以下の例では --depth 1 で,master ブランチにある最新のコミットだけを落としている.コミット履歴が大量にある Git リポジトリでも,素早く落とすことができるため便利.

ちなみに Git のドキュメントを読むと,--depth オプションを使った git clone のことを「シャロークローン (shallow clone)」と呼んでいた.合わせて覚えておくと良さそう.

$ git clone --depth 1 git@github.com:kakakakakku/togoo.git
$ cd togoo
$ git log --oneline | cat
80ff421 Added listing column "created_at"

shallow clone をしても pull / push に制約は無かった

shallow clone をすると pull / push ができないと書いてある記事が多かったが,少なくとも Git 2.12.1 では,普通に pull / push / fetch をすることができた.ようするに --depth 1git clone して,開発をして,プルリクエストを作ることができる.

よって,最初はある程度の深さ(100 など)で shallow clone をして開発を始めて,コミット履歴が必要になったり,git blame 警察をするときになってから,過去のコミットを取得し直せば良いということになる.

追加で過去のコミットを取得する

では,過去のコミットを取得したくなったときにどうすれば良いかと言うと,git fetch--depth オプションを使って深さを指定するか,--unshallow オプションで全てを取得することになる.

# 深さを指定して取得する場合
$  git fetch --depth 5

# 全てを取得する場合
$ git fetch --unshallow

git diff --indent-heuristic とは

せっかく Git の話を書いているので,案外知られていないように思う「便利な git diff オプション」の話も書いておこうと思う.

git diff で,差分結果が微妙にズレていて気持ち悪いなと感じた人は多いのでは?例えば,以下のようなサンプルコードで再現することができる.追加したのは最初の3行なのに...!という感じでツライ.

$ git diff
--- a/hoge.rb
+++ b/hoge.rb
@@ -1,3 +1,7 @@
 [1, 2, 3].each {|i|
+  puts i
+}
+
+[1, 2, 3].each {|i|
   puts i + 1
 }

この問題を解決するために Git 2.9 で git diff --compaction-heuristic というオプションが追加されている.ただし,このオプションは不完全で,Git のデフォルト設定としては採用されなかった.そして Git 2.11 で,さらに改善された git diff --indent-heuristic というオプションが追加された.--indent-heuristic は全てのケースで改善がされているらしく,今後のバージョンで Git のデフォルト設定になる予定があると公式ブログにも書いてあった.

The new option goes under the name --indent-heuristic (and diff.indentHeuristic), and will likely become the default in a future version of Git.

github.com

--indent-heuristic オプションを使うと,以下のように意図した差分結果になる.最高!

$ git diff --indent-heuristic
--- a/hoge.rb
+++ b/hoge.rb
@@ -1,3 +1,7 @@
+[1, 2, 3].each {|i|
+  puts i
+}
+
 [1, 2, 3].each {|i|
   puts i + 1
 }

--indent-heuristic オプションをデフォルトにする

~/.gitconfig に以下の設定をすれば,git diff のデフォルト設定が --indent-heuristic になる.個人的には必須な設定!

[diff]
  indentHeuristic = true

まとめ

  • Git は定期的にバージョンアップする
  • 巨大な Git リポジトリをサクッと落とす場合は git clone --depth を使う
  • 最新バージョンの Git なら shallow clone をしても pull / push ができる
  • ~/.gitconfigdiff.indentHeuristic を有効化すれば git diff が便利になる

関連記事

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

採用面接は難しい /『その「エンジニア採用」が不幸を生む』を読んだ

エンジニアの採用をテーマにした本って,今まで How Google Works ぐらいしか読んだことがなくて,タイトルに惹かれたので『その「エンジニア採用」が不幸を生む』を読んでみた.

今まで数十回は採用面接をしてきたと思うけど,常に採用は難しいなと思う.僕が採用面接をするときに意識していることは2点あって,お互いにとって有意義な時間になるように心掛けている.

  • 候補者の良さを職務経歴書ではなく,話で引き出せるような質問をすること
  • 採用結果に関わらず,良いプロダクトだなと感じてもらえるような話をすること

本書では採用面接の Tips などは載っていなくて,エンジニアの採用に対する間違った認識や,エンジニアを惹き付けるための仕組みの話が載っている.人事部門だけではなく,採用面接に関わるエンジニアも読んでみると良いのではないかなと感じた.

まず「第1章」に経営者が IT に理解がないと,エンジニア主導でイノベーションを起こす企業文化を作ることができず,その場合は経営者と対等に議論できる CTO ポジションを用意する必要がある,という話が書いてあった.これは最近特に感じていることで,個人的にはその通りだと思う.経営者が IT を理解することは必須ではあるけど,輪郭が見えるレベルに理解することと,技術特性を踏まえて議論できるレベルに理解することには雲泥の差がある.エンジニアの採用を重視したり,戦略的な技術選定をしたりするときに,全てを民主的に決めることが必ずしも正しくないのではないか.「第6章」には評価制度の話もあって参考になった.CTO の感性を活かした評価制度を作ると良いという話は今まで聞いたことがなかったけど,企業文化に沿って評価することは今では一般的だし,その延長線上にあるものなのかな.

本書では「リファラル採用」という表現こそ出てこなかったが,「第5章」に知人からの紹介で採用をするケースが増えているという話は書いてあった.良いエンジニアほどエージェントを使うことはなく,直接企業にコンタクトしたり,紹介で入社することが多いため,企業の情報発信力こそ重要という話だった.僕も直近半年間は技術広報を兼任で担当していて,どのようにプロダクトとして技術アピールをするか,という点にフォーカスしているので,引き続き様々な情報を発信していきたいと思う.

その「エンジニア採用」が不幸を生む ~良い人材を見つけ、活躍してもらうには何が必要か?

その「エンジニア採用」が不幸を生む ~良い人材を見つけ、活躍してもらうには何が必要か?

関連書籍

「A ランクの人は A ランクを選び,B ランクの人は C ランクの人を選ぶ」という話は How Google Works にも載っている.他にも「ラーニングアニマル(学び続けられる人)」の話も載っている.合わせて読むと良いと思う.How Google Works は特に良本だから!

How Google Works (ハウ・グーグル・ワークス)  ―私たちの働き方とマネジメント

How Google Works (ハウ・グーグル・ワークス) ―私たちの働き方とマネジメント

  • 作者: エリック・シュミット,ジョナサン・ローゼンバーグ,アラン・イーグル,ラリー・ペイジ,土方奈美
  • 出版社/メーカー: 日本経済新聞出版社
  • 発売日: 2014/10/09
  • メディア: 単行本
  • この商品を含むブログ (18件) を見る

Lambda + Apex 事例紹介 ~ 全ては AWS コスト削減のために ~

今日は社内の別プロダクトと合同勉強会があって,Lambda + Apex 便利だよ!という発表をしてきた.

タイトルは「Lambda + Apex 事例紹介 ~ 全ては AWS コスト削減のために ~」で,発表内容としては,大きく3点にフォーカスした.

  • CloudWatch Events + Lambda + CircleCI + Apex で Lambda をサーバレスに実行 & デプロイする話
  • Apex で Golang with Lambda を動かす仕組みとは
  • AWS 便利ツールの紹介

メインは Lambda + Apex の話で,前に書いた記事をベースにした.

kakakakakku.hatenablog.com

発表資料

speakerdeck.com

Golang with Lambda

Apex の珍しい点を挙げるとすると「Lambda で Golang を動かせる」ことだと思う.今回実際に試してみて,理解できた挙動をまとめた.詳しくは発表資料に書いたけど,Node.js から Golang バイナリを動かす子プロセスを生成して,プロセス間通信をすることで実現しているところまで確認することができた.

github.com

github.com

AWS 便利ツール

最後に AWS 便利ツールの紹介をした.awslogs は非常に便利!

github.com

github.com

github.com

github.com

gapps を使って Google Apps Script を継続的にデプロイする

最近 GAS (Google Apps Script) を書く機会があって,Google 関連のリソースに簡単にアクセスできて便利だなー!と感じる反面,直接 js を書いて実行する開発プロセスに違和感を感じていた.普通に GitHub でバージョン管理して,継続的にデプロイする方法を探していたところ,専用の公式ツール gapps を知って,実際に使ってみたら便利だったので,紹介する.

developers-jp.googleblog.com

gapps (node-google-apps-script)

gapps を使うと GAS を CLI でデプロイできるようになる.あまり難しいところはなく,README の通りに進めていけば使えるようになる.ザッと手順を残しておく.

github.com

gapps をインストールする

npm で簡単に使えるようになる.

$ npm install -g node-google-apps-script
$ gapps version
1.1.5

Google API Console でプロジェクトを作成して認証をする

事前に以下の設定が必要なため Google API Console でプロジェクトを作成する.今回は “gas-dmm” という名前にした.

  • “Google Drive API” を有効にする
  • OAuth 認証情報を取得する

console.developers.google.com

“Google Drive API” は「有効にする」を選択しておく.

f:id:kakku22:20170226030029p:plain

次に OAuth 認証情報を取得したら JSON をローカルにダウンロードする.

f:id:kakku22:20170226030256p:plain

ダウンロードした JSON を引数に gapps auth コマンドを実行すると URL が表示されるため,アクセスすると自動的に認証が完了する.

$ gapps auth xxx.json
Please visit the following url in your browser (you'll only have to do this once): https://accounts.google.com/o/oauth2/auth?xxx
Successfully Authenticated with Google Drive!

具体的には以下のメッセージが表示される.

You’ve been authenticated with Google Drive! You may close this page.

GAS を用意する

事前に GAS を用意しておく必要がある.GAS の URL に入ってる ID をコピーして gapps init コマンドの引数にすると,自動的に gapps.config.json が生成される.この gapps.config.json は秘匿情報のため,必ず .gitignore に含めること!

$ gapps init xxx

f:id:kakku22:20170226031020p:plain

コードを書いてデプロイする

最後にコードを書いてデプロイする.gapps upload コマンドで簡単にデプロイができる.

$ gapps upload

「DMM 英会話」のスケジュールを Google Calendar に同期する

今年から使ってる「DMM 英会話」で,1点残念なところを挙げるとすると「Google Calendar にスケジュールを同期できない」ところだと思う.個人的にスケジュールを振り返るために,今までは手動で登録していたけど,この作業を自動化したいと思った.

「DMM 英会話」の予約完了メールをトリガーに Google Calendar にスケジュール登録する GAS は既に公開されていて,非常に参考になった.個人的なユースケースとして,スケジュール名に「DMM 英会話」と「先生」の情報が入っていれば十分だったので,少し処理を減らしたりした.

yoonchulkoh.hatenablog.com

コードを雑に push しておいた.

github.com

フロー図

gapps のことも含めると,ザッとこんなフロー図になる.

f:id:kakku22:20170224234002p:plain

GAS を定期的に実行するにはトリガーを設定する必要がある.簡単!

f:id:kakku22:20170226032415p:plain

まとめ

  • gapps を使うと GAS の管理をアプリケーション開発のベストプラクティスに乗せることができる
  • 「DMM 英会話」のスケジュールを自動的に Google Calendar に同期できる

f:id:kakku22:20170226033953p:plain

Aurora のスロークエリログを Fluentd 経由で Amazon Elasticsearch Service に転送する

Aurora でスロークエリログを出力して,Fluentd 経由で Amazon Elasticsearch Service に転送する構成を作った.MySQL on EC2 のときは,サーバ内で直接スロークエリログを確認していて非常に面倒だったので(単なる運用上の考慮不足),運用面で大きく改善できるし,アプリケーションエンジニアも自由に Kibana で確認できるため,パフォーマンス改善が捗ると思う.非常に良い.

アーキテクチャ図

f:id:kakku22:20170223135353p:plain

Aurora インスタンスパラメータ

デフォルトのインスタンスパラメータではスロークエリログは有効になっていないため,パラメータを変更する必要がある.今回はスロークエリログを有効にするだけではなく,デフォルトで10秒になっている閾値を5秒に変更した.

パラメータ名 Before After
slow_query_log <engine-default> 1
long_query_time <engine-default> 5

どちらも「適用タイプ」は「dynamic」なので,Aurora の再起動は必要なく,自動的に反映される.反映されたかどうかは,実際に SQL で確認することができる.

mysql> SHOW VARIABLES LIKE 'slow_query_log';
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | ON    |
+----------------+-------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES LIKE 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 5.000000 |
+-----------------+----------+
1 row in set (0.00 sec)

Aurora のスロークエリはデフォルトでテーブルに書き込まれる

MySQL on EC2 と違って,Aurora ではスロークエリログがデフォルトでテーブルに書き込まれる.もしファイルに書き込む場合は,log_output パラメータを変更する必要がある.今回はテーブルのまま運用することにした.

mysql> SHOW VARIABLES LIKE 'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | TABLE |
+---------------+-------+
1 row in set (0.00 sec)

スロークエリログは mysql.slow_log というテーブルに書き込まれるが,Aurora 側で用意されているストアドプロシージャ mysql.rds_rotate_slow_log を実行すると mysql.slow_log_backup にローテートされる.よって,例えば mysql.slow_log_backup を2回連続で実行すると,スロークエリログは完全に消すことができる.後述する Fluentd Plugin でも mysql.rds_rotate_slow_log を実行して,mysql.slow_log_backup からスロークエリログを取得する実装になっている.簡単に動作確認をすると,以下のようになる.

--
-- スロークエリログなし
--

mysql> SELECT * FROM mysql.slow_log\G
Empty set (0.00 sec)

mysql> SELECT * FROM mysql.slow_log_backup\G
Empty set (0.00 sec)

--
-- スロークエリログを発行する
--

mysql> SELECT SLEEP(10);
+-----------+
| SLEEP(10) |
+-----------+
|         0 |
+-----------+
1 row in set (10.01 sec)

--
-- スロークエリログが `mysql.slow_log` に登録された
--

mysql> SELECT * FROM mysql.slow_log\G
*************************** 1. row ***************************
    start_time: 2017-02-23 13:06:28
     user_host: xxx[xxx] @  [1.2.3.4]
    query_time: 00:00:10
     lock_time: 00:00:00
     rows_sent: 1
 rows_examined: 0
            db:
last_insert_id: 0
     insert_id: 0
     server_id: 0
      sql_text: SELECT SLEEP(10)
     thread_id: 42
1 row in set (0.00 sec)

mysql> SELECT * FROM mysql.slow_log_backup\G
Empty set (0.01 sec)

--
-- ストアドプロシージャを実行してローテートする
--

mysql> CALL mysql.rds_rotate_slow_log;
Query OK, 0 rows affected (0.02 sec)

--
-- スロークエリログが `mysql.slow_log_backup` に移動された
--

mysql> SELECT * FROM mysql.slow_log\G
Empty set (0.00 sec)

mysql> SELECT * FROM mysql.slow_log_backup\G
*************************** 1. row ***************************
    start_time: 2017-02-23 13:06:28
     user_host: xxx[xxx] @  [1.2.3.4]
    query_time: 00:00:10
     lock_time: 00:00:00
     rows_sent: 1
 rows_examined: 0
            db:
last_insert_id: 0
     insert_id: 0
     server_id: 0
      sql_text: SELECT SLEEP(10)
     thread_id: 42
1 row in set (0.00 sec)

Aurora のログ関連の仕様とストアドプロシージャの仕様はドキュメントに書いてある.

docs.aws.amazon.com

docs.aws.amazon.com

SELECT SLEEP(10)

MySQL には標準で SLEEP 関数が付属していて,意図的にスロークエリを再現することができる.案外知らない人も多そうだし,僕も今回検証をしていて知ったので,一応載せておく.

fluent-plugin-rds-slowlog

今回スロークエリログを Fluentd 経由で転送するために,以下のプラグインを使った.

github.com

td-agent.conf は以下のように書いた.ポイントを箇条書きにしておく.

  • Aurora Writer と Aurora Reader のスロークエリログを転送するため source を2個用意した
  • interval はデフォルトの10秒だと短すぎるため,300秒に変更した
<source>
    @type rds_slowlog
    tag rds_slowlog.writer
    host example1.ap-northeast-1.rds.amazonaws.com
    username xxx
    password yyy
    interval 300
</source>

<source>
    @type rds_slowlog
    tag rds_slowlog.reader
    host example2.ap-northeast-1.rds.amazonaws.com
    username xxx
    password yyy
    interval 300
</source>

<match rds_slowlog.*>
    ...
</match>

また,接続するユーザーも専用に用意した.必要なのは参照権限とプロシージャ実行権限なので,以下のように SELECTEXECUTE だけを GRANT した.

mysql> GRANT SELECT, EXECUTE ON *.* TO 'fluentd'@'1.2.3.4' IDENTIFIED BY 'xxx';

今日 2/23 にリリースされた fluent-plugin-rds-slowlog v0.0.8 を使おう!

今までずっと公開されていた最新バージョンは v0.0.7 で,実はこれだと MySQL の再接続でエラーになる問題があり,既に master では修正が取り込まれていた.しかし,Gem がリリースされていなかったため,v0.0.7 だと source を2個用意したことによって,コネクションがバッティングする場合があり,そのときに以下のエラーが出てしまっていた.

2017-02-22 15:35:12 +0900 [error]: Lost connection to MySQL server during query
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/mysql2-0.3.21/lib/mysql2/client.rb:80:in `_query'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/mysql2-0.3.21/lib/mysql2/client.rb:80:in `block in query'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/mysql2-0.3.21/lib/mysql2/client.rb:79:in `handle_interrupt'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/mysql2-0.3.21/lib/mysql2/client.rb:79:in `query'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/fluent-plugin-rds-slowlog-0.0.7/lib/fluent/plugin/in_rds_slowlog.rb:69:in `output'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/fluent-plugin-rds-slowlog-0.0.7/lib/fluent/plugin/in_rds_slowlog.rb:92:in `call'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/fluent-plugin-rds-slowlog-0.0.7/lib/fluent/plugin/in_rds_slowlog.rb:92:in `on_timer'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/cool.io-1.4.2/lib/cool.io/loop.rb:88:in `run_once'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/cool.io-1.4.2/lib/cool.io/loop.rb:88:in `run'
  2017-02-22 15:35:12 +0900 [error]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/fluent-plugin-rds-slowlog-0.0.7/lib/fluent/plugin/in_rds_slowlog.rb:62:in `watch'
2017-02-22 15:45:22 +0900 [error]: MySQL server has gone away
  2017-02-22 15:45:22 +0900 [error]: suppressed same stacktrace
2017-02-22 15:55:32 +0900 [error]: closed MySQL connection
  2017-02-22 15:55:32 +0900 [error]: suppressed same stacktrace

エラーの詳細は Issue にも書いた.

github.com

Issue を書いた後に,直接コードを書き換えて master の状態にして検証をしてみたら,自動的に再接続が行われることによって問題は解決した.よって,メンテナ側に頼んで v0.0.8 を今日 2/23 にリリースしてもらった.

@winebarrel さん,助かりました!!!

github.com

ちなみに v0.0.8 だと,上記エラーが出た後に,以下のログが追加で出るようになっている.引き続きスタックトレース自体は出てしまうため,捨てる方法があるなら捨てたいなぁ…という感じ.どうしたら良いんだろう.

2017-02-23 12:03:28 +0900 [info]: fluent-plugin-rds-slowlog: try to reconnect to RDS

Amazon Elasticsearch Service + Kibana で可視化する

Elasticsearch 側は特に難しいことはなく,テンプレートを用意して,Kibana で可視化した.

{
    "template": "rds_slowlog-*",
    "mappings": {
        "rds_slowlog": {
            "properties": {
                "user_host": {
                    "type": "string",
                    "index": "not_analyzed"
                },
                "sql_text": {
                    "type": "string",
                    "index": "not_analyzed"
                }
            }
        }
    }
}

Elasticsearch のインデックスは統一しているけど,@log_name で Writer と Reader の識別ができるため,以下のようなクエリを書くとデータを分割できる.

@log_name: rds_slowlog.reader
@log_name: rds_slowlog.writer

スロークエリログを可視化できて便利!

f:id:kakku22:20170223135408p:plain

検証してるときに Fluentd → Amazon Elasticsearch Service の転送でエラーになってて,困ってたら @yoshi_ken さんに Twitter でリプを頂いて,デバッグ方法などを教えてもらった.結果的に aggregator の時刻がズレてて AWS API のコールがエラーになっていることが原因だった.NTP 入って無くて残念な感じだった.アドバイスありがとうございました!

まとめ

  • Aurora のスロークエリを Fluentd 経由で Amazon Elasticsearch Service に転送した
  • Kibana でスロークエリを可視化できると,アプリケーションエンジニアのパフォーマンス改善が捗る
  • fluent-plugin-rds-slowlogv0.0.8 を使おう