Introduced the FlightZone concept (inbound and outbound) and separated logic of storage system.
This commit is contained in:
@@ -48,14 +48,14 @@ class MapDBMessagesStore implements IMessagesStore {
|
||||
|
||||
@Override
|
||||
public void initStore() {
|
||||
LOG.info("Initializing store...");
|
||||
m_retainedStore = m_db.getHashMap("retained");
|
||||
m_persistentMessageStore = m_db.getHashMap("persistedMessages");
|
||||
LOG.info("Initialized store");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeRetained(Topic topic, MessageGUID guid) {
|
||||
LOG.debug("Storing retained messages. Topic = {}, guid = {}.", topic, guid);
|
||||
LOG.debug("Storing retained messages. Topic={}, guid={}", topic, guid);
|
||||
m_retainedStore.put(topic, guid);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class MapDBMessagesStore implements IMessagesStore {
|
||||
}
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("The retained messages have been scanned. MatchingMessages = {}.", results);
|
||||
LOG.trace("The retained messages have been scanned. MatchingMessages={}", results);
|
||||
}
|
||||
|
||||
return results;
|
||||
@@ -80,75 +80,35 @@ class MapDBMessagesStore implements IMessagesStore {
|
||||
|
||||
@Override
|
||||
public MessageGUID storePublishForFuture(StoredMessage storedMessage) {
|
||||
assert storedMessage.getClientID() != null : "The message to be persisted must have a valid client ID";
|
||||
assert storedMessage.getClientID() != null : "Message to be persisted must have a valid client ID";
|
||||
MessageGUID guid = new MessageGUID(UUID.randomUUID().toString());
|
||||
storedMessage.setGuid(guid);
|
||||
LOG.debug(
|
||||
"Storing publish event. MqttClientId = {}, messageId = {}, guid = {}, topic = {}.",
|
||||
storedMessage.getClientID(),
|
||||
storedMessage.getMessageID(),
|
||||
guid,
|
||||
storedMessage.getTopic());
|
||||
LOG.debug("Storing publish event. CId={}, guid={}, topic={}", storedMessage.getClientID(), guid,
|
||||
storedMessage.getTopic());
|
||||
m_persistentMessageStore.put(guid, storedMessage);
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db
|
||||
.getHashMap(messageId2GuidsMapName(storedMessage.getClientID()));
|
||||
messageIdToGuid.put(storedMessage.getMessageID(), guid);
|
||||
return guid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropMessagesInSession(String clientID) {
|
||||
LOG.debug("Dropping stored messages. ClientId = {}.", clientID);
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db.getHashMap(messageId2GuidsMapName(clientID));
|
||||
for (MessageGUID guid : messageIdToGuid.values()) {
|
||||
removeStoredMessage(guid);
|
||||
}
|
||||
messageIdToGuid.clear();
|
||||
}
|
||||
public void dropInFlightMessagesInSession(Collection<MessageGUID> pendingAckMessages) {
|
||||
//remove all guids from retained
|
||||
Collection<MessageGUID> messagesToRemove = new HashSet<>(pendingAckMessages);
|
||||
messagesToRemove.removeAll(m_retainedStore.values());
|
||||
|
||||
void removeStoredMessage(MessageGUID guid) {
|
||||
// remove only the not retained and no more referenced
|
||||
StoredMessage storedMessage = m_persistentMessageStore.get(guid);
|
||||
if (!storedMessage.isRetained()) {
|
||||
LOG.debug(
|
||||
"Dropping stored message. ClientId = {}, messageId = {}, guid = {}, topic = {}.",
|
||||
storedMessage.getClientID(),
|
||||
storedMessage.getMessageID(),
|
||||
guid,
|
||||
storedMessage.getTopic());
|
||||
for (MessageGUID guid : messagesToRemove) {
|
||||
m_persistentMessageStore.remove(guid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredMessage getMessageByGuid(MessageGUID guid) {
|
||||
LOG.debug("Retrieving stored message. Guid = {}.", guid);
|
||||
LOG.debug("Retrieving stored message. Guid={}", guid);
|
||||
return m_persistentMessageStore.get(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanRetained(Topic topic) {
|
||||
LOG.debug("Cleaning retained messages. Topic = {}.", topic);
|
||||
LOG.debug("Cleaning retained messages. Topic={}", topic);
|
||||
m_retainedStore.remove(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingPublishMessages(String clientID) {
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuidMap = m_db
|
||||
.getHashMap(messageId2GuidsMapName(clientID));
|
||||
return messageIdToGuidMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID mapToGuid(String clientID, int messageID) {
|
||||
LOG.debug("Mapping message ID to GUID CId={}, messageId={}", clientID, messageID);
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db.getHashMap(messageId2GuidsMapName(clientID));
|
||||
MessageGUID result = messageIdToGuid.get(messageID);
|
||||
LOG.debug("Message ID has been mapped to a GUID CId={}, messageId={}, guid={}", clientID, messageID, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static String messageId2GuidsMapName(String clientID) {
|
||||
return "guidsMapping_" + clientID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import org.mapdb.DB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -310,9 +311,24 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
return m_inflightStore.get(clientID).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredMessage inboundInflight(String clientID, int messageID) {
|
||||
LOG.debug("Mapping inbound message ID to GUID CId={}, messageId={}", clientID, messageID);
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db.getHashMap(inboundMessageId2GuidsMapName(clientID));
|
||||
final MessageGUID guid = messageIdToGuid.get(messageID);
|
||||
LOG.debug("Inbound message ID has been mapped CId={}, messageId={}, guid={}", clientID, messageID, guid);
|
||||
return m_messagesStore.getMessageByGuid(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markAsInboundInflight(String clientID, int messageID, MessageGUID guid) {
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db.getHashMap(inboundMessageId2GuidsMapName(clientID));
|
||||
messageIdToGuid.put(messageID, guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingPublishMessagesNo(String clientID) {
|
||||
return m_messagesStore.getPendingPublishMessages(clientID);
|
||||
return queue(clientID).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -322,4 +338,22 @@ class MapDBSessionsStore implements ISessionsStore {
|
||||
else
|
||||
return m_secondPhaseStore.get(clientID).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageGUID> pendingAck(String clientID) {
|
||||
ConcurrentMap<Integer, MessageGUID> messageGUIDMap = m_db.getHashMap(messageId2GuidsMapName(clientID));
|
||||
if (messageGUIDMap == null || messageGUIDMap.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return new ArrayList<>(messageGUIDMap.values());
|
||||
}
|
||||
|
||||
static String messageId2GuidsMapName(String clientID) {
|
||||
return "guidsMapping_" + clientID;
|
||||
}
|
||||
|
||||
static String inboundMessageId2GuidsMapName(String clientID) {
|
||||
return "inboundInflightGuidsMapping_" + clientID;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-24
@@ -16,9 +16,6 @@
|
||||
|
||||
package io.moquette.persistence.mapdb;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.server.config.MemoryConfig;
|
||||
@@ -29,15 +26,19 @@ import io.moquette.spi.ISessionsStore.ClientTopicCouple;
|
||||
import io.moquette.spi.MessageGUID;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import static io.netty.handler.codec.mqtt.MqttQoS.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.moquette.BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@@ -92,14 +93,11 @@ public class MapDBPersistentStoreTest {
|
||||
ClientSession session1 = m_sessionsStore.createNewSession("SESSION_ID_1", true);
|
||||
|
||||
// Subscribe on /topic with QOSType.MOST_ONE
|
||||
Subscription oldSubscription = new Subscription(session1.clientID, new Topic("/topic"), MqttQoS.AT_MOST_ONCE);
|
||||
Subscription oldSubscription = new Subscription(session1.clientID, new Topic("/topic"), AT_MOST_ONCE);
|
||||
session1.subscribe(oldSubscription);
|
||||
|
||||
// Subscribe on /topic again that overrides the previous subscription.
|
||||
Subscription overridingSubscription = new Subscription(
|
||||
session1.clientID,
|
||||
new Topic("/topic"),
|
||||
MqttQoS.EXACTLY_ONCE);
|
||||
Subscription overridingSubscription = new Subscription(session1.clientID, new Topic("/topic"), EXACTLY_ONCE);
|
||||
session1.subscribe(overridingSubscription);
|
||||
|
||||
// Verify
|
||||
@@ -156,18 +154,15 @@ public class MapDBPersistentStoreTest {
|
||||
|
||||
@Test
|
||||
public void testDropMessagesInSessionCleanAllNotRetainedStoredMessages() {
|
||||
m_sessionsStore.createNewSession("TestClient", true);
|
||||
IMessagesStore.StoredMessage publishToStore = new IMessagesStore.StoredMessage(
|
||||
"Hello".getBytes(),
|
||||
MqttQoS.EXACTLY_ONCE,
|
||||
"/topic");
|
||||
m_sessionsStore.createNewSession(TEST_CLIENT, true);
|
||||
IMessagesStore.StoredMessage publishToStore = new IMessagesStore.StoredMessage("Hello".getBytes(), EXACTLY_ONCE,
|
||||
"/topic");
|
||||
publishToStore.setClientID(TEST_CLIENT);
|
||||
publishToStore.setMessageID(1);
|
||||
publishToStore.setRetained(false);
|
||||
MessageGUID guid = m_messagesStore.storePublishForFuture(publishToStore);
|
||||
|
||||
// Exercise
|
||||
m_messagesStore.dropMessagesInSession("TestClient");
|
||||
m_messagesStore.dropInFlightMessagesInSession(singletonList(guid));
|
||||
|
||||
// Verify the message store for session is empty.
|
||||
IMessagesStore.StoredMessage storedPublish = m_messagesStore.getMessageByGuid(guid);
|
||||
@@ -176,18 +171,16 @@ public class MapDBPersistentStoreTest {
|
||||
|
||||
@Test
|
||||
public void testDropMessagesInSessionDoesntCleanAnyRetainedStoredMessages() {
|
||||
m_sessionsStore.createNewSession("TestClient", true);
|
||||
IMessagesStore.StoredMessage publishToStore = new IMessagesStore.StoredMessage(
|
||||
"Hello".getBytes(),
|
||||
MqttQoS.EXACTLY_ONCE,
|
||||
"/topic");
|
||||
m_sessionsStore.createNewSession(TEST_CLIENT, true);
|
||||
IMessagesStore.StoredMessage publishToStore = new IMessagesStore.StoredMessage("Hello".getBytes(), EXACTLY_ONCE,
|
||||
"/topic");
|
||||
publishToStore.setClientID(TEST_CLIENT);
|
||||
publishToStore.setMessageID(1);
|
||||
publishToStore.setRetained(true);
|
||||
MessageGUID guid = m_messagesStore.storePublishForFuture(publishToStore);
|
||||
m_messagesStore.storeRetained(new Topic("/topic"), guid);
|
||||
|
||||
// Exercise
|
||||
m_messagesStore.dropMessagesInSession("TestClient");
|
||||
m_messagesStore.dropInFlightMessagesInSession(singletonList(guid));
|
||||
|
||||
// Verify the message store for session is empty.
|
||||
IMessagesStore.StoredMessage storedPublish = m_messagesStore.getMessageByGuid(guid);
|
||||
|
||||
Reference in New Issue
Block a user