[ 
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)

Reply via email to