CompletableFutureの備忘録

CompletableFutureの使い方をよく忘れるので備忘録。

Memo

staticメソッド

completedFuture

CompletableFuture<U> completedFuture(U value)

CompletableFuture.completedFuture("OK")

supplyAsync

CompletableFuture<U> supplyAsync(Supplier<U> supplier)

CompletableFuture.supplyAsync(() -> "OK")

runAsync

CompletableFuture<Void> runAsync(Runnable runnable)

CompletableFuture.runAsync(() -> System.out.println("OK"))

allOf

CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
戻り値はVoid。

CompletableFuture.allOf(CompletableFuture.runAsync(() -> System.out.println("OK")),
                        CompletableFuture.runAsync(() -> System.out.println("OK")))

anyOf

CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
戻り値を返せる。

CompletableFuture.anyOf(CompletableFuture.completedFuture(1),
                        CompletableFuture.completedFuture(2))
                 .thenAccept(System.out::println);

thenXxxx

thenRun

CompletableFuture<Void> thenRun(Runnable action)
Runnableを実行する。

CompletableFuture.runAsync(() -> System.out.println("OK"))
                 .thenRun(() -> System.out.println("OK"));

thenAccept

CompletableFuture<Void> thenAccept(Consumer<? super T> action)
Consumerを実行する。

CompletableFuture.completedFuture("OK")
                 .thenAccept(System.out::println);

thenApply

CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
Functionを実行する。

CompletableFuture.completedFuture("OK")
                 .thenApply(value -> value + "OK")
                 .thenAccept(System.out::println);

thenCompose

CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
Functionを実行する。thenApplyとの違いは、CompletionStageを返す。

CompletableFuture.completedFuture("OK")
                 .thenCompose(value -> CompletableFuture.completedFuture(value + "OK"))
                 .thenAccept(System.out::println);

thenCombine

CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
2つの実行結果をBiFunctionで受け取る。

CompletableFuture.completedFuture("OK")
                 .thenCombine(CompletableFuture.completedFuture("OK"),
                              (value1, value2) -> value1 + value2)
                 .thenAccept(System.out::println);

xxxxEither

runAfterEither

CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
どちらかが実行された後で、Runnableを実行する。

CompletableFuture.runAsync(() -> System.out.println(1))
                 .runAfterEither(CompletableFuture.runAsync(() -> System.out.println(2)),
                                 () -> System.out.println("OK"));

acceptEither

CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
どちらかの実行結果をConsumerで受け取る。

CompletableFuture.completedFuture(1)
                 .acceptEither(CompletableFuture.completedFuture(2),
                               System.out::println);

applyToEither

CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
どちらかの実行結果をFunctionで受け取る。

CompletableFuture.completedFuture(1)
                 .applyToEither(CompletableFuture.completedFuture(2),
                                value -> value)
                 .thenAccept(System.out::println);

xxxxBoth

runAfterBoth

CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action) 両方が実行された後で、Runnableを実行する。

CompletableFuture.runAsync(() -> System.out.println(1))
                 .runAfterBoth(CompletableFuture.runAsync(() -> System.out.println(2)),
                               () -> System.out.println("OK"));

thenAcceptBoth

CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
両方の実行結果をBiConsumerで受け取る。

CompletableFuture.completedFuture(1)
                 .thenAcceptBoth(CompletableFuture.completedFuture(2),
                                 (value1, value2) -> System.out.println(value1 + value2));

例外ハンドリング

exceptionally

CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
例外発生時にのみ呼ばれて、値を返せる。

CompletableFuture.failedFuture(new RuntimeException())
                 .exceptionally(throwable -> "Error")
                 .thenAccept(System.out::println)

handle

CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
例外発生時に値を返せる。Tを受け取ってUを返すので、別の型を返すこともできる。

CompletableFuture.failedFuture(new RuntimeException())
                 .handle((value, throwable) -> {
                     if (throwable == null) {
                         return value;
                     } else {
                         return "Error";
                     }
                 })
                 .thenAccept(System.out::println);

whenComplete

CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
例外発生時の処理を追加できるが、BiConsumerなので値は返せない。

CompletableFuture.failedFuture(new RuntimeException())
                 .whenComplete((value, throwable) -> {
                     if (throwable == null) {
                         System.out.println(value);
                     } else {
                         System.err.println("Error");
                     }
                 });

参考

WiresharkでHTTPSを復号化する

Wiresharkhttps通信を復号化するときの備忘録。
今回はMacOSChromelocalhostのnginxでhttpsの通信をする。

準備

証明書

mkcertlocalhostの証明書と秘密鍵を用意する。コマンドを実行するとlocalhost.pemlocalhost-key.pemが作成されるので、これをnginxに設定する。

% brew install mkcert

% mkcert -install

% mkcert localhost

NGINX

localhost.conf

server {
    listen 443 ssl;
    server_name localhost;
    root /usr/share/nginx/html;

    ssl_certificate localhost.pem;
    ssl_certificate_key localhost-key.pem;
}

compose.yaml

services:
  nginx:
    image: nginx:latest
    ports:
      - "443:443"
    volumes:
      - ./localhost.conf:/etc/nginx/conf.d/localhost.conf
      - ./localhost-key.pem:/etc/nginx/localhost-key.pem
      - ./localhost.pem:/etc/nginx/localhost.pem

Dockerでnginxを起動する。

% docker compose up

Google Chrome

環境変数SSLKEYLOGFILEコマンドライン引数の--ssl-key-log-fileSSL session keyのログファイルを指定して、コマンドラインからChromeを起動する。今回はコマンドライン引数でsslkey.logファイルを指定してChromeを起動する。

% /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ssl-key-log-file=./sslkey.log

Wireshark

Preferences -> Protocols -> TLS から「(Pre)-Master-Secret log filename」にsslkey.logファイルを設定後、Wiresharkを再起動する。

Demo

コマンドラインから起動したChromehttps://localhost にアクセスすると、復号化されたHTTPSのパケットが確認できる。

参考

PrometheusのHTTP Service Discoveryを試す

PrometheusのHTTP Service Discoveryを試したときのメモ。

準備

Prometheus

DockerのPrometheusとNode exporter2台を使う。

prometheus.yml

global:
  scrape_interval: 30s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['host.docker.internal:9090']
  - job_name: http-sd
    http_sd_configs:
      - url: 'http://host.docker.internal:8080/http-sd'

compose.yml

services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  node-exporter1:
    image: quay.io/prometheus/node-exporter:latest
    ports:
      - "9101:9100"
  node-exporter2:
    image: quay.io/prometheus/node-exporter:latest
    ports:
      - "9102:9100"

PromethusとNod exporterを起動する。

% docker compose up

HTTP SD endpoint

HTTP SD endpointはspring bootで実装する。

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.2'
    id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'io.micrometer:micrometer-registry-prometheus'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

application.yml

management:
  endpoints:
    web:
      exposure:
        include: prometheus

HttpSdController.java

ドキュメントに記載されているフォーマットで、Node exporter2台とこのspring bootアプリの情報を返す。

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HttpSdController {
    private static final Logger logger = LoggerFactory.getLogger(HttpSdController.class);
    public record HttpSd(List<String> targets, Map<String, String> labels) {}

    @GetMapping("/http-sd")
    public List<HttpSd> httpSd(@RequestHeader("X-Prometheus-Refresh-Interval-Seconds") String refreshInterval) {
        logger.info("Refresh-Interval-Seconds: {}", refreshInterval);
        return List.of(new HttpSd(List.of("host.docker.internal:9101",
                                          "host.docker.internal:9102"),
                                  Map.of("job", "node-exporter")),
                       new HttpSd(List.of("host.docker.internal:8080"),
                                  Map.of("job", "spring-boot",
                                         "__metrics_path__", "/actuator/prometheus")));
    }
}

具体的にはこんなJSONを返す。

[
  {
    "targets": [
      "host.docker.internal:9101",
      "host.docker.internal:9102"
    ],
    "labels": {
      "job": "node-exporter"
    }
  },
  {
    "targets": [
      "host.docker.internal:8080"
    ],
    "labels": {
      "job": "spring-boot",
      "__metrics_path__": "/actuator/prometheus"
    }
  }
]

動作確認

spring bootアプリを起動後、http://localhost:9090/targets にアクセスすると、SD endpointのtargetsがprometheusに反映されていることが確認できた。

http_sd_config.refresh_intervalのデフォルトは1mなので、1分ごとにprometheusがSD endpointにアクセスしていることも確認できた。

参考

MongoDBのクエリの備忘録 (Element)

準備

使用するMongoDBはこちらと同じ。
MongoDB Shellで接続して、マニュアルと同じテストデータを挿入する。

test> db.inventory.insertMany([
...  { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
...  { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
...  { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
...  { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
...  { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
...  ]);
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId("63945c181ce8aac5f4f6e632"),
    '1': ObjectId("63945c181ce8aac5f4f6e633"),
    '2': ObjectId("63945c181ce8aac5f4f6e634"),
    '3': ObjectId("63945c181ce8aac5f4f6e635"),
    '4': ObjectId("63945c181ce8aac5f4f6e636")
  }
}

テストデータを変更して、"notebook"のqty fieldを削除、"paper"のqtyにnullを設定する。

test> db.inventory.updateOne({item: "notebook"}, {$unset: {qty: ""}})
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}

test> db.inventory.updateOne({item: "paper"}, {$set: {qty: undefined}})
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}

Element Query Operators

exists

qty fieldがあるdocumentを取得
valueがnullのdocumentも含む)

test> db.inventory.find({qty: {$exists: true}})
[
  {
    _id: ObjectId("63945c181ce8aac5f4f6e632"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("63945c181ce8aac5f4f6e634"),
    item: 'paper',
    qty: null,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("63945c181ce8aac5f4f6e635"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  },
  {
    _id: ObjectId("63945c181ce8aac5f4f6e636"),
    item: 'postcard',
    qty: 45,
    tags: [ 'blue' ],
    dim_cm: [ 10, 15.25 ]
  }
]

qty fieldがないdocumentを取得

test> db.inventory.find({qty: {$exists: false}})
[
  {
    _id: ObjectId("63945c181ce8aac5f4f6e633"),
    item: 'notebook',
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  }
]

type

qtyvalueがnumberのdocumentを取得

test> db.inventory.find({qty: {$type: "number"}})
[
  {
    _id: ObjectId("63945c181ce8aac5f4f6e632"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("63945c181ce8aac5f4f6e635"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  },
  {
    _id: ObjectId("63945c181ce8aac5f4f6e636"),
    item: 'postcard',
    qty: 45,
    tags: [ 'blue' ],
    dim_cm: [ 10, 15.25 ]
  }
]

qtyvalueがnullのdocumentを取得

test> db.inventory.find({qty: {$type: "null"}})
[
  {
    _id: ObjectId("63945c181ce8aac5f4f6e634"),
    item: 'paper',
    qty: null,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  }
]

参考

MongoDBのクエリの備忘録 (Array)

準備

使用するMongoDBはこちらと同じ。
MongoDB Shellで接続して、マニュアルと同じテストデータを挿入する。

test> db.inventory.insertMany([
... { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
... { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
... { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
... { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
... { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
... ]);
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId("634137309de97ee89b8130a8"),
    '1': ObjectId("634137309de97ee89b8130a9"),
    '2': ObjectId("634137309de97ee89b8130aa"),
    '3': ObjectId("634137309de97ee89b8130ab"),
    '4': ObjectId("634137309de97ee89b8130ac")
  }
}

Array Query Operators

all

指定した全ての要素を含むarrayを取得
redblankを含むtags

test> db.inventory.find( { tags: { $all: ["red", "blank"] } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130a8"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130a9"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130aa"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130ab"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

このクエリと同じ

test> db.inventory.find( { $and: [ { tags: "red" }, { tags: "blank" } ] } )
[
  {
    _id: ObjectId("634137309de97ee89b8130a8"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130a9"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130aa"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130ab"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

これらのクエリは完全一致

test> db.inventory.find( { tags: { $all: [["red", "blank"]] } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130a9"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  }
]
test> db.inventory.find( { $and: [ { tags: ["red", "blank"] } ] } )
[
  {
    _id: ObjectId("634137309de97ee89b8130a9"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  }
]
test> db.inventory.find( { tags: ["red", "blank"] } )
[
  {
    _id: ObjectId("634137309de97ee89b8130a9"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  }
]

elemMatch

指定した全てのクエリに一致する要素を少なくとも1つは含んでいるarrayを取得
dim_cmvalue > 15 and value < 20 は15.25のみ

test> db.inventory.find( { dim_cm: { $elemMatch: { $gt: 15, $lt: 20 } } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130ac"),
    item: 'postcard',
    qty: 45,
    tags: [ 'blue' ],
    dim_cm: [ 10, 15.25 ]
  }
]

これは、arrayのいずれかの要素が指定したクエリのいずれかに一致する場合
dim_cm: [ 22.85, 30 ] のみ20未満の要素がないので該当しない

test> db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130a8"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130a9"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130aa"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130ac"),
    item: 'postcard',
    qty: 45,
    tags: [ 'blue' ],
    dim_cm: [ 10, 15.25 ]
  }
]

size

指定したサイズのarrayを取得

test> db.inventory.find( { tags: { $size: 1 } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130ac"),
    item: 'postcard',
    qty: 45,
    tags: [ 'blue' ],
    dim_cm: [ 10, 15.25 ]
  }
]

test> db.inventory.find({ tags: { $size: 2 } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130a8"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130a9"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("634137309de97ee89b8130ab"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

test> db.inventory.find({ tags: { $size: 3 } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130aa"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  }
]

Array Index Position

クエリの条件にarrayのindexを指定する場合

test> db.inventory.find( { "dim_cm.0": { $gt: 15 } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130ab"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

test> db.inventory.find( { "dim_cm.1": { $lt: 20 } } )
[
  {
    _id: ObjectId("634137309de97ee89b8130ac"),
    item: 'postcard',
    qty: 45,
    tags: [ 'blue' ],
    dim_cm: [ 10, 15.25 ]
  }
]

参考

jfrコマンドの備忘録

Memo

準備

JFRファイルのサイズを大きくしたいので、大量のJFRイベントを書き込むデモアプリを用意する。

Main.java

import java.time.LocalDateTime;
import jdk.jfr.Category;
import jdk.jfr.Description;
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.Name;

public class Main {
    @Name("hirakida.Hello")
    @Label("Hello")
    static class HelloEvent extends Event {
        @Label("Message")
        String message;
        @Label("DateTime")
        LocalDateTime dateTime;
    }

    public static void main(String[] args) {
        while (true) {
            HelloEvent event = new HelloEvent();
            event.message = "Hello!";
            event.dateTime = LocalDateTime.now();
            event.begin();
            event.commit();
        }
    }
}

JFRを有効にして、デモアプリを実行する。

% sdk use java 17.0.4.1-tem

% java -XX:StartFlightRecording:filename=recording.jfr Main.java
[1.240s][info][jfr,startup] Started recording 1. No limit specified, using maxsize=250MB as default.
[1.240s][info][jfr,startup] 
[1.240s][info][jfr,startup] Use jcmd 6058 JFR.dump name=1 to copy recording data to file.

コマンド

print

flight recordingの内容を表示する。

% jfr print --events hirakida.Hello recording.jfr
hirakida.Hello {
  startTime = 14:53:33.505
  duration = 0.000011 ms
  message = "Hello!"
  eventThread = "main" (javaThreadId = 1)
  stackTrace = [
    Main.main(String[]) line: 29
    jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Method, Object, Object[])
    jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Object, Object[]) line: 77
    jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
    java.lang.reflect.Method.invoke(Object, Object[]) line: 568
    ...
  ]
}

...

JSON形式

% jfr print --json --events hirakida.Hello recording.jfr
{
  "recording": {
    "events": [{
      "type": "hirakida.Hello", 
      "values": {
        "startTime": "2022-09-17T14:53:33.505442889+09:00", 
        "duration": "PT0.000000011S", 
        "eventThread": {
          "osName": "main", 
          "osThreadId": 6659, 
          "javaName": "main", 
          "javaThreadId": 1, 
          "group": {
            "parent": {
              "parent": null, 
              "name": "system"
            }, 
            "name": "main"
          }
        }, 
...

metadata

イベントの情報を表示する。

% jfr metadata recording.jfr
class boolean {
}

class byte {
}

class char {
}

class double {
}

class float {
}

class int {
}

class long {
}

class short {
}

@Name("java.lang.Class")
@Label("Java Class")
class Class {
  @Label("Class Loader")
  ClassLoader classLoader;

  @Label("Name")
  String name;

  @Label("Package")
  Package package;

  @Label("Access Modifiers")
  int modifiers;

  @Label("Hidden")
  boolean hidden;
}

...

disassemble

JFRファイルを分割する。

% mkdir out

% jfr disassemble --output out recording.jfr

Examining recording /Users/hirakida/work/recording.jfr ...

File consists of 19 chunks. The recording will be split into 4 files

Writing /Users/hirakida/work/out/recording_0.jfr ... 66784534
Writing /Users/hirakida/work/out/recording_1.jfr ... 66796485
Writing /Users/hirakida/work/out/recording_2.jfr ... 66693161
Writing /Users/hirakida/work/out/recording_3.jfr ... 50546118

assemble

JFRファイルを統合する。

% jfr assemble out assemble.jfr

Assembling files... 

 /Users/hirakida/work/out/recording_0.jfr
 /Users/hirakida/work/out/recording_1.jfr
 /Users/hirakida/work/out/recording_2.jfr
 /Users/hirakida/work/out/recording_3.jfr

Finished.

summary

サマリーを表示する。

% jfr summary recording.jfr

 Version: 2.1
 Chunks: 19
 Start: 2022-09-17 05:53:33 (UTC)
 Duration: 19 s

 Event Type                                   Count  Size (bytes) 
==================================================================
 hirakida.Hello                            11686763     245434096
 jdk.BooleanFlag                               9975        304950
 jdk.NativeLibrary                             9728        858382
 jdk.ModuleExport                              9063         96786
 jdk.GCPhaseParallel                           6668        177786
 jdk.ActiveSetting                             6308        194047
 jdk.SystemProcess                             4645        373916
 jdk.LongFlag                                  3762        125020
 jdk.ModuleRequire                             2869         28690
 jdk.UnsignedLongFlag                          2774         94696
 jdk.ObjectAllocationSample                    2428         38397
 ...

参考

MongoDBのクエリの備忘録 (Comparison, Logical)

準備

Docker ComposeでMongoDBを起動する。

% docker compose up -d

compose.yml

services:
  mongo:
    image: mongo:6.0
    restart: always
    ports:
      - 27017:27017
    environment:
      MONGO_INITDB_ROOT_USERNAME: user1
      MONGO_INITDB_ROOT_PASSWORD: xxxx

MongoDB Shellで接続する。

% mongosh "mongodb://localhost:27017" --username user1
Enter password: ****
Current Mongosh Log ID: 633acb1ef03fa39a9f8e3ec1
Connecting to:      mongodb://<credentials>@localhost:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.6.0
Using MongoDB:      6.0.2
Using Mongosh:      1.6.0

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

test>

テストデータを挿入する。

test> db.product.insertMany([
... {name: "product1", price: 100},
... {name: "product2", price: 200},
... {name: "product3", price: 300},
... {name: "product4", price: 400},
... {name: "product5", price: 500}
... ])
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId("633acbf5f03fa39a9f8e3ec2"),
    '1': ObjectId("633acbf5f03fa39a9f8e3ec3"),
    '2': ObjectId("633acbf5f03fa39a9f8e3ec4"),
    '3': ObjectId("633acbf5f03fa39a9f8e3ec5"),
    '4': ObjectId("633acbf5f03fa39a9f8e3ec6")
  }
}

Comparison Query Operators

eq (equal)

price = 300

test> db.product.find({price: {$eq: 300}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec4"),
    name: 'product3',
    price: 300
  }
]

ne (not equal)

price <> 300

test> db.product.find({price: {$ne: 300}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec2"),
    name: 'product1',
    price: 100
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec3"),
    name: 'product2',
    price: 200
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec5"),
    name: 'product4',
    price: 400
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec6"),
    name: 'product5',
    price: 500
  }
]

gt (greater than)

price > 300

test> db.product.find({price: {$gt: 300}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec5"),
    name: 'product4',
    price: 400
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec6"),
    name: 'product5',
    price: 500
  }
]

gte (greater than or equal)

price >= 300

test>  db.product.find({price: {$gte: 300}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec4"),
    name: 'product3',
    price: 300
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec5"),
    name: 'product4',
    price: 400
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec6"),
    name: 'product5',
    price: 500
  }
]

lt (less than)

price < 300

test> db.product.find({price: {$lt: 300}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec2"),
    name: 'product1',
    price: 100
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec3"),
    name: 'product2',
    price: 200
  }
]

lte (less than or equal)

price <= 300

test> db.product.find({price: {$lte: 300}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec2"),
    name: 'product1',
    price: 100
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec3"),
    name: 'product2',
    price: 200
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec4"),
    name: 'product3',
    price: 300
  }
]

in

price in (200, 300)

test> db.product.find({price: {$in: [200,300]}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec3"),
    name: 'product2',
    price: 200
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec4"),
    name: 'product3',
    price: 300
  }
]

nin

price not in (200, 300)

test> db.product.find({price: {$nin: [200,300]}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec2"),
    name: 'product1',
    price: 100
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec5"),
    name: 'product4',
    price: 400
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec6"),
    name: 'product5',
    price: 500
  }
]

Logical Query Operators

and

price >= 300 and price <= 400

test> db.product.find({$and: [{price: {$gte: 300}}, {price: {$lte: 400}}]})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec4"),
    name: 'product3',
    price: 300
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec5"),
    name: 'product4',
    price: 400
  }
]

or

price = 300 or price = 400

test> db.product.find({$or: [{price: {$eq: 300}}, {price: {$eq: 400}}]})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec4"),
    name: 'product3',
    price: 300
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec5"),
    name: 'product4',
    price: 400
  }
]

nor

not (price = 300 or price = 400)

test> db.product.find({$nor: [{price: {$eq: 300}}, {price: {$eq: 400}}]})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec2"),
    name: 'product1',
    price: 100
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec3"),
    name: 'product2',
    price: 200
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec6"),
    name: 'product5',
    price: 500
  }
]

not

not price >= 300

test> db.product.find({price: {$not: {$gte: 300}}})
[
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec2"),
    name: 'product1',
    price: 100
  },
  {
    _id: ObjectId("633acbf5f03fa39a9f8e3ec3"),
    name: 'product2',
    price: 200
  }
]

参考