JavaのQuartzでJobを更新するのが厄介な件

f:id:ts0818:20220211161949j:plain

nazology.net

チームは2018年から、アスリビス遺跡にある大神殿の西側で発掘を進めていたところ、大量のオストラコン(陶器のかけら)を掘り当てました。

古代エジプト民が残した「メモ帳」を大量発見! 書かれていた内容とは? - ナゾロジー 

神殿はクレオパトラの父であるプトレマイオス12世(BC117〜51年)により建造されたもので、これらのオストラコンも同時代に記録されたと考えられます。

古代エジプト民が残した「メモ帳」を大量発見! 書かれていた内容とは? - ナゾロジー 

発見されたオストラコンは壺やその他の陶器の破片を使用しており、文字の約80%はデモティック(民衆文字)でした。

古代エジプト民が残した「メモ帳」を大量発見! 書かれていた内容とは? - ナゾロジー 

調査の結果、メモは書記や商人、学生による記録と判明しました。

古代エジプト民が残した「メモ帳」を大量発見! 書かれていた内容とは? - ナゾロジー 

さらに、オストラコンの裏表に同じ文字が何度も繰り返して書かれたものが100以上も発見されました。

古代エジプト民が残した「メモ帳」を大量発見! 書かれていた内容とは? - ナゾロジー 

発掘主任のクリスチャン・ライツ(Christian Leitz)氏は、これについて「教師が罰則として学生に書かせたもの」と指摘します。

古代エジプト民が残した「メモ帳」を大量発見! 書かれていた内容とは? - ナゾロジー 

⇧ 時代が変わっても変わらないもの...

 というわけで、Javaについてです。

レッツトライ~。

JavaQuartzでJobを更新するのが厄介な件

JavaQuartzというライブラリで、バッチ処理ができるということを知ったのですが、Quartzの仕組みとしては、

www.javarticles.com

⇧ 上図のように、

  • Job
  • Trigger
  • Job Store

の三位一体で、バッチ処理インスタンスを作成して、Thread Poolにスレッドとして登録しておいて、Triggerで指定しておいた日時が来たら実行されるような構造になってますと。

で、バッチ処理インスタンスを作成するのが、Quartz Schedulerというもので、

flylib.com

When the factory creates the Scheduler instance, it also passes the Scheduler an instance of an org.quartz.core.QuartzSchedulerResources. 

The QuartzSchedulerResources contains, among others things, a ThreadPool instance that provides a pool of worker threads that are responsible for executing jobs. In Quartz, a ThreadPool is represented by the org.quartz.spi.ThreadPool interface (because Quartz was invented before JDK 1.5, it needed to create its own ThreadPool classto ensure backward compatibility, Quartz still utilizes its THReadPool instead of Java's) and includes a concrete implementation called org.quartz.simpl.SimpleThreadPool.

 The SimpleThreadPool has a fixed number of worker threads and doesn't shrink or grow based on load. Figure 4.6 shows the sequence of steps that the framework undergoes during startup as it relates to threads.

https://flylib.com/books/en/2.65.1/thread_usage_in_quartz.html

⇧ このあたりのインスタンスは、QuartzというライブラリがJVMJava Virtual Machine)のクラスローダーとかで読み込まれた時に生成されるということかと。

Worker Threadsっていうものは、

What Are Quartz Worker Threads?

Quartz doesn't process your jobs on the main thread. If it did, it would severely reduce the scalability of the application.

Instead, Quartz delegates the thread-management responsibilities to a separate component.

For the generic Quartz setup, which most users employ, the SimpleThreadPool class handles thread management.

The SimpleThreadPool creates a number of WorkerThread instances that are responsible for processing a job on a separate thread.

The WorkerThread is an inner class defined within the SimpleThreadPool class and is essentially a thread.

The number of WorkerThreads that are created and the priority for these threads are specified by the configuration settings in the quartz.properties file or are passed into the factory.

https://flylib.com/books/en/2.65.1/thread_usage_in_quartz.html

⇧ アプリケーションmainスレッド上のプロセスとして、バッチ処理をすることはできないけど別スレッドで管理します、という仕組みの結果、生成されたスレッド群ということらしい。

で、Worker Threadsは、Thread Poolにプールされていくバッチ処理群のことになると思うのだけど、

When the QuartzSchedulerThread asks the ThreadPool to run a JobRunShell instance, the ThreadPool checks to see if a worker thread is available. If all the configured worker threads are busy, the ThreadPool waits until one becomes available.

When a worker thread is available and a JobRunShell is waiting to be executed, the worker thread calls the run() method on the JobRunShell class.

https://flylib.com/books/en/2.65.1/thread_usage_in_quartz.html

⇧ Thread PoolがWorker Threadを監視をしていて、Worker Threadが実行可能な状態になったタイミング(Triggerで指定した日時など)で、QuartzSchedulerThreadに通知して、QuartzSchedulerThreadがWorker Threadに実行のゴーサインを出し、Worker ThreadはJobRunShellのrunメソッドを呼び出すことで、バッチ処理が実行されるってことになりますと。

ちなみに、Javaのプログラミングというのは、mainスレッドの1スレッドで動いてるのが基本っぽいのですが、

Quartzというライブラリのように、mainスレッドとは別にスレッド管理します、のような仕組みを導入すると、

⇧ 上記のように、mainスレッドは別にスレッドが増えることになり、「マルチスレッド」のような状況が発生するということですかね。

Quartzの全体的な流れとしては、

⇧上図のような感じになりますと。

長々と脱線しましたが、Quartzのドキュメントを確認すると、

www.quartz-scheduler.org

Job Persistence

  • The design of Quartz includes a JobStore interface that can be implemented to provide various mechanisms for the storage of jobs.

  • With the use of the included JDBCJobStore, all Jobs and Triggers configured as "non-volatile" are stored in a relational database via JDBC.

  • With the use of the included RAMJobStore, all Jobs and Triggers are stored in RAM and therefore do not persist between program executions - but this has the advantage of not requiring an external database.

http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/introduction.html

⇧ Jobを永続化したい場合に、

  • JDBCJobStore
    JDBCJava Database Connectivity)に対応したデータベースに保存
  • RAMJobStore
    RAM(Random Access Memory)に保存

の2つの選択肢を用意してくれていますと。

今回は、「JDBCJava Database Conectivity)」に対応したデータベースのケースで考えていくのですが、その前に、Quartzが用意してくれてるテーブルについては、

github.com

⇧ 上記のGitHubで確認ができるのですが、PostgreSQLの場合だと、

f:id:ts0818:20220211152130p:plain

⇧ のようなテーブルを作成して、Jobなどに関するデータが保存されることになるのですが、ここでJobを更新したいとなった時に問題になるのが、

stackoverflow.com

stackoverflow.com

⇧ 上記サイト様のように、Quartzでメソッドを用意はしてくれているのですが、更新したいJobを確かめるための検索っぽいことはできない模様。

検索とかしたい場合は、

Quartzで用意されてるテーブルから引っ張てこれるような仕組みを自分で作る必要があるみたいね。

話が脱線しましたが、バッチ処理とかって、テーブルのレコードに対する集計や一括更新など定期的なタイミング(毎日深夜0:00など決まった時間)で処理をするようなケースで利用されることが多いとは思いますと。

一方で、テーブルのレコードに対する逐次処理など不定期なタイミング(メルマガの発信など、配信時間の変更が頻繁に発生しうるような状況)でも使われるケースはあり、その場合は、Jobの数が膨大になってくると思うので、Jobを特定できるようなカラムをテーブルに追加して保持しておいたほうが良さ気ですね。

今回はこのへんで。