第7回 The Software Architect Elevator 読書会@リモート

javaee-study.connpass.com


参加者トピック

ディスカッション

IV. Organizations

  • ビジネス=組織みたいに説明されてるのはよく分からなかった
    • 中心になるのが「システム(アーキテクチャ)」ならよく分からない
    • 中心になるのが「アーキテクト」なら分かる

26. Reverse-Engineering Organizations

27. Control Is an Illusion

  • To claim that the inmates are running the asylum.
    • センシティブな言葉・・・・・・囚人が、「この監獄の支配者は自分だ」と叫んでるような感じ
    • うわべだけの主導権を揶揄するフレーズのようだった
  • 「各自が最適だと思っていることを実行する」のは自律性ではなく無秩序だ。
    • EnablementとStrategyとFeedbackが必要だという説明の出典はどこだろう

28. They Don’t Build ’Em Quite Like That Anymore

  • 組織のピラミッドの一番上にいる人たちは、実際のユーザーから離れた場所でITシステムのピラミッドの最下層を設計するのが好きなのだ。

29. Black Markets Are Not Efficient

  • 闇市場を枯渇させるための主なハードルは、プロセスの改善には測定可能な初期費用がかかるのに対し、闇市場のコストは通常測定されないことです。このギャップにより、変化がない場合のコスト(第33章)が低いと認識され、変化へのインセンティブが低下してしまうのです

30. Scaling an Organization

  • 同期コミュニケーションと非同期コミュニケーションの話

参考情報

JDK から分離した ECMAScript エンジンの Nashorn を依存ライブラリで使用する

参考リンク。

環境

$ JAVA_HOME=$JAVA14_HOME mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: C:\Users\y_okazawa\.sdkman\candidates\maven\current
Java version: 14, vendor: AdoptOpenJDK, runtime: C:\Users\y_okazawa\scoop\apps\adopt14-hotspot\current
Default locale: ja_JP, platform encoding: UTF-8
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

$ JAVA_HOME=$JAVA15_HOME mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: C:\Users\y_okazawa\.sdkman\candidates\maven\current
Java version: 15.0.2, vendor: AdoptOpenJDK, runtime: C:\Users\y_okazawa\scoop\apps\adopt15-hotspot\current
Default locale: ja_JP, platform encoding: UTF-8
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: C:\Users\y_okazawa\.sdkman\candidates\maven\current
Java version: 16, vendor: Oracle Corporation, runtime: C:\Users\y_okazawa\scoop\apps\openjdk16\current
Default locale: ja_JP, platform encoding: UTF-8
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

実験

プロジェクトの詳細は後述。

次の条件で、ただの Hello World ができることを確認しただけ。

  • OpenJDK 14/OpenJDK 15/OpenJDK 16について
    • 依存ライブラリ指定なしで実行
    • 依存ライブラリ指定ありで実行

結果は以下のとおり。

OpenJDK 依存ライブラリ指定の有無 結果
14 なし 動く
14 あり 動く
15 なし 動かない
15 あり 動く
16 なし 動かない
16 あり 動く
# OpenJDK 14, 依存ライブラリなし
$ JAVA_HOME=$JAVA14_HOME mvn clean compile exec:java -q -Djava.version=14 -Dexec.mainClass=com.example.nashorn.NashornAgain
Warning: Nashorn engine is planned to be removed from a future JDK release
Hello, World!
# OpenJDK 14, 依存ライブラリあり
$ JAVA_HOME=$JAVA14_HOME mvn clean compile exec:java -q -Pwith-dependency -Djava.version=14 -Dexec.mainClass=com.example.nashorn.NashornAgain
Hello, World!

# OpenJDK 15, 依存ライブラリなし
$ JAVA_HOME=$JAVA15_HOME mvn clean compile exec:java -q -Djava.version=15 -Dexec.mainClass=com.example.nashorn.NashornAgain
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:java (default-cli) on project nashorn-again: An exception occured while executing the Java class. Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
# OpenJDK 15, 依存ライブラリあり
$ JAVA_HOME=$JAVA15_HOME mvn clean compile exec:java -q -Pwith-dependency -Djava.version=15 -Dexec.mainClass=com.example.nashorn.NashornAgain
Hello, World!

# OpenJDK 16, 依存ライブラリなし
$ JAVA_HOME=$JAVA16_HOME mvn clean compile exec:java -q -Djava.version=16 -Dexec.mainClass=com.example.nashorn.NashornAgain
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:java (default-cli) on project nashorn-again: An exception occured while executing the Java class. Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
# OpenJDK 15, 依存ライブラリあり
$ JAVA_HOME=$JAVA16_HOME mvn clean compile exec:java -q -Pwith-dependency -Djava.version=16 -Dexec.mainClass=com.example.nashorn.NashornAgain
Hello, World!

実験した素材の説明

こういう配置の Maven プロジェクトを作成した。

pom.xml
src/main/java/com/example/nashorn/NashornAgain.java

pom.xml のレイアウトは以下。

  • JDKのバージョンは java.version を経由して maven.compiler.target に指定する
  • 依存ライブラリはプロファイル with-dependency に入れてる
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>nashorn-again</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>nashorn-again</name>
    <description>Demo project for Nashorn dependency</description>
    <properties>
        <java.version>16</java.version>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.release>${java.version}</maven.compiler.release>
    </properties>
    <profiles>
        <profile>
            <id>with-dependency</id>
            <dependencies>
                <dependency>
                    <groupId>org.openjdk.nashorn</groupId>
                    <artifactId>nashorn-core</artifactId>
                    <version>15.3</version>
                </dependency>
            </dependencies>
        </profile>
    </profiles>
</project>

コンパイル、実行してるコードはこれ。

package com.example.nashorn;

import javax.script.*;

public class NashornAgain {
    public static void main(String[] args) throws Exception {
        // create a script engine manager
        ScriptEngineManager factory = new ScriptEngineManager();
        // create a Nashorn script engine
        ScriptEngine engine = factory.getEngineByName("nashorn");
        // evaluate JavaScript statement
        try {
            engine.eval("print('Hello, World!');");
        } catch (final ScriptException se) { se.printStackTrace(); }
    }
}

Alibaba Dragonwell JDK のコルーチン実装 Wisp2 の調査

  • AlibabaはOpenJDKのディストリビュータ
  • Dragonwell JDKという名前のJDKを開発、提供している
  • Dragonwell JDKにはWispというコルーチン実装が組み込まれている
    • Kotlin のコルーチンとは違って、JVMのスレッド実装を入れ替える構造になっている
  • どのような特性があるのか計測した

環境

コンポーネント 利用したバージョン
Docker Engine Docker Desktop 3.4.0 (Windows 10, WSL based engine)
Alibaba Dragonwell JDK dragonwell-8.7.7_jdk8u292-ga
Bellsoft Liberica JDK jdk-8u292-bellsoft

https://github.com/alibaba/dragonwell11 は Wisp2 に未対応だった

結果

それぞれの条件で5回ずつ計測。値は実行時間の平均値(ミリ秒)。

JDK Wisp2無効(ActiveProcessorCount=1) Wisp2無効(ActiveProcessorCount=4) Wisp2有効(ActiveProcessorCount=1) Wisp2有効(ActiveProcessorCount=4)
Dragonwell 108259.4 80422.6 1054.2 45304.6
Liberica 116458 70475.6 n/a n/a
  • t(Wisp2無効:ActiveProcessorCount=1) > t(Wisp2無効:ActiveProcessorCount=4)
    • Wisp2無効では、ActiveProcessorCount=4の実行時間は、ActiveProcessorCount=1より短い(75%くらい)
    • 👍CPUコア数に応じて処理速度が速くなっている
  • t(Wisp2有効:ActiveProcessorCount=1) > t(Wisp2有効:ActiveProcessorCount=4)
    • Wisp2有効では、ActiveProcessorCount=4の実行時間は、ActiveProcessorCount=1より大幅に長い(4300%くらい)
    • 👎CPUコア数に応じて処理速度が遅くなっている
  • t(Wisp2無効:ActiveProcessorCount=1) > t(Wisp2有効:ActiveProcessorCount=1)
    • ActiveProcessorCount=1では、Wisp2無効の実行時間は、Wisp2有効より大幅に長い(10271%くらい)
    • 👍シングルコアCPUならとても処理速度が速くなっている
  • t(Wisp2無効:ActiveProcessorCount=4) > t(Wisp2有効:ActiveProcessorCount=4)
    • ActiveProcessorCount=4では、Wisp2無効の実行時間は、Wisp2有効より長い(177%くらい)
    • 🤔マルチコアCPUだとそんなに処理速度は速くならない(1.7倍速くなっているから十分かもしれない)
  • t(Dragonwell) == t(Liberica)
    • Dragonwell JDK と Liberica JDK の実行時間はほとんど変わらない

考察

  • Wispの効果
    • 実行環境がシングルコアCPUなら、処理速度の大幅な向上が見込める
    • 実行環境がマルチコアCPUなら、逆にボトルネックになってしまう可能性がある
      • コルーチンスケジューラがマルチコアCPUを想定していないのかもしれない
  • デフォルトのスレッド実装
    • CPUコア数に応じて処理速度は速くなる傾向がある
      • ただしコア数に比例するわけではない

操作

実際に動かすコード。なんかピンポンする。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class PingPong {
    static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();

    public static void main(String[] args) throws Exception {
        BlockingQueue<Byte> q1 = new LinkedBlockingQueue<>(), q2 = new LinkedBlockingQueue<>();
        THREAD_POOL.submit(() -> pingpong(q2, q1)); // thread A
        Future<?> f = THREAD_POOL.submit(() -> pingpong(q1, q2)); // thread B
        q1.put((byte) 1);
        System.out.println(String.format("%s,%s,%s,%d", args[0], args[1], args[2], f.get()));
    }

    private static long pingpong(BlockingQueue<Byte> in, BlockingQueue<Byte> out) throws Exception {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1_000_000; i++) out.put(in.take());
        return System.currentTimeMillis() - start;
    }
}

Dockerfile はこれ。

FROM bellsoft/liberica-openjdk-alpine:8u292

RUN set -x \
 && wget -O /tmp/jdk8.tar.gz https://github.com/alibaba/dragonwell8/releases/download/dragonwell-8.7.7_jdk8u292-ga/Alibaba_Dragonwell_8.7.7_x64_linux.tar.gz \
 && mkdir -p /usr/lib/jvm/dragonwell8 \
 && tar -xzf /tmp/jdk8.tar.gz --strip-components 1 -C /usr/lib/jvm/dragonwell8 \
 && rm -rf /tmp/jdk8.tar.gz

計測スクリプト。結果は適当に編集する。

$ docker build -t test-wisp -f Dockerfile .
# Git for Windows only
$ export MSYS_NO_PATHCONV=1
$ for c in 1 4; do
docker run --rm -it --volume ${PWD}:/app -e JAVA_HOME=/usr/ilb/jvm/dragonwell8 --cpus ${c} test-wisp sh -c "/usr/lib/jvm/dragonwell8/bin/javac -d /tmp /app/PingPong.java; for _ in 1 2 3 4 5; do /usr/lib/jvm/dragonwell8/bin/java -XX:+UnlockExperimentalVMOptions -XX:-UseWisp2 -XX:ActiveProcessorCount=${c} -cp /tmp PingPong dragonwell8 noWisp2 ${c}; done"
docker run --rm -it --volume ${PWD}:/app -e JAVA_HOME=/usr/ilb/jvm/dragonwell8 --cpus ${c} test-wisp sh -c "/usr/lib/jvm/dragonwell8/bin/javac -d /tmp /app/PingPong.java; for _ in 1 2 3 4 5; do /usr/lib/jvm/dragonwell8/bin/java -XX:+UnlockExperimentalVMOptions -XX:+UseWisp2 -XX:ActiveProcessorCount=${c} -cp /tmp PingPong dragonwell8 useWisp2 ${c}; done"
done
$ for c in 1 4; do
docker run --rm -it --volume ${PWD}:/app -e JAVA_HOME=/usr/ilb/jvm/jdk-8u292-bellsoft-x86_64 --cpus ${c} test-wisp sh -c "/usr/lib/jvm/jdk-8u292-bellsoft-x86_64/bin/javac -d /tmp /app/PingPong.java; for _ in 1 2 3 4 5; do /usr/lib/jvm/jdk-8u292-bellsoft-x86_64/bin/java -XX:+UnlockExperimentalVMOptions -XX:ActiveProcessorCount=${c} -cp /tmp PingPong jdk-8u292-bellsoft-x86_64 noWisp2 ${c}; done"
done

第6回 The Software Architect Elevator 読書会@リモート

javaee-study.connpass.com


参加者トピック

  • 最近コンテナセキュリティが盛り上がって、同僚に教えてもらったのはこういうやつ
  • ザリガニを飼育し始めた
    • ずっとピースしててかわいい

ディスカッション

20. Writing for Busy People

時間の無い読み手のためにドキュメントを作成するときに気をつけること。

  • 品質、難しい
    • 見せる人を意識して体裁を整えないといけない
    • 複数人で書いてるときはもっと大変
  • 書き方次第で社内の組織的な対立を生み出してしまう場合もある

21. Emphasis Over Completeness

モデルを表現するために図を作るんだけど、完全な図を作るより説明したいところを強調するんだよ。

  • A Pop Quiz はどういう場面でやるエクササイズなんだろう?
    • 本当の会議でやるの?無理でしょ
    • スピーカーがリスナーより強い場合にしかできなさそう

22. Diagram-Driven Design

たぶん今読んでる章の元の文章(2010年)

DDD - Diagram Driven Design - Enterprise Integration Patterns

23. Drawing the Line

箱と箱の関係性を説明する線を引きましょう。

24. Sketching Bank Robbers

顧客にヒアリングしながら、システムのアーキテクチャをスケッチする。

25. Software Is Collaboration

プレゼンテーションスライド as a ソフトウェア

  • プレゼンテーションは、インクリメンタルではなくイテレーティブに作った方がいい
    • 一概にそうは言えない
  • Office 系ドキュメントの辛み
    • Google Docs は自動的にバージョン管理されているので気にしたこと無い
      • URLを共有してる人たちの中だけで情報共有が閉じてしまう問題もある
    • ファイルとして保存したい習慣にとらわれている人を解放したい

参考情報

Spring Batch にアプリケーションとは別のデータベーススキーマを使わせたい

yujiorama/spring-batch-different-database-schema-example

経緯

  • Spring Batchでジョブを実行するには、ジョブ実行管理テーブル群を作らないといけない
  • アプリケーションのデータベーススキーマはFlywayでマイグレーションするので、分けておきたい
  • テーブル群を作るSQLスクリプトは、jarに内包されているスクリプトを使いたい
  • Spring Batchを構成する BatchConfigurer には、データソースを設定するインターフェイスしかない
    • データソースの初期データベーススキーマが、テーブル群を作成するデータベーススキーマになる

目標の設定

(HikariCP決め打ちになっちゃうけど)データベーススキーマを指定したデータソースとして自動構成させる。

問題になるかどうかの考察。

  • アプリケーションがクエリを実行するとき、明示的にデータベーススキーマを指定する場合
  • アプリケーションがクエリを実行するとき、明示的にデータベーススキーマを指定しない場合
    • JdbcTemplate とか
    • JDBC Connectionオブジェクトを取得できればどうにかなるので、そういうラッパークラスを用意すればよい

調査と検討

Spring Bootの自動構成プロパティをこうすればよかった。

spring.datasource:
  name: demo
  driver-class-name: org.postgresql.Driver
  url: jdbc:postgresql://localhost:5432/postgres
  username: app
  password: app
  schema:
    - classpath:db/schema/batchdemo.sql
  schema-username: app
  schema-password: app
  initialization-mode: always
  hikari:
    schema: batchdemo

aurl を改造

クラスメソッドさんが公開している classmethod/aurl というツールが OIDC に対応している WebAPI へアクセスするのに便利だった。

そして int128 さんの CLIでOAuth/OIDCを快適に利用する - Speaker Deck を読んだらもう少し便利になりそうだったので、fork して改造してみた。

yujiorama/aurl branch=rp-authentication-flow

プロファイルを認可コードフローにすると、ブラウザで認証した後、aurl の内部で実行している http サーバーで認可コードレスポンスを受信できる。 なるほど便利だ。