Skip to content

Fix KV Stores for same message in multiple groups #8737

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

Merged

Conversation

artembilan
Copy link
Member

If same message is stored into different groups with the same KV store, the removal of one group would lead to removal the message for the other one.

  • Improve KV Store to save message with the key including a group id
  • Respectively, refine the removal API to include group id into keys
  • Also change the MESSAGE_GROUP_KEY_PREFIX for group records to GROUP_OF_MESSAGES_ since the MESSAGE_ prefix includes group records as well for various operations based on key pattern

The problem was spotted in JDBC: #8732

This is a breaking change not only for group prefix, but for key pattern for records in the store.
Therefore this is not recommended for back-porting: the respective Migration Guide note will be added after merge.

The workaround is similar to one proposed for JDBC: don't store same message to different groups; don't use same store instance in different components.
The MESSAGE_* pattern is not fixable though with any workarounds: the getMessageCount() may provide false information.

@artembilan
Copy link
Member Author

Hold off from merging yet, please.
I think I see how to fix the count problem without a breaking change: it is based on regexp in the end anyway...

@artembilan artembilan force-pushed the Fix_KeyValueStore_for_multi_groups branch from 33dcd17 to 8543919 Compare September 18, 2023 13:09
@artembilan artembilan marked this pull request as ready for review September 18, 2023 14:01
@artembilan
Copy link
Member Author

Now this is good for review, merge and cherry-pick.

Thanks

@@ -161,11 +167,10 @@ public Message<?> removeMessage(UUID id) {
@Override
@ManagedAttribute
public long getMessageCount() {
Collection<?> messageIds = doListKeys(this.messagePrefix + '*');
Collection<?> messageIds = doListKeys(this.messagePrefix + "[^GROUP_]*");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear how this works; can you explain further?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two prefixes: MESSAGE_ and MESSAGE_GROUP_.
When we do request for MESSAGE_ pattern, the result also includes entries for groups.
Which we don't want to have when we only count message entries.
The new pattern works this way (by default): MESSAGE_[^GROUP_]*. So, include those keys which starts from MESSAGE_, but don't follow with the GROUP_ sub-prefix.

Does it makes sense now?

Borrowed from here: https://redis.io/commands/keys/.
And it turns out to be similar to regexp, so we can reuse this pattern for similar fix in Hazelcast store impl.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But, with regex, [^GROUP_] means match any string that does not contain any of those characters (not that sequence of characters); I would expect the same semantics from Redis.

Also, for regex, it must be .* to match all remaining chars (but I do see the change to .+ in Hazelcast).

...

But, it does seem to work, at least with Redis; I just connected to the test container with a breakpoint...

% redis-cli -p 61866
127.0.0.1:61866> keys *
1) "MESSAGE_GROUP_1"
2) "MESSAGE_1_b487eb34-5e29-6dcc-abeb-ffdcc70f7bf8"
3) "MESSAGE_2_b487eb34-5e29-6dcc-abeb-ffdcc70f7bf8"
4) "MESSAGE_GROUP_2"
127.0.0.1:61866> keys 'MESSAGE_[^GROUP_]*'
1) "MESSAGE_1_b487eb34-5e29-6dcc-abeb-ffdcc70f7bf8"
2) "MESSAGE_2_b487eb34-5e29-6dcc-abeb-ffdcc70f7bf8"

See this for an explanation of how to find matches that don't contain a sequence of chars.

Copy link
Contributor

@garyrussell garyrussell Sep 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, however, that if the user's group is GROUP_1 (instead of just 1) the query would fail.

Also, FYI...

127.0.0.1:62123> keys 'MESSAGE_[^G]*'
1) "MESSAGE_2_28b71e31-8d30-0725-bad8-96e5a56925c6"
2) "MESSAGE_1_28b71e31-8d30-0725-bad8-96e5a56925c6"

works too, but that would be even worse; any group starting with G would fail to be found.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Redis pattern is not fully regexp, therefore that one.
but according to your findings to make it working properly with Hazelcast we have to do this:

MESSAGE_(?!GROUP_).*

Correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. So, to make it more robust we indeed have to rename that prefix for groups. 🤷

Is that what you trying to say?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct?

Yes, I think so, according to that answer.

Is that what you trying to say?

Yes, test fails (0 messages) if I change it to

		store.addMessageToGroup("GROUP_1", testMessage);
		store.addMessageToGroup("GROUP_2", testMessage);

Copy link
Contributor

@garyrussell garyrussell Sep 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just another FYI; this

 store.addMessageToGroup("ONE", testMessage);

Fails too (because of the [^O] in [^GROUP_]).

127.0.0.1:63751> keys 'MESSAGE_[^GROUP_]*'
1) "MESSAGE_TWO_f4c2d730-c8ce-f955-d8e0-f54710639264"
127.0.0.1:63751> keys *
1) "MESSAGE_GROUP_TWO"
2) "MESSAGE_GROUP_ONE"
3) "MESSAGE_ONE_f4c2d730-c8ce-f955-d8e0-f54710639264"
4) "MESSAGE_TWO_f4c2d730-c8ce-f955-d8e0-f54710639264"

If same message is stored into different groups with the same KV store,
the removal of one group would lead to removal the message for the other one.

* Improve KV Store to save message with the key including a group id
* Respectively, refine the removal API to include group id into keys
* Also change the `MESSAGE_GROUP_KEY_PREFIX` for group records to `GROUP_OF_MESSAGES_`
since the `MESSAGE_` prefix includes group records as well for various operations
based on key pattern
to avoid complex regexp and don't bother for edge cases,
where even that regexp may fail
@artembilan artembilan force-pushed the Fix_KeyValueStore_for_multi_groups branch from bdca88a to cdcdecc Compare September 19, 2023 14:14
@artembilan
Copy link
Member Author

OK. I brought back a GROUP_OF_MESSAGES_ prefix.
Now it is a breaking for existing apps especially, so no back-port as it was stated originally.

@artembilan artembilan modified the milestones: 6.2.0-M3, 6.2.0-RC1 Sep 19, 2023
@artembilan artembilan modified the milestones: 6.2.0-RC1, 6.2.0-M3 Sep 19, 2023
@garyrussell garyrussell merged commit 64f8ed5 into spring-projects:main Sep 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants