Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ForceReleaseProcessor will never be run when group condition is used and MongoDbMessageStore #8685

Closed
antonvovk opened this issue Jul 21, 2023 · 5 comments · Fixed by #8686
Closed

Comments

@antonvovk
Copy link

antonvovk commented Jul 21, 2023

In what version(s) of Spring Integration are you seeing this issue?

6.1.0

To Reproduce
Use MongoDbMessageStore or ConfigurableMongoDbMessageStore as the MessageGroupStore implementation.
Create aggregator:

  • Provide the groupConditionSupplier for the AbstractCorrelatingMessageHandler.
  • Provide the groupTimeoutExpression for the AbstractCorrelatingMessageHandler with a positive value of 5-10 seconds.

Describe the bug

Let's take a look at this function from AbstractCorrelatingMessageHandler:

	private void processForceRelease(Object groupId, long timestamp, long lastModified) {
		MessageGroup messageGroup = this.messageStore.getMessageGroup(groupId);
		if (messageGroup.getTimestamp() == timestamp && messageGroup.getLastModified() == lastModified) {
			this.forceReleaseProcessor.processMessageGroup(messageGroup);
		}
	}

To run the forceReleaseProcessor the timestamp and lastModified must be the same as the corresponding properties from the message group that was fetched at the beginning of this function.
The timestamp and lastModified parameters come from the message group that was fetched in the processMessageForGroup function.

The problem is that after fetching the message group in the processMessageForGroup we do a call to setGroupConditionIfAny(message, messageGroup). And if we are using the group condition, this function will call messageStore.setGroupCondition() which will update the message group condition and its lastModifiedTime property in MongoDB implementation.

public void setGroupCondition(Object groupId, String condition) {
	updateGroup(groupId, lastModifiedUpdate().set("condition", condition));
}

So we end up in a situation where the lastModifiedTime is newer in the database but the message group fetched in the processMessageForGroup has an old value. This means that the condition in the forceReleaseProcessor will never be true if we are using the group condition.

Expected behavior

The message group instance in the processMessageForGroup should be updated after a call to setGroupConditionIfAny.

@antonvovk antonvovk added status: waiting-for-triage The issue need to be evaluated and its future decided type: bug labels Jul 21, 2023
@martaisty
Copy link

+1 on this. Precisely described, @antonvovk

@artembilan
Copy link
Member

Thank you for the report!

Indeed this is a problem we had caused via inconsistency with entity in the memory and whatever we have just update in the DB.

I see one way to fix quickly: do not update a lastModifiedTime field when we set a condition property. Technically we do not add any new messages to the group to really justify its modification.
So, probably we can treat updating options on a group metadata as non-modification to the whole group.

Another quick way is to re-fetch group in that setGroupConditionIfAny() and return its fresh instance.
This may cause a performance degradation since we are going to use one more DB call.

And final (probably the best) fix is to implement a new updateGroupCondition() API which would return for us just update in DB entity with fresh options respectively.

Since that API was introduced in an old still in support version:

	 * @since 5.5
	 */
	void setGroupCondition(Object groupId, String condition);

we have to fix this down to there as well, but we have to be careful do not make a breaking change.

I believe any solution we chose would be possible to back-port, but we have to decide together which one is good for all of us.

Please, help me with a choice 😄

@artembilan artembilan added in: core backport 5.5.x (EOL) for: backport-to-6.1.x and removed status: waiting-for-triage The issue need to be evaluated and its future decided labels Jul 21, 2023
@artembilan artembilan added this to the 6.2.0-M2 milestone Jul 21, 2023
@antonvovk
Copy link
Author

Thanks for your quick response 😄

It would be good to have a quick fix that can be easily backported in 5.5.x/6.0.x/6.1.x. I think the second option will be the easiest and safest to do. The performance impact shouldn't be that noticeable and it will only affect those who use group condition.

Then in 6.2.0, the third option can be used which is really the best one to make a proper fix.

@artembilan
Copy link
Member

OK! Let it be!

Then I'll implement quickly the second option.

@artembilan artembilan self-assigned this Jul 21, 2023
artembilan added a commit to artembilan/spring-integration that referenced this issue Jul 21, 2023
Fixes spring-projects#8685

The `AbstractCorrelatingMessageHandler` updates the group metadata in DB
not only for provided `condition`, but also a `lastModified` field.
A subsequent scheduling for group timeout takes the `lastModified`
to compare with the value in the store after re-fetching group in task.
This does not reflect reality since adding `condition` modifies the data in DB,
but in-memory state remains the same.

* Re-fetch a group from the store in the `AbstractCorrelatingMessageHandler.setGroupConditionIfAny()`.
* Verify expected behavior via new `ConfigurableMongoDbMessageGroupStoreTests.groupIsForceReleaseAfterTimeoutWhenGroupConditionIsSet()`

**Cherry-pick to `6.1.x`, `6.0.x` & `5.5.x`**
@artembilan
Copy link
Member

This the fix in related PR: #8686

garyrussell pushed a commit that referenced this issue Jul 24, 2023
* GH-8685: Re-fetch group after setting condition

Fixes #8685

The `AbstractCorrelatingMessageHandler` updates the group metadata in DB
not only for provided `condition`, but also a `lastModified` field.
A subsequent scheduling for group timeout takes the `lastModified`
to compare with the value in the store after re-fetching group in task.
This does not reflect reality since adding `condition` modifies the data in DB,
but in-memory state remains the same.

* Re-fetch a group from the store in the `AbstractCorrelatingMessageHandler.setGroupConditionIfAny()`.
* Verify expected behavior via new `ConfigurableMongoDbMessageGroupStoreTests.groupIsForceReleaseAfterTimeoutWhenGroupConditionIsSet()`

**Cherry-pick to `6.1.x`, `6.0.x` & `5.5.x`**

* * Fix Checkstyle violation in the test
garyrussell pushed a commit that referenced this issue Jul 24, 2023
* GH-8685: Re-fetch group after setting condition

Fixes #8685

The `AbstractCorrelatingMessageHandler` updates the group metadata in DB
not only for provided `condition`, but also a `lastModified` field.
A subsequent scheduling for group timeout takes the `lastModified`
to compare with the value in the store after re-fetching group in task.
This does not reflect reality since adding `condition` modifies the data in DB,
but in-memory state remains the same.

* Re-fetch a group from the store in the `AbstractCorrelatingMessageHandler.setGroupConditionIfAny()`.
* Verify expected behavior via new `ConfigurableMongoDbMessageGroupStoreTests.groupIsForceReleaseAfterTimeoutWhenGroupConditionIsSet()`

**Cherry-pick to `6.1.x`, `6.0.x` & `5.5.x`**

* * Fix Checkstyle violation in the test
garyrussell pushed a commit that referenced this issue Jul 24, 2023
* GH-8685: Re-fetch group after setting condition

Fixes #8685

The `AbstractCorrelatingMessageHandler` updates the group metadata in DB
not only for provided `condition`, but also a `lastModified` field.
A subsequent scheduling for group timeout takes the `lastModified`
to compare with the value in the store after re-fetching group in task.
This does not reflect reality since adding `condition` modifies the data in DB,
but in-memory state remains the same.

* Re-fetch a group from the store in the `AbstractCorrelatingMessageHandler.setGroupConditionIfAny()`.
* Verify expected behavior via new `ConfigurableMongoDbMessageGroupStoreTests.groupIsForceReleaseAfterTimeoutWhenGroupConditionIsSet()`

**Cherry-pick to `6.1.x`, `6.0.x` & `5.5.x`**

* * Fix Checkstyle violation in the test
garyrussell pushed a commit that referenced this issue Jul 24, 2023
* GH-8685: Re-fetch group after setting condition

Fixes #8685

The `AbstractCorrelatingMessageHandler` updates the group metadata in DB
not only for provided `condition`, but also a `lastModified` field.
A subsequent scheduling for group timeout takes the `lastModified`
to compare with the value in the store after re-fetching group in task.
This does not reflect reality since adding `condition` modifies the data in DB,
but in-memory state remains the same.

* Re-fetch a group from the store in the `AbstractCorrelatingMessageHandler.setGroupConditionIfAny()`.
* Verify expected behavior via new `ConfigurableMongoDbMessageGroupStoreTests.groupIsForceReleaseAfterTimeoutWhenGroupConditionIsSet()`

**Cherry-pick to `6.1.x`, `6.0.x` & `5.5.x`**

* * Fix Checkstyle violation in the test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants