429 lines
24 KiB
Plaintext
429 lines
24 KiB
Plaintext
[[springBatchArchitecture]]
|
|
= Spring Batch Architecture
|
|
|
|
|
|
Spring Batch is designed with extensibility and a diverse group of end users in mind. The
|
|
following image shows the layered architecture that supports the extensibility and ease of
|
|
use for end-user developers.
|
|
|
|
.Spring Batch Layered Architecture
|
|
image::spring-batch-layers.png[Figure 1.1: Spring Batch Layered Architecture, scaledwidth="60%"]
|
|
|
|
This layered architecture highlights three major high-level components: Application,
|
|
Core, and Infrastructure. The application contains all batch jobs and custom code written
|
|
by developers using Spring Batch. The Batch Core contains the core runtime classes
|
|
necessary to launch and control a batch job. It includes implementations for
|
|
`JobLauncher`, `Job`, and `Step`. Both Application and Core are built on top of a common
|
|
infrastructure. This infrastructure contains common readers and writers and services
|
|
(such as the `RetryTemplate`), which are used both by application developers(readers and
|
|
writers, such as `ItemReader` and `ItemWriter`), and the core framework itself (retry,
|
|
which is its own library).
|
|
|
|
[[batchArchitectureConsiderations]]
|
|
== General Batch Principles and Guidelines
|
|
|
|
The following key principles, guidelines, and general considerations should be considered
|
|
when building a batch solution.
|
|
|
|
* Remember that a batch architecture typically affects on-line architecture and vice
|
|
versa. Design with both architectures and environments in mind by using common building
|
|
blocks when possible.
|
|
|
|
* Simplify as much as possible and avoid building complex logical structures in single
|
|
batch applications.
|
|
|
|
* Keep the processing and storage of data physically close together (in other words, keep
|
|
your data where your processing occurs).
|
|
|
|
* Minimize system resource use, especially I/O. Perform as many operations as possible in
|
|
internal memory.
|
|
|
|
* Review application I/O (analyze SQL statements) to ensure that unnecessary physical I/O
|
|
is avoided. In particular, the following four common flaws need to be looked for:
|
|
** Reading data for every transaction when the data could be read once and cached or kept
|
|
in the working storage.
|
|
** Rereading data for a transaction where the data was read earlier in the same
|
|
transaction.
|
|
** Causing unnecessary table or index scans.
|
|
** Not specifying key values in the `WHERE` clause of an SQL statement.
|
|
|
|
* Do not do things twice in a batch run. For instance, if you need data summarization for
|
|
reporting purposes, you should (if possible) increment stored totals when data is being
|
|
initially processed, so your reporting application does not have to reprocess the same
|
|
data.
|
|
|
|
* Allocate enough memory at the beginning of a batch application to avoid time-consuming
|
|
reallocation during the process.
|
|
|
|
* Always assume the worst with regard to data integrity. Insert adequate checks and
|
|
record validation to maintain data integrity.
|
|
|
|
* Implement checksums for internal validation where possible. For example, flat files
|
|
should have a trailer record telling the total of records in the file and an aggregate of
|
|
the key fields.
|
|
|
|
* Plan and execute stress tests as early as possible in a production-like environment
|
|
with realistic data volumes.
|
|
|
|
* In large batch systems, backups can be challenging, especially if the system is running
|
|
concurrent with online applications on a 24-7 basis. Database backups are typically well taken care
|
|
of in online design, but file backups should be considered to be just as important.
|
|
If the system depends on flat files, file backup procedures should not only be in place
|
|
and documented but be regularly tested as well.
|
|
|
|
[[batchProcessingStrategy]]
|
|
== Batch Processing Strategies
|
|
|
|
To help design and implement batch systems, basic batch application building blocks and
|
|
patterns should be provided to the designers and programmers in the form of sample
|
|
structure charts and code shells. When starting to design a batch job, the business logic
|
|
should be decomposed into a series of steps that can be implemented by using the following
|
|
standard building blocks:
|
|
|
|
* __Conversion Applications:__ For each type of file supplied by or generated for an
|
|
external system, a conversion application must be created to convert the transaction
|
|
records supplied into a standard format required for processing. This type of batch
|
|
application can partly or entirely consist of translation utility modules (see Basic
|
|
Batch Services).
|
|
// TODO Add a link to "Basic Batch Services", once you discover where that content is.
|
|
* __Validation Applications:__ A validation application ensures that all input and output
|
|
records are correct and consistent. Validation is typically based on file headers and
|
|
trailers, checksums and validation algorithms, and record-level cross-checks.
|
|
* __Extract Applications:__ An extract application reads a set of records from a database or
|
|
input file, selects records based on predefined rules, and writes the records to an
|
|
output file.
|
|
* __Extract/Update Applications:__ An extract/update applications reads records from a database or
|
|
an input file and makes changes to a database or an output file, driven by the data found
|
|
in each input record.
|
|
* __Processing and Updating Applications:__ A processing and updating application performs processing on
|
|
input transactions from an extract or a validation application. The processing usually
|
|
involves reading a database to obtain data required for processing, potentially updating
|
|
the database and creating records for output processing.
|
|
* __Output/Format Applications:__ An output/format applications reads an input file, restructures data
|
|
from this record according to a standard format, and produces an output file for printing
|
|
or transmission to another program or system.
|
|
|
|
Additionally, a basic application shell should be provided for business logic that cannot
|
|
be built by using the previously mentioned building blocks.
|
|
// TODO What is an example of such a system?
|
|
|
|
In addition to the main building blocks, each application may use one or more standard
|
|
utility steps, such as:
|
|
|
|
* Sort: A program that reads an input file and produces an output file where records
|
|
have been re-sequenced according to a sort key field in the records. Sorts are usually
|
|
performed by standard system utilities.
|
|
* Split: A program that reads a single input file and writes each record to one of
|
|
several output files based on a field value. Splits can be tailored or performed by
|
|
parameter-driven standard system utilities.
|
|
* Merge: A program that reads records from multiple input files and produces one output
|
|
file with combined data from the input files. Merges can be tailored or performed by
|
|
parameter-driven standard system utilities.
|
|
|
|
Batch applications can additionally be categorized by their input source:
|
|
|
|
* Database-driven applications are driven by rows or values retrieved from the database.
|
|
* File-driven applications are driven by records or values retrieved from a file.
|
|
* Message-driven applications are driven by messages retrieved from a message queue.
|
|
|
|
The foundation of any batch system is the processing strategy. Factors affecting the
|
|
selection of the strategy include: estimated batch system volume, concurrency with
|
|
online systems or with other batch systems, available batch windows. (Note that, with
|
|
more enterprises wanting to be up and running 24x7, clear batch windows are
|
|
disappearing).
|
|
|
|
Typical processing options for batch are (in increasing order of implementation
|
|
complexity):
|
|
|
|
* Normal processing during a batch window in offline mode.
|
|
* Concurrent batch or online processing.
|
|
* Parallel processing of many different batch runs or jobs at the same time.
|
|
* Partitioning (processing of many instances of the same job at the same time).
|
|
* A combination of the preceding options.
|
|
|
|
Some or all of these options may be supported by a commercial scheduler.
|
|
|
|
The remainder of this section discusses these processing options in more detail.
|
|
Note that, as a rule of thumb, the commit and locking strategy adopted by batch
|
|
processes depends on the type of processing performed and that the online locking
|
|
strategy should also use the same principles. Therefore, the batch architecture cannot be
|
|
simply an afterthought when designing an overall architecture.
|
|
|
|
The locking strategy can be to use only normal database locks or to implement an
|
|
additional custom locking service in the architecture. The locking service would track
|
|
database locking (for example, by storing the necessary information in a dedicated
|
|
database table) and give or deny permissions to the application programs requesting a database
|
|
operation. Retry logic could also be implemented by this architecture to avoid aborting a
|
|
batch job in case of a lock situation.
|
|
|
|
*1. Normal processing in a batch window* For simple batch processes running in a separate
|
|
batch window where the data being updated is not required by online users or other batch
|
|
processes, concurrency is not an issue and a single commit can be done at the end of the
|
|
batch run.
|
|
|
|
In most cases, a more robust approach is more appropriate. Keep in mind that batch
|
|
systems have a tendency to grow as time goes by, both in terms of complexity and the data
|
|
volumes they handle. If no locking strategy is in place and the system still relies on a
|
|
single commit point, modifying the batch programs can be painful. Therefore, even with
|
|
the simplest batch systems, consider the need for commit logic for restart-recovery
|
|
options as well as the information concerning the more complex cases described later in
|
|
this section.
|
|
|
|
*2. Concurrent batch or on-line processing* Batch applications processing data that can
|
|
be simultaneously updated by online users should not lock any data (either in the
|
|
database or in files) that could be required by on-line users for more than a few
|
|
seconds. Also, updates should be committed to the database at the end of every few
|
|
transactions. Doing so minimizes the portion of data that is unavailable to other processes
|
|
and the elapsed time the data is unavailable.
|
|
|
|
Another option to minimize physical locking is to have logical row-level locking
|
|
implemented with either an optimistic locking pattern or a pessimistic locking pattern.
|
|
|
|
* Optimistic locking assumes a low likelihood of record contention. It typically means
|
|
inserting a timestamp column in each database table that is used concurrently by both batch and
|
|
online processing. When an application fetches a row for processing, it also fetches the
|
|
timestamp. As the application then tries to update the processed row, the update uses the
|
|
original timestamp in the `WHERE` clause. If the timestamp matches, the data and the
|
|
timestamp are updated. If the timestamp does not match, this indicates that another
|
|
application has updated the same row between the fetch and the update attempt. Therefore,
|
|
the update cannot be performed.
|
|
|
|
* Pessimistic locking is any locking strategy that assumes there is a high likelihood of
|
|
record contention and, therefore, either a physical or a logical lock needs to be obtained at
|
|
retrieval time. One type of pessimistic logical locking uses a dedicated lock-column in
|
|
the database table. When an application retrieves the row for update, it sets a flag in
|
|
the lock column. With the flag in place, other applications attempting to retrieve the
|
|
same row logically fail. When the application that sets the flag updates the row, it also
|
|
clears the flag, enabling the row to be retrieved by other applications. Note that
|
|
the integrity of data must be maintained also between the initial fetch and the setting
|
|
of the flag -- for example, by using database locks (such as `SELECT FOR UPDATE`). Note also that
|
|
this method suffers from the same downside as physical locking except that it is somewhat
|
|
easier to manage building a time-out mechanism that gets the lock released if the user
|
|
goes to lunch while the record is locked.
|
|
|
|
These patterns are not necessarily suitable for batch processing, but they might be used
|
|
for concurrent batch and online processing (such as in cases where the database does not
|
|
support row-level locking). As a general rule, optimistic locking is more suitable for
|
|
online applications, while pessimistic locking is more suitable for batch applications.
|
|
Whenever logical locking is used, the same scheme must be used for all applications
|
|
that access the data entities protected by logical locks.
|
|
|
|
Note that both of these solutions only address locking a single record. Often, we may
|
|
need to lock a logically related group of records. With physical locks, you have to
|
|
manage these very carefully to avoid potential deadlocks. With logical locks, it
|
|
is usually best to build a logical lock manager that understands the logical record
|
|
groups you want to protect and that can ensure that locks are coherent and
|
|
non-deadlocking. This logical lock manager usually uses its own tables for lock
|
|
management, contention reporting, time-out mechanism, and other concerns.
|
|
|
|
*3. Parallel Processing* Parallel processing lets multiple batch runs or jobs run in
|
|
parallel to minimize the total elapsed batch processing time. This is not a problem as
|
|
long as the jobs are not sharing the same files, database tables, or index spaces. If they do,
|
|
this service should be implemented by using partitioned data. Another option is to build an
|
|
architecture module for maintaining interdependencies by using a control table. A control
|
|
table should contain a row for each shared resource and whether it is in use by an
|
|
application or not. The batch architecture or the application in a parallel job would
|
|
then retrieve information from that table to determine whether it can get access to the
|
|
resource it needs.
|
|
|
|
If the data access is not a problem, parallel processing can be implemented through the
|
|
use of additional threads to process in parallel. In a mainframe environment, parallel
|
|
job classes have traditionally been used, to ensure adequate CPU time for all
|
|
the processes. Regardless, the solution has to be robust enough to ensure time slices for
|
|
all the running processes.
|
|
|
|
Other key issues in parallel processing include load balancing and the availability of
|
|
general system resources, such as files, database buffer pools, and so on. Also, note that
|
|
the control table itself can easily become a critical resource.
|
|
|
|
*4. Partitioning* Using partitioning lets multiple versions of large batch applications
|
|
run concurrently. The purpose of this is to reduce the elapsed time required to
|
|
process long batch jobs. Processes that can be successfully partitioned are those where
|
|
the input file can be split or the main database tables partitioned to let the
|
|
application run against different sets of data.
|
|
|
|
In addition, processes that are partitioned must be designed to process only their
|
|
assigned data set. A partitioning architecture has to be closely tied to the database
|
|
design and the database partitioning strategy. Note that database partitioning does not
|
|
necessarily mean physical partitioning of the database (although, in most cases, this is
|
|
advisable). The following image illustrates the partitioning approach:
|
|
|
|
.Partitioned Process
|
|
image::partitioned.png[Figure 1.2: Partitioned Process, scaledwidth="60%"]
|
|
|
|
The architecture should be flexible enough to allow dynamic configuration of the number
|
|
of partitions. You should consider both automatic and user controlled configuration.
|
|
Automatic configuration may be based on such parameters as the input file size and the
|
|
number of input records.
|
|
|
|
*4.1 Partitioning Approaches* Selecting a partitioning approach has to be done on a
|
|
case-by-case basis. The following list describes some of the possible partitioning
|
|
approaches:
|
|
|
|
_1. Fixed and Even Break-Up of Record Set_
|
|
|
|
This involves breaking the input record set into an even number of portions (for example,
|
|
10, where each portion has exactly 1/10th of the entire record set). Each portion is then
|
|
processed by one instance of the batch/extract application.
|
|
|
|
To use this approach, preprocessing is required to split the record set up. The
|
|
result of this split is a lower and upper bound placement number that you can use
|
|
as input to the batch/extract application to restrict its processing to only its
|
|
portion.
|
|
|
|
Preprocessing could be a large overhead, as it has to calculate and determine the bounds
|
|
of each portion of the record set.
|
|
|
|
_2. Break up by a Key Column_
|
|
|
|
This involves breaking up the input record set by a key column, such as a location code,
|
|
and assigning data from each key to a batch instance. To achieve this, column
|
|
values can be either:
|
|
|
|
* Assigned to a batch instance by a partitioning table (described later in this
|
|
section).
|
|
|
|
* Assigned to a batch instance by a portion of the value (such as 0000-0999, 1000 - 1999,
|
|
and so on).
|
|
|
|
Under option 1, adding new values means a manual reconfiguration of the batch or extract to
|
|
ensure that the new value is added to a particular instance.
|
|
|
|
Under option 2, this ensures that all values are covered by an instance of the batch
|
|
job. However, the number of values processed by one instance is dependent on the
|
|
distribution of column values (there may be a large number of locations in the 0000-0999
|
|
range and few in the 1000-1999 range). Under this option, the data range should be
|
|
designed with partitioning in mind.
|
|
|
|
Under both options, the optimal even distribution of records to batch instances cannot be
|
|
realized. There is no dynamic configuration of the number of batch instances used.
|
|
|
|
_3. Breakup by Views_
|
|
|
|
This approach is basically breakup by a key column but on the database level. It involves
|
|
breaking up the record set into views. These views are used by each instance of the batch
|
|
application during its processing. The breakup is done by grouping the data.
|
|
|
|
With this option, each instance of a batch application has to be configured to hit a
|
|
particular view (instead of the main table). Also, with the addition of new data
|
|
values, this new group of data has to be included into a view. There is no dynamic
|
|
configuration capability, as a change in the number of instances results in a change to
|
|
the views.
|
|
|
|
_4. Addition of a Processing Indicator_
|
|
|
|
This involves the addition of a new column to the input table, which acts as an
|
|
indicator. As a preprocessing step, all indicators are marked as being non-processed.
|
|
During the record fetch stage of the batch application, records are read on the condition
|
|
that an individual record is marked as being non-processed, and, once it is read (with lock),
|
|
it is marked as being in processing. When that record is completed, the indicator is
|
|
updated to either complete or error. You can start many instances of a batch application
|
|
without a change, as the additional column ensures that a record is only processed once.
|
|
// TODO On completion, what is the record marked as? Same for on error. (I expected a
|
|
// sentence or two on the order of "On completion, indicators are marked as having
|
|
// a particular status.")
|
|
|
|
With this option, I/O on the table increases dynamically. In the case of an updating
|
|
batch application, this impact is reduced, as a write must occur anyway.
|
|
|
|
_5. Extract Table to a Flat File_
|
|
|
|
This approach involves the extraction of the table into a flat file. This file can then be split into
|
|
multiple segments and used as input to the batch instances.
|
|
|
|
With this option, the additional overhead of extracting the table into a file and
|
|
splitting it may cancel out the effect of multi-partitioning. Dynamic configuration can
|
|
be achieved by changing the file splitting script.
|
|
|
|
_6. Use of a Hashing Column_
|
|
|
|
This scheme involves the addition of a hash column (key or index) to the database tables
|
|
used to retrieve the driver record. This hash column has an indicator to determine which
|
|
instance of the batch application processes this particular row. For example, if there
|
|
are three batch instances to be started, an indicator of 'A' marks a row for
|
|
processing by instance 1, an indicator of 'B' marks a row for processing by instance 2,
|
|
and an indicator of 'C' marks a row for processing by instance 3.
|
|
|
|
The procedure used to retrieve the records would then have an additional `WHERE` clause
|
|
to select all rows marked by a particular indicator. The inserts in this table would
|
|
involve the addition of the marker field, which would be defaulted to one of the
|
|
instances (such as 'A').
|
|
|
|
A simple batch application would be used to update the indicators, such as to
|
|
redistribute the load between the different instances. When a sufficiently large number
|
|
of new rows have been added, this batch can be run (anytime, except in the batch window)
|
|
to redistribute the new rows to other instances.
|
|
|
|
Additional instances of the batch application require only the running of the batch
|
|
application (as described in the preceding paragraphs) to redistribute the indicators to
|
|
work with a new number of instances.
|
|
|
|
*4.2 Database and Application Design Principles*
|
|
|
|
An architecture that supports multi-partitioned applications that run against
|
|
partitioned database tables and use the key column approach should include a central
|
|
partition repository for storing partition parameters. This provides flexibility and
|
|
ensures maintainability. The repository generally consists of a single table, known as
|
|
the partition table.
|
|
|
|
Information stored in the partition table is static and, in general, should be maintained
|
|
by the DBA. The table should consist of one row of information for each partition of a
|
|
multi-partitioned application. The table should have columns for Program ID Code,
|
|
Partition Number (the logical ID of the partition), Low Value of the database key column for this
|
|
partition, and High Value of the database key column for this partition.
|
|
|
|
On program start-up, the program `id` and partition number should be passed to the
|
|
application from the architecture (specifically, from the control processing tasklet). If
|
|
a key column approach is used, these variables are used to read the partition table
|
|
to determine what range of data the application is to process. In addition, the
|
|
partition number must be used throughout the processing to:
|
|
|
|
* Add to the output files or database updates, for the merge process to work
|
|
properly.
|
|
* Report normal processing to the batch log and any errors to the architecture error
|
|
handler.
|
|
|
|
*4.3 Minimizing Deadlocks*
|
|
|
|
When applications run in parallel or are partitioned, contention for database resources
|
|
and deadlocks may occur. It is critical that the database design team eliminate
|
|
potential contention situations as much as possible, as part of the database design.
|
|
|
|
Also, the developers must ensure that the database index tables are designed with
|
|
deadlock prevention and performance in mind.
|
|
|
|
Deadlocks or hot spots often occur in administration or architecture tables, such as log
|
|
tables, control tables, and lock tables. The implications of these should be taken into
|
|
account as well. Realistic stress tests are crucial for identifying the possible
|
|
bottlenecks in the architecture.
|
|
|
|
To minimize the impact of conflicts on data, the architecture should provide services
|
|
(such as wait-and-retry intervals) when attaching to a database or when encountering a
|
|
deadlock. This means a built-in mechanism to react to certain database return codes and,
|
|
instead of issuing an immediate error, waiting a predetermined amount of time and
|
|
retrying the database operation.
|
|
|
|
*4.4 Parameter Passing and Validation*
|
|
|
|
The partition architecture should be relatively transparent to application developers.
|
|
The architecture should perform all tasks associated with running the application in a
|
|
partitioned mode, including:
|
|
|
|
* Retrieving partition parameters before application start-up.
|
|
* Validating partition parameters before application start-up.
|
|
* Passing parameters to the application at start-up.
|
|
|
|
The validation should include checks to ensure that:
|
|
|
|
* The application has sufficient partitions to cover the whole data range.
|
|
* There are no gaps between partitions.
|
|
|
|
If the database is partitioned, some additional validation may be necessary to ensure
|
|
that a single partition does not span database partitions.
|
|
|
|
Also, the architecture should take into consideration the consolidation of partitions.
|
|
Key questions include:
|
|
|
|
* Must all the partitions be finished before going into the next job step?
|
|
* What happens if one of the partitions aborts?
|