Repository navigation
fix(mqtt): scope session state per instance and guard a missing client id - #8092
Merged
yuluo-yx merged 1 commit intoOct 24, 2026
Merged
Conversation
…t id (apache#8021) SessionRepository kept its sessions in a static map while every other repository in the package keeps them per instance. Two consequences: - state leaked between instances, so a repository created for one connection, or for one test, could observe and evict the sessions of another; - the module tests only passed because Singleton still held whatever the previously executed test class had registered. Subscribe and Unsubscribe look the session up with the client id taken from ChannelRepository, which is null for a channel that CONNECT never registered or whose ownership was taken over. The session index rejects a null key, so the SUBSCRIBE failed with an NPE before the SUBACK was sent. Publish and PubRel already guard this case; the two handlers now do the same. Tests: SessionRepositoryTest covers the instance isolation, SubscribeQosTest and UnsubscribeTest cover the missing client id, and MqttTransportHandlerTest registers the repositories it reaches through the handler so it no longer depends on a leaked Singleton.
yuluo-yx
approved these changes
Oct 24, 2026
This was referenced Oct 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
SessionRepositorywas the only repository inshenyu-protocol-mqttthat kept its state in astaticmap. It is now a per-instance map, likeChannelRepository,InflightRepository,SubscribeRepositoryandWillRepositoryalready are.SubscribeandUnsubscribelooked the session up with the client id read fromChannelRepository:ChannelRepository.get(channel)returnsnullfor a channel that CONNECT never registered, orwhose client id ownership was taken over by a newer connection (#7918). The session index is a
ConcurrentHashMapkeyed by client id, and anullkey throws:so the whole SUBSCRIBE / UNSUBSCRIBE aborted before the SUBACK / UNSUBACK was written.
Publish.registerInboundandPubRel.pubRelalready guard this case; the two handlers now do thesame and simply skip the session bookkeeping.
Why the tests passed before
Because the map was
static,Singleton.INST.single(SessionRepository.class, new SessionRepository())did not create fresh state: every instance shared one map, so a test could see sessions registered by
a test class that ran earlier in the same JVM. That masked both the missing fixtures and the null-key
NPE.
MqttTransportHandlerTestshows the pattern clearly — run on its own, before this change, itfails with
Singleton.get(InflightRepository.class)returningnull; it only passed as part of thewhole module because an earlier test class had left that registration behind.
Tests
SessionRepositoryTest(new)removeonly affects the owning instanceSubscribeQosTest#subscribeWithoutClientIdStillAcknowledgesUnsubscribeTest#unsubscribeWithoutClientIdStillAcknowledgesMqttTransportHandlerTestInflightRepositoryandTopicRepository, so the class no longer depends on leakedSingletonstateReverting only the three main-code files makes all four new assertions fail with the exact
production error (
Cannot invoke "Object.hashCode()" because "key" is null), which is theregression this PR removes.
checkstyle:checkon the module is clean.Notes
ChannelRepository, so the session is still updated on SUBSCRIBE / UNSUBSCRIBE.#8089also targets the MQTT fixture isolation but does not compile (variable channelRepository is already defined in class); this PR supersedes it.