Screaming Loud

日々是精進

Scalaの値クラスのバグ?

値クラスを使っていて不可解な挙動があったので、まとめたのとその対処法をメモしておきます。

値クラスとは

値クラスとはIntやStringが継承しているAnyValを継承させたクラスです。
一つの値しか持ちませんが拡張メソッドを実装できるため、DDDなどを実現するためのドメインモデルの記述に役立つ記法です。
値クラスと汎用トレイト - Scala Documentation

問題

コンパイル時の型は統一されていますが、実行時の型が不定という問題。

gist.github.com

上のように、コンパイル時の内部で保持している型は共にAttributesクラス(値クラス)なのですが、
実際に実行してみると、

  1. json4sから流し込んだ方:String型で保持
  2. 値クラスをそのままセット:Attributesクラスで保持

ということで、非常に不便な状況です。

対処法

続きを読む

ServerlessConf Tokyoに参加してきました。

serverlessconfに参加してきました。

serverlessに関しての知見があまりなかったので、すごい有意義な勉強会となりました。

  • そもそもserverlessとは
  • serverlessによって実現できること
  • serverlessの思想
  • 現状

そもそもserverlessとは

パネルディスカッションでも盛り上がっていましたが、 Serverlessとは何を指すのか?

実際にCPUなどのリソース管理をしないServerlessと FaaS(Function as a Service)の機能をベースとして構成としてのServerless の2つが議論に上がっていました。

自分なりには、前者に関してはいわゆるマネージドサービスであると思っているので、 後者のものをServerlessの概念として考えています。

serverlessによって実現できること

Serverless自体が注目されてたのは、AWS lambdaがきっかけです。 AWS lambdaとは、サーバを管理するのではなく、コード(ファンクション)を管理するのみで、デプロイもコードです。 lambdaが呼び出されるたびに毎回起動してファンクションが実行されます。

そのlambdaの登場によってリソース管理が必要なくなります。 今まではサーバにデプロイしていたため、そのサーバの要件に対してリソースが少ない場合、作り直す必要がありました。 しかし、lambdaを利用する場合、リソースに関しては気にしなくてもよいのです。 そう、圧倒的に運用負荷が下がるのです。

serverlessの思想

www.slideshare.net

上記のスライドを是非見てもらいたいのですが、serverless の思想が示されています

  1. オンデマンドで計算リソースを利用するべき
  2. 一つの目的のためのステートレスなファンクションを書くべき
  3. プッシュ型でイベント駆動のパイプラインで設計すべし
  4. フロントエンドに機能を充実させる
  5. 3rdパーティサービスを積極的に取り入れる

ItoNaoyaさんも言っていましたが、マイクロサービス的観点だと、1サーバに載せる機能は境界づけられたコンテキスト内の内容でした。 しかし上記の思想を取り入れると、lambdaで担保するものは1機能になっていきます。

現状

現状のlambdaでは、以下のデメリットがあります。

  • 計算時間がコストになりうる上、5分のタイムアウト制約もありますので、重い処理は実行できません。
  • lambdaなどのFaaSでは、使える言語が特定されてしまいます。
  • Javaなどは起動時間が遅いため、それをwebサービスのレスポンスとして利用するとサービスとして提供できるレベルに達しません。

薄いサービスならば作成できると思いますが、全てをserverlessで置き換えることはまだまだ難しそうです。 そもそも、その要件が満たせる状況になっても置き換えるかというのは疑問です。 というのも、複雑なビジネスロジックであればあるほど、コードとして再利用がし辛くなり抽象化ができなくなってしまうからです。

では、対応例として考えられるものは

  • botなどの厳しいレイテンシを求められないもの
  • バッチの発火
  • 薄いサービス
    でしょうか。

まとめ

serverlessという単語自体が曖昧な単語となってしまって、実際バズワードにもなりそうですが、 今後一つの潮流として、素早いサービスを作る上で絶対に避けて通れない考え方だと思います。

サービスを作る際に、serverlessも一つの手法の対象として考えれるようにキャッチアップしていく必要がありそうです。

spray-canで何故か同時に接続できずconnection reset by peerが発生する

sprayフレームワークを使っていると、HTTPリクエストの部分はspray-canを利用すると思います。

しばらく、spray-canを使っていたのですが、いくつか重い処理を走らせるとCPUを使い切っていないのに、sprayがリクエストを受け付けなくなってしまい、502 BadGateway を出すようになってしまいました。

そこで色々試したのですが、結果として設定のconfをいじるのではなく、responseを非同期にすればできました。

発生した現象

負荷をかけると以下のような connection reset by peerという502エラーが発生しました。

read: connection reset by peer

パラメータ変更

application.confを色々いじってみましたが、結局実装が間違っていたため、根本的なパフォーマンスは変わりませんでした。

spray/application.conf at master · spray/spray · GitHub
が、本家のベンチマークに使っているconf設定です。

sprayのconfは、

  • reference.conf:デフォルトでsprayに読み込まれているconf。いじれません。
  • application.conf:TypesafeConfigを使っていると自動で起動時に読み込まれるconf。起動時に -Dconfig.file=hoge で変更可能。includeを使うと、他の別confファイルも呼び出せます。

以下のようにいじりました。
spray-stress/application.conf at master · moc-yuto/spray-stress · GitHub


が計測してもパフォーマンスは変わらず。。。

解決策

FutureDirectiveを使うことでした。
使い方は、
spray | Documentation » 1.2.2 / 1.3.2 » spray-routing » Predefined Directives (by trait) » List of predefined directives by trait » FuturesDirectives » onComplete
です。

レスポンスにFutureを組み込むことで、502エラーが発生していたものが一切発生しなくなり、パフォーマンスを一気にあげることができました。

負荷実施

負荷にはvegetaを使用しました。
https://github.com/tsenart/vegeta

vegeta attack -rate=100 -duration=30s -targets=st.txt > st.bin

#st.txt
GET http://localhost:8080/st

Get http://localhost:8080/st に対する結果

Requests      [total, rate]            3000, 100.03
Duration      [total, attack, wait]    44.486880886s, 29.989999801s, 14.496881085s
Latencies     [mean, 50, 95, 99, max]  7.155629141s, 4.389581282s, 22.700462005s, 30.001109336s, 30.003361727s
Bytes In      [total, mean]            0, 0.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  0.00%
Status Codes  [code:count]             0:3000
Error Set:
Get http://localhost:8080/st: dial tcp 0.0.0.0:0->[::1]:8080: getsockopt: connection refused
Get http://localhost:8080/st: dial tcp 0.0.0.0:0->[::1]:8080: i/o timeout

Get http://localhost:8080/st2 に対する結果

Requests      [total, rate]            3000, 100.03
Duration      [total, attack, wait]    43.215084995s, 29.989999893s, 13.22508510
2s
Latencies     [mean, 50, 95, 99, max]  6.767752449s, 2.182035276s, 30.002461808s
, 30.005316637s, 30.074779909s
Bytes In      [total, mean]            30691, 10.23
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  31.10%
Status Codes  [code:count]             200:933  0:2067
Error Set:
Get http://localhost:8080/st2: dial tcp 0.0.0.0:0->[::1]:8080: getsockopt: connection refused
Get http://localhost:8080/st2: EOF
Get http://localhost:8080/st2: read tcp 127.0.0.1:54219->127.0.0.1:8080: read: c
onnection reset by peer
Get http://localhost:8080/st2: read tcp 127.0.0.1:54217->127.0.0.1:8080: read: c
onnection reset by peer

以上のような結果になりました。
さらにパラメータをチューニングした値は、また別途検証したいと思います。

負荷を受けさせたソースはこちら
github.com

ドメイン駆動を実践してみた

以前(http://yuutookun.hatenablog.com/entry/2015/04/04/190539)、ドメイン駆動に関して調べてみてから、ちょこちょこ勉強しつつ、ちょうど自分の新規プロジェクトが動き始めたので、反映させてみた感想をまとめてみました。

概要

・採用したDDD手法
・言語
・パッケージ構成
・どうやって作っていったか
・良かったところ
・悪かったところ

採用したDDD手法

昔ながらのレイヤードであったり、オニオンだったり、ヘキサゴナルだったりありますが、
今回はクリーンアーキテクチャを採用しました。

言語

Scalaで実装しています

パッケージ構成

以下を参考に作成しました。
https://speakerdeck.com/yoskhdia/ddd-plus-clean-architecture-plus-ucdom-fullban
http://qiita.com/kondei/items/41c28674c1bfd4156186
https://speakerdeck.com/usrnameu1/genbafalsekurinakitekutiya-scala-play-puroziekutoshe-ji-falseohua

|- domain # データモデル
     L enum # ドメインに定数
 |- usecase # コアロジック(旧Model), 別パッケージからは呼び出さない
 |- util or service  # 状態を持たないutil系
 L  adaptor #  各usecaseとcontrollerと橋渡し (daoとのやり取りなど)
      |- repository # DBとのアクセス
     |- input # リクエストを受け取る型の定義
     |- output # レスポンスを受け取る型の定義
          L  transformer # repository層とuse case層の型変換
   |- controller # リクエスト受け取り部分
     |- input # リクエストを受け取る型の定義
     |- output # レスポンスを受け取る型の定義
          L  transformer # controller層とuse case層の型変換

進め方

ドメイン駆動開発(以下DDD)は、ドメインエキスパート(そのビジネス領域に関してのエキスパート)との対話によって、設計を行う手法です。
まずは、何がエンティティで何が値オブジェクトかを確定させます。
エンティティに応じた操作をusecaseで記述します。
DBとのアクセスはrepositoryを通じて行います。
http ⇔ controller ⇔ use case ⇔ repository ⇔ DB
entity

良かった点

DBで使うオブジェクトとフロントで使うオブジェクトがキレイに切り離されるので、影響範囲が特定しやすいです。
Entityをベースに考えるので、アウトプットを変えたいと言う時に、アウトプットのオブジェクトを作って、そこにEntityの情報を突っ込むだけで追加できるというのが変更に強いです。

悪かった点

やはり、モデルの変換が多いため、実装がどうしても多くなってしまいます。

所感

まだそこまで大きな範囲には適用していないのですが、DDDの威力が垣間見れました。
特にScalaは、DDDと相性がいいなーと思った次第です。