まっしろけっけ

めもてきなやーつ

ペパボに転職して 1 年経ってた

はじめに

転職から 1 年経っていたのでペパボに入って何してたとか、
思ったことを書いていく。

転職した経緯は下の記事に書いてあります。

shiro-16.hatenablog.com

なにしてたの?

  • EC で 2 ヶ月だけカートの開発してた
  • minne で API 開発することになった
  • 検索のあれこれをやることになった
  • 本に記事を書いた
  • お見合いした
  • API チームのテクニカルリードになった
  • シニアエンジニアになった

ざっくりという感じ(他にもあるけど)

EC で 2 ヶ月だけカートの開発してた

EC 事業の求人に応募して採用されたので、EC 事業で働き始めた。
なぜ 2 ヶ月だけかというと社内で minne というサービスの API 開発者を募集していたので応募して通ったから。

2 ヶ月で応募するということで、悩みはしたのですよ。
EC 事業の求人に応募して採用されたので申し訳ないなとか、入社 2 ヶ月で異動ってとかその他いろいろ
申し訳ないなというのは本当に思っている

でも、単純に面白そうだなって思ったので面白そうだと思ったらとりあえず一回やってみるという考えで生きてるのと
アプリに関わる開発から離れてみてやっぱりそっちが自分には合ってるのかなという考えから。(別に EC の仕事がつまらなかったとかでは全然ない)

minne で API 開発することになった

ということで minne の API 開発することになり、minne チームは当時はフロアが違ったり、福岡と一緒に開発を行っているということもあり独特な雰囲気があったという気がしている。

  • hsbt さんと仕事で絡む機会が大幅に増えた
  • class 設計の知識が大幅に向上した(気がしている)
  • web アプリケーション開発以外の面白いと思えるものが見つかった
  • 目標にするべき人と仕事が出来ている

他にも多々あるが上記の理由から結果的に minne に来て正解だったなと思った。

hsbt さんと仕事で絡む機会が大幅に増えた

これは説明する必要もないのだが、自分自身がペパボに転職した理由の 1 つだったので単純に嬉しいという感じ

class 設計の知識が大幅に向上した(気がしている)

レビュー等を通して class 設計だったり綺麗なコードのとは?とか、それに対して妥協しない姿勢というものを身につけることができた。
今までも出来ていたつもりでいたが、ワンランク上のステージでそういうのを考えられるようになった気がする。

既にペパボを退職してしまった人だが、その方にそういうことに関して感心させられることが多々ありその人みたいになりたいと思ったこと。
その人が退職するということを知った日から出来る限り盗めるところは盗もうと思い行動する(コードを書く)ようになった。
あわせて年末にリファクタリングの本を読みながらコード書いてたのもよかった点かなと思う。

※あくまで個人の感想です。

web アプリケーション開発以外の面白いと思えるものが見つかった

これは後述する、検索のこととか画像のこととか
web アプリケーションの一部といえば一部なのだが…

目標にするべき人と仕事が出来ている

class 設計の知識が...で書いた方の他にも、今目標にしている人がいるのだが
自分の場合目標にする人が近くにいる方がやる気が出るっぽい。

目標にするというか、開発が好きだから負けたくない人、勝ちたい人といった方が良いのかもしれない。

検索のあれこれをやることになった

minne の検索をいい感じにするみたいな話になった時に、面白そうだなって思ったのでやってみたいと手を挙げたのが始まり。
ペパボは手を挙げればやらせて貰える環境なのでとても良い。
日本語って難しいなとか思いながら、いい感じの検索とは?みたいなのを日々やっていて凄く難しいと思う反面凄く楽しいなと思いながら開発している。

その一環で画像を解析して物体を抽出してその色を判別するみたいなことをやって色検索をリリースすることが出来た。
画像を解析するのも楽しいなと思い始めた。

ここら辺は結構ネタを持っているので、どこかで発表したいと思っている。

本に記事を書いた

shiro-16.hatenablog.com

上記参照

今年の目標の一つ言語化を頑張るという自分が苦手としていることに挑戦するいい機会だと思った。
自分の頭の中で理解している技術的なことを他人が理解出来る言語にするのはとても難しいというかとても苦手なので、
出来るだけがんばっていこうというのを目標の一つにしていて日々の業務で作成している PR や issue 等でも言語化することに気をつけて日本語を書くようにしている。
そういう意味でも記事を書けたことはとても勉強になることが多かったし、みんなこんな感じで記事をかいてるのかぁなるほどとなれたのはよかった。

お見合いした

haken.inte.co.jp

面白そうだと思ったのでやった(強制されたとかは無い)

API チームのテクニカルリードになった

API の開発を行う人が増えてテクニカルリードというものをやることになった。
テクニカルリードはこちらの記事が参考になりそう。
とりあえず頑張っているという気持ち

シニアエンジニアになった

ペパボの職位制度に関してはここらへん
シニアになって思うのはペパボのいるだけで成長できる環境というのはその通りだと中の人として思うのだが、
シニア以上になる人はそれだけでは満足せずに自分から成長する為の何かを取りに行くことが出来る人なのかなと思う。

自分の場合は検索周りのこととか上に書いてないことも面白そうだからやってみるかという思いから取りにいって、
一定の成果を出し、終わってみたら成長を感じられたということが多く結果としてシニアになれたのでシニアになる為にとかではないのだが…

※あくまで個人の感想です。

でこれがシニアエンジニアになった際の抱負(?)
シニアの面談のフィードバックであんちぽさんに頂いたノリが良いっていうのは個人的にとても気に入っている。

やっておきますというのはタイポとかではなく、今までだと「いい感じによろしく」と言われてからの「やります!」という返事だったのを、
これからは「いい感じによろしく」と言われた際に「やっておきました!」と言える機会を増やすという意味での(言われる前に先に)やっておきますという意味です。

ということで頑張ります。

さいごに

ということで約 1 年何やってたかというのとペパボで働いて感じたことを書いてみた。
優秀な人が多くて毎日楽しく開発出来るという環境なので最高かという気持ち


最後に自戒の念を込めて貼っておきます…

MySQL の binlog について調べたメモ

MySQL の binlog について

実際に実行された更新系クエリの情報が記述されていてなんらかの理由によりデータが壊れた際の
データ復旧とかにも役にたつ。

binlog の format には以下の 3 種類ある

フォーマットの種類 設定値(文字列) 設定値(数字) 備考
ステートメントベース STATEMENT 1 実際に実行された SQL を記録
行ベース ROW 2 実際に変更された行のデータの情報を記録
ミックス MIXED 0 基本的にはステートメントベースと同じで非決定性のクエリの際は行ベースと同じ形式のログを出力する

ここら辺は DB server を構築する際にレプリケーションとかを考えると思うので
基本的には理解している内容だと思われます。

今回は binlog の中身を除いて実際にどうなってるの?というところを調べた。

実際にログを出力させて比べる

環境
my.cnf

my.cnf を変更して binlog を出力するようにする

[mysqld]
log_bin
binlog_format = 1 # ステートメントベースのログを出力する
table

今回は下記の table を使用する

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
使用する SQL
INSERT INTO users SET name = UUID(), created_at = NOW(), updated_at = NOW(); # 非決定性のクエリ
INSERT INTO users SET name = "test", created_at = NOW(), updated_at = NOW();

ステートメントベース

$ mysqlbin mysql-bin.000001
# 一部抜粋
BEGIN
/*!*/;
# at 247
# at 279
#160612 14:56:46 server id 1  end_log_pos 279 CRC32 0xc624c39f 	Intvar
SET INSERT_ID=20/*!*/;
#160612 14:56:46 server id 1  end_log_pos 476 CRC32 0x6a52b700 	Query	thread_id=1	exec_time=0	error_code=0
use `db`/*!*/;
SET TIMESTAMP=1465711006/*!*/;
INSERT INTO users SET name = UUID(), created_at = NOW(), updated_at = NOW()
/*!*/;
# at 476
#160612 14:56:46 server id 1  end_log_pos 507 CRC32 0xf803a15e 	Xid = 21
COMMIT/*!*/;
# at 507
#160612 14:56:53 server id 1  end_log_pos 634 CRC32 0xd617f39d 	Query	thread_id=1	exec_time=0	error_code=0
SET TIMESTAMP=1465711013/*!*/;
BEGIN
/*!*/;
# at 634
# at 666
#160612 14:56:53 server id 1  end_log_pos 666 CRC32 0xe6f7f9d2 	Intvar
SET INSERT_ID=21/*!*/;
#160612 14:56:53 server id 1  end_log_pos 863 CRC32 0x765b9f4d 	Query	thread_id=1	exec_time=0	error_code=0
SET TIMESTAMP=1465711013/*!*/;
INSERT INTO users SET name = "test", created_at = NOW(), updated_at = NOW()
/*!*/;
# at 863
#160612 14:56:53 server id 1  end_log_pos 894 CRC32 0x828b94cf 	Xid = 22
COMMIT/*!*/;

確かに発行された SQL がそのまま出力されている

行ベース

$ mysqlbin mysql-bin.000002
# 一部抜粋
BEGIN
/*!*/;
# at 220
#160612 14:58:55 server id 1  end_log_pos 295 CRC32 0xa71dbfff 	Table_map: `db`.`users` mapped to number 83
# at 295
#160612 14:58:55 server id 1  end_log_pos 383 CRC32 0xb2425e19 	Write_rows: table id 83 flags: STMT_END_F

BINLOG '
H/pcVxMBAAAASwAAACcBAAAAAFMAAAAAAAEAGGJsb2dfc2FtcGxlc19kZXZlbG9wbWVudAAFdXNl
cnMABAMPEhIE/QIAAAL/vx2n
H/pcVx4BAAAAWAAAAH8BAAAAAFMAAAAAAAEAAgAE//AWAAAAJABiZjYwNTc0MC0zMDYyLTExZTYt
OGQ2Zi0yZjg2MTFjNWE2ZjGZmZjut5mZmO63GV5Csg==
'/*!*/;
# at 383
#160612 14:58:55 server id 1  end_log_pos 414 CRC32 0xf290e6c8 	Xid = 21
COMMIT/*!*/;
# at 414
#160612 14:58:57 server id 1  end_log_pos 514 CRC32 0xc1908135 	Query	thread_id=1	exec_time=0	error_code=0
SET TIMESTAMP=1465711137/*!*/;
BEGIN
/*!*/;
# at 514
#160612 14:58:57 server id 1  end_log_pos 589 CRC32 0x96559f32 	Table_map: `db`.`users` mapped to number 83
# at 589
#160612 14:58:57 server id 1  end_log_pos 645 CRC32 0x56be64d2 	Write_rows: table id 83 flags: STMT_END_F

BINLOG '
IfpcVxMBAAAASwAAAE0CAAAAAFMAAAAAAAEAGGJsb2dfc2FtcGxlc19kZXZlbG9wbWVudAAFdXNl
cnMABAMPEhIE/QIAAAIyn1WW
IfpcVx4BAAAAOAAAAIUCAAAAAFMAAAAAAAEAAgAE//AXAAAABAB0ZXN0mZmY7rmZmZjuudJkvlY=
'/*!*/;
# at 645
#160612 14:58:57 server id 1  end_log_pos 676 CRC32 0x24726578 	Xid = 22
COMMIT/*!*/;

な、なるほどわからん
もっと詳しく見れるようにオプションを追加する

$ mysqlbinlog -vvv --base64-output=DECODE-ROWS mysql-bin.000002
# 一部抜粋
BEGIN
/*!*/;
# at 220
#160612 14:58:55 server id 1  end_log_pos 295 CRC32 0xa71dbfff 	Table_map: `db`.`users` mapped to number 83
# at 295
#160612 14:58:55 server id 1  end_log_pos 383 CRC32 0xb2425e19 	Write_rows: table id 83 flags: STMT_END_F
### INSERT INTO `db`.`users`
### SET
###   @1=22 /* INT meta=0 nullable=0 is_null=0 */
###   @2='bf605740-3062-11e6-8d6f-2f8611c5a6f1' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */
###   @3='2016-06-12 14:58:55' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
###   @4='2016-06-12 14:58:55' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
# at 383
#160612 14:58:55 server id 1  end_log_pos 414 CRC32 0xf290e6c8 	Xid = 21
COMMIT/*!*/;
# at 414
#160612 14:58:57 server id 1  end_log_pos 514 CRC32 0xc1908135 	Query	thread_id=1	exec_time=0	error_code=0
SET TIMESTAMP=1465711137/*!*/;
BEGIN
/*!*/;
# at 514
#160612 14:58:57 server id 1  end_log_pos 589 CRC32 0x96559f32 	Table_map: `db`.`users` mapped to number 83
# at 589
#160612 14:58:57 server id 1  end_log_pos 645 CRC32 0x56be64d2 	Write_rows: table id 83 flags: STMT_END_F
### INSERT INTO `db`.`users`
### SET
###   @1=23 /* INT meta=0 nullable=0 is_null=0 */
###   @2='test' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */
###   @3='2016-06-12 14:58:57' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
###   @4='2016-06-12 14:58:57' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
# at 645
#160612 14:58:57 server id 1  end_log_pos 676 CRC32 0x24726578 	Xid = 22
COMMIT/*!*/;

column 名が書かれているわけではなく @1 とか書かれてあるのだなぁ。
定義した table の column の順番通り @1 = id, @2 = name, @3 = created_at, @4 = updated_at という順番っぽい
UUID() も展開後の文字列が入っているのがわかる。

ミックス

$ mysqlbin mysql-bin.000003
# 一部抜粋
BEGIN
/*!*/;
# at 220
#160612 15:05:14 server id 1  end_log_pos 295 CRC32 0x3b4cb14b 	Table_map: `db`.`users` mapped to number 83
# at 295
#160612 15:05:14 server id 1  end_log_pos 383 CRC32 0x9905bb6a 	Write_rows: table id 83 flags: STMT_END_F

BINLOG '
mvtcVxMBAAAASwAAACcBAAAAAFMAAAAAAAEAGGJsb2dfc2FtcGxlc19kZXZlbG9wbWVudAAFdXNl
cnMABAMPEhIE/QIAAAJLsUw7
mvtcVx4BAAAAWAAAAH8BAAAAAFMAAAAAAAEAAgAE//AYAAAAJABhMTY1OWFmNi0zMDYzLTExZTYt
OTc1Yi1hZjExNDczMDFkMDGZmZjxTpmZmPFOarsFmQ==
'/*!*/;
# at 383
#160612 15:05:14 server id 1  end_log_pos 414 CRC32 0x84766030 	Xid = 21
COMMIT/*!*/;
# at 414
#160612 15:05:16 server id 1  end_log_pos 541 CRC32 0xe4c9b7a8 	Query	thread_id=1	exec_time=0	error_code=0
SET TIMESTAMP=1465711516/*!*/;
BEGIN
/*!*/;
# at 541
# at 573
#160612 15:05:16 server id 1  end_log_pos 573 CRC32 0x10f97ca5 	Intvar
SET INSERT_ID=25/*!*/;
#160612 15:05:16 server id 1  end_log_pos 770 CRC32 0xd9adc7e3 	Query	thread_id=1	exec_time=0	error_code=0
use `db`/*!*/;
SET TIMESTAMP=1465711516/*!*/;
INSERT INTO users SET name = "test", created_at = NOW(), updated_at = NOW()
/*!*/;
# at 770
#160612 15:05:16 server id 1  end_log_pos 801 CRC32 0xa8cd90ff 	Xid = 22
COMMIT/*!*/;

本当にミックスされている
もっと詳しく見てみる

$ mysqlbinlog -vvv --base64-output=DECODE-ROWS mac-no-MacBook-Pro-bin.000003
# 一部抜粋
BEGIN
/*!*/;
# at 220
#160612 15:05:14 server id 1  end_log_pos 295 CRC32 0x3b4cb14b 	Table_map: `db`.`users` mapped to number 83
# at 295
#160612 15:05:14 server id 1  end_log_pos 383 CRC32 0x9905bb6a 	Write_rows: table id 83 flags: STMT_END_F
### INSERT INTO `db`.`users`
### SET
###   @1=24 /* INT meta=0 nullable=0 is_null=0 */
###   @2='a1659af6-3063-11e6-975b-af1147301d01' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */
###   @3='2016-06-12 15:05:14' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
###   @4='2016-06-12 15:05:14' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
# at 383
#160612 15:05:14 server id 1  end_log_pos 414 CRC32 0x84766030 	Xid = 21
COMMIT/*!*/;
# at 414
#160612 15:05:16 server id 1  end_log_pos 541 CRC32 0xe4c9b7a8 	Query	thread_id=1	exec_time=0	error_code=0
SET TIMESTAMP=1465711516/*!*/;
BEGIN
/*!*/;
# at 541
# at 573
#160612 15:05:16 server id 1  end_log_pos 573 CRC32 0x10f97ca5 	Intvar
SET INSERT_ID=25/*!*/;
#160612 15:05:16 server id 1  end_log_pos 770 CRC32 0xd9adc7e3 	Query	thread_id=1	exec_time=0	error_code=0
use `db`/*!*/;
SET TIMESTAMP=1465711516/*!*/;
INSERT INTO users SET name = "test", created_at = NOW(), updated_at = NOW()
/*!*/;
# at 770
#160612 15:05:16 server id 1  end_log_pos 801 CRC32 0xa8cd90ff 	Xid = 22
COMMIT/*!*/;

非決定性のクエリの際は行ベースと同じ形式のログを出力することがわかった。

mysqlbinlog コマンドのオプション + SQL

よく使いそうなオプションをまとめる

$ mysqlbinlog -h host --read-from-remote-server mysql-bin.000001 # リモートサーバの binlog を見ることができる
$ mysqlbinlog --start-datetime="2016-01-01 00:00:00" --stop-datetime="2016-01-01 12:00:00" mysql-bin.000001 # 日時を指定して binlog を見ることができる

binlog 情報を出力してくれる SQL をまとめる

mysql> SHOW GLOBAL VARIABLES LIKE 'binlog_format'; # binlog format を表示
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+

mysql> SHOW BINARY LOGS; # server の binlog 一覧を表示
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 |       531 |
| mysql-bin.000002 |       437 |
| mysql-bin.000003 |       933 |
+------------------+-----------+

mysql> SHOW BINLOG EVENTS; # binlog 内のイベントを表示
+------------------+-----+-------------+-----------+-------------+----------------------------------------------------------------------------------------+
| Log_name         | Pos | Event_type  | Server_id | End_log_pos | Info                                                                                   |
+------------------+-----+-------------+-----------+-------------+----------------------------------------------------------------------------------------+
| mysql-bin.000001 |   4 | Format_desc |         1 |         120 | Server ver: 5.6.27-log, Binlog ver: 4                                                  |
| mysql-bin.000001 | 120 | Query       |         1 |         247 | BEGIN                                                                                  |
| mysql-bin.000001 | 247 | Intvar      |         1 |         279 | INSERT_ID=16                                                                           |
| mysql-bin.000001 | 279 | Query       |         1 |         477 | use `db`; insert into users  set name = UUID(), created_at = NOW(), updated_at = NOW() |
| mysql-bin.000001 | 477 | Xid         |         1 |         508 | COMMIT /* xid=24 */                                                                    |
| mysql-bin.000001 | 508 | Stop        |         1 |         531 |                                                                                        |
+------------------+-----+-------------+-----------+-------------+----------------------------------------------------------------------------------------+

最後に

ということで今回は MySQL の binlog についての format による違いを確認してみました。
binlog にどのような情報が記述されているか最低限覚えておくと
ここぞという時に役に立つかと思います

Ruby と Google Cloud Platform の Cloud Vision API で画像を解析して貰う

Vision API is 何?

画像のさまざまな情報を解析してくれる API です。

物体検知、有害コンテンツ検知、ロゴ検知、ランドマーク検知、OCR、顔検知、色検知等を行ってくれる。

料金は下記に詳細が書いてありますが 1000 ユニット / 月 は無料で利用出来るので、
どんなものか遊んでみたいとか Google すげぇって思ったりするのは問題ないかと思います。
cloud.google.com

実際に使って見る

事前準備

事前準備としては Google Cloud Platform のアカウントを作成して、
API KEY を取得しておく必要があります。(クレジットカードの登録が必要)

google-api-client を使う

Google の様々な API の client がまとまっている gem の google-api-client を使って Vision API に画像を投げてみます。
物体検知と色検知を指定しています。

$ gem install google-api-client
require 'google/apis/vision_v1'
Vision = Google::Apis::VisionV1
vision = Vision::VisionService.new
vision.key = "API KEY"

request = Google::Apis::VisionV1::BatchAnnotateImagesRequest.new(
  requests: [
    {
      image:{
        content: File.read("1.png")
      },
      features: [
        {
          type: "LABEL_DETECTION",
          maxResults: 10
        },
        {
          type: "IMAGE_PROPERTIES",
          maxResults: 10
        }
      ]
    }
  ]
)

vision.annotate_image(request)

gcp-vision を使う

google-api-client は require した際に一瞬固まる(load に時間がかかる)のが気になったので Vision API 専用の軽量な client を自作してみた。

$ gem install gcp-vision
require 'gcp/vision'

Gcp::Vision.configure do |config|
  config.api_key = "API KEY"
end

request = {
  requests: [
    {
      image:{
        content: Base64.encode64(File.read("1.png"))
      },
      features: [
        {
          type: "LABEL_DETECTION",
          maxResults: 10
        },
        {
          type: "IMAGE_PROPERTIES",
          maxResults: 10
        }
      ]
    }
  ]
}

Gcp::Vision.annotate_image(request)

レスポンスとしてどんな情報が返ってくるか?

実際にどんな情報が Vision API から返って来るのか?
ということで自分が普段 SNS 等のアイコンに使用している下記の愛犬の画像を Vision API に投げてみました。
f:id:shiro-16:20160508183558j:plain:w100

{
  "responses": [
    {
      "labelAnnotations": [
        {
          "mid": "/m/068hy",
          "description": "pet",
          "score": 0.98951507
        },
        {
          "mid": "/m/0bt9lr",
          "description": "dog",
          "score": 0.98687589
        },
        {
          "mid": "/m/04rky",
          "description": "mammal",
          "score": 0.9618566
        },
        {
          "mid": "/m/0jbk",
          "description": "animal",
          "score": 0.95587397
        },
        {
          "mid": "/m/02cyl6",
          "description": "maltese",
          "score": 0.89896905
        },
        {
          "mid": "/m/09686",
          "description": "vertebrate",
          "score": 0.89741188
        },
        {
          "mid": "/m/01lrl",
          "description": "carnivoran",
          "score": 0.85160905
        },
        {
          "mid": "/m/0kpmf",
          "description": "dog breed",
          "score": 0.77777779
        },
        {
          "mid": "/m/036hyn",
          "description": "toy dog",
          "score": 0.7
        },
        {
          "mid": "/m/0b26w3",
          "description": "schnoodle",
          "score": 0.68533921
        }
      ],
      "imagePropertiesAnnotation": {
        "dominantColors": {
          "colors": [
            {
              "color": {
                "red": 197,
                "green": 199,
                "blue": 195
              },
              "score": 0.53806108,
              "pixelFraction": 0.33916494
            },
            {
              "color": {
                "red": 86,
                "green": 84,
                "blue": 82
              },
              "score": 0.030347142,
              "pixelFraction": 0.046761185
            },
            {
              "color": {
                "red": 220,
                "green": 230,
                "blue": 229
              },
              "score": 0.31318155,
              "pixelFraction": 0.19654006
            },
            {
              "color": {
                "red": 165,
                "green": 162,
                "blue": 158
              },
              "score": 0.047778718,
              "pixelFraction": 0.19640999
            },
            {
              "color": {
                "red": 53,
                "green": 55,
                "blue": 55
              },
              "score": 0.021588614,
              "pixelFraction": 0.016324142
            },
            {
              "color": {
                "red": 123,
                "green": 116,
                "blue": 115
              },
              "score": 0.017005477,
              "pixelFraction": 0.10022113
            },
            {
              "color": {
                "red": 63,
                "green": 92,
                "blue": 77
              },
              "score": 0.009515699,
              "pixelFraction": 0.012291883
            },
            {
              "color": {
                "red": 25,
                "green": 28,
                "blue": 29
              },
              "score": 0.0089049507,
              "pixelFraction": 0.0029916754
            },
            {
              "color": {
                "red": 35,
                "green": 56,
                "blue": 49
              },
              "score": 0.0033917534,
              "pixelFraction": 0.0018210198
            },
            {
              "color": {
                "red": 89,
                "green": 121,
                "blue": 105
              },
              "score": 0.0033548647,
              "pixelFraction": 0.017364724
            }
          ]
        }
      }
    }
  ]
}

物体検知の結果は pet, dog, mammal, animal, maltese... 等が返ってきた。
dog だけではなく maltese といった感じで正確な犬種まで返って来るのには驚くばかりだ。

次に色検知だが RGB だとわかりづらいので下記のようにしてみた。
⬛︎⬛︎⬛︎⬛︎⬛︎
全体的に白いのでまぁこんな感じかぁ

最後に

その他の検知方法を使用したい場合などは下記のマニュアルに載っているので興味があれば調べてみるのが良いかと。
cloud.google.com

Elasticsearch の勉強会を社内で行なった話

最近圧倒的インプットによって、圧倒的にアウトプットが減っている僕です。
ということでリハビリがてら 4 月の初めに Elasticsearch の勉強会を社内でやったのでそのことについて書いてみます。
そこまで技術的に深い話はないので期待しないでください。

勉強会の開催経緯

今のプロジェクトで検索に関してあれこれ頑張っていく中で Elasticsearch を使用することになり、
index の設計とか Rails から Elasticsearch 使うとことか作ったわけですが
チームメンバーから知見を共有してくれ!という要望があったのと自分自信も自分だけわかっても後々辛い感じになるだろうという感じだった + 弊社はいるだけで成長出来るらしいのでみんなを成長させようではないかという思いから、
やるぞ!ってなったのが 2 月末です。

で開催したのが 4 月初め...だいぶ遅い...(反省はしている)

実際の内容

Elasticsearch とは?みたいなところをざっくり説明した後に、
tokenizer, filter, analyzer の説明を行なった後に実際に設定している settings 周りの解説を行なった。

その後、実際に設定している mappings 周りの説明をしたり、ドキュメントが登録される際の流れや、クエリを投げた際の流れを説明

最後に、ハンズオンとして index を定義した後に基本的な CRUD + 検索クエリを投げる、現在プロジェクトで実際に投げられているクエリの解説という流れで行なった。

若干資料作成をミスって手順がグダッてしまった感はあったが、所々で @monochromegane さんがナイスな質問を投げてくれたので参加者のみんなの理解が深まったのでは?と勝手に思っている。

同時間内で @yano3クラスタについてより詳しく話してもらう予定だったのだが、
時間が押してしまい別日で説明をしてもらった。その内容が下記の記事になります。

yano3.hatenablog.jp

最後に

Elasticsearch をリアルタイムログ解析以外で触ったことがなかったので色々大変だったのですがそれはまた別のお話。
やるぞ!って言ってから 1 ヶ月経過したのは本当に反省している。
Elasticsearch を ES と略すのに若干の違和感を覚えている僕なのでした

圧倒的インプットによりネタはいっぱいあるので、またちょいちょいブログ書いていこうと思っています。。。

WEB+DB PRESS Vol.92 「Web開発新人研修」の一部を書きました。

久しぶりのブログです。
4 月なのに 2016 年初ブログです。

本題

4 月 23 日発売の WEB+DB PRESS Vol.92 内の特集の一つである「Web開発新人研修」をペパボのエンジニアで寄稿させていただきました。
献本を頂いたのでざっくりと説明をしていこうと思います。
Web 開発とはどんなものなのかを浅く広く把握できる内容になっています。

自分は 3 章の「Web アプリケーションを作ろう」を担当しました。
Web アプリケーションの概要、フレームワークについて、Rails を使用して MVC を解説しながら、
ToDo アプリケーションを開発するという内容になっています。

コード自体は scaffold で作成されるファイルの中でも必要最低限なコードの作成と解説をしています。
scaffold は凄いのですが、凄過ぎてよくわからないけど動くという状況になることもあるかと思うので
このような形式で解説をさせて頂きました。


説明を行おうと思えば記事では触れていない部分もいくらでも説明出来るのですが、
今回は新人研修というのが目的の記事になるので、詳細な説明を省いている為説明不足な点も多いかと思います。
そのような点はイケメン先輩エンジニアが新人さんに教えてあげたり、逆に新人さんは先輩エンジニアにがんがん聞いていくのが良いのではないでしょうか

記事のまとめにも書いたのですが、Web アプリケーションを作るのは本当に楽しいので
今回の記事を読んでそのようなエンジニアが増えてくれたら嬉しく思います。

最後に、執筆の機会を下さった @june29 さん、執筆でお世話になりました株式会社技術評論社の池田さんありがとうございました。

gihyo.jp

STF を使ってブラウザ上から Android をいじってみた

STF is 何 ?

STF | Smartphone Test Farm

CyberAgent 社がオープンソースとして公開しているブラウザ上から Android を操作できるようになるやつ。
ブラウザ上から apk を install させたりもたしかできたはず
Android は様々な端末があり、
端末ごとに動作が違ったりで検証が大変 + 多数のプロダクトで Android アプリを開発していると端末の貸し借りが発生したりするので
端末の管理が大変という問題とか開発拠点が離れている場合などもあるのでそこら辺を解消できる。

なぜか使った記憶がありとても便利だった。(元社員だっただけです)

なぜやってみたか

@misyobun さんがやってたのを見て
そういえば弊社CTOのあんちぽさんに「導入しといて」って雑に振られたので
面白そうだし「わかりました」って言ったのを思い出したから
(2週間前くらいだっただろうか…)

さっそく導入

homebrew があると便利なので入れとくといいかと

  • rethinkdb
  • graphicsmagick
  • zeromq
  • protobuf
  • yasm
  • pkg-config

上記をまとめて install

brew install rethinkdb graphicsmagick zeromq protobuf yasm pkg-config

node.js を install

$ brew install node
$ node -v
v5.0.0

stf を install

$ npm install -g stf
...
# node-gyp の install で error が発生した…

調べると node-gyp は最新の node に対応してないとかなんとか
なので node v0.12.7 を入れた

node の uninstall は下記で行う

$ brew uninstall node

もう一度 stf を install

$ npm install -g stf
...
# pkg-config が見つけられないてきな error が発生した…

# PKG_CONFIG_PATH を指定してやる (path は各々の環境に合わせてください)
$ export PKG_CONFIG_PATH=/usr/local/Cellar/zeromq/4.1.3/lib/pkgconfig/
$ npm install -g stf

install 関連は終わり

stf を立ち上げる

まずは rethinkdb から

$ rethinkdb
Recursively removing directory /hoge/rethinkdb_data/tmp
Initializing directory /hoge/rethinkdb_data
Running rethinkdb 2.1.5-2 (CLANG 7.0.0 (clang-700.0.72))...
Running on Darwin 14.5.0 x86_64
Loading data from directory /Users/mac/rethinkdb_data
...
...

stf を立ち上げる
端末はUSBでつないでおく

$ stf local
# ログがいっぱいでる

http://localhost:7100/ にアクセスすると下記のような画面が表示される
f:id:shiro-16:20151103180449p:plain

画像で「停止する」の部分が「準備中」になっていていくら待っても準備が完了しない…

log を見てみるとなにか失敗している

INF/device:resources:service 44364 [hoge] Installing STFService
FTL/device 44364 [hoge] Setup had an error TimeoutError: operation timed out
    at afterTimeout (/usr/local/lib/node_modules/stf/node_modules/adbkit/node_modules/bluebird/js/main/timers.js:11:15)
    at timeoutTimeout (/usr/local/lib/node_modules/stf/node_modules/adbkit/node_modules/bluebird/js/main/timers.js:53:9)
    at Timer.listOnTimeout (timers.js:119:15)

STFService.apk の install が失敗してしまっている様子?
とりあえず手動で STFService.apk を install する
(adb コマンド is 何 って人はググってくれ!)

apk のソースは ここ にあるのでこれを build してもいいと思う

$ adb install /usr/local/lib/node_modules/stf/vendor/STFService/STFService.apk

これで stf を再起動してみたが同じエラーが出てたので USB を一度抜き挿ししたら端末を操作できるようになった。
結果は下記をみてください


最後に

結構簡単にできた。
RethinkDB触ったことないのでちょっといじってみるのは良さそう
実際にこれを会社で導入する場合 server 用意してそれに USB ハブを挿しまくるのだろう。
そこら辺はなんかいい感じに相談してみよう。

やはり便利だ。

Sidekiq の queue を眺める際に使用するコマンドを雑にまとめる

はじめに

前回書いたこの記事を書く際に実際に積まれている queue をあれこれ見ていたのですが、
その際に使ったコマンドを忘れそうだったのでまとめておく。
めんどくさくてまとめなかったわけでは…

shiro-16.hatenablog.com

redis-cli

まずは基本的なこと redis-cli を使って redis のデータを見ることが出来る

Redis server に接続して対話的に実行
$ redis-cli
redis 127.0.0.1:6379> KEYS *
引数を指定して実行
$ redis-cli KEYS '*'

perform_async

※ perform_async で user 3 つ default 2 つ queue を積んだ状態

どんな key が生成されるか
$ $ redis-cli KEYS '*'
1) "queue:default"
2) "queues"
3) "queue:user"
key の member を全て取得
# SMEMBERS key
$ redis-cli SMEMBERS 'queues'
1) "user"
2) "default"
queue の件数を取得
$ redis-cli LLEN "queue:user"
(integer) 3
queue を 1 件取得
# LINDEX index
$ redis-cli LINDEX "queue:user" 0
"{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"user\",\"jid\":\"adddb1f368e7a622225ef315\",\"created_at\":1445260869.8274186,\"enqueued_at\":1445260869.8274736}"
queue を複数件取得
# LRANGE start end
$ redis-cli LRANGE "queue:user" 1 2
1) "{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"user\",\"jid\":\"b009bbcaa2b0dca70201b448\",\"created_at\":1445260841.7604742,\"enqueued_at\":1445260841.760538}"
2) "{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"user\",\"jid\":\"c9d40578849234b9f8b8d429\",\"created_at\":1445260828.8738196,\"enqueued_at\":1445260828.8746564}"

perform_in

※ perform_in で 3 つ queue を積んだ状態

どんな key が生成されるか
$ redis-cli KEYS '*'
1) "schedule"
schedule の member を見る

start と end を指定する

# ZRANGE key start end
$ redis-cli ZRANGE schedule 0 0
1) "{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"default\",\"jid\":\"2b6f8042a496b71128881c12\",\"created_at\":1445259670.319731}"

全て取得する

$ redis-cli ZRANGEBYSCORE schedule -inf +inf
1) "{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"default\",\"jid\":\"2b6f8042a496b71128881c12\",\"created_at\":1445259670.319731}"
2) "{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"default\",\"jid\":\"edc66e38f51e4b405ef2b984\",\"created_at\":1445260137.1156876}"
3) "{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"default\",\"jid\":\"8eb4b0bf44dee20a4c797424\",\"created_at\":1445260138.2186956}"

削除する

# ZREM key member
$ redis-cli ZREM schedule "{\"class\":\"UserWorker\",\"args\":[1],\"retry\":true,\"queue\":\"default\",\"jid\":\"2b6f8042a496b71128881c12\",\"created_at\":1445259670.319731}"

全部消したい!

普通はやらないけど全部のデータを消したい時に使う

$ redis-cli FLUSHALL
OK

最後に

ここら辺が使えれば Sidekiq の Redis 周りのことを調べるには困らないはず!