-
Notifications
You must be signed in to change notification settings - Fork 38.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot receive message sometimes with SimpleBrokerMessageHandler [SPR-15282] #19848
Comments
Rossen Stoyanchev commented Any idea why the accessCache has destinations while the updateCache is cleared? Looking at |
Rossen Stoyanchev commented I'm guessing this comment is related to the above since it was made on the same day and seems very similar?
How many unique destinations are these 5000 clients subscribing to -- a small number of shared "topic" destinations or a larger number of unique "user" destinations? For the 5000 sent messages, are those 1 "user" destination message per user or 5000 messages to a shared topic that all users are subscribed to? Not that it should matter, just trying to get a better sense of the scenario and reason about it. I still can't see why the accessCache would not clear together with the updateCache. Note also that the cache size is exposed for configuration and can be optimized through the |
flyingsu commented Thank u. /** Map from destination -> <sessionId, subscriptionId> for fast look-ups */
private final Map<String, MultiValueMap<String, String>> accessCache =
new ConcurrentHashMap<String, MultiValueMap<String, String>>(DEFAULT_CACHE_LIMIT);
/** Map from destination -> <sessionId, subscriptionId> with locking */
@SuppressWarnings("serial")
private final Map<String, MultiValueMap<String, String>> updateCache =
new LinkedHashMap<String, MultiValueMap<String, String>>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, MultiValueMap<String, String>> eldest) {
return size() > getCacheLimit();
}
};
/** Default maximum number of entries for the destination cache: 1024 */
public static final int DEFAULT_CACHE_LIMIT = 1024;
public void updateAfterRemovedSession(SessionSubscriptionInfo info) {
synchronized (this.updateCache) {
Set<String> destinationsToRemove = new HashSet<String>();
for (Map.Entry<String, MultiValueMap<String, String>> entry : this.updateCache.entrySet()) {
String destination = entry.getKey();
MultiValueMap<String, String> sessionMap = entry.getValue();
if (sessionMap.remove(info.getSessionId()) != null) {
if (sessionMap.isEmpty()) {
destinationsToRemove.add(destination);
}
else {
this.accessCache.put(destination, new LinkedMultiValueMap<String, String>(sessionMap));
}
}
}
for (String destination : destinationsToRemove) {
this.updateCache.remove(destination);
this.accessCache.remove(destination);
}
}
}
public void updateAfterNewSubscription(String destination, String sessionId, String subsId) {
synchronized (this.updateCache) {
for (Map.Entry<String, MultiValueMap<String, String>> entry : this.updateCache.entrySet()) {
String cachedDestination = entry.getKey();
if (getPathMatcher().match(destination, cachedDestination)) {
MultiValueMap<String, String> subs = entry.getValue();
subs.add(sessionId, subsId);
this.accessCache.put(cachedDestination, new LinkedMultiValueMap<String, String>(subs));
}
}
}
} 5.It will call +findSubscriptionsInternal,getSubscriptions+ functions when sending message. public MultiValueMap<String, String> getSubscriptions(String destination) {
return this.accessCache.get(destination);
} @Override
protected MultiValueMap<String, String> findSubscriptionsInternal(String destination, Message<?> message) {
MultiValueMap<String, String> result = this.destinationCache.getSubscriptions(destination);
if (result != null) {
return result;
}
result = new LinkedMultiValueMap<String, String>();
for (SessionSubscriptionInfo info : this.subscriptionRegistry.getAllSubscriptions()) {
for (String destinationPattern : info.getDestinations()) {
if (this.pathMatcher.match(destinationPattern, destination)) {
for (String subscriptionId : info.getSubscriptions(destinationPattern)) {
result.add(info.sessionId, subscriptionId);
}
}
}
}
if (!result.isEmpty()) {
this.destinationCache.addSubscriptions(destination, result);
}
return result;
}
|
flyingsu commented This +Glodon+ is my leader. haha |
Rossen Stoyanchev commented For the cacheLimit something like this (as of 4.3.2): @Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setCacheLimit(1024);
// ...
}
} |
Rossen Stoyanchev commented Okay I think I know where the problem is. I see your point that the accessCache can be larger than the updateCache but they are intended to remain in sync (same size, same contents). I think the issue is that when getting subscriptions, and adding to the cache, we should be putting into the accessCacheFirst. I'll experiment and confirm. |
flyingsu commented Thank you~ |
Rossen Stoyanchev commented flyingsu I thought I had this but I cannot find a way to grow the access cache beyond the size of the cacheLimit. In Basically in your steps #1 and #2 above:
How did the accessCache get to 5000 in the first place? What sequence leads to this? |
Rossen Stoyanchev commented hi with 4.3.8 coming next week, just wondering if you have any further input? I presume you're looking at an actual case as opposed to theoretical possibility? |
Rossen Stoyanchev commented I believe #20102 may have something to do with this ticket as well. Do you have a way to confirm that in your environment? It would be useful to know whether we can close this ticket which we would have to in any case otherwise with no further clues. |
glodon opened SPR-15282 and commented
when large number of clients reconnected, some client cannot receive data
this problem seems to be caused by class DefaultSubscriptionRegistry,
if connections is more than CacheLimit, when all of them disconnected, updatecache will be cleared, but some destination will be remain in accessCache,
when clients reconnect , if the destination exist in accessCache,destination Info will not be update
so the message will not send sucessfully
// code for this problem
Affects: 4.2.9, 4.3.6, 5.0 M5
Issue Links:
The text was updated successfully, but these errors were encountered: