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

[1.2.0] Event ordering [API-1102] #696

Merged
merged 38 commits into from
Dec 31, 2021

Conversation

utku-caglayan
Copy link
Contributor

@utku-caglayan utku-caglayan commented Dec 5, 2021

Fixes #692

Hazelcast Cluster members, respects the order of the events. Different order guarantees are given for different event types (see docs for detail)

Java Client summarizes these guarantees as "Order of the events originated from the same partition is preserved". For example events which belong to the same key of a map are received(on the clients) in the same order as they created. This is achieved via StripeExecutor implementation which is also mentioned in the link above.

Java Client processes incoming events on a StripedExecutor, by treating event handlers as "stripedRunnable"s with respect to their partitionID. Please check it for reference.

This PR fixes event processing (execution of event-handlers) logic by respecting the order of events from the same partition.

@codecov-commenter
Copy link

codecov-commenter commented Dec 6, 2021

Codecov Report

Merging #696 (76ee128) into master (40b05ce) will decrease coverage by 2.20%.
The diff coverage is 87.71%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #696      +/-   ##
==========================================
- Coverage   74.37%   72.17%   -2.21%     
==========================================
  Files         311      330      +19     
  Lines       13571    15214    +1643     
==========================================
+ Hits        10094    10981     +887     
- Misses       2739     3458     +719     
- Partials      738      775      +37     
Impacted Files Coverage Δ
internal/invocation/invocation_service.go 80.64% <76.92%> (+2.02%) ⬆️
internal/invocation/stripe_executor.go 90.90% <90.90%> (ø)
internal/cluster/address_provider.go 66.66% <0.00%> (-20.00%) ⬇️
internal/proto/codec/builtin.go 45.16% <0.00%> (-19.46%) ⬇️
internal/cloud/address_provider.go 28.57% <0.00%> (-14.29%) ⬇️
internal/cluster/cluster_service.go 72.80% <0.00%> (-5.56%) ⬇️
internal/serialization/default_portable_reader.go 92.18% <0.00%> (-5.33%) ⬇️
client.go 56.95% <0.00%> (-3.32%) ⬇️
internal/serialization/object_data.go 88.14% <0.00%> (-2.60%) ⬇️
internal/event/dispatch_service.go 80.00% <0.00%> (-2.50%) ⬇️
... and 49 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 40b05ce...76ee128. Read the comment docs.

@emreyigit
Copy link

I think we should add more details to PRs. We experienced similar situation recently at .net side. @ihsandemir suggested it. Otherwise, reviewing becomes frustrating.

@yuce yuce changed the title Event ordering [1.2.0] Event ordering Dec 23, 2021
Copy link
Collaborator

@yuce yuce left a comment

Choose a reason for hiding this comment

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

Here's some initial comments, will continue to review.

cluster/event_config.go Outdated Show resolved Hide resolved
cluster/event_config.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor_test.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor_test.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor_test.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor_test.go Outdated Show resolved Hide resolved
client_it_test.go Outdated Show resolved Hide resolved
@yuce yuce changed the title [1.2.0] Event ordering [1.3.0] Event ordering Dec 29, 2021
@yuce yuce removed this from the 1.2.0 milestone Dec 29, 2021
}

// dispatch sends the handler "task" to one of the appropriate taskQueues, "tasks" with the same key end up on the same queue. Returns true if queue is full and could not dispatch
func (se stripeExecutor) dispatch(key int, task func()) (queueFull bool) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this function should return false when the task cannot be dispatched. That could also help getting rid of the return variable name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is definitely more idiomatic thanks, addressed it at 3bb2d0f

@yuce yuce added this to the 1.2.0 milestone Dec 30, 2021
client_it_test.go Outdated Show resolved Hide resolved
@@ -140,6 +143,64 @@ func TestClientMemberEvents(t *testing.T) {
})
}

func calcPartitionID(ss *serialization.Service, key interface{}) (int32, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this function may be named better. Maybe calculatePartitionID as the last resort.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sadly, could not come up with a better one. Renamed at 5fadb18

}

// newStripeExecutor returns a new stripeExecutor with configured queueCount and queueSize.
func newStripeExecutorWithConfig(queueCount, queueSize int) (stripeExecutor, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about returning only the stripeExecutor? error is always nil. We can update it if in the future we need to return an error. Also, should it return *stripeExecutor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got rid off error at 67d42e5

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored stripeExecutor to pointer semantics as you suggested at f8f778f

internal/invocation/stripe_executor_test.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor_test.go Outdated Show resolved Hide resolved
internal/invocation/stripe_executor_test.go Outdated Show resolved Hide resolved
queueCount int
key int
expectedIndex int
}{
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should there be a test case for key == -1 as well? If it's hard to do that in this function due to random stuff, at the very least we should have a separate test that checks dispatch works with negative integers too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great idea! Created a separate test for that at 99b7971

Comment on lines +146 to +152
go func() {
for _, task := range tasks {
if ok := se.dispatch(task.key, task.handler); !ok {
panic("could not dispatch event handler")
}
}
}()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you refactor this, so each task is dispatched by a different goroutine?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

List of tasks is created in a way that randomizes tasks but respect the order of them with respect to their keys, just like the situation on clusters. If we dispatch each one in a different goroutine, we cannot guarantee the order among the tasks of the same key.

client_it_test.go Outdated Show resolved Hide resolved
Comment on lines +193 to +195
for i := 1; i <= 1000; i++ {
it.MustValue(m.Put(ctx, i, "test"))
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you refactor this so m.Puts run concurrently?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead implemented a blackbox one as we discussed df8e738

@yuce yuce changed the title [1.3.0] Event ordering [1.2.0] Event ordering Dec 31, 2021
Copy link
Collaborator

@yuce yuce left a comment

Choose a reason for hiding this comment

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

Thanks, looks good!

@utku-caglayan utku-caglayan merged commit 649a34c into hazelcast:master Dec 31, 2021
@utku-caglayan utku-caglayan changed the title [1.2.0] Event ordering [1.2.0] Event ordering [API-1102] Dec 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
to-jira Use to create a placeholder Jira issue in Jira APIs Project Type: Defect
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ensure event handlers run in the receive order per partition [API-1102]
4 participants