From 0f989d9c9f1bc737d3ce84f01be5c19196d8b232 Mon Sep 17 00:00:00 2001 From: Jay Bryant Date: Fri, 13 Oct 2017 16:03:45 -0500 Subject: [PATCH] 90-character pass for scalability.adoc I removed extraneous characters from non-code lines and arranged the non-code lines to fit into 90 characters. --- spring-batch-docs/asciidoc/scalability.adoc | 403 ++++++++------------ 1 file changed, 166 insertions(+), 237 deletions(-) diff --git a/spring-batch-docs/asciidoc/scalability.adoc b/spring-batch-docs/asciidoc/scalability.adoc index 8e33b6668..9ea57eb04 100644 --- a/spring-batch-docs/asciidoc/scalability.adoc +++ b/spring-batch-docs/asciidoc/scalability.adoc @@ -6,48 +6,36 @@ == Scaling and Parallel Processing -Many batch processing problems can be solved with single threaded, - single process jobs, so it is always a good idea to properly check if that - meets your needs before thinking about more complex implementations. Measure - the performance of a realistic job and see if the simplest implementation - meets your needs first. You can read and write a file of several hundred - megabytes in well under a minute, even with standard hardware. +Many batch processing problems can be solved with single threaded, single process jobs, +so it is always a good idea to properly check if that meets your needs before thinking +about more complex implementations. Measure the performance of a realistic job and see if +the simplest implementation meets your needs first. You can read and write a file of +several hundred megabytes in well under a minute, even with standard hardware. -When you are ready to start implementing a job with some parallel - processing, Spring Batch offers a range of options, which are described in - this chapter, although some features are covered elsewhere. At a high level, - there are two modes of parallel processing: +When you are ready to start implementing a job with some parallel processing, Spring +Batch offers a range of options, which are described in this chapter, although some +features are covered elsewhere. At a high level, there are two modes of parallel +processing: * Single process, multi-threaded * Multi-process -These break down into categories as well, as - follows: - +These break down into categories as well, as follows: * Multi-threaded Step (single process) - - * Parallel Steps (single process) - - * Remote Chunking of Step (multi process) - - * Partitioning a Step (single or multi process) -First, we review the single-process options. Then we review the - multi-process options. +First, we review the single-process options. Then we review the multi-process options. [[multithreadedStep]] - === Multi-threaded Step -The simplest way to start parallel processing is to add a - `TaskExecutor` to your Step configuration. For example, you might add an - attribute of the `tasklet`, as shown in the following example: - +The simplest way to start parallel processing is to add a `TaskExecutor` to your Step +configuration. For example, you might add an attribute of the `tasklet`, as shown in the +following example: [source, xml] ---- @@ -56,25 +44,21 @@ The simplest way to start parallel processing is to add a ---- -In this example, the taskExecutor is a reference to another bean - definition that implements the `TaskExecutor` - interface. https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/task/TaskExecutor.html[`TaskExecutor`] is a standard Spring - interface, so consult the Spring User Guide for details of available - implementations. The simplest multi-threaded - `TaskExecutor` is a - `SimpleAsyncTaskExecutor`. - -The result of the above configuration is that the `Step` - executes by reading, processing, and writing each chunk of items - (each commit interval) in a separate thread of execution. Note - that this means there is no fixed order for the items to be - processed, and a chunk might contain items that are - non-consecutive compared to the single-threaded case. In addition - to any limits placed by the task executor (such as whether it is backed by - a thread pool), there is a throttle limit in the tasklet - configuration which defaults to 4. You may need to increase this - to ensure that a thread pool is fully utilized, as shown in the following example: +In this example, the taskExecutor is a reference to another bean definition that +implements the `TaskExecutor` interface. +https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/task/TaskExecutor.html[`TaskExecutor`] +is a standard Spring interface, so consult the Spring User Guide for details of available +implementations. The simplest multi-threaded `TaskExecutor` is a +`SimpleAsyncTaskExecutor`. +The result of the above configuration is that the `Step` executes by reading, processing, +and writing each chunk of items (each commit interval) in a separate thread of execution. +Note that this means there is no fixed order for the items to be processed, and a chunk +might contain items that are non-consecutive compared to the single-threaded case. In +addition to any limits placed by the task executor (such as whether it is backed by a +thread pool), there is a throttle limit in the tasklet configuration which defaults to 4. +You may need to increase this to ensure that a thread pool is fully utilized, as shown in +the following example: [source, xml] ---- @@ -84,49 +68,41 @@ The result of the above configuration is that the `Step` ---- -Note also that there may be limits placed on concurrency by - any pooled resources used in your step, such as - a `DataSource`. Be sure to make the pool in - those resources at least as large as the desired number of - concurrent threads in the step. +Note also that there may be limits placed on concurrency by any pooled resources used in +your step, such as a `DataSource`. Be sure to make the pool in those resources at least +as large as the desired number of concurrent threads in the step. -There are some practical limitations of using multi-threaded `Step` implementations - for some common batch use cases. Many participants in a `Step` (such as readers - and writers) are stateful. If the state is not segregated by thread, - then those components are not usable in a multi-threaded `Step`. In - particular, most of the off-the-shelf readers and writers from Spring Batch - are not designed for multi-threaded use. It is, however, possible to work - with stateless or thread safe readers and writers, and there is a sample - (called `parallelJob`) in the https://github.com/spring-projects/spring-batch/tree/master/spring-batch-samples[Spring Batch Samples] that show the use of a process - indicator (see <>) to keep - track of items that have been processed in a database input table. - -Spring Batch provides some implementations of - `ItemWriter` and - `ItemReader`. Usually, they say in the - Javadoc if they are thread safe or not or what you have to do to - avoid problems in a concurrent environment. If there is no - information in the Javadoc, you can check the implementation to see - if there is any state. If a reader is not thread safe, it may - still be efficient to use it in your own synchronizing delegator. - You can synchronize the call to `read()` and as - long as the processing and writing is the most expensive part of - the chunk, your step may still complete much faster than it would in a - single threaded configuration. +There are some practical limitations of using multi-threaded `Step` implementations for +some common batch use cases. Many participants in a `Step` (such as readers and writers) +are stateful. If the state is not segregated by thread, then those components are not +usable in a multi-threaded `Step`. In particular, most of the off-the-shelf readers and +writers from Spring Batch are not designed for multi-threaded use. It is, however, +possible to work with stateless or thread safe readers and writers, and there is a sample +(called `parallelJob`) in the +https://github.com/spring-projects/spring-batch/tree/master/spring-batch-samples[Spring +Batch Samples] that show the use of a process indicator (see +<>) to keep track +of items that have been processed in a database input table. +Spring Batch provides some implementations of `ItemWriter` and `ItemReader`. Usually, +they say in the Javadoc if they are thread safe or not or what you have to do to avoid +problems in a concurrent environment. If there is no information in the Javadoc, you can +check the implementation to see if there is any state. If a reader is not thread safe, +it may still be efficient to use it in your own synchronizing delegator. You can +synchronize the call to `read()` and as long as the processing and writing is the most +expensive part of the chunk, your step may still complete much faster than it would in a +single threaded configuration. [[scalabilityParallelSteps]] === Parallel Steps -As long as the application logic that needs to be parallelized can - be split into distinct responsibilities and assigned to individual steps, - then it can be parallelized in a single process. Parallel Step execution - is easy to configure and use, for example, to execute steps - `(step1,step2)` in parallel with - `step3`, as shown in the following example: - +As long as the application logic that needs to be parallelized can be split into distinct +responsibilities and assigned to individual steps, then it can be parallelized in a +single process. Parallel Step execution is easy to configure and use, for example, to +execute steps `(step1,step2)` in parallel with `step3`, as shown in the following +example: [source, xml] ---- @@ -146,91 +122,76 @@ As long as the application logic that needs to be parallelized can ---- -The configurable "task-executor" attribute is used to specify which - `TaskExecutor` implementation should be used to execute the individual - flows. The default is `SyncTaskExecutor`, but an - asynchronous `TaskExecutor` is required to run the steps in parallel. Note - that the job ensures that every flow in the split completes before - aggregating the exit statuses and transitioning. +The configurable "task-executor" attribute is used to specify which `TaskExecutor` +implementation should be used to execute the individual flows. The default is +`SyncTaskExecutor`, but an asynchronous `TaskExecutor` is required to run the steps in +parallel. Note that the job ensures that every flow in the split completes before +aggregating the exit statuses and transitioning. -See the section on <> for more - detail. +See the section on <> for moredetail. [[remoteChunking]] - === Remote Chunking -In remote chunking, the `Step` processing is split across multiple - processes, communicating with each other through some middleware. The following - image shows the pattern: +In remote chunking, the `Step` processing is split across multiple processes, +communicating with each other through some middleware. The following image shows the +pattern: .Remote Chunking image::{batch-asciidoc}images/remote-chunking.png[Remote Chunking, scaledwidth="60%"] -The master component is a single process, and the slaves are - multiple remote processes. This pattern works best if the master - is not a bottleneck, so the processing must be more expensive than the - reading of items (as is often the case in practice). +The master component is a single process, and the slaves are multiple remote processes. +This pattern works best if the master is not a bottleneck, so the processing must be more +expensive than the reading of items (as is often the case in practice). -The master is an implementation of a Spring Batch - `Step` with the `ItemWriter` replaced by a generic - version that knows how to send chunks of items to the middleware as - messages. The slaves are standard listeners for whatever middleware is - being used (for example, with JMS, they would be - `MesssageListener` implementations), and their role is to process - the chunks of items using a standard `ItemWriter` or - `ItemProcessor` plus - `ItemWriter`, through the - `ChunkProcessor` interface. One of the advantages of - using this pattern is that the reader, processor, and writer components are - off-the-shelf (the same as would be used for a local execution of the - step). The items are divided up dynamically and work is shared through the - middleware, so that, if the listeners are all eager consumers, then load - balancing is automatic. +The master is an implementation of a Spring Batch `Step` with the `ItemWriter` replaced +by a generic version that knows how to send chunks of items to the middleware as +messages. The slaves are standard listeners for whatever middleware is being used (for +example, with JMS, they would be `MesssageListener` implementations), and their role is +to process the chunks of items using a standard `ItemWriter` or `ItemProcessor` plus +`ItemWriter`, through the `ChunkProcessor` interface. One of the advantages of using this +pattern is that the reader, processor, and writer components are off-the-shelf (the same +as would be used for a local execution of the step). The items are divided up dynamically +and work is shared through the middleware, so that, if the listeners are all eager +consumers, then load balancing is automatic. -The middleware has to be durable, with guaranteed delivery and a - single consumer for each message. JMS is the obvious candidate, but other - options (such as Java Spaces exist in the grid computing and shared memory product space. +The middleware has to be durable, with guaranteed delivery and a single consumer for each +message. JMS is the obvious candidate, but other options (such as Java Spaces exist in +the grid computing and shared memory product space. [[partitioning]] - === Partitioning -Spring Batch also provides an SPI for partitioning a `Step` execution - and executing it remotely. In this case, the remote participants are - `Step` instances that could just as easily have been configured and used for - local processing. The following image shows the pattern: +Spring Batch also provides an SPI for partitioning a `Step` execution and executing it +remotely. In this case, the remote participants are `Step` instances that could just as +easily have been configured and used for local processing. The following image shows the +pattern: .Partitioning image::{batch-asciidoc}images/partitioning-overview.png[Partitioning Overview, scaledwidth="60%"] -The `Job` runs on the left-hand side as a sequence of `Step` instances, - and one of the `Step` instances is labeled as a master. The slaves in this picture - are all identical instances of a `Step`, which could in fact take the place - of the master, resulting in the same outcome for the `Job`. The slaves are - typically going to be remote services but could also be local threads of - execution. The messages sent by the master to the slaves in this pattern - do not need to be durable or have guaranteed delivery. Spring Batch - metadata in the JobRepository ensures that - each slave is executed once and only once for each `Job` execution. +The `Job` runs on the left-hand side as a sequence of `Step` instances, and one of the +`Step` instances is labeled as a master. The slaves in this picture are all identical +instances of a `Step`, which could in fact take the place of the master, resulting in the +same outcome for the `Job`. The slaves are typically going to be remote services but +could also be local threads of execution. The messages sent by the master to the slaves +in this pattern do not need to be durable or have guaranteed delivery. Spring Batch +metadata in the JobRepository ensures that each slave is executed once and only once for +each `Job` execution. -The SPI in Spring Batch consists of a special implementation of `Step` - (called the `PartitionStep`) and two strategy interfaces - that need to be implemented for the specific environment. The strategy - interfaces are `PartitionHandler` and - `StepExecutionSplitter`, and their role is shown in - the following sequence diagram: +The SPI in Spring Batch consists of a special implementation of `Step` (called the +`PartitionStep`) and two strategy interfaces that need to be implemented for the specific +environment. The strategy interfaces are `PartitionHandler` and `StepExecutionSplitter`, +and their role is shown in the following sequence diagram: .Partitioning SPI image::{batch-asciidoc}images/partitioning-spi.png[Partitioning SPI, scaledwidth="60%"] -The `Step` on the right in this case is the "remote" slave, so, - potentially, there are many objects and or processes playing this role, and - the `PartitionStep` is shown driving the execution. The following example shows the `PartitionStep` - configuration: - +The `Step` on the right in this case is the "remote" slave, so, potentially, there are +many objects and or processes playing this role, and the `PartitionStep` is shown driving +the execution. The following example shows the `PartitionStep` configuration: [source, xml] ---- @@ -241,53 +202,44 @@ The `Step` on the right in this case is the "remote" slave, so, ---- -Similar to the multi-threaded step's `throttle-limit` - attribute, the `grid-size` attribute prevents the task executor from - being saturated with requests from a single step. +Similar to the multi-threaded step's `throttle-limit` attribute, the `grid-size` +attribute prevents the task executor from being saturated with requests from a single +step. -There is a simple example that can be copied and extended in the - unit test suite for https://github.com/spring-projects/spring-batch/tree/master/spring-batch-samples/src/main/resources/jobs[Spring Batch Samples] (see - `Partition*Job.xml` configuration). +There is a simple example that can be copied and extended in the unit test suite for +https://github.com/spring-projects/spring-batch/tree/master/spring-batch-samples/src/main/resources/jobs[Spring +Batch Samples] (see `Partition*Job.xml` configuration). -Spring Batch creates step executions for the partitions called - "step1:partition0", and so on. Many people prefer to call the master step - "step1:master" for consistency. You can use an - alias for the step (by specifying the `name` attribute - instead of the `id` attribute). +Spring Batch creates step executions for the partitions called "step1:partition0", and so +on. Many people prefer to call the master step "step1:master" for consistency. You can +use an alias for the step (by specifying the `name` attribute instead of the `id` +attribute). [[partitionHandler]] - ==== PartitionHandler -The `PartitionHandler` is the component that - knows about the fabric of the remoting or grid environment. It is able - to send `StepExecution` requests to the remote - `Step` instances, wrapped in some fabric-specific format, like a DTO. It does not - have to know how to split the input data or how to aggregate the - result of multiple `Step` executions. Generally speaking, it probably also - does not need to know about resilience or failover, since those are - features of the fabric in many cases. In any case, Spring Batch always - provides restartability independent of the fabric. A failed `Job` can - always be restarted and only the failed `Steps` are - re-executed. +The `PartitionHandler` is the component that knows about the fabric of the remoting or +grid environment. It is able to send `StepExecution` requests to the remote `Step` +instances, wrapped in some fabric-specific format, like a DTO. It does not have to know +how to split the input data or how to aggregate the result of multiple `Step` executions. +Generally speaking, it probably also does not need to know about resilience or failover, +since those are features of the fabric in many cases. In any case, Spring Batch always +provides restartability independent of the fabric. A failed `Job` can always be restarted +and only the failed `Steps` are re-executed. -The `PartitionHandler` interface can have - specialized implementations for a variety of fabric types, including simple - RMI remoting, EJB remoting, custom web service, JMS, Java Spaces, shared - memory grids (like Terracotta or Coherence), and grid execution fabrics - (like GridGain). Spring Batch does not contain implementations for any - proprietary grid or remoting fabrics. - -Spring Batch does, however, provide a useful implementation of - `PartitionHandler` that executes `Step` instances locally in - separate threads of execution, using the - `TaskExecutor` strategy from Spring. The - implementation is called - `TaskExecutorPartitionHandler`, and it is the - default for a step configured with the XML namespace shown previously. It can - also be configured explicitly, as shown in the following example: +The `PartitionHandler` interface can have specialized implementations for a variety of +fabric types, including simple RMI remoting, EJB remoting, custom web service, JMS, Java +Spaces, shared memory grids (like Terracotta or Coherence), and grid execution fabrics +(like GridGain). Spring Batch does not contain implementations for any proprietary grid +or remoting fabrics. +Spring Batch does, however, provide a useful implementation of `PartitionHandler` that +executes `Step` instances locally in separate threads of execution, using the +`TaskExecutor` strategy from Spring. The implementation is called +`TaskExecutorPartitionHandler`, and it is the default for a step configured with the XML +namespace shown previously. It can also be configured explicitly, as shown in the +following example: [source, xml] ---- @@ -302,27 +254,23 @@ Spring Batch does, however, provide a useful implementation of ---- -The `gridSize` attribute determines the number of separate - step executions to create, so it can be matched to the size of the - thread pool in the `TaskExecutor`. Alternatively, it can - be set to be larger than the number of threads available, which makes - the blocks of work smaller. +The `gridSize` attribute determines the number of separate step executions to create, so +it can be matched to the size of the thread pool in the `TaskExecutor`. Alternatively, it +can be set to be larger than the number of threads available, which makes the blocks of +work smaller. -The `TaskExecutorPartitionHandler` is - useful for IO-intensive `Step` instances, such as copying large numbers of files or - replicating filesystems into content management systems. It can also be - used for remote execution by providing a `Step` implementation that is a - proxy for a remote invocation (such as using Spring Remoting). +The `TaskExecutorPartitionHandler` is useful for IO-intensive `Step` instances, such as +copying large numbers of files or replicating filesystems into content management +systems. It can also be used for remote execution by providing a `Step` implementation +that is a proxy for a remote invocation (such as using Spring Remoting). [[stepExecutionSplitter]] - ==== Partitioner -The `Partitioner` has a simpler responsibility: to generate - execution contexts as input parameters for new step executions only (no - need to worry about restarts). It has a single method, as shown in the following interface definition: - +The `Partitioner` has a simpler responsibility: to generate execution contexts as input +parameters for new step executions only (no need to worry about restarts). It has a +single method, as shown in the following interface definition: [source, java] ---- @@ -331,57 +279,40 @@ public interface Partitioner { } ---- -The return value from this method associates a unique name for - each step execution (the `String`) with input - parameters in the form of an `ExecutionContext`. - The names show up later in the Batch metadata as the step name in the - partitioned `StepExecutions`. The - `ExecutionContext` is just a bag of name-value - pairs, so it might contain a range of primary keys, line numbers, or - the location of an input file. The remote `Step` - then normally binds to the context input using `#{...}` - placeholders (late binding in step scope), as illustrated in the next - section. +The return value from this method associates a unique name for each step execution (the +`String`) with input parameters in the form of an `ExecutionContext`. The names show up +later in the Batch metadata as the step name in the partitioned `StepExecutions`. The +`ExecutionContext` is just a bag of name-value pairs, so it might contain a range of +primary keys, line numbers, or the location of an input file. The remote `Step` then +normally binds to the context input using `#{...}` placeholders (late binding in step +scope), as illustrated in the next section. -The names of the step executions (the keys in the - `Map` returned by - `Partitioner`) need to be unique amongst the step - executions of a `Job` but do not have any other specific requirements. - The easiest way to do this (and to make the names meaningful for users) - is to use a prefix+suffix naming convention, where the prefix is the - name of the step that is being executed (which itself is unique in the - `Job`), and the suffix is just a counter. There is - a `SimplePartitioner` in the framework that uses - this convention. +The names of the step executions (the keys in the `Map` returned by `Partitioner`) need +to be unique amongst the step executions of a `Job` but do not have any other specific +requirements. The easiest way to do this (and to make the names meaningful for users) is +to use a prefix+suffix naming convention, where the prefix is the name of the step that +is being executed (which itself is unique in the `Job`), and the suffix is just a +counter. There is a `SimplePartitioner` in the framework that uses this convention. -An optional interface called - `PartitionNameProvider` can be used to - provide the partition names separately from the partitions - themselves. If a `Partitioner` implements - this interface, then, on a restart, only the names are queried. - If partitioning is expensive, this can be a useful optimization. - The names provided by the - `PartitionNameProvider` must match those - provided by the `Partitioner`. +An optional interface called `PartitionNameProvider` can be used to provide the partition +names separately from the partitions themselves. If a `Partitioner` implements this +interface, then, on a restart, only the names are queried. If partitioning is expensive, +this can be a useful optimization. The names provided by the `PartitionNameProvider` must +match those provided by the `Partitioner`. [[bindingInputDataToSteps]] - ==== Binding Input Data to Steps -It is very efficient for the steps that are executed by the - `PartitionHandler` to have identical configuration and for their input - parameters to be bound at runtime from the `ExecutionContext`. This is - easy to do with the StepScope feature of Spring Batch (covered in more - detail in the section on <>). For example, - if the `Partitioner` creates - `ExecutionContext` instances with an attribute key called - `fileName`, pointing to a different file (or - directory) for each step invocation, the - `Partitioner` output might resemble the content of the following table: +It is very efficient for the steps that are executed by the `PartitionHandler` to have +identical configuration and for their input parameters to be bound at runtime from the +`ExecutionContext`. This is easy to do with the StepScope feature of Spring Batch +(covered in more detail in the section on <>). For +example, if the `Partitioner` creates `ExecutionContext` instances with an attribute key +called `fileName`, pointing to a different file (or directory) for each step invocation, +the `Partitioner` output might resemble the content of the following table: .Example step execution name to execution context provided by `Partitioner` targeting directory processing - |=============== |__Step Execution Name (key)__|__ExecutionContext (value)__ |filecopy:partition0|fileName=/home/data/one @@ -389,10 +320,8 @@ It is very efficient for the steps that are executed by the |filecopy:partition2|fileName=/home/data/three |=============== - -Then the file name can be bound to a step using late binding to - the execution context, as shown in the following example: - +Then the file name can be bound to a step using late binding to the execution context, as +shown in the following example: [source, xml] ----