Architechtural refactory, moved subscription out of tree and into db store

This commit is contained in:
andsel
2015-12-06 11:43:29 +01:00
parent 490c6a763d
commit 9ce9a6597f
15 changed files with 260 additions and 250 deletions
@@ -60,6 +60,10 @@ public class Subscription implements Serializable {
return topicFilter;
}
/**
* @deprecated this concept must be moved inside ClientSession and not subscription.
* */
@Deprecated
public boolean isCleanSession() {
return this.cleanSession;
}
@@ -71,20 +75,15 @@ public class Subscription implements Serializable {
Subscription that = (Subscription) o;
if (cleanSession != that.cleanSession) return false;
if (clientId != null ? !clientId.equals(that.clientId) : that.clientId != null) return false;
if (requestedQos != that.requestedQos) return false;
if (topicFilter != null ? !topicFilter.equals(that.topicFilter) : that.topicFilter != null) return false;
return !(topicFilter != null ? !topicFilter.equals(that.topicFilter) : that.topicFilter != null);
return true;
}
@Override
public int hashCode() {
int result = requestedQos.hashCode();
result = 31 * result + (clientId != null ? clientId.hashCode() : 0);
int result = clientId != null ? clientId.hashCode() : 0;
result = 31 * result + (topicFilter != null ? topicFilter.hashCode() : 0);
result = 31 * result + (cleanSession ? 1 : 0);
return result;
}
@@ -21,6 +21,7 @@ import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicReference;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -68,8 +69,8 @@ public class SubscriptionsStore {
public void visit(TreeNode node, int deep) {
String subScriptionsStr = "";
String indentTabs = indentTabs(deep);
for (Subscription sub : node.m_subscriptions) {
subScriptionsStr += indentTabs + sub.toString() + "\n";
for (ClientTopicCouple couple : node.m_subscriptions) {
subScriptionsStr += indentTabs + couple.toString() + "\n";
}
s += node.getToken() == null ? "" : node.getToken().toString();
s += "\n" + (node.m_subscriptions.isEmpty() ? indentTabs : "") + subScriptionsStr /*+ "\n"*/;
@@ -92,6 +93,7 @@ public class SubscriptionsStore {
private AtomicReference<TreeNode> subscriptions = new AtomicReference<>(new TreeNode());
private static final Logger LOG = LoggerFactory.getLogger(SubscriptionsStore.class);
private volatile ISessionsStore m_sessionsStore;
/**
* Initialize the subscription tree with the list of subscriptions.
@@ -99,22 +101,23 @@ public class SubscriptionsStore {
*/
public void init(ISessionsStore sessionsStore) {
LOG.debug("init invoked");
List<Subscription> subscriptions = sessionsStore.listAllSubscriptions();
m_sessionsStore = sessionsStore;
List<ClientTopicCouple> subscriptions = sessionsStore.listAllSubscriptions();
//reload any subscriptions persisted
if (LOG.isDebugEnabled()) {
LOG.debug("Reloading all stored subscriptions...subscription tree before {}", dumpTree());
}
for (Subscription subscription : subscriptions) {
LOG.debug("Re-subscribing {} to topic {}", subscription.getClientId(), subscription.getTopicFilter());
add(subscription);
for (ClientTopicCouple clientTopic : subscriptions) {
LOG.debug("Re-subscribing {} to topic {}", clientTopic.clientID, clientTopic.topicFilter);
add(clientTopic);
}
if (LOG.isTraceEnabled()) {
LOG.trace("Finished loading. Subscription tree after {}", dumpTree());
}
}
public void add(Subscription newSubscription) {
public void add(ClientTopicCouple newSubscription) {
TreeNode oldRoot;
NodeCouple couple;
do {
@@ -146,13 +149,12 @@ public class SubscriptionsStore {
if ((matchingChildren = current.childWithToken(token)) != null) {
//copy the traversed node
current = matchingChildren.copy();
// current.m_parent = parent;
//update the child just added in the children list
parent.updateChild(matchingChildren, current);
parent = current;
} else {
//create a new node for the newly inserted token
matchingChildren = new TreeNode(/*current*/);
matchingChildren = new TreeNode();
matchingChildren.setToken(token);
current.addChild(matchingChildren);
current = matchingChildren;
@@ -168,19 +170,7 @@ public class SubscriptionsStore {
oldRoot = subscriptions.get();
couple = recreatePath(topic, oldRoot);
//do the job
//search for the subscription to remove
Subscription toBeRemoved = null;
for (Subscription sub : couple.createdNode.subscriptions()) {
if (sub.topicFilter.equals(topic) && sub.getClientId().equals(clientID)) {
toBeRemoved = sub;
break;
}
}
if (toBeRemoved != null) {
couple.createdNode.subscriptions().remove(toBeRemoved);
}
couple.createdNode.remove(new ClientTopicCouple(clientID, topic));
//spin lock repeating till we can, swap root, if can't swap just re-do the operation
} while(!subscriptions.compareAndSet(oldRoot, couple.root));
}
@@ -216,16 +206,17 @@ public class SubscriptionsStore {
}
Queue<Token> tokenQueue = new LinkedBlockingDeque<>(tokens);
List<Subscription> matchingSubs = new ArrayList<>();
List<ClientTopicCouple> matchingSubs = new ArrayList<>();
subscriptions.get().matches(tokenQueue, matchingSubs);
//remove the overlapping subscriptions, selecting ones with greatest qos
Map<String, Subscription> subsForClient = new HashMap<>();
for (Subscription sub : matchingSubs) {
Subscription existingSub = subsForClient.get(sub.getClientId());
for (ClientTopicCouple matchingCouple : matchingSubs) {
Subscription existingSub = subsForClient.get(matchingCouple.clientID);
Subscription sub = m_sessionsStore.getSubscription(matchingCouple);
//update the selected subscriptions if not present or if has a greater qos
if (existingSub == null || existingSub.getRequestedQos().byteValue() < sub.getRequestedQos().byteValue()) {
subsForClient.put(sub.getClientId(), sub);
subsForClient.put(matchingCouple.clientID, sub);
}
}
return new ArrayList<>(subsForClient.values());
@@ -15,22 +15,17 @@
*/
package io.moquette.spi.impl.subscriptions;
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
class TreeNode {
private class ClientIDComparator implements Comparator<Subscription> {
public int compare(Subscription o1, Subscription o2) {
return o1.getClientId().compareTo(o2.getClientId());
}
}
Token m_token;
List<TreeNode> m_children = new ArrayList<>();
List<Subscription> m_subscriptions = new ArrayList<>();
//TODO move to set of ClientIDthe set of clientIDs that has subscriptions to this topic
Set<ClientTopicCouple> m_subscriptions = new HashSet<>();
TreeNode() {
}
@@ -43,19 +38,7 @@ class TreeNode {
this.m_token = topic;
}
void addSubscription(Subscription s) {
//avoid double registering for same clientID, topic and QoS
if (m_subscriptions.contains(s)) {
return;
}
//remove existing subscription for same client and topic but different QoS
Comparator<Subscription> comparator = new ClientIDComparator();
Collections.sort(m_subscriptions, comparator);
int existingSubIdx = Collections.binarySearch(m_subscriptions, s, comparator);
if (existingSubIdx >= 0) {
m_subscriptions.remove(existingSubIdx);
}
void addSubscription(ClientTopicCouple s) {
m_subscriptions.add(s);
}
@@ -70,7 +53,7 @@ class TreeNode {
TreeNode copy() {
final TreeNode copy = new TreeNode();
copy.m_children = new ArrayList<>(m_children);
copy.m_subscriptions = new ArrayList<>(m_subscriptions);
copy.m_subscriptions = new HashSet<>(m_subscriptions);
copy.m_token = m_token;
return copy;
}
@@ -94,11 +77,15 @@ class TreeNode {
m_children.add(newChild);
}
List<Subscription> subscriptions() {
Collection<ClientTopicCouple> subscriptions() {
return m_subscriptions;
}
void matches(Queue<Token> tokens, List<Subscription> matchingSubs) {
public void remove(ClientTopicCouple clientTopicCouple) {
m_subscriptions.remove(clientTopicCouple);
}
void matches(Queue<Token> tokens, List<ClientTopicCouple> matchingSubs) {
Token t = tokens.poll();
//check if t is null <=> tokens finished
@@ -147,14 +134,14 @@ class TreeNode {
TreeNode removeClientSubscriptions(String clientID) {
//collect what to delete and then delete to avoid ConcurrentModification
TreeNode newSubRoot = this.copy();
List<Subscription> subsToRemove = new ArrayList<>();
for (Subscription s : newSubRoot.m_subscriptions) {
if (s.clientId.equals(clientID)) {
List<ClientTopicCouple> subsToRemove = new ArrayList<>();
for (ClientTopicCouple s : newSubRoot.m_subscriptions) {
if (s.clientID.equals(clientID)) {
subsToRemove.add(s);
}
}
for (Subscription s : subsToRemove) {
for (ClientTopicCouple s : subsToRemove) {
newSubRoot.m_subscriptions.remove(s);
}
@@ -166,21 +153,4 @@ class TreeNode {
newSubRoot.m_children = newChildren;
return newSubRoot;
}
/**
* @return the set of subscriptions for the given client.
* */
Set<Subscription> findAllByClientID(String clientID) {
Set<Subscription> subs = new HashSet<>();
for (Subscription s : m_subscriptions) {
if (s.clientId.equals(clientID)) {
subs.add(s);
}
}
//go deep
for (TreeNode child : m_children) {
subs.addAll(child.findAllByClientID(clientID));
}
return subs;
}
}