JobRunrを試してるんだけどクセが強い

JobRunrという、バッチ処理とかバックグラウンドタスクとかを試してみたんだけど、よくわからない・・・
https://www.jobrunr.io/en/

dependencyにはjobrunrと、あと何かJSONパーサーを入れます。ここではjacksonにしてますが、GSONかyassonでもいいぽい。

<dependency>
    <groupId>org.jobrunr</groupId>
    <artifactId>jobrunr</artifactId>
    <version>6.0.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.0</version>
</dependency>

とりあえず設定。

JobRunr.configure()
        .useStorageProvider(new InMemoryStorageProvider())
        .useBackgroundJobServer()
        .initialize();

Jobを放り込んでみる。

BackgroundJob.enqueue(() -> System.out.println("Hello"));

SLF4Jのログと共に、Helloと表示されました。動いてそう。以降、SLF4Jのログは省略。

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.
Hello

ビルダーがあるで、ってことで使ってみる。

BackgroundJob.create(JobBuilder.aJob()
    .withName("my job")
    .withDetails(() -> System.out.println("Yeah!!!")));

なんか出てる。よさそう。ここまでは・・・

Hello
Yeah!!!

繰り返しタスクを投入してみる。3秒を指定すると5秒以上にしろと怒られたので、5秒にする。

BackgroundJob.scheduleRecurrently(Duration.ofSeconds(5),
        () -> System.out.println("Yooo!!!"));

なんか3つずつ出てくる。

Yooo!!!
Yeah!!!
Yooo!!!
Yooo!!!
Hello
Yooo!!!
Yooo!!!
Yooo!!!

ちょっと時間を同時に表示しようとLocalTime.now()を付け加える。

BackgroundJob.scheduleRecurrently(Duration.ofSeconds(5),
        () -> System.out.println("Yooo!!! %s".formatted(LocalTime.now())));

全部同じ時刻だ・・・どういうこと?

Yooo!!! 12:15:34.239878800
Hello
Yeah!!!
Yooo!!! 12:15:34.239878800
Yooo!!! 12:15:34.239878800
Yooo!!! 12:15:34.239878800
Yooo!!! 12:15:34.239878800
Yooo!!! 12:15:34.239878800

カウンターを付け加えてみる。

int[] count = {0};
BackgroundJob.scheduleRecurrently(Duration.ofSeconds(5),
        () -> System.out.println("Yooo!!!(%d) %s"
                .formatted(count[0]++, LocalTime.now())));

死んだ・・・・

Exception in thread "main" org.jobrunr.JobRunrException: JobRunr encountered a problematic exception. Please create a bug report (if possible, provide the code to reproduce this and the stacktrace)
   at org.jobrunr.JobRunrException.shouldNotHappenException(JobRunrException.java:43)
   at org.jobrunr.jobs.details.instructions.AllJVMInstructions.get(AllJVMInstructions.java:82)
   at org.jobrunr.jobs.details.AbstractJobDetailsFinder$1.visitInsn(AbstractJobDetailsFinder.java:46)
   at org.objectweb.asm.ClassReader.readCode(ClassReader.java:2213)
   at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1514)
   at org.objectweb.asm.ClassReader.accept(ClassReader.java:744)
...

結局、scheduleRecurrentlyメソッドなどでジョブ登録するラムダ式は、直接よびだされるのではなくてシリアライズされたあとで復元されて呼び出されるぽい。ので、ラムダの中で計算をしてはダメで、メソッド呼び出してそっちでいろいろやるべきってことらしい。

ということでメソッドを定義する。このとき、このメソッドはJobRunrの内部から呼び出されるのでpublicである必要がある。

static int count = 0;
public static void some() {
    System.out.println("Yooo![%d] %s".formatted(++count, LocalTime.now()));
}

で呼び出しジョブを登録する。

BackgroundJob.scheduleRecurrently(Duration.ofSeconds(5),
        () -> some());

いい感じ

Yooo![1] 12:41:48.847377500
Yooo![2] 12:41:48.859376600
Yooo![3] 12:41:48.887877700
Yooo![4] 12:42:03.937876600
Yooo![5] 12:42:03.986377300
Yooo![6] 12:42:04.018877300

あと、15秒ごとに呼び出されて3回実行しているので、まあ5秒ごとに3回というのが間に合わなかったという感じっぽい。

最後に、useBackgroundJobServer()を設定しておくと、ダッシュボードが表示できる。

JobRunr.configure()
        .useStorageProvider(new InMemoryStorageProvider())
        .useBackgroundJobServer()
        .useDashboard(8000)
        .initialize();

現実的には、@Jobアノテーションでジョブを指定することが多そうなので問題はない気がするけど、ちょっと遊ぼうとして雑に呼び出すとハマるという話。