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:
Artem Bilan
2016-04-12 13:38:21 -04:00
committed by Gary Russell
parent 4e4763d24f
commit 286c421c1a
34 changed files with 1085 additions and 505 deletions

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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() {