shashank created CAMEL-26784:
--------------------------------

             Summary: camel-hazelcast - HazelcastRoutePolicy keeps the 
leadership after its route is removed, and a follower consumes after its route 
is suspended and resumed
                 Key: CAMEL-26784
                 URL: https://issues.apache.org/jira/browse/CAMEL-26784
             Project: Camel
          Issue Type: Bug
          Components: camel-hazelcast
            Reporter: shashank
            Assignee: shashank


{{HazelcastRoutePolicy}} implements the leadership with a thread that takes the 
Hazelcast lock and then sleeps while holding it. The thread is only interrupted 
(and the lock released) when the policy itself stops, which happens when the 
CamelContext stops. Two route lifecycle callbacks are not handled:

*A removed route keeps the leadership.* The policy does not override 
{{onRemove}}. When the route is removed on the leader (with {{removeRoute}}, or 
a route reload, which removes the route and adds it again), the policy keeps 
the lock. The other nodes wait for the lock forever, so the route runs nowhere 
in the cluster:
* removed on the leader: no other node takes over;
* reloaded on the leader with a new policy instance (for example a policy 
created in the route builder): the new policy waits for the lock its 
predecessor still holds, and so do the other nodes.

A route added again with the same policy instance still works, as the policy 
stays the leader.

*A follower starts its consumer when its route is resumed.* On a node that is 
not the leader the policy stops the consumer of the route in {{onStart}} and 
starts it when the node takes the leadership. {{onSuspend}} removes the route 
from the routes to start on takeover, but {{onResume}} is not overridden, so a 
{{suspendRoute}} / {{resumeRoute}} on a follower (from JMX, the controlbus, a 
scheduled route policy) resumes the consumer and nothing stops it again. 
Depending on the consumer the follower then consumes next to the leader, or 
(SEDA, whose resume does not restart the threads that a stop ended) its 
consumer is reported started without consuming. The same happens when the 
follower takes the leadership while its route is suspended: the takeover does 
not start the suspended route, and the resume does not really start the 
consumer the policy had stopped, so no node consumes.

Expected: a removed route gives up the leadership (when no other route uses the 
policy), and a policy that gets a route again competes for it again; a resumed 
route on a follower keeps its consumer stopped until the node takes the 
leadership, as after a start, and a resumed route on a node that took the 
leadership meanwhile consumes.

Note for the fix: interrupting the leadership thread is not enough on a client 
instance ({{HazelcastRoutePolicy}} created with a {{HazelcastClient}}): a 
client call made by an interrupted thread fails at once, so the {{remove}} and 
{{unlock}} in the thread's {{finally}} block fail and the lock stays held until 
the client disconnects. This already applies today to a policy that stops while 
it holds the lock.

Suggested fix: count the routes of the policy; in {{onRemove}} of the last one 
make the leadership thread release the lock and end without interrupting it 
(for example it waits on a latch instead of sleeping), and start a new one in 
{{onInit}} when a route is added again; release the lock with the interrupt 
flag cleared; in {{onResume}} stop the consumer when the node is not the 
leader, as {{onStart}} does, and restart a consumer the policy had stopped when 
the node is now the leader.

Found with Specula (TLA+ model checked against traces of the real code) and 
confirmed with tests on the real classes (two CamelContexts on an embedded 
Hazelcast member, and a Hazelcast client).

_Filed with Claude Code on behalf of allthingssecurity._



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

Reply via email to