Home Java Batch Processing with Spring Cloud Data Flow: Task Launchers and Schedules
Advanced 5 min · July 14, 2026

Batch Processing with Spring Cloud Data Flow: Task Launchers and Schedules

Master batch processing with Spring Cloud Data Flow.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 15-20 min read
  • Java 17+
  • Spring Boot 3.x
  • Spring Cloud Task (see related article)
  • Basic knowledge of microservices and REST APIs
  • Access to a SCDF server (local or cloud)
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Spring Cloud Data Flow orchestrates batch jobs as tasks, enabling complex workflows.
  • Task Launchers trigger tasks on demand or via schedules, supporting dynamic scaling.
  • Schedules use cron expressions or event-driven triggers for automated batch processing.
  • Production pitfalls include state management, error handling, and resource contention.
  • Use Spring Cloud Task for short-lived tasks and Spring Batch for long-running jobs.
✦ Definition~90s read
What is Batch Processing with Spring Cloud Data Flow?

Spring Cloud Data Flow is a microservices-based orchestration tool for building and managing data pipelines, enabling you to launch and schedule batch tasks with ease.

Think of Spring Cloud Data Flow as an automated factory conveyor belt for data processing.
Plain-English First

Think of Spring Cloud Data Flow as an automated factory conveyor belt for data processing. Tasks are like individual workstations that perform specific operations (e.g., cleaning data, generating reports). The Task Launcher is the button that starts a workstation, and Schedules are the pre-programmed timers that start workstations at specific times or when certain conditions are met. This system ensures that data moves efficiently through the factory without manual intervention.

I've been building microservices since the Netflix stack days, and one thing I've learned is that batch processing never goes away. Whether you're reconciling payments, generating nightly reports, or processing IoT sensor data, you need a robust way to run short-lived tasks and long-running jobs. Spring Cloud Data Flow (SCDF) is the orchestrator that makes this manageable.

But here's the hard truth: most teams get batch processing wrong. They either cram everything into a monolithic scheduler or over-engineer with complex workflow engines. SCDF strikes a balance by providing a unified platform for defining, deploying, and orchestrating data pipelines using Spring Boot applications.

In this article, we'll dive deep into Task Launchers and Schedules—the core components for batch processing in SCDF. You'll learn how to launch tasks on demand, schedule them with cron expressions, and handle the inevitable production failures. I'll share war stories from a fintech startup where a misconfigured schedule caused a cascade of duplicate payments, and how we fixed it.

If you're already familiar with Spring Cloud Task (check out my article on it), you're ready. Otherwise, go read that first. We'll build on those concepts with real-world examples from a payment reconciliation system. By the end, you'll know how to design batch pipelines that scale and recover gracefully.

What is Spring Cloud Data Flow?

Spring Cloud Data Flow is an orchestration platform for building composable data pipelines. It's built on top of Spring Cloud Task, Spring Cloud Stream, and Spring Batch. The core idea is that you define applications (tasks or streams) and compose them into pipelines using a DSL or UI.

For batch processing, SCDF treats each batch job as a Spring Cloud Task—a short-lived microservice that runs and exits. You can launch tasks manually, via REST API, or on a schedule. SCDF manages the lifecycle, including execution tracking, logging, and error handling.

Why use SCDF over plain cron jobs? Because cron doesn't scale. With SCDF, you get: - Dynamic scaling: launch tasks on Kubernetes or Cloud Foundry with resource limits. - Execution history: track every run, including parameters and exit codes. - Composable workflows: chain tasks together with conditional logic. - Monitoring: integrate with Micrometer and Actuator for metrics.

If you're still using shell scripts and cron, stop. SCDF gives you observability and resilience out of the box.

TaskDefinition.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// A simple Spring Cloud Task that processes a batch file
@SpringBootApplication
@EnableTask
public class PaymentReconciliationTask {

    public static void main(String[] args) {
        SpringApplication.run(PaymentReconciliationTask.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(TaskRepository taskRepository) {
        return args -> {
            System.out.println("Processing batch file: " + args.getOptionValues("batchFile"));
            // Business logic here
            taskRepository.updateExternalExecutionId("batch-" + System.currentTimeMillis());
        };
    }
}
Output
Processing batch file: /data/batch_20250315.csv
💡Always Use @EnableTask
📊 Production Insight
I've seen teams skip @EnableTask and then wonder why tasks aren't showing up in the UI. It's a common oversight.
🎯 Key Takeaway
SCDF treats batch jobs as tasks, providing orchestration, tracking, and scaling capabilities.

Task Launchers: On-Demand and Programmatic

A Task Launcher is a Spring Cloud Stream application that listens for task launch requests and executes them on a target platform (local, Cloud Foundry, Kubernetes). SCDF provides a default Task Launcher for each platform.

You can launch a task in three ways: 1. SCDF Shell: task launch myTask --arguments "--batchFile=/data/file.csv" 2. REST API: POST to /tasks/definitions/{name} with parameters. 3. Dashboard UI: Click 'Launch' on a task definition.

For production, you'll likely use the REST API from another service. For example, when a new file lands in S3, an event triggers a task launch. This is where the Task Launcher shines—it decouples the trigger from the execution.

Here's the gotcha: Task Launchers are stateful. If the Task Launcher application crashes, pending launches are lost. To mitigate, use a message broker (like RabbitMQ or Kafka) to buffer requests. SCDF's default Task Launcher uses a database-backed queue, but for high throughput, switch to a broker.

Another pitfall: resource contention. If you launch 100 tasks simultaneously without limits, you'll overwhelm your cluster. Always configure spring.cloud.task.max-concurrent-tasks in the Task Launcher properties.

TaskLaunchController.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/api")
public class TaskLaunchController {

    @Autowired
    private TaskLauncher taskLauncher;

    @PostMapping("/launch/{taskName}")
    public String launchTask(@PathVariable String taskName, @RequestParam String batchFile) {
        TaskLaunchRequest request = new TaskLaunchRequest();
        request.setName(taskName);
        request.setArguments(new String[]{"--batchFile=" + batchFile});
        taskLauncher.launch(request);
        return "Task launched";
    }
}
Output
Task launched
⚠ Don't Use Local Task Launcher in Production
📊 Production Insight
At a fintech company, we used the local launcher by accident in a Kubernetes deployment. Tasks ran on the SCDF pod, causing OOM kills. Took us hours to figure out why the server kept restarting.
🎯 Key Takeaway
Use REST API or message-driven triggers for production task launches, and always set concurrency limits.

Scheduling Tasks with Cron Expressions

SCDF integrates with Spring Cloud Task and uses a scheduler (like Spring's TaskScheduler or a platform-specific one like Kubernetes CronJob) to trigger tasks on a cron schedule.

To create a schedule, you define a cron expression and associate it with a task definition. For example: `` dataflow:>schedule create --name dailyReconciliation --definition "reconcile" --expression "0 0 2 ?" ` This runs the reconcile` task daily at 2 AM.

Cron expressions in SCDF follow the Spring cron format (6 fields: seconds, minutes, hours, day-of-month, month, day-of-week). The ? means 'no specific value' and is used for day-of-month or day-of-week when the other is specified.

Here's where most teams mess up: timezone. By default, cron expressions are evaluated in the server's timezone (usually UTC). If your business operates in EST, you need to set the timezone explicitly. In SCDF, you can configure spring.cloud.dataflow.scheduler.timezone globally or per schedule.

Another common mistake: not handling daylight saving time. A cron expression like 0 0 2 ? will run at 2 AM regardless of DST changes. In spring-forward, it runs at 3 AM; in fall-back, it runs twice. To avoid this, use cron expressions that are timezone-aware or schedule tasks at times that don't fall in the DST gap.

ScheduleConfig.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class ScheduleConfig {

    @Bean
    public ScheduleInfoRepository scheduleInfoRepository(DataSource dataSource) {
        return new ScheduleInfoRepository(dataSource);
    }

    @Bean
    public Scheduler scheduler(ScheduleInfoRepository repository, TaskLauncher taskLauncher) {
        return new CronScheduler(repository, taskLauncher);
    }
}
🔥Cron Expression Testing
📊 Production Insight
We once had a schedule that ran at 2 AM EST, but the server was in UTC. The task ran at 7 AM UTC, which was 2 AM EST in winter but 3 AM in summer. Caused a lot of confusion until we set the timezone explicitly.
🎯 Key Takeaway
Schedules are cron-based but require careful timezone management and DST handling.

Advanced Scheduling: Event-Driven and Chained Tasks

Cron is great for fixed intervals, but not all batch processing is time-based. Sometimes you need to launch a task when a file arrives, or after another task completes. SCDF supports this through event-driven scheduling and task chaining.

For event-driven scheduling, you can use Spring Cloud Stream to listen for events (e.g., file uploads) and launch a task. SCDF's Task Launcher can be bound to a message channel. When a message arrives, it launches the task with the message payload as arguments.

Task chaining is more complex. SCDF doesn't have a built-in DAG scheduler like Airflow, but you can simulate it using task execution listeners. For example, after Task A completes, its exit message can trigger Task B. You do this by setting spring.cloud.task.closecontext.enable=true and using a listener to send a message to a channel that launches the next task.

I once worked on a data pipeline that required three sequential tasks: extract, transform, load. We used SCDF with a custom listener that checked the exit status of the previous task and launched the next one. It worked, but error handling was painful. If you need complex workflows, consider using a dedicated workflow engine like Spring Cloud Data Flow's own 'composed tasks' or even Apache Airflow.

TaskChainingListener.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class TaskChainingListener {

    @Autowired
    private TaskLauncher taskLauncher;

    @EventListener
    public void onTaskEnd(TaskExecutionEndEvent event) {
        String taskName = event.getTaskName();
        if ("extract".equals(taskName)) {
            TaskLaunchRequest request = new TaskLaunchRequest();
            request.setName("transform");
            request.setArguments(new String[]{"--inputFile=" + event.getExitMessage()});
            taskLauncher.launch(request);
        }
    }
}
⚠ Chained Tasks Are Fragile
📊 Production Insight
We lost a batch of 10,000 records because the transform task never launched after the extract task completed. The exit message was sent to a non-durable queue that disappeared on restart. Always use durable queues.
🎯 Key Takeaway
Event-driven and chained tasks extend scheduling beyond cron, but require robust error handling.

What the Official Docs Won't Tell You

I've read the Spring Cloud Data Flow reference docs cover to cover. They're decent, but they gloss over several production realities.

  1. Task Launcher High Availability: The docs say you can run multiple Task Launcher instances for HA. What they don't say is that without a shared database or message broker, you'll get duplicate launches. Each launcher has its own queue. You need to configure a shared TaskRepository and a distributed lock to prevent races.
  2. Schedule Persistence: By default, schedules are stored in the SCDF database. But if you're using Kubernetes, the schedule might be implemented as a CronJob. If the SCDF server restarts, it re-creates the CronJobs from the database. However, if the database is lost, schedules are gone. Always back up the SCDF database.
  3. Task Timeouts: There's no built-in timeout for tasks. If a task hangs (e.g., due to a deadlock), it will run forever, consuming resources. You must implement a timeout in your task code or use the platform's resource limits (e.g., Kubernetes activeDeadlineSeconds).
  4. Error Handling in Schedules: When a scheduled task fails, SCDF logs the failure but does not automatically retry. You need to implement retry logic in your task or use a wrapper that catches exceptions and relaunches.
  5. Monitoring: SCDF exposes metrics via Micrometer, but the docs don't tell you that you need to configure a metrics exporter (like Prometheus) manually. Also, task execution logs are not aggregated by default—you need a centralized logging solution.
TaskTimeoutConfiguration.javaJAVA
1
2
3
// Example: Setting a timeout for a task using Spring Cloud Task properties
// application.properties
spring.cloud.task.timeout=PT30M  # 30 minutes timeout
⚠ No Default Timeout
📊 Production Insight
I once had a task that hung due to a database connection pool exhaustion. It ran for 12 hours before we noticed. Now every task has a timeout.
🎯 Key Takeaway
The docs miss critical production concerns like HA, persistence, timeouts, and monitoring.

Production Best Practices for Task Launchers and Schedules

Based on years of experience, here are my non-negotiable best practices:

  1. Idempotency: Every task must be idempotent. Use a unique batch ID (e.g., based on date and file hash) and check for duplicates before processing. This prevents double processing when schedules run unexpectedly.
  2. Resource Limits: Always set CPU and memory limits for tasks. In Kubernetes, use resource requests and limits. Without them, a rogue task can starve other tasks.
  3. Logging and Monitoring: Use structured logging (JSON) and aggregate logs in a central system (ELK, Splunk). Set up alerts for task failures and long-running tasks.
  4. Graceful Shutdown: Implement a shutdown hook to clean up resources (close DB connections, release locks) when a task is interrupted.
  5. Testing Schedules: Test cron expressions in a staging environment with the same timezone. Use SCDF's dry-run feature if available.
  6. Versioning: When you update a task definition, create a new version and update the schedule to use it. Don't overwrite existing definitions—this can cause running tasks to fail.
  7. Security: Use Spring Security to protect SCDF endpoints. Don't expose the task launch REST API without authentication.
IdempotentTask.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class IdempotentTask {

    @Autowired
    private BatchRepository batchRepository;

    public void process(String batchId) {
        if (batchRepository.existsByBatchId(batchId)) {
            System.out.println("Batch already processed: " + batchId);
            return;
        }
        // process batch
        batchRepository.save(new BatchRecord(batchId));
    }
}
Output
Batch already processed: 20250315-001
💡Use Externalized Configuration
📊 Production Insight
After the duplicate payment incident, we added idempotency checks to all tasks. It added complexity but saved us from future disasters.
🎯 Key Takeaway
Idempotency, resource limits, and monitoring are the pillars of production-ready batch processing.
● Production incidentPOST-MORTEMseverity: high

The Midnight Duplicate Payment Disaster

Symptom
Users saw duplicate transactions in their bank statements. The payment system processed the same batch file multiple times.
Assumption
The developer assumed that the schedule would only trigger once per hour, but the cron expression was wrong.
Root cause
The cron expression '0 0 * ?' was intended to run at the top of every hour, but due to a timezone mismatch (UTC vs local), it ran on both the hour change and the half-hour in some environments. Additionally, the task was not idempotent—it didn't check if the batch file was already processed.
Fix
We fixed the cron expression to '0 0 *' (without the ?) and added a unique batch ID check in the task to ensure idempotency. We also switched to using SCDF's built-in schedule management with timezone configuration.
Key lesson
  • Always test cron expressions with a cron tester tool and verify timezone settings.
  • Make tasks idempotent by using unique batch identifiers and checking for duplicates.
  • Use SCDF's schedule management features rather than raw cron expressions in Kubernetes or Cloud Foundry.
  • Monitor schedule triggers and task executions with alerts for failures or unexpected runs.
  • Implement a manual override mechanism to stop or skip scheduled tasks in emergencies.
Production debug guideSymptom to Action5 entries
Symptom · 01
Task is not launching at scheduled time
Fix
Check SCDF server logs for schedule trigger events. Verify cron expression syntax using a validator like crontab.guru. Ensure the task definition exists and is deployed.
Symptom · 02
Task launches multiple times unexpectedly
Fix
Look for duplicate schedule definitions in SCDF UI or REST API. Check for overlapping cron triggers due to timezone misconfiguration. Review task execution history for concurrent runs.
Symptom · 03
Task fails with 'Task already running' error
Fix
Configure task platform to allow concurrent executions if needed, or set max concurrent tasks. For stateful tasks, ensure proper cleanup in case of abrupt termination.
Symptom · 04
Task execution hangs indefinitely
Fix
Check for deadlocks or network timeouts in the task code. Set a task timeout in SCDF. Use Spring Cloud Task's exit messages to detect stuck tasks.
Symptom · 05
Schedule disappears after SCDF restart
Fix
Ensure schedules are persisted in the SCDF database (e.g., using RDBMS). For Kubernetes, verify that ConfigMaps or Secrets are not lost. Use SCDF's ScheduleInfoRepository to check persistence.
★ Quick Debug Cheat SheetImmediate actions for common batch processing issues
Task not launching
Immediate action
Verify schedule exists in SCDF: dataflow:>schedule list
Commands
dataflow:>schedule list
dataflow:>task execution list --name <taskName>
Fix now
Re-deploy the task definition and re-create the schedule.
Duplicate executions+
Immediate action
Check schedule triggers: dataflow:>schedule info <scheduleName>
Commands
dataflow:>schedule info <scheduleName>
dataflow:>task execution list --name <taskName> --last 10
Fix now
Delete duplicate schedules and fix cron expression.
Task stuck running+
Immediate action
Force stop the task: dataflow:>task execution stop <executionId>
Commands
dataflow:>task execution stop <executionId>
dataflow:>task execution list --name <taskName> --status RUNNING
Fix now
Set a maximum execution duration in task properties.
FeatureSpring Cloud Data FlowApache AirflowPlain Cron
Task LifecycleManaged (tracking, logs)Managed (DAGs)None
ScalingPlatform-native (K8s, CF)Custom executorsManual
Error HandlingBasic retry (manual)Rich retry & alertsNone
UI/DashboardYesYesNo
Learning CurveModerateSteepLow
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
TaskDefinition.java@SpringBootApplicationWhat is Spring Cloud Data Flow?
TaskLaunchController.java@RestControllerTask Launchers
ScheduleConfig.java@ConfigurationScheduling Tasks with Cron Expressions
TaskChainingListener.java@ComponentAdvanced Scheduling
TaskTimeoutConfiguration.javaspring.cloud.task.timeout=PT30M # 30 minutes timeoutWhat the Official Docs Won't Tell You
IdempotentTask.java@ComponentProduction Best Practices for Task Launchers and Schedules

Key takeaways

1
Spring Cloud Data Flow provides a robust platform for orchestrating batch jobs as tasks, with on-demand launching and cron-based scheduling.
2
Production success requires idempotent tasks, proper resource limits, and thorough monitoring. Avoid common pitfalls like local launchers and timezone mismatches.
3
Event-driven and chained tasks extend scheduling beyond cron, but introduce complexity—use them only when necessary and with robust error handling.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
How does Spring Cloud Data Flow ensure exactly-once execution for schedu...
Q02JUNIOR
What is the difference between a Task and a Stream in Spring Cloud Data ...
Q03SENIOR
How do you scale Task Launchers in a Kubernetes environment?
Q01 of 03SENIOR

How does Spring Cloud Data Flow ensure exactly-once execution for scheduled tasks?

ANSWER
SCDF does not guarantee exactly-once execution out of the box. It uses a database to track task executions, but network issues or crashes can cause duplicates. To achieve exactly-once, tasks must be idempotent and use a unique batch identifier. Additionally, the scheduler should be configured with a distributed lock to prevent overlapping triggers.
FAQ · 3 QUESTIONS

Frequently Asked Questions

01
How do I pass arguments to a scheduled task?
02
Can I run multiple instances of the same task concurrently?
03
How do I handle task failures in a schedule?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Spring Cloud. Mark it forged?

5 min read · try the examples if you haven't

Previous
ETL with Spring Cloud Data Flow: Batch and Stream Processing Pipelines
27 / 34 · Spring Cloud
Next
Spring Cloud AWS Messaging: SQS and SNS Integration