4 ポイント 投稿者 GN⁺ 2025-12-09 | 1件のコメント | WhatsAppで共有
  • Scala 2.13からScala 3へのコードベース移行の過程で、想定外のパフォーマンス低下が発生
  • 初期テストとデプロイ環境ではすべての指標が正常だったが、数時間後にKafka lagが増加
  • 負荷テストの結果、細分化したメッセージ処理時にスループットが急低下する現象が確認された
  • async-profiler解析により、Quicklensライブラリのチェーン評価の非効率バグが原因であることが判明
  • ライブラリを更新した後、性能は回復し、Scalaバージョン間のライブラリ挙動差に注意する必要性が強調された

サービス移行プロセス

  • 既存サービスはScala 2.13からScala 3.7.3に移行
    • マクロを使用しないデータ収集中心のサービスで、性能が重要な構成だった
    • 依存関係、コンパイラオプション、型、および構文変更を適用した後にコンパイル成功
  • テスト環境と段階的デプロイでも、ログとメトリクスの両方が正常と表示
    • インフラ、JVM、アプリケーションレベルの指標もすべて健全な状態を確認

原因不明の性能低下

  • デプロイ後、約5~6時間後にKafka lag増加現象が発生
    • データスパイクがない状況でもインスタンスあたりのスループット低下
    • ロールバック後にスループットが即時回復し、コード変更が原因であることを確認

性能分析と原因追跡

  • 負荷テストでは初期には性能退行(リグレッション)が再現されなかった
    • メッセージの細分化と異種ペイロードでのみ処理率の急減が発生
  • 依存ライブラリ(シリアライズ、DB SDK、Dockerイメージ、設定ライブラリなど)を1つずつロールバックしてテストしたが変化なし
  • async-profilerでCPUプロファイリングを実施した結果、
    • Scala 3ではJITコンパイラとデコード工程のCPU使用率が急増
    • Flamegraph上部でQuicklens呼び出しが全CPU時間の半分を占める
    • Scala 2.13では同一の呼び出しが0.5%程度にとどまる

問題の根本原因

  • Quicklensライブラリのチェーン評価の非効率バグがScala 3で発生
    • 該当する修正はGitHub PR #115に反映
    • ライブラリ更新後、Scala 3と2.13間の性能差は解消

教訓と推奨事項

  • ライブラリのメタプログラミング依存がScalaバージョン間で性能差を引き起こす可能性がある
  • 移行が正常に完了していても、ホットスポットとボトルネックをベンチマークする必要がある
  • 性能が重要なサービスでは「正常に動作している」という前提ではなく、実測ベースの検証が必須
  • コードではなくベンチマークがボトルネックを露わにする状況を防ぐための事前点検が必要

1件のコメント

 
GN⁺ 2025-12-09
Hacker Newsの意見
  • 私はScalaのファンではないが、問題を深く分析してデバッグした過程が印象的だった
    技術ブログはこういうふうに書かれるべきだ。AIがこのレベルの思考過程を代替するのは難しい
    • 私たちのサービスの1つをリフレッシュする際に、Scala 2.13からScala 3へ移行した
      最初の問いは単純で、「なぜわざわざアップグレードしなければならないのか?」だった
  • Scala 3ではinlineキーワードがマクロシステムの一部として動作する
    パラメータにinlineを使うと、呼び出し箇所で式をインライン化するようコンパイラに指示する
    しかしこれが大きいと、JITコンパイラに大きな負担を与える
    Scala 2では@inlineは単なる提案だったが、3では必ず適用される
    したがって、単に@inlineinlineに置き換えるのは大きなミス
    • この違いは、昔のC/C++のregisterキーワードがたどった経緯に似ている
      初期には強制だったが、最適化が発達するにつれて単なる推奨事項になり、最終的には無視されるようになった
      C++のinlineも似たような過程をたどった
    • Kotlinはほぼあらゆる場所でinlineを積極的に使っている
      mapのような関数でラムダのオーバーヘッド削減を行うためだ
      パフォーマンス上の問題はほとんどなかったが、Scalaのマクロシステムと結び付くと複雑な式が生まれ、問題になる可能性がありそうだ
  • ライブラリをアップグレードしたところ、Scala 3の性能はScala 2.13とほぼ同等になった
    Ruby 2→3のアップグレード時にも似た経験があった
    単に言語だけを上げるのではなく、依存関係全体を最新化してこそシステムは安定する
  • Scala 3の問題は、誰も望んでいなかったこと
    Scala 2の型推論の問題は依然として解決されておらず、その代わりに言語だけが変わった
    市場の要求を無視して、誰も望まない製品を作ったようなものだ
    PS: コンパイラには本物のユニットテストスイートを作るべきだ
    • 悲しいが同意する。Scalaは2010〜15年ごろには有望だった
      しかしScala 3のリライトはコンパイル速度とツーリングの問題を解決できず、プロジェクトの勢いを完全に失わせた
      今の2025年に新規プロジェクトをScalaで始める人がいるだろうかと思う
    • 何度もScalaを学ぼうとしたが、結局Clojureに戻った
      Scalaは学者が作った言語のようで、産業界で一時流行したことのほうがむしろ不思議だった
    • 核心をよく突いている
      今ではすべてのツールがScala 3に対応しなければならず、IntelliJですらまだ完全にはサポートできていない
      Scala 2を段階的に改善していけばよかったのに、学術的成功にしか関心がなかったように見える
      たとえばtpolecatの記事のようにearly returnをめぐっても議論が多いが、Kotlinは何の問題もなくサポートしている
    • 「テストがない」というのは誤情報
      Scalaコンパイラには数千、数万のテストがあり、
      公式テストディレクトリ
      コミュニティビルドシステムによって数百万LOCを検証している
    • 記事をちゃんと読んでいないようだ。論点が完全にずれている
  • この記事で最も興味深かったのは、自動化された性能テストとflamegraph分析を標準で備えるべきだという点だ
    特に言語バージョンのアップグレードのような大きな変更では必須だ
    • ベンチマークテストは一般のテストとは違って、細かな設定が必要だ
      私たちはC++で書かれたツールを継続的にベンチマークしているが、環境ノイズのせいで結果の一貫性を保つのが難しい
      同じマシンで複数回実行して比較する方法を検討している
    • JVMでは最近どんな性能テストツールが使われているのか気になる
  • Scala 3の唯一の問題はPythonへの憧れ
    第2の構文を作り、それを未来だと押し出したのが問題だった
    そのせいでツーリングのエコシステムも遅くなった
    • 今では大半のツールが新構文をサポートしている
      コンパイラやscalafmtでスタイルを自動変換することもできる
    • Scalaらしく、同じことを複数の方法でできるようになった
      今や中括弧構文とインデント構文の2通りに増えた
    • むしろHaskellと比べたほうが魅力的だったかもしれない
    • かつてのScalaファンとして、新構文の例を見て戸惑った
      match構文が冗長すぎて、Pythonの真似のように見える
  • 私は以前Scala 2.xの移行をやったが、依存関係待ちが最も大変だった
    当時はSparkのおかげでScalaが注目を集めたが、商用言語として発展する機会を逃した
    今ではSparkはPythonへ、JVMのモダン言語の座はKotlinが占めている
    結局Scalaは再び学術的な言語に戻ったように感じる
    • それでもScalaは依然として大企業で広く使われている
      Scala Adoption Trackerを見ればわかる
      Scala 3の新機能には、言語エコシステムを再び革新する潜在力がある
      例: Capture Checkingの解説
    • Kotlinが本当にJVMを支配したのかは疑問だ
      Javaが関数型機能を追加したことで、Scalaの魅力の一部を吸収した
    • サーバーサイドJVMではKotlinの存在感はほとんどない
      私の経験では市場の需要もごく小さい
    • Kotlinは実質的にAndroid専用言語
      GoogleがJavaサポートを制限したことでそうなったにすぎない
      JVM全体の市場では10%程度のシェアにすぎない
  • ライブラリのバージョンを上げずにScala 3へ移ったというのが不思議だった
    セキュリティ監査(PCI-DSSなど)を受けるなら、最新ライブラリの維持は必須だ
    • 「最新を保つのがよい習慣だ」という言い方は議論を止める表現だ
      私はむしろ依存関係を古いまま保つほうだ
      新バージョンは新しいバグを持ち込み、メンテナ変更やセキュリティリスクもある
    • 私も混乱した。記事では「依存関係を更新した」と言っていたのに、後になって「更新後に性能が同じになった」と言っている
      最初は一部だけ上げたようだ。小さな段階に分けるのは一般的だが、運が悪かったのだろう
    • バグの原因が分かってからは、それほど印象的ではなかった
      問題はScala 3自体ではなく、複数要因の相互作用だった
    • 言語バージョンのアップグレードのような大きな変更ほど、一度に一つずつ変えるのがよい
    • Maven/Gradle/SBTでバージョン制約をかければ、言語バージョンとは別に維持される
      ただしScala専用ライブラリはバージョンにScalaバージョンが含まれているので注意が必要だ
  • SoftwareMillとScala GitHubのバグレポートは、小さいが的確な修正だった
    Scalaの表現力と型安全性が際立っていた
    Li Haoyiの記事のように、Pythonの代替言語としても十分魅力的だ
  • 主な教訓は、言語やフレームワークをメジャーアップグレードする際にはライブラリも一緒に最新化すべきだということだ
    特にマジック機能を多用するライブラリが多いほど重要だ