レビュー

  • このエントリーをはてなブックマークに追加

今やコードのレビューは当たり前のように行われているところが多いと思います。でも実装の前にアーキテクチャを考えたり、設計を考えたり、思想を考えたりしますよね。実装する前に、そういうの先に共有してレビューする必要があると思うのです。

そんなの、当たり前だろ。という人が殆どだと思います。

でも、意外とやらない人が多い。都市伝説ではなく。

そして障害を多発させたり、日々の運用に耐えられなくなったり、ビジネスのスケールに対して技術が足を引っ張ったり、最悪の場合はそもそもゴールに着地出来なかったり。

大事な土台なのに。

上記はエンジニアの話ですが、別にエンジニアに限った話ではありません。

例えば商品設計の話。
商品設計が誰のレビューも受けていないとかあり得ない。というかその分野の識者じゃない人が考えたものを、すぐ近くに識者が居るのに尋ねたりレビューをしてもらったりをしないのは何故なのか。
商品設計という土台をレビューしないで、売り方の話ばっかり。これも前述と同じですね。

こういう事を指摘すると、エンジニアの人がそうだそうだと同調するケースがありますが、あなたも土台のレビューをすっ飛ばしているじゃないかとツッコミを入れたくなります。

前の記事でも似たような事を書きましたが、表面的な内容は異なれど、土台を識者に相談してレビューを受けようという本質的な所は同じなんだけどな。

最近コレ系の事案が多い。

結果をトレースするのではなく考え方をトレースする

  • このエントリーをはてなブックマークに追加

物事の応用が上手な人は、結果をトレースしているのではなく考え方をトレースしているのだと思っています。
考え方というのはWHYにあたる部分。なぜそうしたのか。

仕事の仕方が下手な人は総じて、結果がこうだったらこう対応するという1:1の結論を出している傾向があるように思えます。なぜそうしたのかを理解しないで真似をするので、なぜそうしているのかが自分で分かっておらず、対応が短絡的に見えてしまう。

他にも、転移学習という言葉を使うと界隈から怒られそうですが、別の分野の事例を聞いた時、パズルピースをはめ変えて自分事の事例に落としこむという事も、考え方は同じです。
これが出来ない人は他分野の話を聞いても、「話としては面白かったけど、僕らには関係ないね」とか言ってしまう。ヒントなんてそこら中にあるのにね。

身近な事例(良くある事例)だと、〇〇社で使っているから僕らも使おう。みたいなやつ。他には、他業種のカンファレンスに参加しても何も得られない人とか。(同じ業界の同じ業種の事例でしか学べない人とか)

私が今まで見てきた人(エンジニアに限らず)の殆どが、結果をトレースする人は微妙な人が多く、考え方をトレースする人は成長が早く、似たような失敗を何度も繰り返さない人が多かった。

フルスタックエンジニアとは

  • このエントリーをはてなブックマークに追加

面接しているとフルスタックエンジニアを名乗る人が一定数居ます。文化の違いなのかはわかりませんが、特に日本人以外の人に多い印象です。また本当に何でも出来る人で、このタイトルを名乗っている人には遭遇したことがほぼありません。
もちろんバイアスはかかっていると思います。

ちなみに、私のフルスタックエンジニアの定義は、その会社またはプロダクトの技術領域全部において、それぞれ10点満点中7点以上の能力を有することと個人的に決めています。

そのため、有するスキルが狭かったり浅かったりする人がフルスタックを名乗っている場合は、その会社やプロダクトはその程度の技量で賄える代物なのだなと思ってしまいます。

今の会社では、次の項目を満たす人だけがフルスタックを名乗りなさいというお気持ちです。

  • 機械学習
  • データ分析
  • サーバプログラミング + 運用の知識
  • Webクライアントサイド
  • iOS
  • Android

なかなか厳しい。

と思ったら創業者がそうでした。
変態か?

文化に合わせる

  • このエントリーをはてなブックマークに追加

既存のコードセットの文化に合わせて書くというのは、OSSに限らず仕事でも必要な事だと思っています。

OSSは時として、そういうPRが来た時に無視するなり突っ返すと言った行動が取れますが、仕事ではそういうわけにもいきません。そして、そこは本質的な部分では無いので処理上の問題との指摘でゴチャゴチャになってしまいます。

コードに限った話ではなく一般的な話で、既存の物に手を入れるときに自分はこうした方が良いと思ったときに、逆に何故そうなっていないのかを考える事は重要な事だと思っています。
その上で、納得いかない部分があったり、自分の考えが正しいと思うのであれば、コメントに考えを併せて書くなどして自分はこう考えたという意思表示をしないと、ただの空気読めないおっさん扱いになってしまいます。

指摘しても毎回同じ事を繰り返してくるの、どうすれば良いのでしょう。

粗大ゴミ

  • このエントリーをはてなブックマークに追加

ここ最近、家の模様替えと大掃除をしていました。
平日夜の3時頃まで要らないものを整理したり、大型の家具を解体していてとても疲れているわけですが、ここ最近やっと一段落しました。(もうちょっとだけレイアウト弄りたいけども)

大量の燃えるゴミと粗大ゴミが出るわけですが、ラックやソファ、布団、大量のPCなどなどを捨てるにあたり区の粗大ゴミでは量が多くて大変なので今回は業者にお願いしました。

粗大ゴミ系とPC回収系のサービスをそれぞれ使いました。

粗大ゴミはワンナップという所を使い、約28000円(区役所の粗大ゴミにチマチマ出せば7000円くらいだと思います)。いくつかのサービスを見ましたが、量によって固定額だったりするので、もっと安くなるところがあるかもしれません)。回収して貰ったものは具体的に挙げませんが、13点引き取って貰いました。

手順としては、引き取って欲しいもののリストを作りサイズを測ります。それを元に電話で見積もり。後は日付を決めるだけです。土曜日も対応していたので助かりました。

私の部屋は1Fなので、荷物移動でプラスアルファで料金がかかることもなく、作業員を一人追加すると3000円追加なのですが、まぁ自分が手伝えば良いかなーと思っていたら、私の前のルートのお客さんの関係なのか2人来て、結局自分は何もしないで重い物全部運んで貰いました。
(追加料金はかからなかったです)

パソコンは、ノートPCが3つ、デスクトップPCが5つ、各種パーツ類が入った段ボールが8箱。そこそこ普通に使えるグラボ(FF14くらいなら普通に動く)やCPUもあったので一部売ることも考えたのですが、これだけ量が多いとそんな気も起きずに全部回収してもらいました。
業者はパソコンファームの無料回収訪問でお願いしました。

こちらも手順は、引き取って欲しいもののリストを作り、Webのフォームから送信します。後日電話が掛かってくるので、日付を確定します。こちらの会社は土日は対応しておらず、平日にお願いしました。時間は保証できないの理解しているし遅れても早く来ても構わないので、半休使うので午前中に(10時〜12時くらいまでには来て欲しい)と交渉をして、結果10時頃来てくれました。
荷物をまとめておいたので、5分もせずにささっと持っていって貰い完了。

それとは別に昔の日報ノートが発掘されて、お蔵入りになった内容とか書いてあって、そういえばそんな企画あったなとか思うのでした(笑)

綺麗になったのもつかの間

新しい家具を買いまして、一時的に広くなった部屋が再度狭くなりました😑
ダイニングテーブルやレンジ台を新しくしました。あとベッドも。(Instagramにちょっとだけアップしてます)
前よりも人を呼びやすい環境にちょっとだけなりましたぞ!

投げ銭サービス

  • このエントリーをはてなブックマークに追加

Patreonというサービスがあります。
Wikipediaによると、

Patreonとは主にYouTubeコンテンツ製作者、ミュージシャン、ウェブコミック作者向けのクラウドファンディングプラットフォームである。作者は自身のファンやパトロンから定期的に、もしくは作品ごとに寄付を募ることが可能である。サンフランシスコに本社を置いており、2013年にミュージシャンのジャック・コンテ(英語版)と技術者のサム・ヤムが設立した。

だそうです。

このサービスは、こちらの方がNierの2BのモデルをTwitterで公開していて、おおお..と思って見つけたサービスです。

クラウドファウンディングはお金を出す代わりに何かしらの見返りがあるわけですが、技術BlogとかIssueとか見ていて、ウォォーまじで助かった!!って体験を少なからずしている身としては、マジ助かったので何かしらのお礼をしたい!と思うときがあります。

Amazonのほしいものリストでも良いのですが公開している人少ないし、いまブロックチェーンとかBitCoinが流行ってるし、投げ銭サービスって作れないのかなーと思って調べてみたら、法律の問題で難しいのだそうで。

参考: http://blog000.net/entry/nagezeni

EC2でClockSourceを変更する件

  • このエントリーをはてなブックマークに追加

EC2はxenで動いていて、clocksourceもxenになっています。
gettimeofdayvDSOという仕組みによってユーザーランドで実行してくれまうが、clocksourceがxenだとカーネルランドで実行されてしまいます。

例えばJavaでSystem.currentTimeMillisSystem.nanoTimeをループで実行する処理を書いてstraceを見てみると、大量のシステムコールが実行されているのが観測できます。(gettimeofday, clock_gettime)

clocksourceはtscに変更すると良いぞという情報はあるのですが、一方で危険なので変更すべきでは無いという情報もあります。

AWSのサポートに聞いたときは、新しめのInstance(C4)と新しめのAMIだったら大丈夫だよと言われたのですが、変更するとちょこちょこKernelメッセージ出るし、収益に直結しているサービスに投入するのも怖い。
というわけで、staging環境ではやってみたんだけど、productionには投入できていません。

参考

MySQLのfrom_unixtimeは32ビットまでしか扱えない

  • このエントリーをはてなブックマークに追加

Databaseに記録されているUnixtimeを表示する時、次のようにfrom_unixtime()を使う事があります。

mysql> select from_unixtime(1500000000);
+---------------------------+
| from_unixtime(1500000000) |
+---------------------------+
| 2017-07-14 11:40:00 |
+---------------------------+

しかしfrom_unixtime2147483648以上の数値を扱えません。NULLを返します。

mysql> select from_unixtime(2147483648);
+---------------------------+
| from_unixtime(2147483648) |
+---------------------------+
| NULL |
+---------------------------+

こうするとdatetime型が返ってくるので表示されます。

mysql> select date_add(from_unixtime(0), interval 2147483648 second);
+--------------------------------------------------------+
| date_add(from_unixtime(0), interval 2147483648 second) |
+--------------------------------------------------------+
| 2038-01-19 12:14:08 |
+--------------------------------------------------------+

Collections.shuffleのRandomについて

  • このエントリーをはてなブックマークに追加

Javaのjava.util.Collectionsにはshuffleという便利なメソッドがありますが、shuffle(List<?> list)はマルチスレッドで遅くなります。JDK8u121の実装では次のようになっています。

public static void shuffle(List<?> list) {
Random rnd = r;
if (rnd == null)
r = rnd = new Random(); // harmless race.
shuffle(list, rnd);
}
private static Random r;

java.util.Randomを共有して処理する事になるので、複数のスレッドでCollections.shuffleを呼び出すと処理が詰まるわけです。Collectionsにはshuffle(List<?> list, Random rnd)というメソッドがあるので、次のように使うのが良いと思います。

Collections.shuffle(list, ThreadLocalRandom.current())

別件ですが、複数のスレッドで同じListに対してCollections.shuffleを呼び出すと壊れます。(そんな事をする人は少ないと思いますが)

どれくらい遅いのか?

JMHで簡単に計ってみました。

Benchmark                               Mode  Cnt        Score   Error  Units
Shuffle.random_list100 thrpt 2 122031.999 ops/s
Shuffle.random_list1000 thrpt 2 11964.147 ops/s
Shuffle.random_list10000 thrpt 2 1306.136 ops/s
Shuffle.thread_local_random_list100 thrpt 2 2370353.238 ops/s
Shuffle.thread_local_random_list1000 thrpt 2 200793.729 ops/s
Shuffle.thread_local_random_list10000 thrpt 2 21029.163 ops/s
@Fork(1)
@Warmup(iterations=3)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Measurement(iterations=2, time=10, timeUnit=TimeUnit.SECONDS)
@Threads(4)
@State(Scope.Thread)
public class Shuffle {

private List<Integer> list100 = new ArrayList<Integer>(100);
private List<Integer> list1000 = new ArrayList<Integer>(1000);
private List<Integer> list10000 = new ArrayList<Integer>(10000);
@Setup
public void setup() {
for (int i = 0; i < 100; i++) {
list100.add(ThreadLocalRandom.current().nextInt());
}
for (int i = 0; i < 1000; i++) {
list1000.add(ThreadLocalRandom.current().nextInt());
}
for (int i = 0; i < 10000; i++) {
list10000.add(ThreadLocalRandom.current().nextInt());
}
}

@Benchmark
public void random_list100() {
Collections.shuffle(list100);
}

@Benchmark
public void random_list1000() {
Collections.shuffle(list1000);
}

@Benchmark
public void random_list10000() {
Collections.shuffle(list10000);
}

@Benchmark
public void thread_local_random_list100() {
Collections.shuffle(list100, ThreadLocalRandom.current());
}

@Benchmark
public void thread_local_random_list1000() {
Collections.shuffle(list1000, ThreadLocalRandom.current());
}

@Benchmark
public void thread_local_random_list10000() {
Collections.shuffle(list10000, ThreadLocalRandom.current());
}

}

RubyのAWS-SDKとForkでハマったこと

  • このエントリーをはてなブックマークに追加

RubyでKinesis Streamからレコードを取得する処理を書いていたのですが、通信が入れ替わったりして別のShardのShardIteratorが混じってくる現象に悩まされていました。

結論から言うと、AWS-SDKは内部でグローバルなConnection Poolを作ります。親プロセスで1回でもAWS-SDKを使って通信をすると、Forkした後に子供にも内部のConnection Poolがそのまま継承されます。
親で作られたCollectionPool=FDが複数の子供で共有されてしまうので、それぞれのプロセスで通信が混線してしまったという訳です。

解決策としては、子プロセスの最初に次のコードを実行する必要があります。

Seahorse::Client::NetHttp::ConnectionPool.pools.each do |pool|
pool.empty!
end

AWS-SDKのバージョンがv2.9.5以降であれば次のコードで同じコードが実行されます。

Aws.empty_connection_pools!

関連チケットです https://github.com/aws/aws-sdk-ruby/issues/1438

他にも使用していたLevelDBやSequelでもForkSafeではないのでそれぞれ対策が必要です。

LevelDBの場合

こんな感じで書き込みが止まってしまいました。
こっちは色々と掃除しても何故か発生してしまうので、親プロセスで使うなという感じ。

#0  0x00007f1e7929a64d in poll () from /lib64/libc.so.6
#1 0x00007f1e7a06c69c in ?? () from /usr/lib64/libruby.so.2.3
#2 0x00007f1e79cb4dc5 in start_thread () from /lib64/libpthread.so.0
#3 0x00007f1e792a4c9d in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7f1e7a56b740 (LWP 75934)):
#0 0x00007f1e79cb86d5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1 0x00007f1e6db494ed in leveldb::port::CondVar::Wait() () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#2 0x00007f1e6db1f4aa in leveldb::DBImpl::MakeRoomForWrite(bool) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#3 0x00007f1e6db22d38 in leveldb::DBImpl::Write(leveldb::WriteOptions const&, leveldb::WriteBatch*) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#4 0x00007f1e6db1f829 in leveldb::DB::Put(leveldb::WriteOptions const&, leveldb::Slice const&, leveldb::Slice const&) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#5 0x00007f1e6db1f869 in leveldb::DBImpl::Put(leveldb::WriteOptions const&, leveldb::Slice const&, leveldb::Slice const&) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#6 0x00007f1e6db1c7a8 in leveldb_put () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#7 0x00007f1e6dd5fcec in ffi_call_unix64 () from /usr/lib64/libffi.so.6

https://github.com/google/leveldb/issues/169#issuecomment-260458097

Sequelの場合

接続を切りましょう

DB.disconnect

http://www.rubydoc.info/github/jeremyevans/sequel/Sequel%2FDatabase%3Adisconnect

まとめ

ライブラリの中身を気にしながらForkを考えるのは難しい。