Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions EJ2CoreSampleBrowser_NET10.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.3.0" Condition="$(DefineConstants.Contains('REDIS'))" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.0" />
Expand Down
1 change: 1 addition & 0 deletions EJ2CoreSampleBrowser_NET8.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.15" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.3.0" Condition="$(DefineConstants.Contains('REDIS'))" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.15" />
Expand Down
1 change: 1 addition & 0 deletions EJ2CoreSampleBrowser_NET9.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.3.0" Condition="$(DefineConstants.Contains('REDIS'))" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
Expand Down
81 changes: 35 additions & 46 deletions Pages/DiagramHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using EJ2CoreSampleBrowser.Models;
using System.Collections.Concurrent;
using Newtonsoft.Json;
using AsyncKeyedLock;

namespace EJ2SDiagramSample.Pages
{
Expand Down Expand Up @@ -117,64 +118,52 @@ public async Task SendCurrentSelectionsToCaller()
}
#endregion

private static readonly ConcurrentDictionary<string, SemaphoreSlim> _connLocks = new();
private static readonly AsyncKeyedLocker<string> _connLocks = new();

private static SemaphoreSlim GetConnectionLock(string connectionId)
{
return _connLocks.GetOrAdd(connectionId, _ => new SemaphoreSlim(1, 1));
}
public async Task BroadcastToOtherClients(List<string> payloads, long clientVersion, List<string>? elementIds, SelectionEvent currentSelection, string roomName)
{
var connId = Context.ConnectionId;
var gate = GetConnectionLock(connId);
await gate.WaitAsync();
try
{
var versionKey = "diagram:version";
using var _ = await _connLocks.LockAsync(connId);
var versionKey = "diagram:version";

var (acceptedSingle, serverVersionSingle) = await _redisService.CompareAndIncrementAsync(versionKey, clientVersion);
long serverVersionFinal = serverVersionSingle;
var (acceptedSingle, serverVersionSingle) = await _redisService.CompareAndIncrementAsync(versionKey, clientVersion);
long serverVersionFinal = serverVersionSingle;

if (!acceptedSingle)
if (!acceptedSingle)
{
var recentUpdates = await GetUpdatesSinceVersionAsync(clientVersion, maxScan: 200);
var recentlyTouched = new HashSet<string>(StringComparer.Ordinal);
foreach (var upd in recentUpdates)
{
var recentUpdates = await GetUpdatesSinceVersionAsync(clientVersion, maxScan: 200);
var recentlyTouched = new HashSet<string>(StringComparer.Ordinal);
foreach (var upd in recentUpdates)
{
if (upd.ModifiedElementIds == null) continue;
foreach (var id in upd.ModifiedElementIds)
recentlyTouched.Add(id);
}

var overlaps = elementIds?.Where(id => recentlyTouched.Contains(id)).Distinct().ToList();
if (overlaps?.Count > 0)
{
await Clients.Caller.SendAsync("RevertCurrentChanges", elementIds);
await Clients.Caller.SendAsync("ShowConflict");
return;
}

var (_, newServerVersion) = await _redisService.CompareAndIncrementAsync(versionKey, serverVersionSingle);
serverVersionFinal = newServerVersion;
if (upd.ModifiedElementIds == null) continue;
foreach (var id in upd.ModifiedElementIds)
recentlyTouched.Add(id);
}

var update = new DiagramUpdateMessage
var overlaps = elementIds?.Where(id => recentlyTouched.Contains(id)).Distinct().ToList();
if (overlaps?.Count > 0)
{
SourceConnectionId = connId,
Version = serverVersionFinal,
ModifiedElementIds = elementIds
};
await Clients.Caller.SendAsync("RevertCurrentChanges", elementIds);
await Clients.Caller.SendAsync("ShowConflict");
return;
}

await StoreUpdateInRedis(update, connId);
SelectionEvent selectionEvent = BuildSelectedElementEvent(currentSelection.ElementIds, currentSelection.SelectorBounds);
await UpdateSelectionBoundsInRedis(selectionEvent, currentSelection.ElementIds, currentSelection.SelectorBounds);
await Clients.OthersInGroup(roomName).SendAsync("ReceiveData", payloads, serverVersionFinal, selectionEvent);
await RemoveOldUpdates(serverVersionFinal);
var (_, newServerVersion) = await _redisService.CompareAndIncrementAsync(versionKey, serverVersionSingle);
serverVersionFinal = newServerVersion;
}
finally

var update = new DiagramUpdateMessage
{
gate.Release();
}
SourceConnectionId = connId,
Version = serverVersionFinal,
ModifiedElementIds = elementIds
};

await StoreUpdateInRedis(update, connId);
SelectionEvent selectionEvent = BuildSelectedElementEvent(currentSelection.ElementIds, currentSelection.SelectorBounds);
await UpdateSelectionBoundsInRedis(selectionEvent, currentSelection.ElementIds, currentSelection.SelectorBounds);
await Clients.OthersInGroup(roomName).SendAsync("ReceiveData", payloads, serverVersionFinal, selectionEvent);
await RemoveOldUpdates(serverVersionFinal);
}
private async Task RemoveOldUpdates(long currentServerVersion)
{
Expand Down Expand Up @@ -379,7 +368,7 @@ public async Task JoinDiagram(string roomName, string diagramId, string userName
_diagramUsers.AddOrUpdate(userId, diagramUser,
(key, existingValue) => diagramUser
);
await RequestAndLoadStateAsync(roomName,diagramId,Context.ConnectionId,Context.ConnectionAborted);
await RequestAndLoadStateAsync(roomName, diagramId, Context.ConnectionId, Context.ConnectionAborted);

long currentServerVersion = await GetDiagramVersion();
await Clients.Caller.SendAsync("UpdateVersion", currentServerVersion);
Expand Down