Skip to content

CAMEL-24272: Migrate reconnection loops from ForegroundTask to BackgroundTask - #25164

Merged
gnodet merged 5 commits into
apache:mainfrom
gnodet:investigate-and-fix-camel-24272-use-backgroundtas
Jul 28, 2026
Merged

CAMEL-24272: Migrate reconnection loops from ForegroundTask to BackgroundTask#25164
gnodet merged 5 commits into
apache:mainfrom
gnodet:investigate-and-fix-camel-24272-use-backgroundtas

Conversation

@gnodet

@gnodet gnodet commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Claude Code on behalf of gnodet

Migrate reconnection loops across 9 components from ForegroundTask to BackgroundTask so they register with TaskManagerRegistry and become visible in the TUI/CLI/dev console internal task view.

Note: The Kafka component (KafkaFetchRecords.java) was already independently migrated to BackgroundTask upstream, so this PR now covers the remaining 9 components.

Components migrated

Component File Pattern
FTP FtpOperations.java Multi-iteration retry (connect)
SFTP SftpOperations.java Multi-iteration retry (connect)
MLLP TcpServerBindThread.java Multi-iteration retry (accept)
Infinispan InfinispanRemoteManager.java Multi-iteration retry (schema registration)
Google Pub/Sub GooglePubsubConsumer.java Single-iteration delay (reconnect backoff)
ZooKeeper ZooKeeperConsumer.java Single-iteration delay (reconnect backoff)
Hazelcast SEDA HazelcastSedaConsumer.java Single-iteration delay (error recovery)
Salesforce SubscriptionHelper.java Single-iteration delay (handshake + subscribe retry)
MongoDB GridFS GridFsConsumer.java Unlimited polling loop

Key changes

  • ForegroundTaskBackgroundTask: Each migrated task now registers with TaskManagerRegistry, making internal retry/reconnect tasks visible through management interfaces
  • IterationBudgetIterationTimeBudget: BackgroundTask requires a TimeBudget; used withUnlimitedDuration() to preserve existing behavior
  • ScheduledExecutorService: Each component gets a dedicated scheduled executor for task scheduling, managed through Camel's ExecutorServiceManager for proper lifecycle
  • Single-iteration delay interval: For tasks using maxIterations(1) as a delay mechanism, interval is set to Duration.ofMillis(1) since ScheduledExecutorService.scheduleWithFixedDelay() rejects delay=0

Test plan

  • Existing tests pass for all 9 affected component modules
  • HazelcastSedaRecoverableConsumerNewTransactionTest and HazelcastSedaRecoverableConsumerRollbackTest verified locally
  • Full CI build passes

🤖 Generated with Claude Code

…oundTask

Switch all reconnection and retry loops from ForegroundTask to
BackgroundTask so they register with the TaskManagerRegistry and
become visible in the dev console, CLI and TUI internal-tasks view.

Affected components:
- camel-kafka: KafkaConsumer create/subscribe retry loops
- camel-ftp: FTP and SFTP reconnection loops
- camel-mllp: TCP server bind retry loop
- camel-infinispan: Schema registration retry loop
- camel-google-pubsub: Error recovery delay
- camel-zookeeper: Reconnection backoff delay
- camel-hazelcast: SEDA error recovery delay
- camel-salesforce: Handshake and subscribe retry delays
- camel-mongodb-gridfs: GridFS polling loop

Each migration:
- Replaces Tasks.foregroundTask() with Tasks.backgroundTask()
- Switches from IterationBudget to TimeBudget via iterationTimeBudget()
- Adds a ScheduledExecutorService with proper lifecycle management
- Assigns a descriptive task name for visibility in monitoring tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-ftp
  • components/camel-google/camel-google-pubsub
  • components/camel-hazelcast
  • components/camel-infinispan/camel-infinispan
  • components/camel-mllp
  • components/camel-mongodb-gridfs
  • components/camel-salesforce/camel-salesforce-component
  • components/camel-zookeeper
  • dsl/camel-jbang/camel-jbang-plugin-tui

ℹ️ Dependent modules were not tested because the total number of affected modules exceeded the threshold (50). Use the test-dependents label to force testing all dependents.


🔬 Scalpel shadow comparison — Scalpel: 23 tested, 29 compile-only — current: 23 all tested

Maveniverse Scalpel detected 52 affected modules (current approach: 23).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 23 modules (9 direct + 14 downstream), skip tests for 29 (generated code, meta-modules)

Modules Scalpel would test (23)
  • camel-azure-files
  • camel-ftp
  • camel-google-pubsub
  • camel-hazelcast
  • camel-infinispan
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-jsch
  • camel-langchain4j-embeddings
  • camel-langchain4j-embeddingstore
  • camel-launcher-container
  • camel-mllp
  • camel-mongodb-gridfs
  • camel-salesforce
  • camel-salesforce-codegen
  • camel-salesforce-maven-plugin
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
  • camel-zookeeper
  • camel-zookeeper-master
Modules with tests skipped (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • components/camel-infinispan/camel-infinispan: 2 test(s) disabled on GitHub Actions
  • components/camel-mllp: 1 test(s) disabled on GitHub Actions
Build reactor — dependencies compiled but only changed modules were tested (52 modules)
  • Camel :: AI :: LangChain4j :: Embedding
  • Camel :: AI :: LangChain4j :: EmbeddingStore
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Azure :: Files
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: FTP
  • Camel :: Google :: PubSub
  • Camel :: HazelCast
  • Camel :: Infinispan :: Remote
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Jsch
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: MLLP
  • Camel :: MongoDB GridFS
  • Camel :: Salesforce
  • Camel :: Salesforce :: CodeGen
  • Camel :: Salesforce :: Maven Plugin
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin
  • Camel :: Zookeeper
  • Camel :: Zookeeper Master

⚙️ View full build and test results

gnodet and others added 2 commits July 27, 2026 21:15
…asks

ScheduledExecutorService.scheduleWithFixedDelay() rejects delay=0
with IllegalArgumentException. Change interval from Duration.ZERO to
Duration.ofMillis(1) for single-iteration delay tasks where the
interval is irrelevant (maxIterations=1).

Affected: camel-hazelcast, camel-zookeeper, camel-google-pubsub,
camel-salesforce.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Upstream already migrated KafkaFetchRecords to BackgroundTask with a unified
reconnectTask() pattern. Taking upstream's version since it supersedes our
separate create/subscribe task approach.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test expectations were not updated when the TypeConvertersTab and
TransformersTab were added on main. Update count from 29 to 31 and add
the Y and F shortcut letters to the historical sequence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the dsl label Jul 27, 2026
@gnodet
gnodet marked this pull request as ready for review July 28, 2026 00:14
@gnodet
gnodet requested review from davsclaus and orpiske July 28, 2026 00:15

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this well-structured migration, Guillaume! The changes look good — the ForegroundTask-to-BackgroundTask migration is mechanically sound across all 9 components, executor lifecycle management is correct, and CI passes.

A couple of minor observations noted inline — nothing blocking.

This review does not replace specialized static analysis tools (SonarCloud) or AI review tools (CodeRabbit).

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of davsclaus

}

executor = endpoint.createExecutor(this);
taskExecutor = endpoint.getCamelContext().getExecutorServiceManager()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor observation: this creates a single-thread scheduled executor shared across all concurrentConsumers threads. When multiple subscribers fail simultaneously, their backoff delays are serialized rather than independent (unlike the prior ForegroundTask which slept per-thread).

In practice the impact is negligible since the delay supplier is () -> true (near-instant execution), but worth noting the behavioral difference. The same applies to HazelcastSedaConsumer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good observation. The single-thread executor does serialize backoff delays across concurrent subscribers. However, this is a single-iteration delay task (maxIterations=1) used only as a timed pause before retry — it completes almost instantly once the initial delay expires. With a 10-second reconnect delay, even if multiple subscribers fail simultaneously, the serialization overhead is negligible compared to the delay itself.

That said, if this becomes a concern in practice (e.g. very high concurrency with frequent failures), the executor pool size could be bumped. For now, keeping it single-threaded matches the pattern used by the other components in this PR.

.withMaxIterations(IterationBoundedBudget.UNLIMITED_ITERATIONS)
BlockingTask task = Tasks.backgroundTask()
.withBudget(Budgets.iterationTimeBudget()
.withMaxIterations(Integer.MAX_VALUE)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Integer.MAX_VALUE is already the default for IterationTimeBoundedBudgetBuilder.maxIterations, so this call could be omitted entirely:

Suggested change
.withMaxIterations(Integer.MAX_VALUE)
// maxIterations defaults to Integer.MAX_VALUE (effectively unlimited)

Alternatively, keeping it explicit is fine for readability — just noting the redundancy.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — removed the redundant withMaxIterations(Integer.MAX_VALUE) and added a comment noting the default. Fixed in the next push.

… GridFsConsumer

Integer.MAX_VALUE is already the default for IterationTimeBoundedBudgetBuilder,
so the explicit call is unnecessary. Added a comment noting the default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet
gnodet requested a review from davsclaus July 28, 2026 06:24
@davsclaus

Copy link
Copy Markdown
Contributor

LGTM

@gnodet gnodet added this to the 4.22.0 milestone Jul 28, 2026
@gnodet gnodet self-assigned this Jul 28, 2026
@gnodet gnodet added the task label Jul 28, 2026
@gnodet
gnodet merged commit 5e245ae into apache:main Jul 28, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants