-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Deduplicate group messages across connections #61810
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -226,7 +226,7 @@ public override Task SendGroupsAsync(IReadOnlyList<string> groupNames, string me | |||||||||||||
{ | ||||||||||||||
// Each task represents the list of tasks for each of the writes within a group | ||||||||||||||
List<Task>? tasks = null; | ||||||||||||||
SerializedHubMessage? message = null; | ||||||||||||||
HashSet<string>? connections = null; | ||||||||||||||
|
||||||||||||||
foreach (var groupName in groupNames) | ||||||||||||||
{ | ||||||||||||||
|
@@ -238,7 +238,26 @@ public override Task SendGroupsAsync(IReadOnlyList<string> groupNames, string me | |||||||||||||
var group = _groups[groupName]; | ||||||||||||||
if (group != null) | ||||||||||||||
{ | ||||||||||||||
DefaultHubLifetimeManager<THub>.SendToGroupConnections(methodName, args, group, null, null, ref tasks, ref message, cancellationToken); | ||||||||||||||
foreach (var connection in group) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit:
Suggested change
|
||||||||||||||
{ | ||||||||||||||
if (connections == null) | ||||||||||||||
{ | ||||||||||||||
connections = new HashSet<string>(); | ||||||||||||||
} | ||||||||||||||
connections.Add(connection.Key); | ||||||||||||||
Comment on lines
+243
to
+247
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI this could be written also as 1-liner.
Suggested change
For me it's readability is the same. In L239 can you also check if the The HashSet could then be also initialized with the proper capacity, to avoid potential resizing and copying of the internal structures of HashSet. |
||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if (connections != null) | ||||||||||||||
{ | ||||||||||||||
foreach (var connectionId in connections) | ||||||||||||||
{ | ||||||||||||||
if (tasks == null) | ||||||||||||||
{ | ||||||||||||||
tasks = new List<Task>(); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+256
to
+259
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HashSet has a |
||||||||||||||
tasks.Add(SendConnectionAsync(connectionId, methodName, args, cancellationToken)); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This call to SendConnectionAsync reserializes the method invocation payload for each connection. aspnetcore/src/SignalR/server/Core/src/DefaultHubLifetimeManager.cs Lines 194 to 196 in aa2e0dc
Prior to this change, SendToGroupConnections would call CreateSerializedInvocationMessage once, send the same serialized invocation payload to each client. I suspect that will have a far bigger performance impact than the HashSet, although I'd also like to pool that if we move forward with this change. |
||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -254,21 +254,38 @@ public override Task SendConnectionsAsync(IReadOnlyList<string> connectionIds, s | |
} | ||
|
||
/// <inheritdoc /> | ||
public override Task SendGroupsAsync(IReadOnlyList<string> groupNames, string methodName, object?[] args, CancellationToken cancellationToken = default) | ||
public override async Task SendGroupsAsync(IReadOnlyList<string> groupNames, string methodName, object?[] args, CancellationToken cancellationToken = default) | ||
{ | ||
ArgumentNullException.ThrowIfNull(groupNames); | ||
var publishTasks = new List<Task>(groupNames.Count); | ||
var payload = _protocol.WriteInvocation(methodName, args); | ||
HashSet<string>? connections = null; | ||
|
||
foreach (var groupName in groupNames) | ||
{ | ||
if (!string.IsNullOrEmpty(groupName)) | ||
{ | ||
publishTasks.Add(PublishAsync(_channels.Group(groupName), payload)); | ||
var groupChannel = _channels.Group(groupName); | ||
if (groupChannel != null) | ||
{ | ||
var connectionStore = _groups.GetStore(groupChannel); | ||
if (connectionStore != null) | ||
{ | ||
foreach (var connection in connectionStore) | ||
{ | ||
if (connections == null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can similar optimizations as above be applied here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And to the ToList() call below. I wonder if we shouldn't be using a pooled Span instead. |
||
{ | ||
connections = new HashSet<string>(); | ||
} | ||
connections.Add(connection.ConnectionId); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return Task.WhenAll(publishTasks); | ||
if (connections != null) | ||
{ | ||
await SendConnectionsAsync(connections.ToList(), methodName, args, cancellationToken); | ||
} | ||
} | ||
|
||
/// <inheritdoc /> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depending on how much we want to put into perf / allocations a few thoughts:
Well, if that's a goal this can be done in a separate PR for sure.