Skip to content

CAMEL-20199: Remove ThreadLocal Yaml caching in SnakeYAMLDataFormat - #25240

Merged
davsclaus merged 1 commit into
apache:mainfrom
gnodet:CAMEL-20199/snakeyaml-remove-threadlocal
Aug 3, 2026
Merged

CAMEL-20199: Remove ThreadLocal Yaml caching in SnakeYAMLDataFormat#25240
davsclaus merged 1 commit into
apache:mainfrom
gnodet:CAMEL-20199/snakeyaml-remove-threadlocal

Conversation

@gnodet

@gnodet gnodet commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Remove ThreadLocal<WeakReference<Yaml>> caching from SnakeYAMLDataFormat that is ineffective with virtual threads (each VT is short-lived, so the cache never gets a hit)
  • Create a fresh Yaml instance per marshal/unmarshal operation instead (Yaml is not thread-safe, and its constructor cost is modest)
  • Rename getYaml() to createYaml() to reflect the new semantics

Part of the broader CAMEL-20199 effort to complete virtual thread support across components.

Test plan

  • All 19 existing tests in camel-snakeyaml pass (including SnakeYAMLConcurrentTest)

Claude Code on behalf of Guillaume Nodet

With virtual threads, the ThreadLocal<WeakReference<Yaml>> cache is
ineffective because each virtual thread is short-lived and creates a
new Yaml instance that is never reused. Since Yaml is not thread-safe
(it stores state during parsing/dumping), the simplest correct approach
is to create a fresh Yaml instance for each marshal/unmarshal operation.
The Yaml constructor cost is modest - the real work happens during
parsing, not construction.

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

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-snakeyaml

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

Maveniverse Scalpel detected 38 affected modules (current approach: 9).

⚠️ 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 9 modules (1 direct + 8 downstream), skip tests for 29 (generated code, meta-modules)

Modules Scalpel would test (9)
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-launcher-container
  • camel-snakeyaml
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
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

All tested modules (38 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • 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 :: 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 :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: SnakeYAML
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@gnodet
gnodet marked this pull request as ready for review July 30, 2026 09:38

@gnodet gnodet left a comment

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.

Looks good ✅

Clean removal of ThreadLocal caching in SnakeYAMLDataFormat in favor of per-operation Yaml instantiation. The rationale is sound: ThreadLocal+WeakReference caching is ineffective with virtual threads (each VT is short-lived, cache never gets a hit), and the Yaml constructor cost is modest relative to parsing/dumping.

Good observations:

  • Method rename from getYaml() to createYaml() correctly communicates the new semantics
  • The concurrent test from CAMEL-24152 (SnakeYAMLConcurrentTest) provides good coverage for validating this change
  • Consistent with other CAMEL-20199 ThreadLocal removal work

Minor pre-existing note (not introduced by this PR): when a user provides a custom constructor/representer/dumperOptions via setters, createYaml() reuses that same stateful instance across concurrent calls. Since SnakeYAML's BaseConstructor and Representer are heavily stateful, concurrent use of a shared user-provided instance could be unsafe. The default code path (no user-provided objects) correctly creates fresh instances and is safe. Consider a follow-up ticket to document that user-provided constructor/representer must be thread-safe.

CI passes on both JDK 17 and JDK 25.

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

Claude Code on behalf of Guillaume Nodet

@gnodet
gnodet requested review from davsclaus and oscerd July 31, 2026 05:35

@gnodet gnodet left a comment

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.

Review Summary (informational — not a formal approval)

Clean, well-motivated removal of ineffective ThreadLocal+WeakReference caching in SnakeYAMLDataFormat. With virtual threads the cache miss rate approaches 100%, making per-operation Yaml instantiation the right approach.

Key observations

  1. Method rename from getYaml() to createYaml() — Excellent naming choice that correctly communicates each call produces a fresh instance rather than returning a cached one. Since getYaml() was private, there is no API compatibility concern.

  2. Thread safety — The default code path (no user-provided constructor/representer) correctly creates fresh instances per call and is safe. The existing SnakeYAMLConcurrentTest (8 threads × 500 iterations) validates this.

  3. Pre-existing concern (not introduced by this PR): When a user sets a custom constructor or representer, createYaml() reuses that same stateful instance across concurrent calls. SnakeYAML's BaseConstructor and Representer maintain internal maps during parse/dump, so concurrent use of a shared user-provided instance is unsafe. This was equally unsafe with the ThreadLocal approach, so this PR does not worsen the situation. A follow-up JIRA ticket to document or address this would be valuable.

  4. Minor: doStop() now only calls super.doStop() (a no-op). The override could be removed, but keeping it for symmetry with doStart() is harmless.

No documentation update needed — purely internal implementation change with no user-visible behavioral difference. Consistent with the broader CAMEL-20199 virtual thread support effort.

Note: This is an informational review only — an agent cannot approve its operator's own PRs. Human review is required.


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

Claude Code on behalf of @gnodet

@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.

Clean, well-motivated simplification.

After CAMEL-24152 (fdd9a7ba2b29), the ThreadLocal<WeakReference<Yaml>> cache was already recreating all collaborators (BaseConstructor, Representer, DumperOptions, Resolver) on every call — so the cache only saved the trivial Yaml constructor cost. Removing it:

  • Eliminates dead complexity
  • Improves virtual thread compatibility (ThreadLocal never hits on short-lived VTs)
  • getYaml()createYaml() rename is safe (private method, final class)
  • SnakeYAMLConcurrentTest (8 threads × 500 iterations) covers concurrent correctness
  • CI green on JDK 17 + JDK 25

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

Claude Code on behalf of davsclaus

@davsclaus davsclaus added the enhancement New feature or request label Aug 3, 2026
@davsclaus davsclaus added this to the 4.22.0 milestone Aug 3, 2026
@davsclaus
davsclaus merged commit b25b261 into apache:main Aug 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

components enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants