INT-3387: MessageGroupStore Improvements
JIRA: https://jira.spring.io/browse/INT-3387, https://jira.spring.io/browse/INT-3806 * Introduce ``` MessageGroupStore void addMessagesToGroup(Object groupId, Message<?>... messages); ``` And implement it in all stores. * Use new `addMessagesToGroup` where it is reasonable, e.g. `DelayHandler` * Optimize test-case to use a new store method (where it is possible) * Fix timing delays in the `JdbcMessageStoreTests` * Introduce `PersistentMessageGroup` * Add `AbstractMessageGroupStore#proxyMessageGroupForLazyLoad` to wrap the raw `MessageGroup` to the `PersistentMessageGroup` for lazy-load * Rework `MessageGroupMetadata` do not be `immutable` and allow to store/restore in the `AbstractKeyValueMessageStore` only the `MessageGroupMetadata` * Refactor `ResequencingMessageHandler` and `SequenceSizeReleaseStrategy` a bit for better performance when interact with the `MessageGroup` * Add `AbstractMessageGroupStore#setLazyLoadMessageGroups` to switch off the `lazy-load` behavior and restore the previous full `MessageGroup` logic * Add `What's New` note and `message-store.adoc` paragraph for the lazy-load functionality `GroupType.PERSISTENT` and not lazy by default PR Comments Fix `JdbcMessageStoreTests` timing issues Address PR comments * Add performance test to the `ConfigurableMongoDbMessageGroupStoreTests` * Add JavaDocs for the `MessageGroupFactory` methods * Add `log4j.properties` into the `test` MongoDB module for better traceability * Fix `JdbcMessageStore#getOneMessageFromGroup()` over the `doPollForMessage()` delegation. The `jdbcTemplate.queryForObject()` requires exactly one and only one raw in `resultSet` * Add performance test results into the `message-store.adoc`
This commit is contained in:
committed by
Gary Russell
parent
4e4763d24f
commit
286c421c1a
@@ -17,7 +17,9 @@
|
||||
package org.springframework.integration.mongodb.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -49,8 +51,9 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.integration.store.AbstractBatchingMessageGroupStore;
|
||||
import org.springframework.integration.store.AbstractMessageGroupStore;
|
||||
import org.springframework.integration.store.BasicMessageGroupStore;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.support.DefaultMessageBuilderFactory;
|
||||
import org.springframework.integration.support.MessageBuilderFactory;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
@@ -66,7 +69,7 @@ import org.springframework.util.Assert;
|
||||
* @since 4.0
|
||||
*/
|
||||
|
||||
public abstract class AbstractConfigurableMongoDbMessageStore extends AbstractBatchingMessageGroupStore
|
||||
public abstract class AbstractConfigurableMongoDbMessageStore extends AbstractMessageGroupStore
|
||||
implements BasicMessageGroupStore, InitializingBean, ApplicationContextAware {
|
||||
|
||||
public final static String SEQUENCE_NAME = "messagesSequence";
|
||||
@@ -219,6 +222,47 @@ public abstract class AbstractConfigurableMongoDbMessageStore extends AbstractBa
|
||||
return Query.query(Criteria.where(MessageDocumentFields.GROUP_ID).is(groupId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public MessageGroup removeMessageFromGroup(Object key, Message<?> messageToRemove) {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMessagesFromGroup(Object key, Collection<Message<?>> messages) {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastReleasedSequenceNumberForGroup(Object groupId, int sequenceNumber) {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<MessageGroup> iterator() {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeGroup(Object groupId) {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> getOneMessageFromGroup(Object groupId) {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMessagesToGroup(Object groupId, Message<?>... messages) {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Message<?>> getMessagesForGroup(Object groupId) {
|
||||
throw new UnsupportedOperationException("The operation isn't implemented for this class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link GenericConverter} implementation to convert {@link Message} to
|
||||
* serialized {@link byte[]} to store {@link Message} to the MongoDB.
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageGroupMetadata;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageGroup;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -53,7 +54,7 @@ import org.springframework.util.Assert;
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurableMongoDbMessageStore extends AbstractConfigurableMongoDbMessageStore
|
||||
implements MessageStore, MessageGroupStore, Iterable<MessageGroup> {
|
||||
implements MessageStore {
|
||||
|
||||
public final static String DEFAULT_COLLECTION_NAME = "configurableStoreMessages";
|
||||
|
||||
@@ -141,40 +142,39 @@ public class ConfigurableMongoDbMessageStore extends AbstractConfigurableMongoDb
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
|
||||
Query query = groupOrderQuery(groupId);
|
||||
List<MessageDocument> messageDocuments = this.mongoTemplate.find(query, MessageDocument.class,
|
||||
this.collectionName);
|
||||
MessageDocument messageDocument = this.mongoTemplate.findOne(query, MessageDocument.class, this.collectionName);
|
||||
|
||||
long createdTime = 0;
|
||||
long lastModifiedTime = 0;
|
||||
int lastReleasedSequence = 0;
|
||||
boolean complete = false;
|
||||
if (messageDocument != null) {
|
||||
long createdTime = messageDocument.getCreatedTime();
|
||||
long lastModifiedTime = messageDocument.getLastModifiedTime();
|
||||
boolean complete = messageDocument.isComplete();
|
||||
int lastReleasedSequence = messageDocument.getLastReleasedSequence();
|
||||
|
||||
MessageGroup messageGroup = getMessageGroupFactory()
|
||||
.create(this, groupId, createdTime, complete);
|
||||
messageGroup.setLastModified(lastModifiedTime);
|
||||
messageGroup.setLastReleasedMessageSequenceNumber(lastReleasedSequence);
|
||||
return messageGroup;
|
||||
|
||||
if (messageDocuments.size() > 0) {
|
||||
MessageDocument document = messageDocuments.get(0);
|
||||
createdTime = document.getCreatedTime();
|
||||
lastModifiedTime = document.getLastModifiedTime();
|
||||
complete = document.isComplete();
|
||||
lastReleasedSequence = document.getLastReleasedSequence();
|
||||
}
|
||||
|
||||
List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
for (MessageDocument document : messageDocuments) {
|
||||
messages.add(document.getMessage());
|
||||
else {
|
||||
return new SimpleMessageGroup(groupId);
|
||||
}
|
||||
MessageGroup group = getMessageGroupFactory().create(messages, groupId, createdTime, complete);
|
||||
group.setLastReleasedMessageSequenceNumber(lastReleasedSequence);
|
||||
group.setLastModified(lastModifiedTime);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGroup addMessageToGroup(final Object groupId, final Message<?> message) {
|
||||
public MessageGroup addMessageToGroup(Object groupId, Message<?> message) {
|
||||
addMessagesToGroup(groupId, message);
|
||||
return getMessageGroup(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMessagesToGroup(Object groupId, Message<?>... messages) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Assert.notNull(message, "'message' must not be null");
|
||||
Assert.notNull(messages, "'message' must not be null");
|
||||
|
||||
Query query = groupOrderQuery(groupId);
|
||||
MessageDocument messageDocument = mongoTemplate.findOne(query, MessageDocument.class, collectionName);
|
||||
MessageDocument messageDocument = this.mongoTemplate.findOne(query, MessageDocument.class, this.collectionName);
|
||||
|
||||
long createdTime = 0;
|
||||
int lastReleasedSequence = 0;
|
||||
@@ -186,18 +186,17 @@ public class ConfigurableMongoDbMessageStore extends AbstractConfigurableMongoDb
|
||||
complete = messageDocument.isComplete();
|
||||
}
|
||||
|
||||
MessageDocument document = new MessageDocument(message);
|
||||
document.setGroupId(groupId);
|
||||
document.setComplete(complete);
|
||||
document.setLastReleasedSequence(lastReleasedSequence);
|
||||
document.setCreatedTime(createdTime == 0 ? System.currentTimeMillis() : createdTime);
|
||||
document.setLastModifiedTime(System.currentTimeMillis());
|
||||
document.setSequence(getNextId());
|
||||
|
||||
addMessageDocument(document);
|
||||
|
||||
return getMessageGroup(groupId);
|
||||
for (Message<?> message : messages) {
|
||||
MessageDocument document = new MessageDocument(message);
|
||||
document.setGroupId(groupId);
|
||||
document.setComplete(complete);
|
||||
document.setLastReleasedSequence(lastReleasedSequence);
|
||||
document.setCreatedTime(createdTime == 0 ? System.currentTimeMillis() : createdTime);
|
||||
document.setLastModifiedTime(System.currentTimeMillis());
|
||||
document.setSequence(getNextId());
|
||||
|
||||
addMessageDocument(document);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -335,7 +334,28 @@ public class ConfigurableMongoDbMessageStore extends AbstractConfigurableMongoDb
|
||||
|
||||
@Override
|
||||
public Message<?> getOneMessageFromGroup(Object groupId) {
|
||||
throw new UnsupportedOperationException("Not yet implemented for this store");
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Query query = groupOrderQuery(groupId);
|
||||
MessageDocument messageDocument = this.mongoTemplate.findOne(query, MessageDocument.class, this.collectionName);
|
||||
if (messageDocument != null) {
|
||||
return messageDocument.getMessage();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Message<?>> getMessagesForGroup(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Query query = groupOrderQuery(groupId);
|
||||
List<MessageDocument> documents = this.mongoTemplate.find(query, MessageDocument.class, this.collectionName);
|
||||
List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
|
||||
for (MessageDocument document : documents) {
|
||||
messages.add(document.getMessage());
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
private void expire(MessageGroup group) {
|
||||
|
||||
@@ -60,6 +60,7 @@ import org.springframework.integration.store.AbstractMessageGroupStore;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageGroup;
|
||||
import org.springframework.integration.support.MutableMessage;
|
||||
import org.springframework.integration.support.MutableMessageBuilder;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
@@ -242,37 +243,30 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
|
||||
public MessageGroup getMessageGroup(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Query query = whereGroupIdOrder(groupId);
|
||||
List<MessageWrapper> messageWrappers = this.template.find(query, MessageWrapper.class, this.collectionName);
|
||||
List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
long timestamp = 0;
|
||||
long lastModified = 0;
|
||||
int lastReleasedSequenceNumber = 0;
|
||||
boolean completeGroup = false;
|
||||
if (messageWrappers.size() > 0) {
|
||||
MessageWrapper messageWrapper = messageWrappers.get(0);
|
||||
timestamp = messageWrapper.get_Group_timestamp();
|
||||
lastModified = messageWrapper.get_Group_update_timestamp();
|
||||
completeGroup = messageWrapper.get_Group_complete();
|
||||
lastReleasedSequenceNumber = messageWrapper.get_LastReleasedSequenceNumber();
|
||||
}
|
||||
MessageWrapper messageWrapper = this.template.findOne(query, MessageWrapper.class, this.collectionName);
|
||||
|
||||
for (MessageWrapper messageWrapper : messageWrappers) {
|
||||
messages.add(messageWrapper.getMessage());
|
||||
}
|
||||
if (messageWrapper != null) {
|
||||
long createdTime = messageWrapper.get_Group_timestamp();
|
||||
long lastModifiedTime = messageWrapper.get_Group_update_timestamp();
|
||||
boolean complete = messageWrapper.get_Group_complete();
|
||||
int lastReleasedSequence = messageWrapper.get_LastReleasedSequenceNumber();
|
||||
|
||||
MessageGroup messageGroup = getMessageGroupFactory().create(messages, groupId, timestamp, completeGroup);
|
||||
messageGroup.setLastModified(lastModified);
|
||||
if (lastReleasedSequenceNumber > 0) {
|
||||
messageGroup.setLastReleasedMessageSequenceNumber(lastReleasedSequenceNumber);
|
||||
}
|
||||
MessageGroup messageGroup = getMessageGroupFactory()
|
||||
.create(this, groupId, createdTime, complete);
|
||||
messageGroup.setLastModified(lastModifiedTime);
|
||||
messageGroup.setLastReleasedMessageSequenceNumber(lastReleasedSequence);
|
||||
return messageGroup;
|
||||
|
||||
return messageGroup;
|
||||
}
|
||||
else {
|
||||
return new SimpleMessageGroup(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGroup addMessageToGroup(final Object groupId, final Message<?> message) {
|
||||
public void addMessagesToGroup(Object groupId, Message<?>... messages) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Assert.notNull(message, "'message' must not be null");
|
||||
Assert.notNull(messages, "'message' must not be null");
|
||||
Query query = whereGroupIdOrder(groupId);
|
||||
MessageWrapper messageDocument = this.template.findOne(query, MessageWrapper.class, this.collectionName);
|
||||
|
||||
@@ -286,18 +280,17 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
|
||||
complete = messageDocument.get_Group_complete();
|
||||
}
|
||||
|
||||
for (Message<?> message : messages) {
|
||||
MessageWrapper wrapper = new MessageWrapper(message);
|
||||
wrapper.set_GroupId(groupId);
|
||||
wrapper.set_Group_timestamp(createdTime == 0 ? System.currentTimeMillis() : createdTime);
|
||||
wrapper.set_Group_update_timestamp(System.currentTimeMillis());
|
||||
wrapper.set_Group_complete(complete);
|
||||
wrapper.set_LastReleasedSequenceNumber(lastReleasedSequence);
|
||||
wrapper.setSequence(getNextId());
|
||||
|
||||
MessageWrapper wrapper = new MessageWrapper(message);
|
||||
wrapper.set_GroupId(groupId);
|
||||
wrapper.set_Group_timestamp(createdTime == 0 ? System.currentTimeMillis() : createdTime);
|
||||
wrapper.set_Group_update_timestamp(System.currentTimeMillis());
|
||||
wrapper.set_Group_complete(complete);
|
||||
wrapper.set_LastReleasedSequenceNumber(lastReleasedSequence);
|
||||
wrapper.setSequence(getNextId());
|
||||
|
||||
addMessageDocument(wrapper);
|
||||
return getMessageGroup(groupId);
|
||||
|
||||
addMessageDocument(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -393,6 +386,32 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
|
||||
this.updateGroup(groupId, lastModifiedUpdate().set(GROUP_COMPLETE_KEY, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> getOneMessageFromGroup(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Query query = whereGroupIdOrder(groupId);
|
||||
MessageWrapper messageWrapper = this.template.findOne(query, MessageWrapper.class, this.collectionName);
|
||||
if (messageWrapper != null) {
|
||||
return messageWrapper.getMessage();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Message<?>> getMessagesForGroup(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Query query = whereGroupIdOrder(groupId);
|
||||
List<MessageWrapper> messageWrappers = this.template.find(query, MessageWrapper.class, this.collectionName);
|
||||
List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
|
||||
for (MessageWrapper messageWrapper : messageWrappers) {
|
||||
messages.add(messageWrapper.getMessage());
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ManagedAttribute
|
||||
public int getMessageCountForAllMessageGroups() {
|
||||
|
||||
Reference in New Issue
Block a user