Federico Mariani created CAMEL-24134:
----------------------------------------

             Summary: camel-resilience4j / camel-microprofile-fault-tolerance - 
Circuit Breaker EIP: timed-out task writes results back to the original 
exchange, racing with fallback processing
                 Key: CAMEL-24134
                 URL: https://issues.apache.org/jira/browse/CAMEL-24134
             Project: Camel
          Issue Type: Bug
          Components: eip, camel-microprofile-fault-tolerance
    Affects Versions: 4.21.0
            Reporter: Federico Mariani


In both Circuit Breaker implementations, the protected processor runs on a 
correlated copy of the exchange, but the worker thread writes the outcome back 
to the *original* exchange with no guard against the caller having already 
moved on after a timeout.

camel-resilience4j, {{ResilienceProcessor.processTask()}} (the identical 
pattern exists in {{FaultToleranceProcessor.CircuitBreakerTask.call()}} with 
SmallRye's thread offload):

{code:java}
// handle the processing result
if (copy.getException() != null) {
    exchange.setException(copy.getException());
} else {
    // copy the result as its regarded as success
    ExchangeHelper.copyResults(exchange, copy);
    
exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_SUCCESSFUL_EXECUTION,
 true);
    ...
}
{code}

When the TimeLimiter (or SmallRye timeout) fires, the calling thread 
immediately runs the fallback and continues routing on the original exchange. 
Meanwhile the worker thread - unless it happens to be blocked at an 
interruptible point, and always when {{timeoutCancelRunningFuture=false}} - 
eventually completes and executes the write-back above, concurrently mutating 
the same original exchange:

* it can overwrite the fallback result with the late result;
* it can reinstate an exception after the fallback cleared it;
* {{ExchangeHelper.copyResults}} is not atomic, so body/headers/properties can 
interleave with the route's own processing.

The exchange copy only protects against *downstream processors* mutating the 
exchange during timeout processing (as the code comment says); the write-back 
path itself is unguarded.

Suggested fix: the worker must not touch the original exchange once the call 
has timed out - e.g. a per-task completed/timed-out flag (CAS) checked before 
the write-back, or perform the write-back on the caller side only when the 
future completed within the timeout.

_Filed by Claude Code on behalf of Federico Mariani (fmariani)_



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to