[
https://issues.apache.org/jira/browse/CAMEL-11114?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18107995#comment-18107995
]
Guillaume Nodet commented on CAMEL-11114:
-----------------------------------------
{noformat:title=AI-generated content}
_Claude Code on behalf of gnodet_
{noformat}
h2. Updated Implementation Proposal: Cache EIP using {{KeyValueRepository}} SPI
This updates the [previous
proposal|https://issues.apache.org/jira/browse/CAMEL-11114?focusedCommentId=18107456]
to leverage the {{KeyValueRepository}} SPI from
[CAMEL-24463|https://issues.apache.org/jira/browse/CAMEL-24463] ([PR
#25631|https://github.com/apache/camel/pull/25631]) instead of introducing a
new {{CacheRepository}} SPI.
h3. Why {{KeyValueRepository}} instead of a new {{CacheRepository}}
The original proposal introduced a {{CacheRepository}} SPI following the
{{IdempotentRepository}} pattern. However,
[CAMEL-24463|https://issues.apache.org/jira/browse/CAMEL-24463] already
introduced {{KeyValueRepository}} in {{camel-api}} to solve exactly the N×M
problem — one SPI per storage technology serves all patterns (Idempotent,
Aggregation, Cache, State Store) through adapters or direct use.
||Method||KeyValueRepository||Proposed CacheRepository||
|{{get(key)}}|Yes|Yes|
|{{put(key, value, ttlMillis)}}|Yes|Yes|
|{{contains(key)}}|Yes|Yes|
|{{clear()}}|Yes|Yes|
|In-memory default|{{MemoryKeyValueRepository}} already exists|Would need new
impl|
|TTL support|Built-in with lazy eviction|Would need to build|
|State-store integration|Direct — {{camel-state-store}} already uses it|Needs
adapter layer|
Adding a {{CacheRepository}} would *contradict* the design of CAMEL-24463. The
Cache EIP should use {{KeyValueRepository}} directly — no new SPI, no adapter.
h3. What changes from the previous proposal
* *Removed:* {{CacheRepository}} SPI ({{camel-api}}) — not needed
* *Removed:* {{MemoryCacheRepository}} ({{camel-support}}) — use
{{MemoryKeyValueRepository}} instead
* *Removed:* {{StateStoreBackendCacheRepository}} adapter — not needed,
{{camel-state-store}} already uses {{KeyValueRepository}}
* *Removed:* {{Exchange.CACHE_HIT}} property constant — can be a
processor-local constant
* *Simplified:* {{CacheDefinition}} references {{KeyValueRepository}} directly
* *Simplified:* {{CacheReifier}} auto-discovers {{KeyValueRepository}} from
registry or auto-creates {{MemoryKeyValueRepository}}
h3. Proposed DSL Syntax (unchanged)
*Java DSL:*
{code:java}
// Minimal — auto-creates MemoryKeyValueRepository, no TTL
from("direct:start")
.cache(simple("${header.productId}"))
.to("http://expensive-service")
.unmarshal().json()
.end()
.to("direct:continue");
// With options
from("direct:start")
.cache(simple("${header.productId}"))
.ttl("10m")
.keyValueRepository("myRedisKvr")
.to("http://expensive-service")
.end();
// Expression clause form
from("direct:start")
.cache().simple("${header.productId}")
.ttl(600000)
.to("http://expensive-service")
.end();
{code}
*XML DSL:*
{code:xml}
<cache keyValueRepository="myCache" ttl="10m">
<simple>${header.productId}</simple>
<to uri="http://expensive-service"/>
</cache>
{code}
*YAML DSL:*
{code:yaml}
- cache:
simple: "${header.productId}"
keyValueRepository: "myCache"
ttl: "10m"
steps:
- to: "http://expensive-service"
{code}
h3. Updated File Inventory (10 files + generated)
||#||File||Action||Description||
|1|{{core/camel-core-model/.../model/CacheDefinition.java}}|CREATE|Model
definition, extends {{OutputExpressionNode}}, fields: {{keyValueRepository}}
(ref), {{keyValueRepositoryBean}} (programmatic), {{ttl}}, {{cacheNull}}|
|2|{{core/camel-core-model/.../model/ProcessorDefinition.java}}|MODIFY|Add
{{cache()}} DSL entry points|
|3|{{core/camel-core-processor/.../processor/CacheProcessor.java}}|CREATE|Runtime
processor using {{KeyValueRepository}} directly|
|4|{{core/camel-core-reifier/.../reifier/CacheReifier.java}}|CREATE|Reifier
extending {{ExpressionReifier}}, auto-creates {{MemoryKeyValueRepository}} if
none configured|
|5|{{core/camel-core-reifier/.../reifier/ProcessorReifier.java}}|MODIFY|Register
{{CacheDefinition}} in {{coreReifier()}}|
|6|{{core/camel-core/src/test/.../processor/CacheProcessorTest.java}}|CREATE|Integration
tests|
|7|{{dsl/camel-yaml-dsl/.../CacheTest.groovy}}|CREATE|YAML DSL parsing tests|
|8|EIP doc page ({{cache-eip.adoc}})|CREATE|Documentation|
|9|EIP nav ({{nav.adoc}})|MODIFY|Navigation entry|
|10|Upgrade guide ({{camel-4x-upgrade-guide-4_23.adoc}})|MODIFY|New feature
entry|
h3. Key Components
*{{CacheDefinition}}* ({{camel-core-model}}):
* Extends {{OutputExpressionNode}} (same base as
{{IdempotentConsumerDefinition}})
* Fields: {{keyValueRepository}} (String ref), {{keyValueRepositoryBean}}
(transient {{KeyValueRepository}}), {{ttl}} (duration string), {{cacheNull}}
(boolean, default false)
*{{CacheProcessor}}* ({{camel-core-processor}}):
* Uses {{KeyValueRepository}} directly for {{get}}/{{put}} with TTL
* Runtime flow: evaluate key → check cache → HIT: set body, skip block → MISS:
execute block, cache result body on success
* Cache exceptions logged but *never* propagated to exchange (graceful
degradation)
* Failed exchanges are never cached
*{{CacheReifier}}* ({{camel-core-reifier}}):
* Extends {{ExpressionReifier<CacheDefinition>}} (same as
{{IdempotentConsumerReifier}})
* Resolution order: explicit bean → registry ref → auto-discover single
{{KeyValueRepository}} from registry → auto-create {{MemoryKeyValueRepository}}
* This mirrors exactly how {{IdempotentConsumerReifier}} resolves
{{KeyValueRepository}} in PR #25631
h3. Integration with camel-state-store
No adapter needed. Since {{camel-state-store}} already uses
{{KeyValueRepository}} as its backend interface (PR #25631 removed the old
{{StateStoreBackend}}), any {{KeyValueRepository}} registered in the Camel
registry works for both the {{state-store:}} component and the Cache EIP:
{code:java}
// One KeyValueRepository serves both Cache EIP and state-store component
@BindToRegistry("myKvr")
public KeyValueRepository myKvr() {
// Any KVR implementation: Redis, JDBC, Infinispan, etc.
return new SomeRedisKeyValueRepository("redis://localhost:6379");
}
// Route using Cache EIP
from("direct:cached")
.cache(simple("${header.key}"))
.keyValueRepository("myKvr")
.ttl("5m")
.to("http://service")
.end();
// Route using state-store component (same backend)
from("direct:store")
.to("state-store:myStore?operation=put");
{code}
h3. Items for Discussion (updated)
# *Naming of the repository attribute:* {{keyValueRepository}} (verbose but
consistent) vs {{repository}} (shorter). Recommendation: {{keyValueRepository}}
for consistency with the SPI name, matching how {{idempotentRepository}} names
its attribute.
# *Auto-discovery:* Should the Cache EIP auto-discover a {{KeyValueRepository}}
from the registry (like the updated {{IdempotentConsumerReifier}} does in PR
#25631)? Recommendation: yes, same resolution chain.
# All other discussion items from the [previous
proposal|https://issues.apache.org/jira/browse/CAMEL-11114?focusedCommentId=18107456]
(default auto-creation, body-only caching, relationship to JCachePolicy)
remain unchanged.
> Create cache DSL
> ----------------
>
> Key: CAMEL-11114
> URL: https://issues.apache.org/jira/browse/CAMEL-11114
> Project: Camel
> Issue Type: New Feature
> Components: camel-core, eip
> Reporter: Nicola Ferraro
> Priority: Major
> Fix For: Future
>
>
> We should evaluate adding a new "cache" dsl that can be used with all cache
> components in Camel. A default implementation may use also caffeine, included
> in camel-core.
> A possible usage example may be:
> {code}
> from("xxx")
> .cache().on("${header.yyy}").ttl(600000) // caches the body
>
> .to("http4://a-service-that-makes-me-pay-for-each-request.com/api/expensive-endpoint")
> .transform().zzz()
>
> .to("http4://or-a-service-that-i-can-call-few-times-a-day.com/api/limited-endpoint")
> .unmarshal()
> .endCache()
> {code}
> It should be also useful to protect internal services when using Camel e.g.
> as a api-gateway (almost what hystrix does in case of failure of the target
> host).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)