taiyoh's memorandum

@ttaiyoh が、技術ネタで気づいたことを書き溜めておきます。

Perl版GraphQLライブラリにおけるresolverの定義方法(修正版)

承前↓ taiyoh.hatenablog.com

これについて、自分がライブラリの実装方法について大きな誤解をしていたのでここに訂正させていただきます。
開発環境にGraphiQLを導入しようとしてinspectがうまくいかず、
原因の調査していたところ、どうやらresolverの定義方法が想定と違っていたのがわかりました。
Perl版GraphQLライブラリの場合、GraphQL::Execution::executeの引数に一次請けとしてのresolverを入れておく必要があり、この中でどのfieldなのかをまず解決する必要がありました。
ということで、以前提示したサンプルは以下のように修正されます。

gistc9aa7a6db938d4f0ad9abae4e25836a5

この組み方でGraphiQLが正常に動作することは確認しています。resolverを特定するために $info の値を解析する必要があるというところでまだ改善の余地はありそうですが、変な注入の方法よりはずっとよさそう。

今年買って読んだ(途中も含む)本

amazonの購入履歴のうち、書籍の購入履歴の一部を抜粋。

仕事に直接絡むところだとこんな感じ

実践ドメイン駆動設計

実践ドメイン駆動設計

ユースケース駆動開発実践ガイド

ユースケース駆動開発実践ガイド

この辺の顛末は

taiyoh.hatenablog.com

にある通り。

9月末くらいに買ったらしい。チームの開発フローをもう少し整備したくて、スクラムの本を読んで徐々に導入を進めている。
開発フローについては未だに悩みが尽きない。

ITエンジニアのための機械学習理論入門

ITエンジニアのための機械学習理論入門

最近BIもできるエンジニアと話すことが多く、今までのスキル(とその延長)だけで今後も生きていこうとすると自分の場合は先が危ういなと思い始めている。

個人的な興味だと

プログラミングElixir

プログラミングElixir

Kotlinスタートブック

Kotlinスタートブック

扱える言語の幅を増やしたいと思って読むだけ読んだ。
Kotlinについては実際少し触ってみて、better javaっぽさがよかった。
今度ElixirでDDDを使った設計を考えてみたいという野望もある。
ただ、上記機械学習のことも考えると、直近はPythonの方がいいのではという思いもある。

世界一やさしい 不動産投資の教科書 1年生

世界一やさしい 不動産投資の教科書 1年生

ここ半年、家のことについて自分なりに情報収集しているのだが、そもそも「(高望みせずに満足できる)家を手に入れるには元手が足りない」という当たり前の事実に気づき、資産形成を本気で考えるようになった。
他にいくつか買った本もあるが、興味本位だったので初心者お断りの本もあった。
とりあえず最近じわじわと話題になってるTHEOには登録し、運用を始めている。

theo.blue

完全図解 スポーツクライミング教本 すべてのクライマー必読の教科書決定版

完全図解 スポーツクライミング教本 すべてのクライマー必読の教科書決定版

1年半くらい前からどっぷりとハマり、ジムによっては3級に手が出せるようになった。
定期的なボルダリングジム通いと妻が作ってくれるバランスのいい食事のおかげで、最近やっと70kgを切ることができた。
外岩にも行ってみたい。せっかくマット買っているので。

あと、6月に妻とハワイに行ったので、事前に歴史なんかを調べておきたいと思って買ったものもあった。

他に、気分を盛り上げるためによしもとばななのハワイ系の書籍( サウスポイント (中公文庫) , まぼろしハワイ )も買ったりしたが、自分達はほぼワイキキ周辺にしかいなかったので、本から滲み出る土着的な空気まで肌で感じることはできなかった。

マンガは「甘々と稲妻」「ダンジョン飯」「聖☆おにいさん」「ワールドトリガー」「東京グール :re」「ゆるキャン」辺りを半分惰性、半分楽しみに発売日に即購入している。

来年はどんな振り返りができるのか楽しみだ。よいお年を。

GraphQLのresolverの登録方法についての一例(Perlの場合)

<2018-01-14追記>
本エントリはライブラリの使用方法について僕が大きな誤解をしています。修正版については以下を参照ください。 taiyoh.hatenablog.com <2018-01-14追記終わり>

所属プロジェクトでは一部のAPIリクエストにGraphQLを採用していて、最近のAPI実装をGraphQLで行うだけでなく、既存のREST APIの実装の順次置き換えを進めている。GraphQL便利すぎる。
GraphQLの各言語の実装では、定義したObject型のfieldはその実像となる集約だけで解決する必要はなく、 resolver と呼ばれるコールバック関数を定義することでfieldの所属する集約から切り離すことができる。
そのresolverの登録方法について、プロジェクトでは以下のような方法を採っている。

#!perl

use 5.014;
use warnings;

use GraphQL::Schema;
use GraphQL::Execution qw/execute/;

use Data::Dumper;

package Person {
    use Moo; # GraphQLがMoo依存なので使わせてもらう

    has name => (
        is       => 'ro',
        required => 1, 
    );

    has favorite_fruit_ids => (
        is       => 'ro',
        required => 1,
    );
};

package Fruit {
    use Moo;

    has id => (
        is       => 'ro',
        required => 1,
    );

    has name => (
        is       => 'ro',
        required => 1, 
    );
};

package MyApp::GraphQL::Resolver::Query {
    sub person {
        my ($root_value, $args, $context, $info) = @_;
        my $person = main::get_person();
        return { name => $person->name, favorite_fruit_ids => $person->favorite_fruit_ids };
    }
};

package MyApp::GraphQL::Resolver::Person {
    sub favoriteFruits {
        my ($root_value, $args, $context, $info) = @_;
        my $fruits = main::get_fruits($root_value->{favorite_fruit_ids});
        return [ map +{ name => $_->name }, @$fruits ];
    }
};

my $idx = 1;
my @fruits = map { Fruit->new(id => $idx++, name => $_) } qw/apple banana orange grape kiwifruit/;
my $person = Person->new(name => 'taiyoh', favorite_fruit_ids => [1, 5]);

sub get_person { $person }
sub get_fruits {
    my $ids = shift;
    my %id_map = map { $_ => 1 } @$ids;
    return [ grep { $id_map{$_->id} } @fruits ];
}

my $schema = GraphQL::Schema->from_doc(<<'EOF');
type Person {
    name: String!
    favoriteFruits: [Fruit!]!
}

type Fruit {
    name: String!
}

type Query {
    person: Person
}

schema {
    query: Query
}
EOF

for my $type (grep { ref($_) eq 'GraphQL::Type::Object' } @{ $schema->types }) {
    my $pkg = sprintf 'MyApp::GraphQL::Resolver::%s', $type->name;
    for my $field (keys %{ $type->fields }) {
        if (my $resolver = $pkg->can($field)) {
            $type->fields->{$field}{resolve} = $resolver;
        }
    }
}

my $query = <<'EOQ';
{
    person {
        name
        favoriteFruits {
            name
        }
    }
}
EOQ

my $res = execute(
    $schema,
    $query,
    {},
);

local $Data::Dumper::Indent = 1;
say Dumper($res);

__END__
$VAR1 = {
  'data' => {
    'person' => {
      'favoriteFruits' => [
        {
          'name' => 'apple'
        },
        {
          'name' => 'kiwifruit'
        }
      ],
      'name' => 'taiyoh'
    }
  }
};

Perl版GraphQLライブラリはバージョン0.16から from_doc という関数が追加され、GraphQLの定義の形式をそのまま読み込めるようになっている。フロントエンドを担当する人間と認識を擦り合わせたいという都合もあるので、schemaはPerlでゴリゴリ書くことはせず、定義ファイルを作ってそれをフロントエンドの人間にも読んでもらっている。
ただ当然ながら、定義ファイルにはresolverの記述はできないのでどうすればいいのかと試行錯誤している最中。
当初は「DSLっぽく登録できたらいいのかも」と思っていたが、しばらくしてテストしづらさを感じてきたので、「resolverが必要な1typeにつき1パッケージ用意する」「resolverを登録したいfieldと同じ名前の関数を定義する」というルールにしている(それが上記のサンプルの方法)。resolverの置き場所はこれで一旦落ち着けることにしたが、resolverの登録方法がhashrefに直接突っ込むスタイルなのであまり気持ちいいものに見えず、かといってそれを解消する手段を考えるために手を止めるわけにもいかないので、「あとでもっといい方法が見つかったらそれに変更する」というのが現状のステータスとなっている。

Elastic Beanstalkのsqsdの劣化コピーみたいなのを書いてみた

github.com

 そろそろいい加減golangはちゃんと勉強しないとと思っていたところ、たまたまElastic Beanstalkのsqsdの話を見つけた。
 本家のsqsdはちょっと探してみる限りossとして公開されてるものがなさそうだったのと、課題としてそんなに大きすぎない規模だと思ったのでいい練習になると思い、書いてみることにした。
 golangについては、大昔にusbドライバー的なものやcgoを使ったものはちょっと書いてみたことがあったが、やっつけみたいな勢いだったので、今回はcontextを使ったりテストをちゃんと書いてみようと思ってそのあたりは気を付けてみた。sqsにただロングポールするだけでは使い勝手が悪そうに思ったので、一緒にHTTP Serverを立てるようにし、稼働中のjob一覧の取得やロングポールの一時停止・再開の操作もできるようにしてみた。
 とりあえず「go test -race」や「go vet」では特に何も出てなさそうという状態にしてあるが、当然ながら実戦に持ち込んだことはないので、メモリリークがどこまでおきるかや、実際の使い勝手についてはまだわからない。今後ちゃんと検証していく必要はある。現状では僕の自己満だが、本当にいい勉強になった。

最近、自分の関わっているサービスをDDDで再設計してるというお話

タイトル以上の意味はない、ただの経験談

 きっかけは半年前、自分の担当するサービスのprを後輩にレビューしてもらったとき、彼から「何書いてあるか分からないですね」と言われたことからだった。
 彼が事あるごとに言ってた「ドメイン駆動設計」について、いい加減腰据えて取り組んでいかないとますますメンテしづらいシステムになるな、という予感はあったので、どれどれ、と軽い気持ちでリファクタリングっぽいprを作ってみた。結果、その後輩から大目玉を食らってしまった。普通のエンジニアなら「おいおい」と思う話であるが、O/R Mapperの行オブジェクトをただ少し拡張しただけだったので、何の意味もないものだった。
 この理解度でDDDをやるにはレビュアーの後輩の負担があまりにも大きすぎると痛感したので、一念発起して先ずは「実践ドメイン駆動設計」(以下IDDD)を読むことに。

実践ドメイン駆動設計 (Object Oriented SELECTION)

実践ドメイン駆動設計 (Object Oriented SELECTION)

 実はEric Evansの「ドメイン駆動設計」(以下Evans)も以前購入はしていたが、概念の説明が全く頭に入ってこなかったので恥ずかしながら早々に脱落してた。が、今回は具体的に自分の関わってるサービスに落とし込むというゴールがあるので、それなりに必死だった。常に付け焼き刃ではあったが、IDDD本で得た知識はなるべくすぐにそのprに反映する形にし、後輩のアドバイスも、本を読み進めるごとに少しずつ何を話しているのかが分かるようになってきた。
 結局、DDD化第一弾となるそのprは、差分1000行ほど、業務の合間にちまちま直し続けて一ヶ月かかって漸く後輩のOKが出た。但し、「多分そのうち色々直すことになるけど、DDD flavoredな実装としてはギリギリ我慢できるレベル」という条件付き。実際、その後何度も大きく手を入れている。

 IDDD本は、付録のEvent Sourcingにさしかかったところで一旦読むのをやめた。まだしばらくESを導入できる土壌がないのと、ESをしなくてもDDDは実践できる、と判断したため。あと僕は理解が遅いので、必要に応じて部分的に何度も読み返して自分の理解を少しずつ深めている。並行して、最近はまた少しずつEvans本を読んでいる。IDDD本には書いてなかったけど重要そうな要素がいくつもあり、モヤモヤしていたところがいくつか晴れた(そしてまた大掛かりなリファクタリングをすることに。。。)。
 因みに、なぜEvans本ではなくIDDD本から読み始めたかというと、「もう少し具体的なプラクティスを知りたかった」というのはあるが、「後輩からまずこちらをおすすめされた」というのも割と大きい。僕の場合、あるパラダイムについて慣れるのに基本的に手を動かして身を切らないと学べない頭の悪い人間なので、まずIDDD本でとっかかりを作り、ある程度DDDのパラダイムに慣れたところでEvans本でより理解を深める、という流れに結果としてできたのはよかった。

 DDDの概念を腹落ちさせるまでにだいぶ時間がかかったが、腹落ちしたきっかけは、自分が「データ」を中心に考えていたというのを把握したからだった。すべてのパラメータやデータオブジェクトを同列視していて、データを丹念に一つ一つ次の処理に送り込む、ということを繰り返して一連の処理を作っていたので、その膨大なパラメータのやりとりによって本来やるべきことを見失いかけていた。データはその由来や誰が変更するかという指向性や軽重があることが段々と見えてきて、これによってエンティティと値オブジェクトの区別ができるようになり、集約を固めてリポジトリが組めるようになってきた。リポジトリへの永続化の際、そのデータの変更はトランザクション整合性なのか結果整合性なのかと考えるようになり、いくつかはドメインイベントを使ってメインの処理からは外しても問題ないとわかった。DDD化を進めるうちに、ActiveRecordパターンはDDDにとってあまりにミスマッチで混乱の元凶なので、行オブジェクトをリポジトリの外に持ち出すことはなくなった。もはや行オブジェクトを「モデル」と呼ぶ時代は(今いるプロジェクト内では)終わった。DDDベースで書いた箇所については、行オブジェクトはリポジトリ内で存在確認とキャッシュにしか使っていない。本当はもっとシンプルにしても良さそうだが、O/R Mapperはトランザクション周りの管理やクエリビルダとして優秀なので、その辺は引き続き使っている。
 エンティティとリポジトリについての理解が飛躍的に上がったのは、「idを誰が発行するのか」についてきっちり切り分けて考えられるようになったからだった。今まではidの発行はMySQLのauto_increment任せだったので、DBにデータが入らないとエンティティとして完成しなかったが、id生成器を使うことでMySQLがなくてもエンティティが作成できるので、リポジトリの責務がよりシンプルで分かりやすくなった。

 こうして戦術面でどう考えていくかがある程度見えてきたので、戦略面での「ユビキタス言語」や「ドメイン」をなぜDDDでは重視するのかというのがもっと立体的になってきた。多分Eric Evansの考え方には「ドメインエキスパートの頭の中のモデルは実はそんなにドラスティックに変わることはないが、そのモデルを設計者(=実装者)が捉えきれてないために、変更要求が実装者にとってはとても大きな出来事として捉えられてしまっている」というのが間違いなくあると思う。あとActiveRecordパターンを許すと、本来ドメイン層で振る舞わせなくちゃいけない処理を「共通化」の名目で容易にインフラ層に押し込めることができてしまうので、「知らないところで何か重要なことが行われている」というのが常態化してしまう。DDDはそうした「認識齟齬」や「ごちゃまぜ」を防ぐためのルールなんだというのが半年かかってやっと体得できた。完璧に実現できてるわけではないが。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

 PDS(Presentation Domain Separation)自体は本当に重要だと思うが、これとレイヤードアーキテクチャActiveRecordパターン「だけ」で設計するとなると指針がプリミティブ過ぎて、というかそれぞれの考え方を繋ぐものがなくて実装者の手腕に委ねる部分が大きくなるので、プロジェクト内でしか通じない記法やダーティハックがはびこることになると最近は感じる。中にははその「つなぎ」を作るのが好きで色々考えたいと思う人もいるかもしれないけど、僕はそういうところに自分の足跡を残すことに興味はない。
 設計は本当に難しい。
 ただ、何故難しいと思うのかについて、その原因の一端は自分自身にあることもわかってきた。それは、設計のセオリーを持ってないことだった。そのセオリーというのもどうやらあるらしく、最近、新規実装やリファクタリングをする際、ロバストネス分析をしてアタリをつける、というのを始めた。
 ロバストネス分析についても例の後輩が見つけてきたものだ。これは「ユースケース駆動開発実践ガイド」(以下ICONIX)に詳しい。

ユースケース駆動開発実践ガイド

ユースケース駆動開発実践ガイド

 ユースケース記述→ロバストネス分析→詳細設計(シーケンス図)をきっちりやるとベストだが、一度流れだけやってみたが、完全にウォーターフォールの開発フローになるのと、開発を始めるまでかなり時間がかかりそうだったのでやめた。現在のチーム体制のスピード感には合わない。が、仕様や実装がある程度ドキュメント化されるのと、実装フローが見える化できて事前に色々話し合いができるので、うまくつまみ食いしながらほどよい距離感で進めていきたい。
 IDDD本だと、ドメインの抽出や設計について「チームメンバーで(ドメインエキスパートも含めて)よく話し合う」くらいにしか書かれていない。単純に書籍のスコープでないから省略されているのか、手法が確立されてないので言語化されてないのかは判断できない。が、ICONIX本によると「ドメインの抽出はユースケースの分析によって行う」(←だいぶ端折っている)と書いてあり、僕はこのICONIX本を読んで自分の中のミッシングピースが埋まったと感じた。確かにDDDは「整頓のしかた」については書いてあるが、肝心の「ドメイン(層)の振舞い方」については書いてない。DDDは「ドメインの振舞い」がまず定義できてこそ、ユースケースに付随するドメイン以外の種種雑多な振舞いを整頓していく、というのが王道の流れなのでは、と思うに至った。

まとめ

 この半年の経験を通して、自分がここまで設計力がなかったんだな、というのをまざまざと思い知った。でも、気づかないままこの後の人生を過ごすよりはずっとよかった。
 何より、辛抱強く色々教えてくれた後輩には本当に感謝しかない。ありがとう。

golangでBME280から情報を吸い出してよしなにする

相変わらず、goではなるべくニッチそうなlow layerなところを攻めております。
go用のI2C周りのライブラリはいくつかありますが、embdが割と素直な印象で、
実は先日BMP180というチップも買って、こちらであっさりと動かせるということを確認していました。

BMP180 気圧センサ(気温センサ付き)

BMP180 気圧センサ(気温センサ付き)

embd/bmp180.go at master · kidoman/embd · GitHub
↑このスクリプトをrpiで動かしてみたら修正なしで動いてしまい、かなりびっくりしました(とても優秀なサンプルでした)
ただ、僕の本命はBME280の方で、これは温度と気圧の他に湿度も計測出来る優れものです。
BME280搭載 温湿度・気圧センサモジュール

BME280搭載 温湿度・気圧センサモジュール

データシートを読んでると、BMP180と全然動かし方が違う。。。
http://trac.switch-science.com/wiki/BME280
当初はスイッチサイエンスさんが用意してくれたpythonスクリプトを元にgoに書き換えようとしましたが、
やっぱりスクリプト言語なので、よしなな型変換が多い。。。
一旦組み上がったので起動してみたら、温度が-84℃とか出てきたので、思わず笑ってしまいました。
github.com
↑動作確認のために上記のスクリプトを動かしてみたらちゃんとそれっぽい値は取れているので、恐らく結線は問題ないはず。
ということで、大元のチップメーカーであるBoschのドライバを参考にすることに。
GitHub - BoschSensortec/BME280_driver: Bosch Sensortec BME280 sensor driver
型や計算を上記のCのソースに沿う形に修正してみたところ、無事動きました!
f:id:sun-basix:20160208221724p:plain
最近、計測値の保存はAT&Tのm2xを使用しております。これも一応値に応じて何かをトリガーするとかできるっぽいです。やったことないけど。
rpiとチップはこんな感じで結線してます。
f:id:sun-basix:20160208212744j:plain

で、golangでBME280を動かすためのドライバは以下となります
github.com

gist7bdffe63dff403546c4e
動作サンプルはこんな感じ

電力モニター「はやわかり」に記録されているデータをgoで吸いだしてmackerelに送ってみる

承前
taiyoh.hatenablog.com
ついに前回のエントリの「hogehogeする」の中身が決まりました。
2年間完全に塩漬けにしてましたが、ふと思い立ったのでやることに。
当時吸い出す部分まで作って止めていたのは2つ理由があって、

  • amd64->armのクロスコンパイルに挑戦しようとしたが、cgoだと相当ハードルが高いということがわかった
  • 得られたデータをどこでどう閲覧・管理するかが決まってなかった

というのがありました。
goの環境についてはraspberry pi上で普通にビルドすればいいじゃん、と思えてきたのと、raspberry piで受け取ったデータをhogehogeする的なのを検索してみると、mackerelに送っているというエントリをいくつか見かけたので、時代はやはりこれだろう、と。実際、mackerel周りの導入はめっちゃ楽でした。
良くも悪くも、当時のコードはgo1.5でもビルドできたんですが、コールバックで渡すのがかっこ悪く見えてきたのと、シグナルのハンドリングはライブラリじゃなくね?とも思えてきたので、その辺を思いっきり書き直しました(どうせ僕しか使わないやつなので)。
また、一日かけて環境を整えていざ動かしてみたら、segmentation faultでホスト毎落ちるというのが見つかりました。参考にしたeagle-owlソースコードをもう一度読みなおしてみたら、大きめのbufferを用意した上でusb_bulk_readをし、返り値のバイト数に応じて小分けにしているという処理があったのを完全に失念していたので、これも追加しました。
で、どうにかこうにか値が取れるように。

f:id:sun-basix:20151229120559p:plain

最初に10Aを超えた値になったのは電子レンジを動かした時で、その直後に15Aを超えてるのは、ケトルでお湯を沸かし始めたからですね。(部屋の中寒いんだよ!)
いい感じで値が取得できるようになったかと。ここから先どうするとかは今後考えよう。。。
実際にraspberry piで動いてるコードは https://github.com/taiyoh/go-cm160/tree/master/example 以下にあります。へっぽこなのでご笑覧ください。
(どれくらいへっぽこだったかというと、昨日時点のコードだとmackerelに無駄にデータを送りまくってて、raspiのCPU使用率が90%を超えてました。大変申し訳ありませんでした!)
因みにこのコードの負荷についてですが、起動時は一瞬80%を超えることがありますがすぐに落ち着き、何かが徐々に上がってるということも今のところ見当たらず、平常時はmackerel-agentとcm160のプロセスを同時に動かしててもトータル12%くらいとなっております。

節電モニター

節電モニター