Java9 だとspotbugs で OBL_UNSATISFIED_OBLIGATION 警告がでるとき
https://github.com/spotbugs/spotbugs/issues/493
try-with-resources してるのに leak しているという警告が出るので謎ダナーと思ったら spotbugs のバグみたい。
https://github.com/spotbugs/spotbugs/issues/493
try-with-resources してるのに leak しているという警告が出るので謎ダナーと思ったら spotbugs のバグみたい。
After boot 2.1, DefaultCookieSerializer applies samesite=lax attribute by default. As a result, the security risk was decreased.
But in the OAuth2 authentication process, OAuth2 provider can pass the data by POST method. It can't work with samesite=lax
attribute(A browser won't send cookie).
You can configure the default behavior by following bean definition.
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setSameSite(null);
return cookieSerializer;
}
https://github.com/rzwitserloot/lombok/issues/1716 https://github.com/rzwitserloot/lombok/pull/1718
https://blog.ik.am/entries/448 https://matsumana.info/blog/2018/04/25/spring-boot-uri-latency/
management:
metrics:
distribution:
percentiles:
http.server.requests:
- 0.5
- 0.75
- 0.95
- 0.99
このへんの設定しておくと便利だってバーの常連の人が言ってたので今後は設定するようにしようと思う。
gRPC-WEB が GA となった。これが我々の生活にどのような変化をもたらすのかについて考える。
gRPC は google が公開している RPC 方式であり、java, golang などの言語で利用可能になっている。 gRPC は protocol buffers over HTTP/2 を基本としているため、通信が multiplexing されるし、schema 定義がきっちりされるのでクライアント側とのコミュニケーションがしやすい。
一方、protocol buffers はbinary であるためにbinaryの取扱が苦手な Browser JavaScript からのアクセスが難しいという問題があった。
grpc-gateway という実装があって、これを利用すれば Browser JavaScript からのアクセスも可能ではある。しかし、専用の gateway server を golang で生成して運用することになって煩雑である。
また、client library を protobuf 定義ファイルから生成することができないので、そういった意味では、gRPC の魅力を 100% 引き出すことができていないといえるかもしれない。 もちろん、Swagger の定義を生成することができるので、そこから codegen することはできる。
外部に向けて JSON API を提供する必要がある場合には grpc-gateway は引き続き有力な選択肢となるが、SPA web application のためのエンドポイントの場合には、grpc-web の方が今後は良い選択となると私は考える。
また、grpc-gateway はコミュニティ実装であるから、公式ではない。
gRPC-WEB のプロトコルは PROTOCOL-WEB.md で解説されている。
多くのブラウザで動作するように、base64で encode する方法などが protocol で設定されている。
現在のところ nginx module と envoy というプロキシサーバーによる実装、grpcwebproxy という go 実装が提供されている。
https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/nginx
nginx module も提供されている。
https://github.com/improbable-eng/grpc-web/tree/master/go/grpcweb
grpcweb は go のライブラリ実装。既存の golang で書かれた gRPC サーバー実装の中に組み込んで、 gRPC-web で export する機能を追加することができる。 便利。
https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy
golang で実装されている grpc-web の実装。go で single binary で導入できるので、一番導入が簡単そう。
Envoy は C++ で書かれた proxy server です。拡張が容易になっていて、C++ で簡単に拡張できるようになっています。
Envoy の grpc-web の実装は envoy の repository の中にあります。C++ ですがコメントがたくさんあるのでわかりやすいですね。
Envoy の中に実装されているのは、Proxy layer でカバーできていればすべてのサーバー実装で使えるので、まずは proxy layer で実装したということのようだ。
Envoy は C++ で書かれているのでビルドがそこそこ面倒なので docker での運用が現実的と思われる。プリビルドバイナリも提供されているが、ubuntu と alpine のみなので centos 勢としては悲しい。
https://github.com/grpc/grpc-web/blob/master/ROADMAP.md
このドキュメントで今後の ROADMAP が述べられている。 いくつか気になったところを紹介する。
Binary の protobuf を base64 などにエンコーディングする方式は CPU overhead/memory overhead が大きい。よって、gmail などで採用されている、text protocol だが高速に処理できるフォーマットを使うようにすれば良いのではないか、とのこと。
各言語用のサーバーライブラリの側に、gRPC-WEB サポートを追加しようというプラン。 今は go だけあるっぽい。Java support が待ち遠しい。
これはほしい。Protobuf から生成されたクライアントに型情報が付けば、IDE 上での作業が快適になることは間違いない。
gRPC-WEB を実際に試せる web console を作りたい、とのこと。
whatwg streams api が各ブラウザに実装されて普及すれば、native の gRPC protocol を利用可能になるのではなるとのこと。
groupID %% artifactID % revision
と groupID % artifactID % revision
の違い。
If you use groupID %% artifactID % revision instead of groupID % artifactID % revision (the difference is the double %% after the groupID), SBT will add your project’s Scala version to the artifact name.
https://stackoverflow.com/questions/17461453/build-scala-and-symbols-meaning
https://github.com/spring-projects/spring-framework/commit/09d9450154be796349dabdc606ade57beae08724#diff-f9fb34c95c900bb40a67429584ebcde4 spring 5.1 から cookie の SameSite のさぽーとが入るっぽい https://github.com/spring-projects/spring-framework/wiki/What's-New-in-Spring-Framework-5.x#whats-new-in-version-50
つまり、boot 2.1 以後は spring 5.1 なので、webflux 使ってれば samesite 属性使える。
久々に http://platform.spring.io/platform/ みたら、"End of Life" の文字が。。
The Platform will reach the end of its supported life on 9 April 2019. Maintenence releases of both the Brussels and Cairo lines will continue to be published up until that time.
とのこと。。
Users of the Platform are encourage to start using Spring Boot's dependency management directory, either by using spring-boot-starter-parent as their Maven project's parent, or by importing the spring-boot-dependencies bom.
というわけで、spring-boot-dependencies bom を利用したら良いっぽい。
によれば、今後は以下のようにすれば良い。Boot のバージョンを上げれば依存ライブラリも互換性のあるいい感じのバージョンに上がっていって便利! って感じっぽい。
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
}
}
<exec executable="python" failonerror="true">
とかすれば良い
https://stackoverflow.com/questions/7799778/ant-conditional-failure-upon-executable-failure
http://blog.k11i.biz/2013/05/java-7-stringsplit.html http://d.hatena.ne.jp/chiheisen/20110801/1312119289
Java 7 以後では String#split(String)
で、引数が1文字の場合は最適化が効いて、高速になるという話。
社内でなんか話題になってたので最近の JVM だとどうなのかなーと調べてみた(元エントリは5年前のもの)。
(Pattern#split
にもいずれ同様の最適化が入る可能性はあるので、このベンチマークの傾向は今でも一緒なのかなーと気になったため)
https://travis-ci.org/tokuhirom/java-string-splitting-benchmark
元のコードは手でベンチマークコードを実装されたものだったが、最近では JMH で簡単に書けるので、JMH で簡単に書いてみた。 以下は travis-ci で openjdk11 を走らせた結果。相変わらず pattern 1 文字の場合の最適化は有効になっていることはわかる。
Benchmark (raw) Mode Cnt Score Error Units
StringSplittingBenchmark.patternSplit thrpt 5 1550191.136 ± 475915.190 ops/s
StringSplittingBenchmark.patternSplit \s thrpt 5 986040.695 ± 217942.160 ops/s
StringSplittingBenchmark.patternSplit [ \t\n\x0B\f\r] thrpt 5 1491352.737 ± 1025015.919 ops/s
StringSplittingBenchmark.patternSplit |\t|\n|\x0B|\f|\r thrpt 5 931442.162 ± 108993.004 ops/s
StringSplittingBenchmark.stringSplit thrpt 5 1820229.132 ± 564445.853 ops/s
StringSplittingBenchmark.stringSplit \s thrpt 5 817729.421 ± 71545.171 ops/s
StringSplittingBenchmark.stringSplit [ \t\n\x0B\f\r] thrpt 5 774425.988 ± 128507.763 ops/s
StringSplittingBenchmark.stringSplit |\t|\n|\x0B|\f|\r thrpt 5 384563.020 ± 81302.831 ops/s
実際に travis-ci でバーっと走らせたときの結果は travis のサイトで見ることができる。 (travis 側の負荷状況によって結果が変わるので、参考程度だけれど) https://travis-ci.org/tokuhirom/java-string-splitting-benchmark
ちなみに今回、初めて JMH で @Param
などのアノテーションを利用した。
以下のように記述することができて超便利〜。複数のパラメータを利用したベンチマークの実装が驚くほど簡単にできるのだ〜
@State(Scope.Benchmark)
public static class BenchmarkState {
@Param({" ", "\\s", "[ \\t\\n\\x0B\\f\\r]", " |\\t|\\n|\\x0B|\\f|\\r"})
private String raw;
private Pattern pattern;
@Setup
public void setup() {
pattern = Pattern.compile(raw);
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@Benchmark
public static void patternSplit(BenchmarkState state) {
state.pattern.split(text);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@Benchmark
public static void stringSplit(BenchmarkState state) {
text.split(state.raw);
}
https://github.com/tokuhirom/jib-docker-spring-boot-examples
https://cloudplatform.googleblog.com/2018/07/introducing-jib-build-java-docker-images-better.html jib を使うと java app を極めて簡単に docker image にすることができるというので試してみた。
tutorial 通りにやったらすんなり動いた。あっけない。 よくできている。
MySQL 8 以後では X Protocol がサポートされている(5.7 系では部分的なサポートであり、X Protocol を本格的に利用する場合には 8 を利用することが推奨されているようだ) 通信は従来の MySQL Protocol と異なり、Protocol Buffers Based となっていて、各言語のドライバの実装が簡単になっている(protocol buffers がその言語でサポートされていれば、だが) これにより今後 libmysqlclient に依存せずに各言語のドライバが実装されるようになって運用管理が簡単になるんじゃないかと私は考えています。 実際に、mysql-connector-nodejsは X Protocol のみをサポートしていて、libmysqlclient への依存がありません。
また、X Protocol/X DevAPI は async を前提に設計されているため、各言語の Connector ではその言語の特性を生かして CompletableFuture/Promise などを利用して実装されています。
X DevAPI というのものがあって、これは MySQL Shell と MySQL Connectors で実装されている API。どの言語を利用していても統一的に MySQL を扱えるプログラミング言語レベルの API になっている。
X Protocol は mysql 8 ではデフォルトで有効になっている模様。有効かどうかは show plugins
などして mysqlx plugin が有効かどうかを確認すれば良い。
port も 3306 ではなく 33060 なので注意。
Node の MySQL connector は promise based になっており使いやすい
NODE_DEBUG=protobuf
という環境変数を設定すれば、protobuf の serialization のログが見れて便利。
Java 実装も割と普通に実行できる。今まで通りの MySQL Connector/J の実装で実行可能。 生で使うなら X DevAPI は JDBC API の 2億倍使いやすい。
package com.example;
import com.mysql.cj.xdevapi.Session;
import com.mysql.cj.xdevapi.SessionFactory;
import com.mysql.cj.xdevapi.SqlResult;
import java.util.List;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) {
SessionFactory sessionFactory = new SessionFactory();
Session session = sessionFactory.getSession("mysqlx://[email protected]:33060/test");
runQuery(session, "SHOW PROCESSLIST");
runQuery(session, "SELECT SLEEP(15)");
runQuery(session, "SHOW PROCESSLIST");
session.close();
}
private static void runQuery(Session session, String query) {
System.out.println(" クエリ開始" + query);
session.sql(query).executeAsync()
.thenAccept(rows -> {
System.out.println(" クエリ完了 " + query);
dumpRows(rows);
});
}
private static void dumpRows(SqlResult rows) {
List<String> columnNames = rows.getColumnNames();
System.out.println("\n\n結果結果結果結果結果結果結果結果結果結果結果");
System.out.println(" " + columnNames.stream().collect(Collectors.joining("\t")));
System.out.println(rows.fetchAll()
.stream()
.map(row -> columnNames.stream()
.map(row::getString)
.collect(Collectors.joining("\t")))
.map(line -> " " + line)
.collect(Collectors.joining("\n")));
System.out.println("\n\n終了終了終了終了終了終了終了終了終了終了終了");
}
}
のような実装では、以下のような結果を得るだろう。
クエリ開始SHOW PROCESSLIST
クエリ完了 SHOW PROCESSLIST
結果結果結果結果結果結果結果結果結果結果結果
Id User Host db Command Time State Info
4 event_scheduler localhost null Daemon 342510 Waiting on empty queue null
25 root 172.17.0.1:36154 null Sleep 26244 null
102 root 172.17.0.1:39884 test Sleep 1599 null PLUGIN
204 root 172.17.0.1:40090 test Query 0 null PLUGIN: SHOW PROCESSLIST
終了終了終了終了終了終了終了終了終了終了終了
クエリ開始SELECT SLEEP(15)
クエリ開始SHOW PROCESSLIST
クエリ完了 SELECT SLEEP(15)
結果結果結果結果結果結果結果結果結果結果結果
SLEEP(15)
0
終了終了終了終了終了終了終了終了終了終了終了
クエリ完了 SHOW PROCESSLIST
結果結果結果結果結果結果結果結果結果結果結果
Id User Host db Command Time State Info
4 event_scheduler localhost null Daemon 342525 Waiting on empty queue null
25 root 172.17.0.1:36154 null Sleep 26259 null
102 root 172.17.0.1:39884 test Sleep 1614 null PLUGIN
204 root 172.17.0.1:40090 test Query 0 null PLUGIN: SHOW PROCESSLIST
終了終了終了終了終了終了終了終了終了終了終了
com.mysql.cj.protocol.x.AsyncMessageSender#writeAsync
に "[SEND] ===> " + message.getMessage().getClass().getSimpleName() + "\n" + message.getMessage().toString()
というデバッグログを設置して、com.mysql.cj.protocol.x.ResultMessageListener#createFromMessage
に "[RECEIVE] <== " + message.getMessage().getClass().getName() + "\n" + message.getMessage().toString()
というデバッグログを設置すると、通信の様子を垣間見ることができる。
上記のコードの場合の出力は以下のようになる。
[SEND] ===> CapabilitiesGet
[SEND] ===> CapabilitiesSet
capabilities {
capabilities {
name: "tls"
value {
type: SCALAR
scalar {
type: V_BOOL
v_bool: true
}
}
}
}
[SEND] ===> AuthenticateStart
mech_name: "PLAIN"
auth_data: "test\000root\000"
[SEND] ===> StmtExecute
stmt: "select @@mysqlx_max_allowed_packet"
クエリ開始SHOW PROCESSLIST
[SEND] ===> StmtExecute
stmt: "SHOW PROCESSLIST"
クエリ開始SELECT SLEEP(15)
[SEND] ===> StmtExecute
stmt: "SELECT SLEEP(15)"
クエリ開始SHOW PROCESSLIST
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: SINT
name: "Id"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
length: 21
flags: 16
[SEND] ===> StmtExecute
stmt: "SHOW PROCESSLIST"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "User"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 96
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "Host"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 192
flags: 16
[SEND] ===> Close
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "db"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 192
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "Command"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 48
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: SINT
name: "Time"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
length: 7
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "State"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 90
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "Info"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 300
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "\b"
field: "event_scheduler\000"
field: "localhost\000"
field: ""
field: "Daemon\000"
field: "\216\353)"
field: "Waiting on empty queue\000"
field: ""
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "2"
field: "root\000"
field: "172.17.0.1:36154\000"
field: ""
field: "Sleep\000"
field: "\272\235\003"
field: "\000"
field: ""
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "\314\001"
field: "root\000"
field: "172.17.0.1:39884\000"
field: "test\000"
field: "Sleep\000"
field: "\260\034"
field: ""
field: "PLUGIN\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "\234\003"
field: "root\000"
field: "172.17.0.1:40094\000"
field: "test\000"
field: "Query\000"
field: "\000"
field: ""
field: "PLUGIN: SHOW PROCESSLIST\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$FetchDone
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxNotice$Frame
type: 3
scope: LOCAL
payload: "\b\004\022\004\b\002\030\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxSql$StmtExecuteOk
クエリ完了 SHOW PROCESSLIST
結果結果結果結果結果結果結果結果結果結果結果
Id User Host db Command Time State Info
4 event_scheduler localhost null Daemon 342727 Waiting on empty queue null
25 root 172.17.0.1:36154 null Sleep 26461 null
102 root 172.17.0.1:39884 test Sleep 1816 null PLUGIN
206 root 172.17.0.1:40094 test Query 0 null PLUGIN: SHOW PROCESSLIST
終了終了終了終了終了終了終了終了終了終了終了
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: SINT
name: "SLEEP(15)"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
length: 21
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$FetchDone
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxNotice$Frame
type: 3
scope: LOCAL
payload: "\b\004\022\004\b\002\030\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxSql$StmtExecuteOk
クエリ完了 SELECT SLEEP(15)
結果結果結果結果結果結果結果結果結果結果結果
SLEEP(15)
0
終了終了終了終了終了終了終了終了終了終了終了
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: SINT
name: "Id"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
length: 21
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "User"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 96
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "Host"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 192
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "db"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 192
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "Command"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 48
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: SINT
name: "Time"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
length: 7
flags: 16
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "State"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 90
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$ColumnMetaData
type: BYTES
name: "Info"
original_name: ""
table: ""
original_table: ""
schema: ""
catalog: "def"
collation: 33
length: 300
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "\b"
field: "event_scheduler\000"
field: "localhost\000"
field: ""
field: "Daemon\000"
field: "\254\353)"
field: "Waiting on empty queue\000"
field: ""
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "2"
field: "root\000"
field: "172.17.0.1:36154\000"
field: ""
field: "Sleep\000"
field: "\330\235\003"
field: "\000"
field: ""
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "\314\001"
field: "root\000"
field: "172.17.0.1:39884\000"
field: "test\000"
field: "Sleep\000"
field: "\316\034"
field: ""
field: "PLUGIN\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$Row
field: "\234\003"
field: "root\000"
field: "172.17.0.1:40094\000"
field: "test\000"
field: "Query\000"
field: "\000"
field: ""
field: "PLUGIN: SHOW PROCESSLIST\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxResultset$FetchDone
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxNotice$Frame
type: 3
scope: LOCAL
payload: "\b\004\022\004\b\002\030\000"
[RECEIVE] <== com.mysql.cj.x.protobuf.MysqlxSql$StmtExecuteOk
クエリ完了 SHOW PROCESSLIST
結果結果結果結果結果結果結果結果結果結果結果
Id User Host db Command Time State Info
4 event_scheduler localhost null Daemon 342742 Waiting on empty queue null
25 root 172.17.0.1:36154 null Sleep 26476 null
102 root 172.17.0.1:39884 test Sleep 1831 null PLUGIN
206 root 172.17.0.1:40094 test Query 0 null PLUGIN: SHOW PROCESSLIST
終了終了終了終了終了終了終了終了終了終了終了
現在の Java connector/mysqld の実装では、select sleep(15)
などのクエリが発行された場合、その後のクエリの結果が先に帰ってくることはない。これは実際問題、session が状態を持つ以上、そうならざるを得ない。このため、他の状態を持たないプロトコルのクライアントと同じ気分で使っているとハマるかも。
そして、transaction は session に紐づく が、 1session あたり 1 TCP connection 以上 という実装に現時点ではなっている(以上、というのは slave への自動送信などを x devapi 上で 1 セッションとして扱う可能性があるため)。
(通信を多重化することも可能だったと思うが、現在の実装はそうなっていない。なんでだろうか。MySQL Server の実装上の制約?)
https://www.slideshare.net/tokuhirom/plenv-and-perl-build-and-searchcpanorg
perl 5.28.0 をインストールする際には perl-build のバージョンアップが必要ですよ、というアナウンス。
If a domain name resolves to several addresses, all of them will be used in a round-robin fashion. In addition, an address can be specified as a server group.
The parameter value can contain variables. In this case, if an address is specified as a domain name, the name is searched among the described server groups, and, if not found, is determined using a resolver. <<<
As a result, we can use free binary distribution.
(Of course, there's an OpenJDK rpm in CentOS' yum repo. You can use it.)
timedatectl set-timezone America/Chicago
これでOK
https://www.cyberciti.biz/faq/centos-linux-6-7-changing-timezone-command-line/
具体的には amon.64p.org を github pages に移したのだが、その際にハマった。 sphinx はデフォルトで _static/ 以下に静的ファイルを生成するからだ。
https://help.github.com/articles/files-that-start-with-an-underscore-are-missing/ に解決策が載ってるよ、と tmaesaka さんに教えてもらった。
.nojekyll 置いて解決。
依存しているライブラリに脆弱性がある場合、それを検出できると嬉しい。
OWASP dependency-check の gradle プラグインを入れると、簡単に検出が可能となる。
設定は以下のようであり、非常に容易である。
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath('org.owasp:dependency-check-gradle:3.2.1')
}
}
apply plugin: 'org.owasp.dependencycheck'
check.dependsOn dependencyCheckAnalyze
// https://jeremylong.github.io/DependencyCheck/dependency-check-gradle/configuration.html
dependencyCheck {
// Threshold value to fail build
// https://www.first.org/cvss/specification-document
// 0.1-3.9=low 4.0-6.9=medium 7.0-8.9=High 9.0-10.0=Critical
failBuildOnCVSS=0
}
failBuildOnCVSS の値を設定することで、CVSS Score が 7.0 以上、つまり脆弱性としての評価が High 以上のもんのの場合は FAIL する、というようなルールを設定することが可能だ。
例えば、spring boot 1.5.0 に依存している場合は以下のような出力になる。
$ ./gradlew check
:dependencyCheckAnalyze
Verifying dependencies for project demo
Checking for updates and analyzing vulnerabilities for dependencies
Generating report for project demo
Found 24 vulnerabilities in project demo
One or more dependencies were identified with known vulnerabilities:
spring-boot-starter-security-1.5.0.RELEASE.jar (org.springframework.boot:spring-boot-starter-security:1.5.0.RELEASE, cpe:/a:pivotal_software:spring_boot:1.5.0, cpe:/a:pivotal_software:spring_security:1.5.0) : CVE-2017-8046, CVE-2018-1196
spring-boot-starter-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-starter:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-aop-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-aop:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-security-config-4.2.1.RELEASE.jar (org.springframework.security:spring-security-config:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-security-web-4.2.1.RELEASE.jar (org.springframework.security:spring-security-web:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-boot-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-boot-autoconfigure-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-autoconfigure:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-boot-starter-logging-1.5.0.RELEASE.jar (cpe:/a:pivotal_software:spring_boot:1.5.0, org.springframework.boot:spring-boot-starter-logging:1.5.0.RELEASE) : CVE-2017-8046, CVE-2018-1196
spring-core-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-core:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-beans-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, org.springframework:spring-beans:4.3.6.RELEASE, cpe:/a:pivotal:spring_framework:4.3.6) : CVE-2018-1199
spring-security-core-4.2.1.RELEASE.jar (org.springframework.security:spring-security-core:4.2.1.RELEASE, cpe:/a:pivotal_software:spring_security:4.2.1) : CVE-2017-4995, CVE-2018-1199
spring-context-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-context:4.3.6.RELEASE) : CVE-2018-1199
spring-expression-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-expression:4.3.6.RELEASE) : CVE-2018-1199
spring-web-4.3.6.RELEASE.jar (cpe:/a:pivotal_software:spring_framework:4.3.6, cpe:/a:pivotal:spring_framework:4.3.6, org.springframework:spring-web:4.3.6.RELEASE) : CVE-2018-1199
logback-classic-1.1.9.jar (cpe:/a:logback:logback:1.1.9, ch.qos.logback:logback-classic:1.1.9) : CVE-2017-5929
logback-core-1.1.9.jar (cpe:/a:logback:logback:1.1.9, ch.qos.logback:logback-core:1.1.9) : CVE-2017-5929
See the dependency-check report for more details.
:dependencyCheckAnalyze FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':dependencyCheckAnalyze'.
>
Dependency-Analyze Failure:
One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '0.0': CVE-2017-5929, CVE-2017-8046, CVE-2017-4995, CVE-2018-1199, CVE-2018-1196
See the dependency-check report for more details.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 6.561 secs
report は HTML で出力される(JSON, CSV 等の形式も指定可能)。 実際のレポートはこのような感じになる → https://jeremylong.github.io/DependencyCheck/general/SampleReport.html
ant/maven のタスクも用意されている。
まあなんか、そんなにブログに書くこともないというか、うーん。 このブログ、Ruby で書かれているんですが、Ruby で書かれていると、変更しづらくて。。
いずれ Ruby 以外の言語で書き直したいと思ってて時々ちょいちょい書き直してるけど、リリースまでは至らずに消してる。 そんな日々です。
https://medium.com/graalvm/instant-netty-startup-using-graalvm-native-image-generation-ed6f14ff7692 によると、graalvm を利用すると Java Application の起動が圧倒的に高速になるようだ。実際にどの程度高速化されるのかを Hello World Application を手元で実行してみることにより体感する。
$ javac Hello.java
$ java Hello
Hello
$ time java Hello
Hello
java Hello 0.08s user 0.02s system 102% cpu 0.097 total
$ ~/Downloads/graalvm-1.0.0-rc1/Contents/Home/bin/native-image Hello
Build on Server(pid: 55301, port: 26681)*
classlist: 918.63 ms
(cap): 1,817.31 ms
setup: 2,839.95 ms
(typeflow): 3,744.88 ms
(objects): 2,570.67 ms
(features): 43.37 ms
analysis: 6,469.76 ms
universe: 281.22 ms
(parse): 1,153.94 ms
(inline): 1,592.75 ms
(compile): 10,151.29 ms
compile: 13,517.63 ms
image: 2,241.96 ms
write: 1,410.72 ms
[total]: 27,753.54 ms
$ ls -lah
total 5.0M
drwxr-xr-x 5 tokuhirom staff 160 May 23 11:14 ./
drwxr-xr-x 121 tokuhirom staff 3.8K May 23 11:11 ../
-rw-r--r-- 1 tokuhirom staff 401 May 23 11:13 Hello.class
-rw-r--r-- 1 tokuhirom staff 111 May 23 11:13 Hello.java
-rwxr-xr-x 1 tokuhirom staff 5.0M May 23 11:14 hello*
$ time ./hello
Hello
./hello 0.00s user 0.00s system 65% cpu 0.011 total
$ file ./hello
./hello: Mach-O 64-bit executable x86_64
結論からいうと、Hello World レベルのプログラムでも起動が高速化されている。ファイルサイズは 5MB 程度。 この速度ならば、日常的に利用する command line application を Java で記述することも現実的といえる。