プラットフォーム・ランタイム・フレームワークの選択
発端
グレゴール・ホープ氏の投稿した記事を読んでたらいろいろ思うところがあった。
- Don't get locked up into avoiding lock-in
- ロックインを避けることに縛られないで!
- 自分用に日本語化したやつ。FAQとか読んでも特に禁止されているわけじゃなかったから公開した
プラットフォーム・ランタイム・フレームワークの選択
基本的には、これらを判断できる情報が集まってればよさそう。
なんか投資信託みたいだな・・・・・・
それなりの期間継続して利用する前提のシステムなら、業務あるいは事業の予算計画や売上計画があるはず。
プラットフォーム・ランタイム・フレームワークという技術要素の選択は、それらの計画に正負両方の影響を与える可能性がある、というのを意識する(させる)必要がありますねと。
具体例を踏まえたよもやま
- 対象:社内業務システム(Webシステム)のリプレース
- 利用期間:今の業務が無くなるまで(つまり未定)
- 開発期間:4ヶ月くらい(できるだけ早くして欲しい)
- 環境:AWS(社内標準だから)
- ランタイム:Java11(社内標準だから)
- フレームワーク:指定なし
- 運用:自社のメンバーでやっていくつもり
ノーコードじゃないんかい、とかいろいろ思うところはあるけど、 できるだけ早く とか Java11 に引っ張られて Wagby や Spring Boot を選んでしまいそう。
たぶん、その考えは落とし穴です。辛い話がいろいろ思いついてしまう。
Spring Boot は依存ライブラリ管理とコンポーネント構成を半自動化するフレームワークで、イニシャルコストはすごく下げられる。 しかし、EOLが15ヶ月なので、比較的短期に deprecated になってしまう。
依存ライブラリのセキュリティフィックスを単独で取り入れようとしても、旧バージョンの Spring Boot が対応していないバージョンなら、依存ライブラリ定義とコンポーネント構成を自前でやらないといけない。 そのためには、MavenやGradleに加えて Spring Frameworkの高度な知識が必要になってしまう。
Spring Boot を最新版にすればいいかというと、それもまた悩ましい判断になる。 全ての機能が正しく動作する保証はないから。
それをランニングコストと呼ぶのか、リスクヘッジのコストと呼ぶのかわかりませんが、安く付くはずが無い。
さらに、Java11 が保守されるのは一般的に 2024 年までとされているので、たとえ今の業務が続いているとしても、その次の LTS へ乗り換えないといけない。 もちろんフレームワークも Spring Framework 6.x へ切り替えないといけないわけで。
pgdiff(Rubyスクリプト)による PostgreSQL データベースの比較
pgdiff を使うと、オンラインの PostgreSQL を比較して、データベース定義を揃えるための SQL を生成できます。
特徴とメリット・デメリット
総合すると、何も使えないときは頼りたくなる、くらいの評価……
- 依存する gem は
pg
だけで、それ以外は pure Ruby- 👎Windowsで実行できるようにするのが難しい(WSLならいける)
- 稼働中のデータベースプロセスを停止せずに比較できる
- 👍 リードレプリカのない環境だと嬉しい
- SQLを生成する
- 👎 冪等性、排他制御、効率は考慮されてない
使い方
$ apt-get install -q -y libpq5 libpq-dev $ gem install pgdiff $ command pgdiff /home/yujiorama/.rbenv/shims/pgdiff $ pgdiff "postgresql://user:pass@host1:port1/database1" "postgresql://user:pass@host2:port2/database"
「イシューからはじめよ」読んだ
ウィッシュリスト経由でいただいたので早速読んだ。
ずいぶん前の忘れ物を見つけたような感覚。
相手に伝わる提案を組み立てるための戦略と戦術を解説してる本だった(この本の構成がまさにそうなっている!)。
本当に解決すべき(白黒はっきりさせるべき)問題をイシューと呼んでいる。
イシューを特定し、解決策のストーリーを構成し、仮説を裏付ける情報を収集し、プレゼンテーションを作成する、という流れ。
抽象度を少しずつ低くして、具体化していくということだ。
「ストーリーを意識する」という点が繰り返し登場するので、方向性を間違えないようにすることが大事なのがよく分かる。
目的に応じて、適切な調査や分析の手法を活用できるよう、訓練・習熟しておくのも重要。
ライト、ついてますか―問題発見の人間学を読んでおくと良さそう。これらの本では、問題を解決するには問題を正しく理解しなければならないことを教えてくれるため、イシューを特定するときの考え方の参考になる。
第5回 The Software Architect Elevator 読書会@リモート
- JavaEE勉強会の読書会
- 開催場所はDiscordのjavaee-study-jp
- グレゴール・ホープさんの「The Software Architect Elevator」を読んでます
- 今回は
16. The IT World Is Flat
から - 次回は6/19(土)、
20. Writing for Busy People
から
参加者トピック
- BERT、アテンション不要論など…DL関連の理論的発展が早くてやばい
- MLP-Mixer という技術
- Transformerよりもシンプル?「MLP-Mixer」爆誕(1日目) ~Abstract / Introduction編~
- [2105.01601] MLP-Mixer: An all-MLP Architecture for Vision
- [2105.08050] Pay Attention to MLPs
ステート・オブ・AI ガイド on Twitter: "我々はひょっとしたら「トランスフォーマー時代」の終わりを目の当たりにしているかも。 Google Brain から新たに発表された多層パーセプトロン (MLP) にゲート機構を組み合わせた「gMLP」、画像認識とBERT的言語モデルにおいてトランスフォーマーに匹敵する性能を叩き出す https://t.co/bADNOTRV6c… https://t.co/HhYTxyQT9l"我々はひょっとしたら「トランスフォーマー時代」の終わりを目の当たりにしているかも。
— ステート・オブ・AI ガイド (@stateofai_ja) 2021年5月18日
Google Brain から新たに発表された多層パーセプトロン (MLP) にゲート機構を組み合わせた「gMLP」、画像認識とBERT的言語モデルにおいてトランスフォーマーに匹敵する性能を叩き出す https://t.co/bADNOTRV6c pic.twitter.com/iclb5nafNE
- この辺を試してる
- KubeCon があった。eBPFとか学ばないと
- MacBook Air (M1) はかなりいい
- iPad Pro とは用途が違うので比較が難しい
- 「レガシーコードからの脱却」の社内読書会を予定中
- 転職先で驚きの出会いがあった「前に会ってますよね?!」
ディスカッション
第16章 ITの世界はフラット
- 顧客として、ベンダーさんには、もうちょっとこっちの事情も汲んでくれよ、という思いが強い
- ベンダーとして、顧客と一緒にソリューションの構築をやっていくようにしている
- そのときはイベントストーミングしたりする Event storming - Wikipedia
- 地図やランドスケープ、という抽象度で話をしているのは納得できるけど、「XXXマッピングで認識合わせをしましょう」みたいなツール先行の進め方は腑に落ちない
- 自分で地図を作れるイメージが湧かない。コアについて2つ質問するだけでどうにかなるもの?ならないよね
- 自分たちの知ってることなら地図に描けるはずなので描く。そこに何を追加したいのか考えるのが大事なのでは。
- 分散トレーシングが必要なら、分散トレーシングを売りにしてる製品を検証していけばいい
17. あなたのコーヒーショップは2フェーズコミットを使用していない
- 前に聞いたことのある話だった
- 著者はしばらく日本に住んでいたこともあるそうで、その辺の話のようだ
- 再試行のトピックから派生するトピック:復旧後のサービスに多量のリクエストが殺到して、本来の性能が発揮できなくなってしまうのを避けるにはどうしたらいいだろう?
III. Communication
- どういうドキュメント書いてる?
- 画面設計書(どういう項目を、どこに、どこから取得した値を表示する、みたいな)
- 画面遷移図
- メンテナンスできてる?
- リリースできるまで維持できてればいいんじゃないかと思ってる(作る人使う人が身近な人だけの場合)
18. 物事の説明
- キーワードが多い
- 👍自分が理解したことを書くんじゃなくて、相手の理解を促す文章を書くのが重要
- 「結論から言え」について
- 経験的に、結論より先にコンテキストの説明が必要だった
- 不要ならスキップすればいいし、そうでない場合もあったけど
- 👍ここまで読み進めて、初めて「具体的に活用できそうな考え方」だと思った
- 基本的に経営陣は事業成長のための判断材料を欲しているのであって、技術的に正確な理解をしたいわけではない
- 経営陣には、技術的な理解を深めることで、効率的に判断材料を収集できることを伝えてあげないといけない
- 技術者には、技術的判断が、事業成長にどのような影響を与えるのか理解してもらわないといけない
19. Show the Kids the Pirate Ship!
- 後で参照しそうなプレゼンテーションの場合は目次付けるよねぇ
参考情報
DNSキャッシュ時間の問題
Cloud Native Build Pack で Spring Boot アプリのコンテナイメージを作れるから簡単だと思っていると、いい感じにDNSキャッシュ時間の設定値を構成する方法が無くて困る。
1. $JAVA_HOME/lib/security/java.security に記述
初期設定では、Cloud Natie Buildpack でイメージを作成すると、JRE にpaketo-buildpack/bellsoft-libericaを使う。
このBuildpackは、環境変数JAVA_TOOLS_OPTS
に-Djava.security.properties=<path>
を埋め込むようになっており、java.security
の代わりに別の場所へ配置したプロパティファイルを使うようになる。
起動時のログでも確認できる。
Setting Active Processor Count to 8 Calculating JVM memory based on 2708640K available memory Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx2327887K -XX:MaxMetaspaceSize=73552K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 2708640K, Thread Count: 50, Loaded Class Count: 10572, Headroom: 0%) Adding 129 container CA certificates to JVM truststore Spring Cloud Bindings Enabled Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=8 -XX:MaxDirectMemorySize=10M -Xmx2327887K -XX:MaxMetaspaceSize=73552K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.4.5)
つまり、Buildpackの作成したレイヤーに作成されていることがわかる。
ビルド時に内容を設定できるのか考えたけど、ただ作ってるだけでそういう機能はないらしい。(paketo-buildpacks/libjvm/blob/main/java_security_properties.go#L47)
まとめ
- Cloud Native Buildpackで作成したコンテナイメージは、標準と違う場所に置いたプロパティファイルを使用する
- コンテナイメージを作成するとき、設定だけでプロパティファイルをカスタマイズする方法は不明
- コンテナイメージを実行するとき、設定だけでプロパティファイルをカスタマイズする方法は不明
- コンテナイメージ実行時に、ボリュームマウントして任意のファイルを読ませることはできる
2. java.security.Security.setProperty() で指定
Spring Bootアプリはmain
メソッドを作ることが多いと思うので、リンク先の記事のようにInetAddressCachePolicy
の読み込み順を意識せずに対応できると思う。
毎回書かないで済むようBuildpackが対応して欲しい。
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { static { try { var cacheTTL = System.getProperty("networkaddress.cache.ttl", "1"); var cacheNegativeTTL = System.getProperty("networkaddress.cache.negative.ttl", "1"); java.security.Security.setProperty("networkaddress.cache.ttl", cacheTTL); java.security.Security.setProperty("networkaddress.cache.negative.ttl", cacheNegativeTTL); } catch (Exception ignore) { // XXX } } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
A5:SQL Mk-2のER図(.a5er)からDDLを生成する
- TL;DR
- コマンドラインユーティリティ自体の実行方法
- Maven で実行する方法
- Gradle で実行する方法
- おまけ:GitリポジトリでER図(.a5er)を管理するときは .gitattributes を作ったほうがいい
TL;DR
- モデリングに利用してる人も多い(であろう)A5:SQL Mk-2
- GUIでDDLを生成できるのがよい
- コマンドラインから生成できるとなおよいのでは?
- 作者さんがコマンドラインユーティリティを開発していた
- Maven/Gradleから実行する方法を整理した
コマンドラインユーティリティ自体の実行方法
A5:SQL Mk-2 コマンドラインユーティリティからzipファイルをダウンロードして適当な場所に展開します。
zipファイルには説明書も付属してます。
ここの説明はそれを元にしてるので、正確な詳しい内容は説明書を参照してください
次のように実行すると、c:\work\model.a5er に置いたER図から c:\work\model.ddl にSQL(DDL)が生成されます。
A5M2cmd.exe /ERDDL ^ /ERD=c:\work\model.a5er ^ /OutFileName=c:\work\model.sql
Maven で実行する方法
A5:SQL Mk-2 で ER 図を開いていると、他のプロセスからファイルを開けなくて失敗します
Maven でDDLを生成できるようになっていると便利な気がします。
mvn generate-sources -Pgenerate-sources-ddl -Derd.path=c:/work/model.a5er
こういうふうに記述すると実現できます。
<?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 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>a5m2cmd-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>a5m2cmd-demo</name> <packaging>jar</packaging> <properties> <java.version>11</java.version> </properties> <dependencies> </dependencies> <profiles> <profile> <id>generate-sources-ddl</id> <activation> <activeByDefault>false</activeByDefault> </activation> <properties> <erd.path>c:/work/model.a5er</erd.path> </properties> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>wagon-maven-plugin</artifactId> <version>2.0.2</version> <executions> <!-- A5m2cmd.zipをダウンロード --> <execution> <id>fetch-a5m2command</id> <phase>generate-sources</phase> <goals> <goal>download-single</goal> </goals> </execution> </executions> <configuration> <skipIfExists>true</skipIfExists> <url>https://ftp.vector.co.jp</url> <fromFile>71/97/3301/A5M2cmd_2.14.3_x64.zip</fromFile> <toFile>${project.build.directory}/A5M2cmd/A5M2cmd.zip</toFile> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <!-- A5m2cmd.zipをtargetに展開 --> <execution> <id>extract-zip</id> <phase>generate-sources</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <unzip src="${project.build.directory}/A5M2cmd/A5M2cmd.zip" dest="${project.build.directory}/A5M2cmd" /> </tasks> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.0.0</version> <executions> <!-- A5m2cmd.exeを実行してDDLを生成 --> <execution> <id>generate-sources-ddl</id> <phase>generate-sources</phase> <goals> <goal>exec</goal> </goals> <configuration> <executable>${project.build.directory}/A5M2cmd/A5M2cmd.exe</executable> <arguments> <argument>/ERDDL</argument> <argument>/ERD=${erd.path}</argument> <argument>/OutEncoding=UTF-8</argument> <argument>/OutFileName=${project.basedir}/src/main/resources/db/migration/V0_0_001__table.sql</argument> <argument>/RDBMSType=POSTGRESQL</argument> <argument>/CreateOrder=Dependent</argument> <argument>/GenerateComment=Y</argument> <argument>/GenerateDropTableStatement=Y</argument> <argument>/DropTableIfExists=Y</argument> <argument>/BackupRestoreTempTable=N</argument> <argument>/ForceQuoteIdentifier=N</argument> <argument>/CreatePkIndex=Y</argument> <argument>/CreateFk=Y</argument> <argument>/CreateFK_ParentCard1Only=Y</argument> <argument>/FKParentIndex=Y</argument> </arguments> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> </project>
Gradle で実行する方法
こういう感じに実行できると便利そうです。
gradle generateDDL -Perd.path=c:/work/model.a5er
こういう風に記述すると実現できます。
plugins { id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' def erdPath = findProperty('erd.path') ?: 'er.a5er' repositories { mavenCentral() } task fetchA5M2cmd { def url = 'https://ftp.vector.co.jp/71/97/3301/A5M2cmd_2.14.3_x64.zip' doLast { file("${buildDir}").mkdirs() def a5M2cmdZip = file("${buildDir}/A5M2cmd.zip") a5M2cmdZip.withOutputStream { os -> new URL(url).withInputStream { is -> os << is }} } } task unzipA5M2cmd(type: Copy) { def zipPath = file("${buildDir}/A5M2cmd.zip") def zipFile = file(zipPath) def outputDir = file("${buildDir}/A5M2cmd") from zipTree(zipFile) into outputDir } task generateDDL(type:Exec) { executable file("${buildDir}/A5M2cmd/A5M2cmd.exe") args = [ "/ERDDL", "/ERD=${erdPath}", "/OutEncoding=UTF-8", "/OutFileName=${projectDir}/src/main/resources/db/migration/V0_0_001__table.sql", "/RDBMSType=POSTGRESQL", "/CreateOrder=Dependent", "/GenerateComment=Y", "/GenerateDropTableStatement=Y", "/DropTableIfExists=Y", "/BackupRestoreTempTable=N", "/ForceQuoteIdentifier=N", "/CreatePkIndex=Y", "/CreateFk=Y", "/CreateFK_ParentCard1Only=Y", "/FKParentIndex=Y", ] } tasks.generateDDL.dependsOn 'fetchA5M2cmd', 'unzipA5M2cmd'
おまけ:GitリポジトリでER図(.a5er)を管理するときは .gitattributes を作ったほうがいい
ER図(.a5er)ファイルの改行コードは CRLF ですが、ソースコードや設定ファイルの改行コードは LF になっている場合がほとんどです。
そういう状態のままER図を編集していると、コミット時に改行コードが変換されることを示す警告メッセージが表示されます。
改行コードの変換を避ける方法はいろいろあるのですが、 .gitattributes を設定するのがいいと思います。
具体的にはリポジトリに .gitattributes というファイルを作成して次のように記述します。
*.a5er text eol=crlf