かまたま日記3

プログラミングメイン、たまに日常

embulk-filter-hash v0.5.0 リリース

イシューで希望をくれたHMACのハッシュ化に対応しました。以下のような感じで algorithm にHMACのアルゴリズムを指定した上で secret_key秘密鍵を入れると使えます

filters:
  - type: hash
    columns:
    - { name: username }
    - { name: phone_number, algorithm: HmacSHA256, secret_key: passw0rd }

JavaでRubyのeach_sliceがしたい

each_sliceというのは配列を指定した要素数の配列に分ける処理です。リストの中身をn件ごとに処理するときに便利です。 Javaには同様の処理が(たぶん)標準APIには無いので、こんな感じで行けそうです。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Test
{
    public static void main(String[] args)
    {
        // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
        System.out.println(slice(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 3));
    }

    private static <T> List<List<T>> slice(List<T> list, int n)
    {
        final int resultSize = (int) Math.ceil((double) list.size() / n);
        return IntStream.range(0, resultSize)
                .mapToObj(i -> list.subList(n * i, Math.min(list.size(), n * (i + 1))))
                .collect(Collectors.toList());
    }
}

参考: Is there a way to do the Ruby each_slice in Java 8? - Stack Overflow

アメリカに行ってきた

先週1週間会社の出張でアメリカ(マウンテンビュー)に行ってきました。海外は前職でのフィリピン出張以来2年ぶりです。

飛行機

行きは羽田からサンフランシスコ国際空港(SFO)の直行便で9時間ほど、帰りは後述の理由からSFOから香港国際空港(HKG)経由の成田への便でした、香港での待ち時間含めて22時間くらい。中国は初上陸でしたが、HKGでWi-Fi使う分には普通にTwitterできました。

気候

滞在した一週間は雨も降らず、サンフランシスコもマウンテンビューも最低10℃最高25℃くらいで朝は肌寒く日中は過ごしやすい、過ごしやすい気候でした。"ベイエリアは湿度が低いので日本ほど暑く感じない" というのをよく聞いていましたが、実感しました。

食事

基本的にアメリカ基準のサイズですべてが大きかったです。会社のランチはEAT Clubというのを使っているのですが、サラダ+主菜で注文したらサラダが直径25cmくらいのボウルに並々入っていて単体でお腹いっぱいになる量でした。結局食べきれず、持って帰って夜食べていました...w

あと、マウンテンビュー最後の夜にSushitomiという店で寿司を食べましたが、日本のクオリティで普通に美味しかったです。近くのshalalaというラーメン屋も美味しいらしいので、今度MVに行った際は寄ってみたいです。

仕事

1週間と短い間の滞在で時差ボケもありあまり効率的に作業ができたとは言いがたかったですが、直属の上司やプロダクトマネージャー、QAチームなどと初めて直接顔を合わせてコミュニケーションできたので、今後の仕事のやり易さに繋がればなあと思います。

歯の詰め物が取れた

3日目に歯の詰め物が取れてしまいました。治療したかったのですが、HRの方とも相談して日本に帰国後治療することに。IT健保の場合、海外での医療費は後で申請すれば返金してもらえるようなのですが*1、日本の治療費基準で返金されるようなので、日本の3~10倍ほどすると言うUSでの歯科治療費を考えると、申請する手間と割にに合わないと判断しました。

Uber, Airbnb

今回の出張で初めてUberAirbnbを利用したのですが、なるほどこれは便利だなあという感じでした。特にUberは精算の手間も無くなるし日本でもUberもしくは同様なサービスが流行ってほしいなと思いました。

コンピュータ歴史博物館

マウンテンビューを経つ直前に行ってきました。そろばんから現代のコンピュータまでの計算機の歴史が一同に介しており、とても見ごたえがありました。時間がなく駆け足で見ることになってしまったので、また行ってじっくり見たいです。

余談

出張後に夏休みを取って、家族とアメリカ観光をする予定だったのですが、直前になり子供が熱を出して渡航できなくなってしまうと言うハプニングが。結局諸々の予定をキャンセルし、帰りの航空券も取り直し日本に帰ることになりました。ホテル、ツアーなど直前キャンセルが利かないものも多く、10%ほどしかお金は戻ってきませんでした(泣)

tfenvのオーナーを移管した

色々issueとかPRとかもらっていたのですが、転職も決まってTerraformユーザーではなくなってしまったこともあり、私自身がメンテナンスするモチベーションが上がらなくなっていたため、別の方にオーナーになってもらうことにしました。

2ヶ月ほどの募集期間と話し合いを経て、前からコミットしてくれていたZordrakさんに移管することになりました。

300以上のスターをもらって世界各地の人からissueやPRをもらうようなOSSを作るというのは光栄で、いい経験になりましたが、OSSを個人でメンテし続けるということの難しさも実感しました。唯一残念なのは、移管することで自分のGitHubプロフィールがスター数的に寂しくなってしまったことです(笑)

オーナーではなくなりましたが、今後もtfenvをご贔屓にしていただけると幸いです。

CircleCIでgradle testがOOMで落ちるのを防ぐ

とあるプライベートなリポジトリで急にCircleCI上のgradleのテストが落ちるようになってしまい、レポートのxmlも出力されなくなってしまいました。

その時のログの一部

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> Process 'Gradle Test Executor 1' finished with non-zero exit value 137

* Try:
Run with --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':test'.

テストのプロセスが exit value 137 で落ちているとのこと。調べてみるとOOMっぽい感じです。

Exit code 137 - Out of memory – CircleCI Support Center

確かに、今回テストの数をParametarizedを使って倍に増やしたので、それが原因っぽいです。

Gradle自体はこちらによるとデフォルトでは1024MBヒープサイズを確保するようなのですが、 test タスクはビルドプロセスとは別のJVMを使うようになっており、そこにはこの制限が適用されないようです。そこで test タスクのmaxHeapSizeを使って最大値をCircleCIのデフォルト(4GB)を超えないよう調整します。

参考:
Testing in Java & JVM projects - Gradle User Manual
Test - Gradle DSL Version 4.8.1

test {
    // Not to exceed the limit of CircleCI (4GB)
    maxHeapSize = "3g"
}

これで落ちずにテストを完遂できました。

Chromeで特定のドメインのアクセスをリダイレクトさせる

Twitterのブックマーク機能は現在PC版のサイトでは提供されておらず、mobile.twitter.com を使ったモバイル版でアクセスする必要があります。

An easier way to save and share Tweets

Bookmarks are now rolling out globally on Twitter for iOS and Android, Twitter Lite, and mobile.twitter.com.

なので、外部のサイトから特定のツイートのリンクを踏んでそれをブックマークしたいとなると、ブラウザバーでドメインmobile. 加えてという作業が発生します。いちいち面倒だったので、ブラウザの設定でリダイレクトできないか色々調べると、こちらの記事が当たりました、2018-6-28公開、タイムリー!

www.lifehacker.jp

ということで、最初に紹介されているSwitcherooを今は使ってます。設定はこんな感じです。

f:id:kamatama_41:20180701232636p:plain

最初は from: twitter.com to: mobile.twitter.com としていたらうまく動きませんでした。部分一致で探しているようで mobile.twitter.com でアクセスしたときも mobile.mobile.twitter.com にアクセスしようとしているようでした。最終的にURLスキームをつけることで解決しました。

続きを読む

Parallel Streamの並列数を調整する

Streamの parallel メソッドを呼ぶとストリームの処理を並列に実行できますが、これは内部的には前回紹介したForkJoinPoolが使われています。ForkJoinPoolは内部でcommon poolと呼ばれる共通プールを持っており、明示的にPoolを指定しない ForkJoinTask#invoke などはこのプールが利用されるようです。

public static void main(String[] args) {
    IntStream.range(0, 16).parallel()
            .forEach(i -> System.out.println(Thread.currentThread().getName() + ": " + i));
}

自分のマシン (MacBook Pro 15 inch) ではプールのサイズは7でした。実装を見るとデフォルトでは "コア数-1" になるようです。

main: 10
ForkJoinPool.commonPool-worker-3: 2
main: 11
ForkJoinPool.commonPool-worker-3: 3
ForkJoinPool.commonPool-worker-3: 1
ForkJoinPool.commonPool-worker-4: 13
ForkJoinPool.commonPool-worker-4: 12
ForkJoinPool.commonPool-worker-1: 5
ForkJoinPool.commonPool-worker-1: 8
ForkJoinPool.commonPool-worker-4: 15
ForkJoinPool.commonPool-worker-3: 6
ForkJoinPool.commonPool-worker-6: 0
main: 9
ForkJoinPool.commonPool-worker-7: 4
ForkJoinPool.commonPool-worker-2: 14
ForkJoinPool.commonPool-worker-5: 7

これは、システムプロパティ -Djava.util.concurrent.ForkJoinPool.common.parallelism で変更可能で、試しに2にしてみると、こんな感じの出力になります。

main: 10
main: 11
ForkJoinPool.commonPool-worker-1: 4
ForkJoinPool.commonPool-worker-1: 5
ForkJoinPool.commonPool-worker-0: 2
ForkJoinPool.commonPool-worker-1: 6
ForkJoinPool.commonPool-worker-1: 7
main: 8
main: 9
ForkJoinPool.commonPool-worker-1: 0
ForkJoinPool.commonPool-worker-1: 1
ForkJoinPool.commonPool-worker-0: 3
ForkJoinPool.commonPool-worker-1: 12
ForkJoinPool.commonPool-worker-1: 13
main: 14
main: 15

また、ParallelStreamで自前のForkJoinPoolを使いたい場合は、少しトリッキーですが、ストリーム処理自体をRunnableでラップしてForkJoinPoolに渡すと言った方法が使えるようです。 参考リンク

public static void main(String[] args) throws Exception {
    ForkJoinPool pool = new ForkJoinPool(2);
    pool.submit(() -> IntStream.range(0, 16).parallel()
            .forEach(i -> System.out.println(Thread.currentThread().getName() + ": " + i))
    ).get();
}
ForkJoinPool-1-worker-1: 10
ForkJoinPool-1-worker-0: 5
ForkJoinPool-1-worker-0: 4
ForkJoinPool-1-worker-0: 7
ForkJoinPool-1-worker-1: 11
ForkJoinPool-1-worker-1: 9
ForkJoinPool-1-worker-0: 6
ForkJoinPool-1-worker-0: 2
ForkJoinPool-1-worker-1: 8
ForkJoinPool-1-worker-1: 14
ForkJoinPool-1-worker-1: 15
ForkJoinPool-1-worker-0: 3
ForkJoinPool-1-worker-0: 1
ForkJoinPool-1-worker-1: 13
ForkJoinPool-1-worker-1: 12
ForkJoinPool-1-worker-0: 0