Enforce cacheLimit in DefaultSubscriptionRegistry

When the cacheLimit is reached and there is an eviction from the
updateCache, the accessCache is now also updated.

This change also ensures that adding a destination to the cache is
protected with synchronization on the updateCache.

Issue: SPR-13555
This commit is contained in:
Rossen Stoyanchev
2015-10-13 11:59:01 -04:00
parent 0f70ac74cd
commit 7ff915a01a
2 changed files with 44 additions and 94 deletions

View File

@@ -21,9 +21,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
@@ -31,11 +28,10 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MultiValueMap;
import org.springframework.util.PathMatcher;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Test fixture for
@@ -402,37 +398,22 @@ public class DefaultSubscriptionRegistryTests {
// no ConcurrentModificationException
}
@Test // SPR-13204
public void findSubscriptionsWithConcurrentUnregisterAllSubscriptions() throws Exception {
final CountDownLatch iterationPausedLatch = new CountDownLatch(1);
final CountDownLatch iterationResumeLatch = new CountDownLatch(1);
final CountDownLatch iterationDoneLatch = new CountDownLatch(1);
PathMatcher pathMatcher = new PausingPathMatcher(iterationPausedLatch, iterationResumeLatch);
this.registry.setPathMatcher(pathMatcher);
@Test // SPR-13555
public void cacheLimitExceeded() throws Exception {
this.registry.setCacheLimit(1);
this.registry.registerSubscription(subscribeMessage("sess1", "1", "/foo"));
this.registry.registerSubscription(subscribeMessage("sess1", "2", "/bar"));
assertEquals(1, this.registry.findSubscriptions(createMessage("/foo")).size());
assertEquals(1, this.registry.findSubscriptions(createMessage("/bar")).size());
this.registry.registerSubscription(subscribeMessage("sess2", "1", "/foo"));
this.registry.registerSubscription(subscribeMessage("sess2", "2", "/bar"));
AtomicReference<MultiValueMap<String, String>> subscriptions = new AtomicReference<>();
new Thread(() -> {
subscriptions.set(registry.findSubscriptions(createMessage("/foo")));
iterationDoneLatch.countDown();
}).start();
assertTrue(iterationPausedLatch.await(10, TimeUnit.SECONDS));
this.registry.unregisterAllSubscriptions("sess1");
this.registry.unregisterAllSubscriptions("sess2");
iterationResumeLatch.countDown();
assertTrue(iterationDoneLatch.await(10, TimeUnit.SECONDS));
MultiValueMap<String, String> result = subscriptions.get();
assertNotNull(result);
assertEquals(0, result.size());
assertEquals(2, this.registry.findSubscriptions(createMessage("/foo")).size());
assertEquals(2, this.registry.findSubscriptions(createMessage("/bar")).size());
}
private Message<?> createMessage(String destination) {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
accessor.setDestination(destination);
@@ -468,35 +449,4 @@ public class DefaultSubscriptionRegistryTests {
return list;
}
/**
* An extension of AntPathMatcher with a pair of CountDownLatches to pause
* while matching, allowing another thread to something, and resume when the
* other thread signals it's okay to do so.
*/
private static class PausingPathMatcher extends AntPathMatcher {
private final CountDownLatch iterationPausedLatch;
private final CountDownLatch iterationResumeLatch;
public PausingPathMatcher(CountDownLatch iterationPausedLatch, CountDownLatch iterationResumeLatch) {
this.iterationPausedLatch = iterationPausedLatch;
this.iterationResumeLatch = iterationResumeLatch;
}
@Override
public boolean match(String pattern, String path) {
try {
this.iterationPausedLatch.countDown();
assertTrue(this.iterationResumeLatch.await(10, TimeUnit.SECONDS));
return super.match(pattern, path);
}
catch (InterruptedException ex) {
ex.printStackTrace();
return false;
}
}
}
}