I observed a race condition where the Jetty server can start on passive broker (slave), whereas it should not be the case.
The problem is that Jetty server is not a broker service, so Jetty doesn't consider the broker type (active or passive).
I investigated and I found the sequence explaining this behavior:
BrokerService.@PostConstruct -> autostart() -> start() is called
- With
startAsync=true, startPersistenceAdapter() and startBroker() both launch background threads and return immediately
- At this point
slave=true - the broker is still waiting to acquire the file/JDBC lock
- Spring continues initializing the remaining beans from
jetty.xml
- The
invokeStart bean - a MethodInvokingFactoryBean that calls Server.start() - fires unconditionally during Spring context initialization
- Jetty is now running on the passive/slave
The passive/slave stays in this state (Jetty up, broker passive) until the master fails. Then the background thread acquires the lock, calls startAllConnectors(), and slave becomes false. But Jetty was already running 😄
NB: this case only happens with startAsync=true in a master/slave configuration - a combination that's commonly recommended to avoid blocking the Spring context during lock contention.
NB: this case doesn't happen with startAsync=false: BrokerService.@PostConstruct blocks until the broker fully starts (lock acquired -> startAllConnectors() -> startedLatch.countDown()). Spring only continues to the jetty.xml beans after that. By then the broker is already master/active, so invokeStart fires the right time.
I observed a race condition where the Jetty server can start on passive broker (slave), whereas it should not be the case.
The problem is that Jetty server is not a broker service, so Jetty doesn't consider the broker type (active or passive).
I investigated and I found the sequence explaining this behavior:
BrokerService.@PostConstruct->autostart()->start()is calledstartAsync=true,startPersistenceAdapter()andstartBroker()both launch background threads and return immediatelyslave=true- the broker is still waiting to acquire the file/JDBC lockjetty.xmlinvokeStartbean - aMethodInvokingFactoryBeanthat callsServer.start()- fires unconditionally during Spring context initializationThe passive/slave stays in this state (Jetty up, broker passive) until the master fails. Then the background thread acquires the lock, calls
startAllConnectors(), andslavebecomesfalse. But Jetty was already running 😄NB: this case only happens with
startAsync=truein a master/slave configuration - a combination that's commonly recommended to avoid blocking the Spring context during lock contention.NB: this case doesn't happen with
startAsync=false:BrokerService.@PostConstructblocks until the broker fully starts (lock acquired ->startAllConnectors()->startedLatch.countDown()). Spring only continues to thejetty.xmlbeans after that. By then the broker is already master/active, soinvokeStartfires the right time.