[ 
https://issues.apache.org/jira/browse/CAMEL-26740?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Claus Ibsen resolved CAMEL-26740.
---------------------------------
    Resolution: Fixed

Merged in https://github.com/apache/camel/pull/29434 (a44c49fa3420) for 4.24.0.

_Claude Code on behalf of davsclaus_

> camel-kafka - KafkaKeyValueRepository keys() and size() fail once an entry 
> has expired, and putIfAbsent can report an insert that did not happen
> ------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-26740
>                 URL: https://issues.apache.org/jira/browse/CAMEL-26740
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-kafka
>            Reporter: shashank
>            Assignee: shashank
>            Priority: Major
>             Fix For: 4.24.0
>
>
> Two problems with the expiry handling of {{KafkaKeyValueRepository}} 
> (camel-kafka, since 4.23):
> *1. keys() and size() throw UnsupportedOperationException once an entry has 
> expired*
> {{keys()}} and {{size()}} (also the JMX attribute {{Size}}) first call 
> {{evictExpired()}}, which removes expired entries with {{Iterator.remove()}} 
> on {{cache.entrySet().iterator()}}. The cache comes from {{LRUCacheFactory}}, 
> by default a {{SimpleLRUCache}}, whose {{entrySet()}} is an unmodifiable copy 
> ({{Collectors.toUnmodifiableSet()}}). So as soon as one entry with a time to 
> live has expired, every call of {{keys()}} and {{size()}} fails with 
> {{UnsupportedOperationException: remove}}, and so do the callers of 
> {{keys()}} (for example the {{keys}} and {{size}} operations of the 
> state-store component). The class Javadoc says that entries replayed from the 
> topic on startup "will appear as expired immediately", so after a restart 
> this can happen at once.
> {code:java}
> repo.put("a", "1", Duration.ofSeconds(1));
> // one second later
> repo.keys(); // UnsupportedOperationException: remove
> {code}
> *2. putIfAbsent can return null ("stored") although nothing was stored or 
> sent*
> {{putIfAbsent}} reads the entry, removes it if it has expired, then calls 
> {{cache.putIfAbsent}}. When another writer stored an entry for the key in 
> between (another thread, or the topic poller applying a record whose expiry 
> time has already passed, which it does when it lags behind by more than the 
> time to live), and that entry has expired, the method returns {{null}}. It 
> has not stored the caller's value and has not sent it to the topic. The 
> interface Javadoc, the method's Javadoc and the state-store docs all say 
> {{null}} means stored, so the caller acts as the owner of the key while no 
> other instance (and no restart) can see its value, and the next 
> {{putIfAbsent}} on that key also succeeds.
> Expected: {{keys()}} and {{size()}} skip and evict expired entries; 
> {{putIfAbsent}} returns {{null}} only when it stored and sent the value.
> Suggested fix: in {{evictExpired()}} remove through the cache 
> ({{cache.remove(key, entry)}}, so a key put again meanwhile is kept); in 
> {{putIfAbsent}} replace an expired entry atomically ({{cache.replace(key, 
> expired, newEntry)}}) and, when that fails because the key changed meanwhile, 
> look again.
> Found by a code review of the Kafka repositories; both reproduce in unit 
> tests with a {{MockProducer}} and no broker.
> _Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to