kagamihogeの日記

kagamihogeの日記です。

Spring Batch 4.1.x - Reference Documentation - The Domain Language of Batchのテキトー翻訳

https://docs.spring.io/spring-batch/4.1.x/reference/html/domain.html#domainLanguageOfBatch

https://qiita.com/kagamihoge/items/12fbbc2eac5b8a5ac1e0 俺の訳一覧リスト

1. The Domain Language of Batch

何らかの経験のあるバッチアーキテクトにとって、Spring Batchのバッチ処理のコンセプトはお馴染みのものです。"Jobs"、"Steps"、開発者が用意する処理単位のItemReaderItemWriterがあります。ただ、Springのパターン, operations, テンプレート、コールバック、イディオムを使えるので、以下も可能です。

  • 関心事の分離による品質向上。
  • インターフェースによるサービスとレイヤーの明確化。
  • シンプルなデフォルト実装により、そのままの設定ですぐ動かせる。
  • 拡張性の大幅な機能強化。

以下の図はここ数十年使われ続けてきたバッチのリファレンスアーキテクチャを単純化したものです。バッチ処理ドメイン言語を構成するコンポーネントの概略を示しています。このアーキテクチャフレームワークの設計はここ数世代の複数プラットフォーム(COBOL/Mainframe, C/Unix, and now Java/anywhere)の実装を通して実証されてきました。JCLとCOBOLの開発者は、C, C#, Java開発者と同様に、このコンセプトに慣れていると思われます。Spring Batchは、広範なバッチアプリケーション開発に使われるメンテナンス性がありロバストなシステムで、広く見られるサービス・コンポーネント・レイヤの実装を提供します。また、非常に複雑な要求を扱うための拡張とインフラも備えています。

Figure 1. Batch Stereotypes

上図はSpring Batchのドメイン言語を構成する中核コンセプトを表しています。Jobは一つか複数のstepを持ち、各stepはItemReader, ItemProcessor, ItemWriterを一つ持ちます。jobは(JobLauncher)で実行し、現在実行中のプロセスのメタデータは(JobRepository)に保存されます。

1.1. Job

このセクションではbatch jobのコンセプトに関する解説をします。Jobバッチ処理全体をカプセル化するエンティティです。他のSpringプロジェクト同様に、JobXMLJavaのどちらかの設定でワイヤリングします。この設定は"job configuration"と呼ばれる事が多いです。なお、以下の図で示すように、Jobは階層全体のトップに居ます。

Figure 2. Job Hierarchy

Spring Batchでは、JobStepインスタンスのコンテナです。jobは複数のstepを持ち、これらはフロー内の論理的なまとまりを構成し、リスタートなど全てのstepにグローバルなプロパティ設定があります。job設定は以下があります。

  • job名。
  • Stepインスタンスの定義と順序。
  • jobがリスタート可能かどうか。

Spring BatchのデフォルトJob実装はSimpleJobクラスで、Jobにいくつかの標準機能を追加しています。Java設定の場合、Jobインスタンス化に利用可能なビルダーが複数あり、以下はその例です。

@Bean
public Job footballJob() {
    return this.jobBuilderFactory.get("footballJob")
                     .start(playerLoad())
                     .next(gameLoad())
                     .next(playerSummarization())
                     .end()
                     .build();
}

1.1.1. JobInstance

JobInstanceはjob実行に相当します。いま、一日の終わりに一度だけ実行する、前述の図の'EndOfDay' Job、などのバッチjobを考えます。jobは'EndOfDay'の1つですが、Jobの個々の実行はそれぞれ別個に扱う必要があります。このjobの場合、1日1つのJobInstanceになります。例えば、1/1の実行、1/2の実行、になりま1す。1/1の実行が初回は失敗したが翌日再実行する場合、1/1の実行を再度走らせます(通常、バッチ実行は処理するデータと対応関係があるので、1/1の実行は1/1のデータを処理します)。よって、JobInstanceは複数の実行を持ちうる(JobExecutionはこの賞の後半で解説します)事になり、特定のJobに対応するJobInstanceは一つだけで、JobParametersである特定時点のJobInstanceを実行します。

JobInstance定義はロードされるデータとは完全に無関係です。データのロード方法はItemReaderの実装次第です。たとえば、EndOfDayのケースだと、 'effective date'や'schedule date'を指すデータ列が考えらます。よって、1/1の実行は1/1のデータのみロードし、1/2は1/2のデータのみロードします。これは仕様次第で、ItemReaderで実装します。ただし、同一JobInstanceを使用すると、以前の実行の'state'(ExecutionContextの事でこの章の後半で解説)かどうかが決まります。新規のJobInstanceは'最初から開始'を意味し、既存インスタンスは基本的には'抜けた場所から開始'を意味します。

1.1.2. JobParameters

JobInstanceとそのJobとの違いを解説したので、"JobInstanceの区別方法は?"という疑問が自然に浮かぶと思われます。答えはJobParametersです。JobParametersオブジェクトはバッチjobに使うパラメータを保持します。識別に使われる他、以下の図のように、実行中の参照データとしても使われます。

Figure 3. Job Parameters

先の例では、二つのインスタンスがあり、一つは1/1でもう一つが1/2、Job1つだけですが、JobParameterオブジェクトは2つあります。前者はパラメータ01-01-2017で開始しており、後者は01-02-2017で開始しています。よって、定義としてはJobInstance = Job + JobParametersになります。これにより、開発者はJobInstanceの定義を、渡すパラメータによって、制御します。

JobInstanceの識別にすべてのjobパラメータが必要なわけではありません。By default, they do so. ただし、このフレームワークでは、JobInstanceの識別に使用しないパラメータでJobのサブミットが可能です。

1.1.3. JobExecution

JobExecutionは1回のjob実行に相当します。実行は失敗か成功で終了しますが、ある実行が正常終了しない限り、対応するJobInstanceは完了したとは見なされません。例としてEndOfDayのJobで解説すると、01-01-2017のJobInstanceが初回実行時に失敗した場合を考えます。初回実行(01-01-2017)と同一のjob parametersで再度実行すると、JobExecutionを新規に作成します。ただし、JobInstanceは1つのままです。

Jobはjobの中身と実行方法を定義し、JobInstanceは実行をグループ化するオブジェクトで、これは主に正しくリスタートするためです。一方で、JobExecutionは、実行中に発生した事を格納する役割があり、制御と永続化されるプロパティがあります。

Table 1. JobExecution Properties

Property Definition
Status 実行ステータスを示すBatchStatus。実行時、BatchStatus#STARTED。失敗、BatchStatus#FAILED。正常終了、BatchStatus#COMPLETED
startTime 実行開始時の現在システム時刻のjava.util.Date 。jobがまだ開始していない場合は空。
endTime 実行終了時の現在システム時刻のjava.util.Date。成功かどうかは無関係。jobがまだ終了していない場合は空。
exitStatus 実行結果を示すExitStatus。呼び出し元に返される終了コードなので、最も重要。詳細は5章参照。jobがまだ終了していない場合は空。
createTime JobExecutionを最初に永続化した時点の現在システム時刻のjava.util.Date 。jobが未開始の場合がある(その場合startTimeも空)が、jobレベルのExecutionContextsを管理する上でフレームワークはcreateTimeを必要とします。
lastUpdated JobExecutionを最後の永続化した時間のjava.util.Date。jobが未開始の場合は空。
executionContext 複数のexecution間で永続化の必要があるユーザデータを持つ、プロパティの入れ物。
failureExceptions Job実行中に発生した例外リスト。Jobの失敗時に複数の例外が発生した場合に役立ちます。

これらプロパティは永続化され、実行ステータスの決定に使われる点が重要です。例えば、1/1のEndOfDay jobが9:00 PMに実行して9:30に失敗した場合、以下のエントリがメタデータテーブルに作られます。

Table 2. BATCH_JOB_INSTANCE

JOB_INST_ID JOB_NAME
1 EndOfDayJob

Table 3. BATCH_JOB_EXECUTION_PARAMS

JOB_EXECUTION_ID TYPE_CD KEY_NAME DATE_VAL IDENTIFYING
1 DATE schedule.Date 2017-01-01 TRUE

Table 4. BATCH_JOB_EXECUTION

JOB_EXEC_ID OB_INST_ID START_TIME END_TIME STATUS
1 1 2017-01-01 21:00 2017-01-01 21:30 FAILED

カラム名は説明のために削除したり簡略化しているものがあります。

いま、jobが失敗し、問題解決には一晩かかるとし、バッチウィンドウ('batch window')は終了しています。次に、ウィンドウ開始を9:00 PM、1/1のjobを再度開始、前回失敗箇所から開始して9:30に正常終了しました。この時すでに翌日になっており、1/2のjobも走らせる必要があるので、その直後の9:31に開始して通常の1時間で10:30に完了しました。1つのJobInstanceを連続実行する事に特に要求事項は無いですが、2つのjobが同一データにアクセスする可能性が無い限りにおいてであり、これはDBレベルでロックを引き起こしかねないためです。いつJobを実行するかどうかはスケジューラーに完全に依存します。別々のJobInstancesなので、Spring Batchはコンカレントな実行を止めません。(同一のJobInstanceが実行中の場合、JobExecutionAlreadyRunningExceptionをスローします。)最終的に、以下表のように、JobInstanceJobParametersテーブルに行が追加され、JobExecutionに2行追加されます。

Table 5. BATCH_JOB_INSTANCE

JOB_INST_ID JOB_NAME
1 EndOfDayJob
2 EndOfDayJob

Table 6. BATCH_JOB_EXECUTION_PARAMS

JOB_EXECUTION_ID TYPE_CD KEY_NAME DATE_VAL IDENTIFYING
1 DATE schedule.Date 2017-01-01 00:00:00 TRUE
2 DATE schedule.Date 2017-01-01 00:00:00 TRUE
3 DATE schedule.Date 2017-01-02 00:00:00 TRUE

Table 7. BATCH_JOB_EXECUTION

JOB_EXEC_ID JOB_INST_ID START_TIME END_TIME STATUS
1 1 2017-01-01 21:00 2017-01-01 21:30 FAILED
2 1 2017-01-02 21:00 2017-01-02 21:30 COMPLETED
3 2 2017-01-02 21:31 2017-01-02 22:29 COMPLETED

カラム名は説明のために削除したり簡略化しているものがあります。

1.2. Step

Stepは、バッチjobのシーケンシャルで独立したフェーズ、をカプセル化したドメインオブジェクトです。よって、すべてのJobは必ず1つ以上のstepで構成します。Stepには実際のバッチ処理を定義したり制御するのに必要なすべての情報を持ちます。これは全く持って曖昧な説明ですが、Stepの中身は開発者が書くJob次第なためです。Stepは開発者次第でシンプルなものから複雑なものまで作成出来ます。シンプルなStepはファイルからDBにデータをロードし、コードは全く書かないか少量で済みます(実装依存)。複雑なStepは、何らかの処理の一部となる複雑なビジネスルールを持つものが考えられます。Job同様、Stepは複数のStepExecutionを持ち、1つのJobExecutionと関連を持ちます。以下がイメージ図です。

igure 4. Job Hierarchy With Steps

1.2.1. StepExecution

StepExecutionStepの1回分の実行を表現したものです。JobExecution同様に、Stepが実行される度に新規にStepExecutionを生成します。もしstepが実行に失敗するとき、原因がそのstepより前にある場合、そのstepのexecutionは永続化しません。StepExecutionStepが実際に開始した後にだけ生成します。

Stepの実行はStepExecutionクラスのオブジェクトで表現します。それぞれの実行は、対応するstep・JobExecutionトランザクション関連データ、このデータはコミット・ロールバックカウント・開始終了時刻など、を持ちます。また、それぞれのstep executionはExecutionContextを持ち、ここには開発者がバッチ実行中に永続化しておきたい任意のデータ、例えば統計やリスタートに必要な状態、を持たせます。以下はStepExecutionのプロパティリストです。

Table 8. StepExecution Properties

Property Definition
Status 実行ステータスを示すBatchStatusオブジェクト。実行中BatchStatus.STARTED。失敗BatchStatus.FAILED。正常終了BatchStatus.COMPLETED
startTime 実行開始時の現在システム時刻のjava.util.Date 。stepがまだ開始していない場合は空。
endTime 実行終了時の現在システム時刻のjava.util.Date。成功かどうかは無関係。stepがまだ終了していない場合は空。
exitStatus 実行結果を示すExitStatus。呼び出し元に返される終了コードなので、最も重要。詳細は5章参照。jobがまだ終了していない場合は空。
executionContext 複数のexecution間で永続化の必要があるユーザデータを持つ、プロパティの入れ物。
readCount 正常に読み込んだアイテム数。
writeCount 正常に書き込んだアイテム数。
commitCount この実行でコミットしたトランザクション数。
rollbackCount Stepで制御するビジネストランザクションロールバック数。
readSkipCount readが失敗した回数。アイテムスキップとなる。
processSkipCount processが失敗した回数。アイテムスキップとなる。
filterCount ItemProcessorがフィルターしたアイテム数。
writeSkipCount writeが失敗した回数。アイテムスキップとなる。

1.3. ExecutionContext

ExecutionContextフレームワークが制御および永続化するkey/valueのコレクションで、開発者が永続化状態を保存する場所です。これはStepExecutionもしくはJobExecutionスコープになります。Quartzで言うところのJobDataMapです。最も良く使われる例としてはリスタートの調整です。入力としてフラットファイルを取る例で言うと、個々の行を処理中、フレームワークは定期的にコミットポイントでExecutionContextを永続化します。これにより、ItemReaderで実行中に致命的エラーが発生してもその状態を保存します。これを実装するには、以下例のように、コンテキストに読み込んだ現在行をputすると、あとはフレームワークがやってくれます。

executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());

例としてJobセクションのEndOfDayサンプルの場合、'loadData'というファイルをDBにロードするstepが一つあるとします。最初の実行が失敗後、メタデータテーブルは以下の例になります。

Table 9. BATCH_JOB_INSTANCE

JOB_INST_ID JOB_NAME
1 EndOfDayJob

Table 10. BATCH_JOB_EXECUTION_PARAMS

JOB_INST_ID TYPE_CD KEY_NAME DATE_VAL
1 DATE schedule.Date 2017-01-01

Table 11. BATCH_JOB_EXECUTION

JOB_EXEC_ID JOB_INST_ID START_TIME END_TIME STATUS
1 1 2017-01-01 21:00 2017-01-01 21:30 FAILED

Table 12. BATCH_STEP_EXECUTION

STEP_EXEC_ID JOB_EXEC_ID STEP_NAME START_TIME END_TIME STATUS
1 1 loadData 2017-01-01 21:00 2017-01-01 21:30 FAILED

Table 13. BATCH_STEP_EXECUTION_CONTEXT

STEP_EXEC_ID SHORT_CONTEXT
1 {piece.count=40321}

上の場合、Stepは30分実行し、このシナリオにおけるファイル行であるところの40,321 'pieces'を処理しています。この値はフレームワークがコミット直前に更新します。また、`この値にはExecutionContextの複数エントリに対応する形で複数行を含められます。コミット前に通知を受けるには何らかのStepListener実装(かItemStream)が必要で、詳細は本が意図の後半で扱います。前述の例同様、ここでもJobを翌日にリスタートする、とします。リスタートすると、最終実行時のExecutionContextの値をDBから再構成します。ItemReaderがopenすると、以下例のように、コンテキストに保存した状態があればチェックしてその値で初期化します。

if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
    log.debug("Initializing for restart. Restart data is: " + executionContext);

    long lineCount = executionContext.getLong(getKey(LINES_READ_COUNT));

    LineReader reader = getReader();

    Object record = "";
    while (reader.getPosition() < lineCount && record != null) {
        record = readLine();
    }
}

この例の場合、上のコードを実行すると、カレントの行は40322になり、Stepは前回失敗箇所からの再実行が可能となります。また、ExecutionContextにはその実行に関して永続化したい統計情報にも使えます。例えば、フラットファイルが複数行に存在する処理命令(orders for processing)を含むケースでは、処理した命令数(読み込んだ行数とは異なる)を保存し、Stepの最後に合計処理命令数をメールで送るような処理が可能です。それぞれのJobInstanceで正確なスコープにするため、フレームワークがそれらの情報を保存します。すでに在るExecutionContextが使われるかどうかを知ることは困難です。たとえば、上記の 'EndOfDay' の例で言うと、1/1を2回目に実行するとき、フレームワークは同一JobInstanceStepであると解釈し、DBからExecutionContextを取得し、その取得データを(StepExecutionの一部として)Stepに渡します。一方で、1/2実行時には、フレームワークは別のインスタンスであると解釈し、よって空のコンテキストをStepに渡します。There are many of these types of determinations that the framework makes for the developer, to ensure the state is given to them at the correct time. また、ある時点において1つのStepExecutionには1つのExecutionContextだけが存在する点は重要です。ExecutionContextを使う際には注意が必要で、このインスタンスは共有のキースペース(a shared keyspace)を生成するためです。このため、データを上書きしないような注意が必要です。ただし、Stepがコンテキストに何もデータを保存しないのであれば、フレームワークには何も影響を与えません。

1つのJobExecutionに少なくとも1つのExecutionContext、それぞれのStepExecutionごとに1つのExecutionContext、が存在する点も重要です。例えば、以下のコード例を考えます。

ExecutionContext ecStep = stepExecution.getExecutionContext();
ExecutionContext ecJob = jobExecution.getExecutionContext();
//ecStepとecJobは異なる

コメントにあるように、ecStepecJobは異なります。この場合は2つの異なるExecutionContextsが存在します。1つはStepのスコープでStepのコミットポイントごとにセーブし、一方、Jobのスコープの方はStep実行の間でセーブします。

1.4. JobRepository

JobRepositoryは上述のすべての機能のための永続化メカニズムです。JobLauncher, Job, Step実装で用いるためのCRUD操作を提供します。Jobをラウンチする時、JobExecutionリポジトリから取得し、また、実行中には、StepExecutionJobExecutionの実装をリポジトリに渡すことで永続化をします。

java configurationを使う場合、@EnableBatchProcessingアノテーションのデフォルト設定でJobRepositoryが自動的に登録されます。

1.5. JobLauncher

JobLauncherは、以下例のように、JobParametersを渡してJobを起動するためのインタフェースです。

public interface JobLauncher {

public JobExecution run(Job job, JobParameters jobParameters)
            throws JobExecutionAlreadyRunningException, JobRestartException,
                   JobInstanceAlreadyCompleteException, JobParametersInvalidException;
}

このインタフェース実装はJobRepositoryからJobExecutionを取得してJobを実行します。

1.6. Item Reader

ItemReaderは、1回に1アイテム、Stepでの入力を取得するためのインタフェースです。ItemReaderが読み込み可能なアイテムが無くなった事を通知するにはnullを返します。ItemReaderインタフェースの詳細と各種実装についてはReaders And Writersを参照してください。

1.7. Item Writer

ItemWriterは、1回に1チャンクか1バッチ、Stepでの出力を行うためのインタフェースです。通常、ItemWriterは次に受け取るであろう入力に関しては関知せず、現在の呼び出し中に渡されたアイテムのみ処理します。ItemWriterインタフェースの詳細と各種実装についてはReaders And Writersを参照してください。

1.8. Item Processor

ItemProcessorはアイテムにビジネス処理をを行うためのインタフェースです。ItemReaderは1アイテム読み込み、ItemWriterはそれらの書き込み、ItemProcessorはそれ以外のビジネス処理の適用や変換処理のアクセスポイントとなります。なお、アイテム処理中に、アイテムがvalidで無い場合、nullを返すとそのアイテムは書き込みません。ItemProcessorインタフェースの詳細と各種実装についてはReaders And Writersを参照してください。

Spring Batch 4.1.x - Reference Documentation - What’s New in Spring Batch 4.1のテキトー翻訳

https://docs.spring.io/spring-batch/4.1.x/reference/html/whatsnew.html#whatsNew

https://qiita.com/kagamihoge/items/12fbbc2eac5b8a5ac1e0 俺の訳一覧リスト

1. What’s New in Spring Batch 4.1

Spring Batch 4.1 releaseは以下の新機能があります。

@SpringBatchTestアノテーション

Spring Batchにはバッチコンポーネントテスト用のユーティリティクラス(JobLauncherTestUtilsJobRepositoryTestUtils)とtest execution listeners(StepScopeTestExecutionListenerJobScopeTestExecutionListener)があります。しかし、これらユーティリティを使用するには、明示的なbean設定が必要です。今回リリースで導入する@SpringBatchTestは自動的にユーティリティのbeanとリスナをテストコンテキストに追加してautowiringで利用可能にします。

@RunWith(SpringRunner.class)
@SpringBatchTest
@ContextConfiguration(classes = {JobConfiguration.class})
public class JobTest {

   @Autowired
   private JobLauncherTestUtils jobLauncherTestUtils;

   @Autowired
   private JobRepositoryTestUtils jobRepositoryTestUtils;


   @Before
   public void clearMetadata() {
      jobRepositoryTestUtils.removeJobExecutions();
   }

   @Test
   public void testJob() throws Exception {
      // given
      JobParameters jobParameters =
            jobLauncherTestUtils.getUniqueJobParameters();

      // when
      JobExecution jobExecution =
            jobLauncherTestUtils.launchJob(jobParameters);

      // then
      Assert.assertEquals(ExitStatus.COMPLETED,
                          jobExecution.getExitStatus());
   }

}

このアノテーションについては、Unit Testingを参照してください。

1.2. @EnableBatchIntegration Annotation

remote chunking jobの設定にはいくつかのbean定義が必要です。

  • メッセージングミドルウェア(JMS, AMQPなど)からコネクションを得るためのコネクションファクトリ
  • MessagingTemplate マスタからワーカーにリクエストを送信してその戻りの処理
  • メッセージングミドルウェアからメッセージを得るSpring Integrationの入出力チャネル
  • 処理と書き込みをするワーカーにデータのchunkを送信する、マスター側のitem writer(ChunkMessageChannelItemWriter
  • マスターからデータを受信する、ワーカー側のメッセージリスナー(ChunkProcessorChunkHandler

これは一見するだけで設定が面倒なのが分かります。今回リリースではAPIに加えて設定を簡易化するための@EnableBatchIntegrationも追加しています。以下はこのアノテーションAPIの使用例です。

@Configuration
@EnableBatchProcessing
@EnableBatchIntegration
public class RemoteChunkingAppConfig {

   @Autowired
   private RemoteChunkingMasterStepBuilderFactory masterStepBuilderFactory;

   @Autowired
   private RemoteChunkingWorkerBuilder workerBuilder;

   @Bean
   public TaskletStep masterStep() {
         return this.masterStepBuilderFactory
                         .get("masterStep")
                         .chunk(100)
                         .reader(itemReader())
                         .outputChannel(outgoingRequestsToWorkers())
                         .inputChannel(incomingRepliesFromWorkers())
                         .build();
   }

   @Bean
   public IntegrationFlow worker() {
         return this.workerBuilder
                         .itemProcessor(itemProcessor())
                         .itemWriter(itemWriter())
                         .inputChannel(incomingRequestsFromMaster())
                         .outputChannel(outgoingRepliesToMaster())
                         .build();
   }

   // Middleware beans setup omitted
}

このアノテーションは基盤となるbean設定の負荷を軽減します。ワーカー側のSpring Integration flowと同じように、マスターのstepを設定します。remote chunkingのこれら新規APIを使用するサンプルはsamples moduleで、詳細はSpring Batch Integrationにあります。

remote chunking設定の簡易化に加えて、今回のバージョンはremote partitioningのセットアップを簡易化するAPIRemotePartitioningMasterStepBuilderRemotePartitioningWorkerStepBuilder、も導入しています。以下例のように@EnableBatchIntegrationを付与することで、これらのbeanをautowiredできます。

@Configuration
@EnableBatchProcessing
@EnableBatchIntegration
public class RemotePartitioningAppConfig {

   @Autowired
   private RemotePartitioningMasterStepBuilderFactory masterStepBuilderFactory;

   @Autowired
   private RemotePartitioningWorkerStepBuilderFactory workerStepBuilderFactory;

   @Bean
   public Step masterStep() {
            return this.masterStepBuilderFactory
               .get("masterStep")
               .partitioner("workerStep", partitioner())
               .gridSize(10)
               .outputChannel(outgoingRequestsToWorkers())
               .inputChannel(incomingRepliesFromWorkers())
               .build();
   }

   @Bean
   public Step workerStep() {
            return this.workerStepBuilderFactory
               .get("workerStep")
               .inputChannel(incomingRequestsFromMaster())
               .outputChannel(outgoingRepliesToMaster())
               .chunk(100)
               .reader(itemReader())
               .processor(itemProcessor())
               .writer(itemWriter())
               .build();
   }

   // Middleware beans setup omitted
}

この新規APIの詳細はSpring Batch Integrationを参照してください。

1.3. JSON support

Spring Batch 4.1はJSONフォーマットをサポートします。今回リリースは以下フォーマットのJSONを読み込むitem readerを追加しました。

[
  {
    "isin": "123",
    "quantity": 1,
    "price": 1.2,
    "customer": "foo"
  },
  {
    "isin": "456",
    "quantity": 2,
    "price": 1.4,
    "customer": "bar"
  }
]

XML用のStaxEventItemReaderの類似機能で、JsonItemReaderはchunkのJSONオブジェクトを読み込むのにストリーミングAPIを使用します。Spring Batchは以下2つのライブラリをサポートします。

上記以外のライブラリを使う場合、JsonObjectReaderインタフェースを実装してください。

また、JSONデータの書き込みにはJsonFileItemWriterがあります。JSONサポートの詳細については、ItemReaders and ItemWritersを参照してください。

1.4. Bean Validation API support

今回リリースではValidatingItemProcessor実装のBeanValidatingItemProcessorを提供しており、これはBean Validation API (JSR-303)アノテーションでitemをvalidateします。例えば、以下のPersonがあるとします。

class Person {

    @NotEmpty
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

BeanValidatingItemProcessorのbeanを作成してchunk stepにprocessorとしてアプリケーションコンテキストに登録してitemをvalidateします。

@Bean
public BeanValidatingItemProcessor<Person> beanValidatingItemProcessor() throws Exception {
        BeanValidatingItemProcessor<Person> beanValidatingItemProcessor = new BeanValidatingItemProcessor<>();
        beanValidatingItemProcessor.setFilter(true);

        return beanValidatingItemProcessor;
}

1.5. JSR-305 support

今回リリースはJSR-305向けの機能を追加しています。Spring FrameworkNull-safetyアノテーションを使用し、Spring Batchのpublic APIに追加します。

これらアノテーションはSpring Batch API使用時にnull-safetyを強制するだけでなく、IDEにnullに関する有用な情報を提供するのにも使えます。たとえば、ItemReaderインタフェースを実装するとして、JSR-305アノテーションをサポートするIDEは以下を生成可能になります。

public class MyItemReader implements ItemReader<String> {

        @Nullable
        public String read() throws Exception {
                return null;
        }

}

readメソッドの@Nullableアノテーションnullを返す可能性があるという契約を明示しています。Javadocの記述である、readメソッドはデータソース読み込み完了時にnullを返す、をアノテーションにより強制しています。

1.6. FlatFileItemWriterBuilder enhancements

今回リリースの小規模変更にフラットファイル書き込み設定簡易化があります。具体的には、デリミタと固定幅ファイルの簡易化です。以下は変更前と後の例です。

// Before
@Bean
public FlatFileItemWriter<Item> itemWriter(Resource resource) {
        BeanWrapperFieldExtractor<Item> fieldExtractor =
            new BeanWrapperFieldExtractor<Item>();
        fieldExtractor.setNames(new String[] {"field1", "field2", "field3"});
        fieldExtractor.afterPropertiesSet();

        DelimitedLineAggregator aggregator = new DelimitedLineAggregator();
        aggregator.setFieldExtractor(fieldExtractor);
        aggregator.setDelimiter(";");

        return new FlatFileItemWriterBuilder<Item>()
                        .name("itemWriter")
                        .resource(resource)
                        .lineAggregator(aggregator)
                        .build();
}

// After
@Bean
public FlatFileItemWriter<Item> itemWriter(Resource resource) {
        return new FlatFileItemWriterBuilder<Item>()
                        .name("itemWriter")
                        .resource(resource)
                        .delimited()
                        .delimiter(";")
                        .names(new String[] {"field1", "field2", "field3"})
                        .build();
}

Spring Batch 4.1.x - Reference Documentation - Spring Batch Introductionのテキトー翻訳

https://docs.spring.io/spring-batch/4.1.x/reference/html/spring-batch-intro.html#spring-batch-intro

https://qiita.com/kagamihoge/items/12fbbc2eac5b8a5ac1e0 俺の訳一覧リスト

1. Spring Batch Introduction

エンタープライズ領域の多くのアプリケーションはミッションクリティカル環境におけるビジネスオペレーションの実行にバルク処理を必要とします。これらのビジネスオペレーションには以下が含まれます。

  • 大規模な情報の複雑な処理を、自動的に、ユーザ操作を介さず出来る限り効率的に処理します。これらのオペレーションは、基本的には、時間駆動のイベント(月末計算・通知・対応)を持ちます。
  • 複数の大規模データセット(保険給付決定または料率調整)を繰り返し処理する複雑なビジネスルールを定期実行するアプリケーション。
  • 内外システムから受信する情報の一貫性保持。これには、フォーマット・validation・そのシステムのレコードに投入するトランザクションルールでの処理、が必要となります。エンタープライズで日々数十億トランザクションを処理するのにバッチ処理を使います。

Spring Batchは軽量・包括的なバッチフレームワークで、エンタープライズシステムの日常的なオペレーションで不可欠であるロバストなバッチアプリケーション開発に用います。Spring BatchはSpring Frameworkの上にあるので開発者が期待する機能(効率的な、POJOベース開発など使い勝手の良い機能群)を備えており、開発者は必要に応じて高度なエンタープライズサービスの利用と活用が出来ます。Spring Batchはスケジューリングフレームワークではありません。エンタープライズのスケジューラは他に良いプロダクトが商用・OSSで多数存在します(Quartz, Tivoli, Control-Mなど)。設計意図としてはスケジューラと組み合わせて動作させ、スケジューラを置き換えるものではありません。

Spring Batchは、大量レコード処理・ログ/追跡・トランザクション管理・ジョブ処理統計・ジョブのリスタートとスキップ・リソース管理、で必要不可欠で繰り返し使用する機能を提供します。また、より高度でテクニカルなサービスも提供し、これらは最適化とパーティショニングを通じて極めて大量でハイパフォーマンスなバッチジョブを可能にします。Spring Batchは、複雑で大規模なユースケース(DB間で大規模データの移動や変換など)だけでなくシンプルなユースケース(ファイルを読んでDBに入れたり、ストアドを実行するなど)の、どちらでも使用出来ます。大規模バッチジョブで大量データを処理するにはフレームワークの非常にスケーラブルな機能が便利です。

1.1. Background

オープンソースソフトウェアプロジェクトとその関連コミュニティはwebとマイクロサービスのアーキテクチャフレームワークに強い関心を寄せていますが、エンタープライズIT環境ではバッチ処理のニーズが根強くあったにも関わらず、Javaベースのバッチ処理のニーズを満たすアーキテクチャフレームワークに対する関心が欠如していました。スタンダードとなるものが無く、バッチアーキテクチャは無数に増殖し、クライアントのエンタープライズIT環境でその企業専用に開発されていました。

SpringSource (now Pivotal) とAccentureはこれを変えるための協力をしました。Accentureのバッチアーキテクチャ実装における実事業と技術的経験、SpringSourceの深い技術的経験、Springの実績あるプログラミングモデルが、高品質でエンタープライズJavaでのギャップを埋めることを目的とした市場向けのソフトウェアを作成、これは自然で強力な協働関係を生みました。Springベースのバッチアーキテクチャソリューションを開発する際に似たような問題を解決してきた多数のクライアントと両社は開発をしてきました。これにより、実世界の課題に適用できるソリューションを保証する、実世界の制約と詳細をもたらしました。

AccentureはSpring Batchプロジェクトに以前からプロプライエタリバッチ処理アーキテクチャフレームワークをコントリビュートし続けており、サポートのためのコミッタ―リソース・機能拡張・既存機能セット、の提供もしています。Accentureのコントリビュートは直近数世代のプラットフォーム、COBOL/Mainframe, C++/Unix, 最近ではJava/anywhere、でのバッチアーキテクチャ構築の数十年にわたる経験の裏付けがあります。

AccentureとSpringSourceの共同作業は、バッチアプリケーション作成時にエンタープライズのユーザが活用可能な、software processing approaches・フレームワーク・ツール、標準化の促進がねらいでした。スタンダードでエンタープライズIT環境に実証済みのソリューションを提供したい企業と政府機関はSpring Batchの恩恵を受けられます。

1.2. Usage Scenarios

一般的なバッチ処理は、

  • DB・ファイル・キューから大量のレコードを読み込み。
  • なんらかのデータ処理。
  • 更新後の形式でデータを書き込み。

Spring Batchは、基本的にはユーザ操作を介さないオフライン環境で、上記のバッチのイテレーションを自動実行し、セットとしてトランザクション処理する機能を提供します。バッチのジョブはたいていのITプロジェクトの一部を形成しており、Spring Batchはロバストエンタープライズスケールのソリューションを提供する唯一のオープンソースフレームワークです。

ビジネスシナリオとしては、

  • 周期的にバッチ処理をコミット
  • コンカレントなバッチ処理: ジョブのパラレル処理
  • Staged, エンタープライズメッセージ駆動処理
  • 大規模なパラレルバッチ処理
  • 失敗後の手動もしくはスケジュールによるリスタート
  • 依存ステップのシーケンシャル処理(ワークフロー駆動バッチのエクステンション使用)
  • パーシャル処理: スキップ駆動(例: ロールバック時に使用)
  • バッチ全体のトランザクション。バッチサイズが小さいか既存のストアドプロシージャないしスクリプトを実行する場合など。

技術上の目的としては、

  • バッチ処理の開発者はSpringのプログラミングモデルを使用する: ビジネスロジックに集中し、インフラ部分はフレームワークに任せる。
  • インフラ・バッチ実行環境・バッチアプリケーションそれぞれの関心事を明確に分離する。
  • インターフェースの形で汎用的なコア実行サービスを提供し、プロジェクトでそれを実装する。
  • 基本的にはそのまま使用できる、コア実行インタフェースのシンプルなデフォルト実装を提供する。
  • springフレームワークをすべてのレイヤで活用し、設定・カスタマイズ・サービス拡張を容易にする。
  • シンプルなデプロイメントモデルを提供し、バッチのjarはMavenでビルドするアプリケーションと完全に分離する。

1.3. Spring Batch Architecture

Spring Batchは拡張性と多様なユーザをターゲットに設計しています。以下は拡張性と開発容易性のためのレイヤーアーキテクチャを示した図です。

Figure 1. Spring Batch Layered Architecture

このレイヤーアーキテクチャは三つの主要な高レベルコンポーネント、Application, Core, Infrastructure、に焦点を当てています。ApplicationはSpring Batchを使用する開発者が書くコードとすべてのバッチジョブです。Batch Coreはバッチジョブの起動と制御に必要なコアランタイムクラスで、JobLauncher, Job, Stepの実装があります。ApplicationとCoreは両者とも共通インフラ上に作ります。このインフラには汎用のリーダー・ライター・サービス(RetryTemplateなど)があり、これらはアプリケーション開発者(リーダーItemReaderとライターItemWriterなど)とコアフレームワーク自身(リトライは自前のライブラリ)の両方が使用します。

1.4. General Batch Principles and Guidelines

以下の核となる原則、ガイドライン、一般的な考慮事項はバッチソリューションの構築時に一考すべきです。

  • バッチのアーキテクチャは基本的にはオンラインのそれに影響を与えるし、逆もまた真です。可能な限り共通のコンポーネントアーキテクチャと環境を設計するよう心掛けてください。
  • 可能な限りシンプルにして、単一バッチアプリケーションに複雑なロジック構造を作り込むのは避けて下さい。
  • 処理とデータストレージは物理的に近くに置いて下さい(つまり処理が発生場所にデータを置く)
  • 特にI/Oのシステムリソース使用を最小化する。可能な限りオペレーションを内部メモリで実行する。
  • 不要な物理I/Oの回避を確認するためアプリケーションI/O(SQLの分析)をレビューする。特に、以下4つのありがちなミスを見つける必要があります。 あるデータを一度読んでワーキング領域に保持したりキャッシュ出来るのに、トランザクションのたびにデータを読み込む。 あるデータを同一トランザクションの最初の方で読んでいるのに、再度データを読み込む。 不要なテーブルもしくはインデックススキャンを発生させる。 SQLのWHEREにキーを指定しない。
  • バッチ実行中に二回同じことをしない。たとえば、レポーティングで何らかのデータ集約をする場合、初回データ処理の際に集約データを(できれば)保存し、レポーティングアプリケーションで同一データの再処理を避けて下さい。
  • ある処理中に時間のかかるアロケーションの再発生を避けるには、バッチアプリケーションの開始時に十分なメモリを割り当ててください。
  • データ整合性は常に最悪を想定してください。十分なチェックとデータ整合性を維持するレコードvalidationを実装してください。
  • 必要に応じて内部的なvalidationにチェックサムを実装してください。たとえば、フラットファイルがファイルの総レコード数およびキーフィールドの集約があるtrailer recordを持つ場合など。
  • 負荷テストの計画と実施を実データと本番環境でなるべく早期に行ってください。
  • 大規模バッチシステムでは、バックアップが困難な場合があり、特に24-7が基本のオンラインでコンカレントに実行する場合です。DBバックアップは基本的にはオンラインのバックアップ機能がありますが、ファイルバックアップも同様に重要な考慮が必要です。バッチがフラットファイルに依存する場合、ファイルバックアップ処理はしかるべき設定とドキュメント化だけでなく、定期的に十分なテストをしてください。

1.5. Batch Processing Strategies

バッチシステムの設計と実装の指針となるよう、基本的なバッチアプリケーションの構成要素とパターンを、設計者と開発者にサンプルの構造図とcode shellsで示します。バッチジョブの設計をする時、ビジネスロジックは以下の標準的な構成要素で実装出来るよう複数のステップに分解します。

  • Conversion Applications: ファイルが外部システムへ生成もしくは提供される種類の場合、提供されるトランザクションレコードを処理に必要な形式へと変換するアプリケーションを構築します。この種のバッチアプリケーションは一部または全体的に変換ユーティリティモジュールで構築します(Basic Batch Services参照)。
  • Validation Applications: validationアプリケーションはすべての入出力レコードが正確かつ矛盾の無いことを保証します。validationは基本的には、ヘッダーとフッター、チェックサムとvalidationアルゴリズム、レコード単位のクロスチェック、で作成します。
  • Extract Applications: この種のアプリケーションは、データベースや入力ファイルからレコードセットを読み込み、定義済みルールでレコードを選択し、出力ファイルにレコードを書き込みます。
  • Extract/Update Applications: アプリケーションがDBないし入力ファイルからレコードを読み込み、DB更新あるいは入力レコード各行に応じて出力ファイルを作成する。
  • Processing and Updating Applications: 抽出やvalidationアプリケーションからの入力トランザクションに対する処理を実行するアプリケーション。通常、この処理は必要なデータ取得のためのDB読み込みがあり、出力処理としてDB更新やレコード作成を伴う場合もある。
  • Output/Format Applications: アプリケーションで入力ファイルを読み込み、そのレコードのデータを何らかの標準フォーマットで再構成し、別のプログラムやシステムで表示や変換するための出力ファイルを生成する。

なお、前述の構築部品では作れないビジネスロジック用の、基礎的なアプリケーションシェルも必要です。

主要な構築部品に加え、個々のアプリケーションは一つ以上の標準ユーティリティステップを使用する場合があります。例えば、

  • Sort: 入力ファイルを読み込んでソートキーで並べ替えした出力ファイルを生成するプログラム。ソートは通常は標準システムユーティリティを使用する。
  • Split: 単一入力ファイルを読み込んで入力行に応じていずれかの出力ファイルに書き込むプログラム。スプリットはパラメータ駆動の標準システムユーティリティで実装あるいは設定します。
  • Merge: 複数入力ファイルから読み込んだレコードを統合して単一の出力ファイルを生成するプログラム。スプリットはパラメータ駆動の標準システムユーティリティで実装あるいは設定します。

また、バッチアプリケーションは入力ソースによる分類もあります。

  • DB駆動アプリケーションはDBから取得した行や値を基に動作する。
  • ファイル駆動アプリケーションはファイルから取得した行や値を基に動作する。
  • メッセージ駆動アプリケーションはメッセージキューから取得したメッセージを基に動作する。

バッチシステムの中核は処理方針です。方針の選択に影響を与える要因としては、概算バッチシステムボリューム、オンラインシステムや他のバッチシステムとの同時並行性、利用可能なバッチウィンドウ*1、があります。(ただし、より多くのエンタープライズ環境が24-7稼働を望んでいるので、明確なバッチウィンドウが消失している)

バッチの一般的な処理オプション(実装の複雑性を増加させる)は以下のとおりです。

  • オフラインモードのバッチウィンドウ内では通常処理。
  • コンカレントバッチ or オンライン処理を選べる。
  • 多数の異なるバッチをパラレル処理、あるいは、同時にジョブをパラレル処理。
  • パーティショニング(同時に同一ジョブの複数インスタンスを処理)
  • 先行オプションの組み合わせ(A combination of the preceding options)

これらオプションのいくつかあるいは全部を商用スケジューラでサポートしている場合があります。

以降のセクションではこれら処理オプションの詳細について解説します。この点は重要で、経験則として、バッチ処理が採るコミットとロッキングストラテジは処理の種類に依存するので、そのオンラインのロッキングストラテジも同一方針を利用します。そうすると、全体アーキテクチャの設計時にバッチアーキテクチャを単なる後付けには出来ません。

ロッキングストラテジは単にDBのロックを使う場合もあれば、そのアーキテクチャでカスタムのロッキングサービスを実装する場合もあります。ロッキングサービスはDBロックに追従(例えば、専用のDBテーブルに必要情報を保存するなど)し、DB操作を要求するアプリケーションプログラムにパーミッションを与えたり拒否したりします。リトライロジックもこのアーキテクチャで実装可能で、ロックが絡む場合にバッチジョブのアボートを回避します。

1. Normal processing in a batch window データ更新があり別々のバッチウィンドウで動作するシンプルなバッチ処理で、オンラインユーザや他のバッチ処理が居ない場合、同時並行性は問題とはならずバッチ実行の最後で単一コミットを実行できます。

大半のケースで、最もロバストな方針が最も適切です。バッチシステムは時が経つにつれて、複雑性と扱うデータボリューム両方が、拡大する傾向に注意してください。ロッキングストラテジを正しく実装しない場合、そのシステムは単一のコミットポイントに依存する事となり、バッチプログラムの修正が困難となります。よって、極めて単純なバッチシステムであっても、リスタート&リカバリのコミットロジックの必要性と、以降のセクションで解説する複雑なケースについても考慮してくださ。

2. Concurrent batch or on-line processing オンラインのユーザからも同時に更新されうるデータをバッチアプリケーションで処理する場合、オンラインで数秒以上必要となるデータ(DBやファイル)はロックしない方が良いです。また、更新は少数のトランザクションが終わるごとにコミットします。それにより、他プロセスで利用できないデータとその時間を最小化します。

物理ロックを最小化する他のオプションとしては、楽観的ロックか悲観的ロックどちらかのパターンで低レベルの論理ロックを実装します。

  • 楽観ロックはレコード競合の可能性が低い前提条件を想定します。これは通常、DBテーブルにタイムスタンプのカラムを定義し、バッチとオンライン処理双方で同時に使用します。アプリケーションが処理で行を取得するとき、タイムスタンプも取得します。次に、アプリケーションが処理対象行を更新するとき、WHERE節にオリジナルのタイムスタンプを使用してUPDATEします。タイムスタンプが合致する場合、データとタイムスタンプは更新されます。もしタイムスタンプが不一致の場合、別のアプリケーションが取得と更新の間に同一行を更新していることを意味します。よって、その更新は実行できません。
  • 悲観ロックはレコード競合の可能性が高い前提条件を想定するため、取得時に物理か論理ロックを必要とします。ある種の悲観的な論理ロックはDBテーブルにロック用のカラムを使用します。アプリケーションが更新で行を取得するとき、ロック用カラムにフラグをセットします。そのフラグを見て、同一行を取得しようとする他アプリケーションは論理レベルで失敗します。フラグをセットするアプリケーションが行を更新するとき、フラグも一緒にクリアすることで、他のアプリケーションからその行が取得可能になります。なお、データの整合性を初回取得とフラグセットの間も維持する事が必須です(例えばDBロック(SELECT FOR UPDATE)などを使用)。また、このやり方は物理ロックと同様の欠点を持ちますが、レコードロック中にユーザが昼飯に行った場合にロックをタイムアウトさせるメカニズムを構築するのが多少簡単なのが異なります。

これらのパターンは必ずしもバッチ処理に最適ではないですが、コンカレントバッチとオンライン処理ではたいてい使用します(DBが行レベルロックをサポートしない場合など)。一般的には、楽観ロックはオンラインアプリケーションに適し、悲観ロックはバッチアプリケーションに適します。論理ロックを使用する場合は、論理ロックで保護するデータエンティティにアクセスするすべてのアプリケーションで同一の方式を採用することが必須となります。

これらの方法は単一レコードのロックを扱うに過ぎない点に注意してください。場合によって、論理的に関連のあるレコードグループのロックの必要が出てきます。物理ロックでは、デッドロックの可能性を避けるために極めて注意深くレコードグループを管理してください。論理ロックでは、保護したい論理レコードグループを管理可能な論理ロックマネージャを構築するのがベストで、このマネージャが一貫性と非デッドロックを保証します。通常、論理ロックマネージャは、ロック管理・競合レポート・タイムアウト機構・他色々、を行うためのテーブルを持ちます。

3. Parallel Processing パラレル処理は、合計処理時時間最小化のため、複数バッチ実行あるいはパラレルにジョブ実行を行います。これはジョブが、同一ファイル・DBテーブル・インデックス空間、を共有しないのであれば問題はありません。共有する場合、サービスはパーティションデータによる実装の必要が出てきます。別の方法として、制御テーブル(control table)で相互依存関係を維持させるアーキテクチャモジュールを構築します。制御テーブルは共有リソースそれぞれに対する行を持ち、その行がアプリケーションで使用中かどうかを持ちます。パラレルジョブのバッチアーキテクチャやアプリケーションは必要とするリソースへのアクセスを取得可能かどうかを確認するために制御テーブルから情報を取得します。

データアクセスが問題とならない場合、パラレル処理にスレッドを追加しての実装が可能です。メインフレーム環境では、すべてのプロセスで十分なCPUタイムを保証する目的で、パラレルジョブクラスが伝統的に使用されています。つまるところ、すべての実行プロセスでタイムスライスを保証するのに十分ロバストでなければなりません。

パラレル処理の別の課題には、ロードバランシング、一般的なシステムリソース、例えばファイル・DBバッファプールなどの可用性、があります。また、制御テーブル自体がたやすくクリティカルなリソースになる危険性があります。

4. Partitioning パーティショニングにより大規模なバッチアプリケーションの複数バージョンをコンカレントに実行できます。この用途は長時間バッチジョブの処理時間短縮です。正常にパーティション化可能なプロセスとは、スプリット可能な入力ファイルや異なるデータセットに対して実行可能なパーティションテーブル、を使用します。

また、パーティション化プロセスは割り当てられたデータセットのみを処理するよう設計する事が必須です。パーティショニングアーキテクチャはDB設計とDBのパーティショニング方法と密接な関係を持ちます。なお、DBパーティショニングは必ずしもDBの物理パーティショニングを意味するわけではありませんが、基本的にはそれを推奨します。下図はパーティショニングのアプローチです。

このアーキテクチャパーティション数を動的設定可能にするため十分に柔軟にします。自動と手動設定の両方が考えられます。自動設定は入力ファイルサイズと入力レコード数などのパラメータに基づく事になると思われます。

4.1 Partitioning Approaches パーティショニング方針の選択はケースバイケースです。以下はいくつかのパーティショニング方針の解説です。

1. Fixed and Even Break-Up of Record Set

この方針では入力レコードセットを(例えば10であれば、各グループはレコードセット全体の1/10となります)偶数のグループに分割します。各グループは単一のバッチ/抽出アプリケーションで処理します。

この方針を使用する際には、事前処理としてレコードセットの分割が必要です。分割結果は上限下限となり、その番号はバッチ/抽出アプリケーションへの入力に用い、そのグループのみ処理するために使われます。

事前処理のオーバーヘッドが大きくなりすぎる可能性があり、これはレコードセットのグループ境界の計算と決定が必要なためです。

2. Break up by a Key Column

この方針では入力レコードを、キーカラム・ロケーションコード・各キーからバッチインスタンスに割り当てたデータ、で分割します。これを行うには、カラム値を以下いずれかにします。

前者の場合、新しい値の追加はバッチ/抽出の手動再設定を意味します。これは新しい値が特定インスタンスに追加されることを保証するためです。

後者の場合、すべての値がバッチジョブインスタンスを介してカバーされることが保証されます。ただし、一つのインスタンスで処理する件数はカラム値の分布に依存します(0000-0999は多数で、1000-1999は少数しかない場合があり得る)。この方針の場合、データ範囲はパーティショニング前提で設計する必要があります。

両者とも、バッチインスタンスに渡すレコードの最適均等配分は出来ません。バッチインスタンスの個数設定が動的では無いためです。

3. Breakup by Views

この方針は基本的にはDBレベルではないキー列で分割します。レコードセットをビューに分割します。このビューを処理中にバッチアプリケーションの個々のインスタンスで使用します。分割はデータのグルーピングにより行います。

この方針では、バッチアプリケーションの個々のインスタンスは(元のテーブルではなく)特定のビューに紐付けます。新規データ追加時、データの新規グループをビューに含めます。動的設定の機能は無く、インスタンス数の変更をするとビューも変更になります。 4. Addition of a Processing Indicator

この方針はインジケーター役となる新規列を入力テーブルに追加します。事前処理段階では、すべてのインジケーターは未処理になっています。バッチアプリケーションのフェッチ段階で、未処理状態のレコードが読み込まれ、一度読み込まれると(ロック扱い)、処理中状態になります。そのれおーどが処理完了すると、インジケーターは完了かエラーのどちらかで更新します。大量のバッチアプリケーションを変更無しに開始可能で、これはインジケーター列でレコードが一度だけ処理したことを保証するためです。端的に言えば"完了時、インジケータが完了状態になる"です。

この方針では、テーブルへのI/Oは動的に増加します。更新系の場合、いずれにせよ書き込みが不可避なので、影響は少ないです。

5. Extract Table to a Flat File

この方針はテーブルをファイルに抽出します。このファイルは複数セグメントに分割し、バッチインスタンスの入力になります。

この方針では、テーブルからファイル抽出と分割の追加オーバーヘッドが発生し、マルチパーティショニングの効果を打ち消す可能性があります。ファイル分割スクリプトの変更により動的設定が可能です。

6. Use of a Hashing Column

レコードを決定するのに使用するハッシュカラム(key/index)をDBテーブルに付けます。このハッシュカラムにはインジケーターを持ち、バッチアプリケーションのインスタンスがどの行を処理するかを決定するのに使用します。たとえば、3つのバッチインスタンスがあるとして、1番インスタンスが処理する行のインジケーターには'A'、2番インスタンスは'B'、3番インスタンスは'C'、にします。

レコードを参照する時には、特定のインジケーターを持つすべての行を選択するためにWHEREを追加します。テーブルへのINSERT時には、デフォルトのインスタンスを示す値(例.'A')をインジケーターカラムに設定します。

インジケータの更新にはシンプルなバッチアプリケーションを使用し、たとえば、異なるインスタンス間で負荷の再配分をします。極めて大量の新規行を追加する時に、新規行を別のインスタンスに振り分け直すのにこのバッチを使います(バッチウィンドウを除けば何時でもOK)。

新規インスタンス用にインジケーターを再配分するには前述の通りなので、追加インスタンスは単にバッチを実行するだけです。

4.2 Database and Application Design Principles

キー列でのパーティションテーブルを使用するアプリケーション方針のアーキテクチャは、パーティションパラメータを格納するセントラルパーティションリポジトリを持ちます。このリポジトリは単一テーブルを持ち、いわゆるpartition tableと呼ばれています。

partition tableに持つ情報は、基本的には静的で、DBAがメンテナンスします。このテーブルはアプリケーションの個々のパーティションに対する一行を持ちます。カラムには、Program ID Code, Partition Number(パーティションの論理ID), パーティションで使うDBキーカラムのLow ValueとHigh Value、を持ちます。

プログラム開始時に、program idとpartition numberをアーキテクチャからアプリケーションに渡します(具体的には、Control Processing Taskletから渡す)。キー列を使用する方針の場合、アプリケーションで処理するデータ範囲を決定するpartition tableを読むのにこれらの変数を使用します。また、partition numberは以下の処理でも使用します。

  • 適切なマージ処理を実行するため、出力ファイルやDB更新に追加
  • バッチログへの正常処理レポートやアーキテクチャエラーハンドラに何らかのエラーを報告

4.3 Minimizing Deadlocks

アプリケーションをパラレルやパーティションで動作させる場合、DBリソースの競合とデッドロックの可能性があります。重要なのは、DB設計チームが潜在的な競合の可能性を削除することで、これをなるべくDB設計の一部として実施します。

また、開発者はデッドロック防止とパフォーマンスに気を付けつつDBのインデックステーブルを設計すべきです。

デッドロックホットスポットが、ログテーブル・制御テーブル・ロックテーブルなど、管理やアーキテクチャが持つテーブルで発生することがあります。これらの影響も同様に気を付ける必要があります。本番に近い負荷テストでアーキテクチャに発生しうるボトルネックを特定することが重要です。

データのコンフリクトの影響を最小化するため、DB接続時やデッドロック発生時に、アーキテクチャはwait-and-retryなどのサービスを提供すべきです。これはDBの特定のリターンコードに対応する機構で、エラーを即時報告するのではなく、所定時間待機してDB操作をリトライします。

4.4 Parameter Passing and Validation

パーティションアーキテクチャはアプリケーション開発者になるべく透過的であるべきです。パーティションモードのアプリケーション実行に紐付くすべてのタスクをアーキテクチャが実行します。

  • アプリケーション開始前のパーティションパラメータの取得
  • アプリケーション開始前のパーティションパラメータのvalidating
  • 開始時にアプリケーションにパラメータを渡す

validationは以下をチェックします。

データベースがパーティション化されている場合、その単一パーティションがDBパーティションを拡張していないことを確認するvalidationも必要な場合があります。

また、アーキテクチャパーティションの統合を考慮に入れておきます。

*1:ディスクバックアップとか時間制約が強いバッチで利用可能な時間のこと