scala 2.13でコンパイルが遅いときにprofileを取る方法

Scalaコンパイルが遅くなった場合にprofileを取る方法について検索すると Speeding Up Compilation Time with scalac-profiling | The Scala Programming Language という記事がでてきたり、 https://github.com/scalacenter/scalac-profiling を利用した方法がヒットしますが、ここにでてくるpluginはscala 2.13では(そのままでは)使えなかったり、もう少し楽な別の方法があったりするのでまとめました。

出力イメージ

こんな感じのグラフの生成を目指します。(といってもほぼ公式が提供してるツールだけで出来る)

出力方法

1. profile-trace というコンパイラオプションを利用することでprofile情報が出力されたjsonを取得する

以下を指定してビルドすれば出力されます

Compile / compile / scalacOptions ++= Seq("-Yprofile-trace",  name.value + "-main.json"),
Test / compile / scalacOptions ++= Seq("-Yprofile-trace", name.value + "-test.json"),

2. chromeのtrace機能に読み込ませる

Chromeを起動し、 chrome://tracing/ をURL欄に入力して遷移 > Loadボタンから先程のファイルを読み込む ことで上記のツイートのような解析結果が見られます

備考

jdk9以降を使っている場合は問題が出てしまう( 参考 のでjdk8でビルドするか以下のようにscala 2.13.11のsnapshotを利用するかなどの工夫が必要になります。

resolvers += "scala-snapshot" at "https://scala-ci.typesafe.com/artifactory/scala-pr-validation-snapshots/",
ThisBuild / scalaVersion := "2.13.11-bin-7e2671b-SNAPSHOT"

おまけ

謝辞

全面的に https://mobile.twitter.com/xuwei_k さんに助けて頂きました。ありがとうございます🙏🙏🙏🙏🙏🙏🙏🙏

ソフトウェアアーキテクチャ・ハードパーツを読んだ

この記事は FOLIO Advent Calendar 2022の一日目です。

ソフトウェアアーキテクチャ・ハードパーツ ―分散アーキテクチャのためのトレードオフ分析 を読んだので感想です。

※ この本は翻訳を担当された@snoozer05 さんから頂きました。ありがとうございます!

感想

マイクロサービス・サーガ・オーケストレーションといった個別の技術についてはある程度理解している人でも、いざ実システムに応用しようとすると「結局、マイクロサービスってどの単位で切るのがいいの?」みたいな疑問が次々出てきてしまう・・・といったことはよくある悩みだと思います。(本書では例として郵送通知とプッシュ通知とメール通知は通知サービスにまとめたほうが良いのか、個別にしたほうが良いのかなんて悩みが紹介されていました。)

(この疑問も含めた)様々なアーキテクチャに関する疑問の回答は「何が良いかは場合による」であり、実際に意思決定を行うためには様々なトレードオフについて考える必要があります。本書ではそういった「何が良いかは場合による」問題に対して、技術的選択肢の特徴を整理し、対立するトレードオフ関係を見出し、ビジネス上の価値・優先度・目的に照らし合わせ、後から変更しにくい事柄(=ハードパーツ)について意思決定を行うためにどのような考え方をしていけばいいか、どのような議論を行えばいいかが記載されています。

議論パートは物語形式になっているのですが、いろいろな人(モノリスを分解したい人、DBを分散させたくない人、...)が自分の立場でメリット・デメリットを語り、挙句の果てに「マイクロサービスだからこうあるべき」、「こういう技術を使うのがモダンらしい」みたいな絵に書いたようなフワッとした会話が行われているところから話を着地させ、意思決定し、ADR(アーキテクチャデシジョンレコード)にまとめていく、という過程が読んでいて面白かったですし、実際の仕事でも役に立ちそうな現実味がありました。

マイクロサービスについてもそれありきではなく、「マイクロサービスにするとあなた達の問題は解決するんですか?」といった問いから初めて、マイクロサービスに伴い得るもの・失うものを技術的な視点から出発しビジネス的な価値につなげるところまで解説しています。

マイクロサービスではDBを共有しない(ので、共有は避けるべきだ!)という話もよくあると思いますが、本書ではDBを共有する形であるサービスベースアーキテクチャとの比較も踏まえ、「○○だからこうあるべき」ではなく「自分たちが求めているのが○○だから△△を採用する」といった正しい意思決定へ導きます。(注: マイクロサービスでDB共有を推奨しているわけではなく、サービスは分けるけどDBは共有する別のパターンもあるよねといった話です。)

また最終的には、(本書で登場するトレードオフを暗記するのではなく)それらのトレードオフを自分で整理し意思決定できるようにするための方法論も紹介されています。アーキテクトという役職は「どちらが良いかは場合による」問題に対して対立軸を見出し、ビジネス上の優先度と照らして意思決定を行っていく役職であるという筆者の思いを感じました。ともするとエンタープライズサービスバスとかコレオグラフィとかそういった概念・技術に詳しい人がアーキテクトだ!みたいな考えにもなりがちですが、後から変えにくいもの(=ハードパーツ)の意思決定に携わるためには手段に精通した上でビジネス上の目的・優先度と照らして意思決定が出来るというのがより重要だというのは個人的にも頷けます。技に溺れたアーキテクトではなく、技を使いこなすアーキテクトになるための一歩としてこの本は非常に有用なのではないかと思いました。

逆に言うとマイクロサービスってなんだろうとか、サーガってそもそもなんだろうみたいな解説については(記載はあるものの)詳しくはないので別の本を読んでからの方が良いかもしれません。

手法面では、どのようにDBを分割していくか・分析データをどのように連携していくか(データレイク・データメッシュetc)などについても記載があるのが嬉しいところでした。

ということで、これからアーキテクトになりたい人・よりアーキテクトとして成長したい人にとってはかなり良い本だと思ったので気になったらぜひ!

証券Fintechに入ったエンジニアが証券ドメインの業界知識を身に着けたいと思って読んだ本

これは何

  • 証券Fintechに入るぞ・入ったぞ!という人がドメインについて学べるような本・ページを集めてみました。
  • 会社特有の業務知識ではなく業界で広く使える知識を学べる物を集めようとしていますが、筆者の守備範囲依存による分野の偏りは否めません。
  • こういう分野があってこういうことが議論されてるんだなーといった形で興味を持つきっかけとしての本を挙げています。(自分で金融商品を考えるぞ!という人向けではなく、ETF扱うサービスの開発するならETFって何かくらいはうっすら知っておきたいよねーくらいのモチベーションの人向き)

最初に何読めばいい?

せっかくだからなにか勉強したいなーという人は資格系の欄にある証券外務員についての本を買っておくと広めに勉強できます。

ただし、個別の金融商品ETF, 投資信託,…)の解説が詳しく載っているわけではないので、そういったことを知りたい場合はETF投資信託の欄からチョイスしてみてもよいかもしれません。

資格系

現物株

投資一任, ロボアド

  • ラップ口座入門
    • ラップ口座の拡大・現在の状況・今後の方向性がファクトベースで記載されている。
    • 投資の基本的なところから説明してくれているので事前知識があまりなくても読める。

投資信託

ETF

資産運用の考え方について

決済系

金商法

  • 金商法入門
    • 解説対象を現物株に限ることで難しさを軽減している。
    • 法の背景にあるやりたいこともきちんと解説してくれる。

リスク管理

aws s3でオブジェクトkeyの先頭にスラッシュ(/)をつけるとどうなるか?

これはなに

  • s3はbucketとkeyの組み合わせでオブジェクトを保存している
  • このbucketとkeyを s3://$bucket/$key のように組み合わせると S3URIとなる
  • keyの先頭のslashがついているとき、aws cliを含む一部のツールでは 先頭のslashを削除する場合がある。
  • この仕様についての記載はaws cli側のドキュメントでは見つからなかったが、本記事では go-sdk での命名に習ってURI cleaningと呼ぶことにする。
  • この場合、ファイルは存在するように見えるが取得しようとすると NoSuchKey が返却されるといった事象が起きる可能性があるので、それについて解説する

HeadObjectの仕様

まずファイルの存在確認ではHeadObjectを利用することが多いので、そこの仕様を確認する。

HeadObject - Amazon Simple Storage Service を読むと

Permissions

You need the relevant read object (or version) permission for this operation. For more information, see Specifying Permissions in a Policy. If the object you request does not exist, the error Amazon S3 returns depends on whether you also have the s3:ListBucket permission.

If you have the s3:ListBucket permission on the bucket, Amazon S3 returns an HTTP status code 404 ("no such key") error.

If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP status code 403 ("access denied") error. 

とある。つまり

  • ListBucket権限がありファイルがないなら404
  • ListBucket権限がないなら403 (access denied)

が返ってくる。

aws java-sdk v2 などでは 404の場合 NoSuchKeyExceptionthrowされ、それ以外の場合は SdkClientException などがthrowされる。

そのため NoSuchKeyException がthrowされている場合は、権限不足ではなくファイルが実際に存在していないことを疑ってもよいだろう。

HeadObjectをcliで叩く

HeadObjectをaws-cliで叩くためには通常の aws s3 <command> ではなく aws s3api <command> という低レベルAPIを直接たたくコマンドを利用する必要がある。(詳細は AWS CLI での API レベル (s3api) コマンドの使用 - AWS Command Line Interface)

head-objectコマンドを利用する場合はkeyとbucketを以下のように分けて指定することが可能なので、先頭にスラッシュを含まないファイルと含むファイルの存在を以下のようにチェックすることができる。

# keyの先頭にスラッシュなし
aws s3api --profile ... head-object --bucket some-bucket --key some/key/foo.json

# keyの先頭にスラッシュあり
aws s3api --profile ... head-object --bucket some-bucket --key /some/key/foo.json

もし後者の先頭スラッシュつきでファイルが存在するレスポンスが返ってきたらURI cleaningの影響でファイルが見つからない状態になっている可能性が高い。

余談

  • 基本的にHeadObjectはGetのbodyを取得しない版と同等なので性能を気にしなくていい場合はaws s3 cp $file . などでファイルを取得してもよい。
  • 今回はURI cleaningを避けるためにs3URI形式ではなくbucket, keyを分割して渡すようにする目的で低レベルAPIを利用している。

各ツールの仕様

java-sdk

java-sdkでは、bucket, keyを分けて put, get する仕様になっている。

このとき、key側の先頭slashが消されると言った挙動は(少なくともHeadObjectでは)確認できなかったのでURI cleaningの罠を踏むことはなさそう。

val request = HeadObjectRequest.builder().bucket("some-bucket").key("some-key").build()

go-sdk

go-sdkでもbucket, keyを分けてput, getする仕様になっている。

しかし Automatic URI cleaning という仕様があり、key先頭のslashは削除される。

この挙動は DisableRestProtocolURICleaning をtrueに設定すると回避できる。

svc := s3.New(sess, &aws.Config{
      DisableRestProtocolURICleaning: aws.Bool(true),
})
out, err := svc.GetObject(&s3.GetObjectInput {
      Bucket: aws.String("bucketname"),
      Key: aws.String("//foo//bar//moo"),
})

aws-cli

aws-cliでは s3://$bucket/$key という形式でget, putすることになる。

このとき key先頭のslashが削除されるために先頭にslashが入っているオブジェクトは取得ができないことがある。

なお、先頭slashは1個だけ削除される仕様ではなく、複数個削除されるようになっているため、以下のように $bucket//$key のような文字列を指定しても、ファイルは存在しない扱いになる。

$ aws s3 --profile ... ls s3://some-bucket//some/key/foo.json

web-console

試してないので誰か教えて ( ◜◡‾)

結論

keyの先頭にスラッシュをつけるとファイルを手動操作したい場合に余計な混乱を呼ぶのでやめたほうが無難そう。

※ s3から静的ファイルを配信する際のURLも難しくなってしまうので、そういう意味でも避けたほうが良さそう

sbt-native-packagerでパッケージングしたアプリケーションのJVMオプションに-Jをつけるべきかどうか

普通に自分が分かってなかったのでメモシリーズ

これはなに

  • -Xmx オプションを指定する際に -J をつけている。 -Dfoo.barなどのプロパティ指定にはつけたりつけなかったりしている
  • いつ -J をつけて、いつつけなくてよいのか、 -J の意味などをまとめる。

-J の意味

-J の意味1 javaコマンドのオプションである -J

java -J” あたりでググるとコレがでてくる。 例: Javaアプリケーション メモ(Hishidama's Java-Application Memo)

これはjavacが使用するjavaコマンド(javacと同バージョンのjava)へのオプションを指定するものなので、Xmxの指定などの実行時のオプション設定には関係しない。

-J の意味2 jarコマンドのオプションである -J

こちらもググってみると、

たとえば、 -J-Xms48m と指定すると、スタートアップ・メモリーは48Mバイトに設定されます。

jar のように記載されている。

しかし「jarコマンド」はjarを作ったりjarの中身を一覧表示するためのコマンドであり、jarの実行(java -jar …)とは異なる物なのがポイントで、こちらもアプリケーション実行時に影響するオプションではない。

つまり jar -J-Xms48m はjarを作る作業中にjvmが使うメモリを変更するオプションであるという意味になる。

-J の意味3 scalaコマンド的な-J

scala -J-Xmx 2GB -Dfoo=bar baz.scala みたいにjavaコマンドではなくscalaコマンドでアプリケーションを実行するときのオプション

scalaコマンドは結局モロモロの処理をした後javaコマンドを実行するものになっている。

ここでのポイントは-Jオプションをscalaコマンドに渡すと以下の2つを行ってくれるということ

  • 渡された -J-Xmx1500m などの文字列をそのままjavaコマンドに渡す
  • -J を消した-Xmx1500mをscala引数として渡す

つまり -J の意味1 で述べたjavacうんぬんとは関係なく、scalaコマンドに -J を渡すとjava側に渡してくれる機能がある( -Jという名前が被っているので混乱するが、全然違う機能) scala/tool-unix.tmpl at c804289144c006e91a206ff12e94e5a39ac73a9f · scala/scala · GitHub

-J の意味4 sbt-native-packager的な -J

こちらはscalaコマンドに似ている。

sbt-native-packagerが生成するスクリプトはモロモロの処理をしてjavaコマンドを実行する。

このとき -J の意味2で述べたscalaコマンドの挙動を模した実装が行われており、scalaコマンドと同じように -J をstripして、 -Xmx 1500m にしてからjavaコマンドにわたすという形式になっている。

(scalaコマンドとは違いscala_argsには値が渡らない気がしているが未調査。気になる人は実装読んだりしてください)

sbt-native-packager/bash-template at master · sbt/sbt-native-packager · GitHub

-J をつけないとどうなるかというと sbt-native-packager/bash-template at 1ee84811c3ce2048e2ea857aece3fbe563b8089e · sbt/sbt-native-packager · GitHub によりresidual_argsに入り sbt-native-packager/bash-template at 1ee84811c3ce2048e2ea857aece3fbe563b8089e · sbt/sbt-native-packager · GitHub でmainClassやappCommandの後に引数として渡されるようです。

まとめ

sbt-native-packagerを使う場合、jvmオプションは -J-Xmx 1500m のようにする必要がある

これはscalaコマンドやsbt-native-packagerが独自実装しているshellスクリプトの機能でしかなく、jvmには-Jが削除された-Xmx 1500mだけが渡るようになっている。(-J-Xmxってなんか気持ち悪いなと思っていたけど-Jという文字列を機械的に削除すると考えれば割と理解しやすい)

この際使われる-J prefixはjavaコマンドについてググった時にでてくるjavacコマンドのオプションとは全く別物

おまけ

sbt-native-packagerを使う場合 -Dfoo=bar はそのままjvmに渡される (sbt-native-packager/bash-template at 1ee84811c3ce2048e2ea857aece3fbe563b8089e · sbt/sbt-native-packager · GitHub) ので、

例えばエンコーディング指定であれば -Dfile.encoding=UTF-8 とすればよい。

が、 -J-Dfile.encoding=UTF-8 としても -J機械的に削除されてjvmに渡るので結局 -Dfile.encoding=UTF-8となり、挙動はかわらない(はず)。