From 4fc2a8b9d03f3589fa9af46009eff76e16b7595d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 02:10:18 +0200 Subject: [PATCH 01/10] Lock around the whole deferred task --- .../ContentTypesSitemapUpdateHandler.cs | 55 +++++++++++++------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index 64b7a9c7aae..6682e14fd04 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -1,36 +1,59 @@ +using System; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Handlers; +using OrchardCore.Environment.Shell.Scope; +using OrchardCore.Locking.Distributed; using OrchardCore.Sitemaps.Handlers; namespace OrchardCore.Contents.Sitemaps { public class ContentTypesSitemapUpdateHandler : ContentHandlerBase { - private readonly ISitemapUpdateHandler _sitemapUpdateHandler; + private readonly IDistributedLock _distributedLock; - public ContentTypesSitemapUpdateHandler(ISitemapUpdateHandler sitemapUpdateHandler) + public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock) { - _sitemapUpdateHandler = sitemapUpdateHandler; + _distributedLock = distributedLock; } - public override Task PublishedAsync(PublishContentContext context) - { - var updateContext = new SitemapUpdateContext - { - UpdateObject = context.ContentItem, - }; + public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); - return _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); - } + public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); - public override Task UnpublishedAsync(PublishContentContext context) + // Doing the update in a deferred and synchronized way makes sure that two simultaneous content item updates + // don't cause a ConcurrencyException due to the same sitemap document being updated. + private async Task UpdateSitemapDeferredAsync(ContentContextBase context) { - var updateContext = new SitemapUpdateContext + var contentItemId = context.ContentItem.ContentItemId; + + var timeout = TimeSpan.FromMilliseconds(20_000); + (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); + if (!locked) { - UpdateObject = context.ContentItem, - }; + throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); + } + else + { + using (locker) + { + ShellScope.AddDeferredTask(async scope => + { + await using var subScope = await ShellScope.CreateChildScopeAsync(); + + var contentManager = subScope.ServiceProvider.GetRequiredService(); + var sitemapUpdateHandler = subScope.ServiceProvider.GetRequiredService(); + + var updateContext = new SitemapUpdateContext + { + UpdateObject = await contentManager.GetAsync(contentItemId), + }; - return _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); + await sitemapUpdateHandler.UpdateSitemapAsync(updateContext); + }); + } + } } } } From 4a6dbc3da318c8aeca40e6ec64e312dfa743f979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 02:11:59 +0200 Subject: [PATCH 02/10] Lock only around the update (this actually waits for the lock) --- .../ContentTypesSitemapUpdateHandler.cs | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index 6682e14fd04..cb8b5dd1835 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -11,39 +11,33 @@ namespace OrchardCore.Contents.Sitemaps { public class ContentTypesSitemapUpdateHandler : ContentHandlerBase { - private readonly IDistributedLock _distributedLock; - - public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock) - { - _distributedLock = distributedLock; - } - public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); // Doing the update in a deferred and synchronized way makes sure that two simultaneous content item updates // don't cause a ConcurrencyException due to the same sitemap document being updated. - private async Task UpdateSitemapDeferredAsync(ContentContextBase context) + private static Task UpdateSitemapDeferredAsync(ContentContextBase context) { var contentItemId = context.ContentItem.ContentItemId; - var timeout = TimeSpan.FromMilliseconds(20_000); - (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); - if (!locked) - { - throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); - } - else + ShellScope.AddDeferredTask(async scope => { - using (locker) + var distributedLock = scope.ServiceProvider.GetRequiredService(); + + var timeout = TimeSpan.FromMilliseconds(20_000); + (var locker, var locked) = await distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); + + if (!locked) + { + throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); + } + else { - ShellScope.AddDeferredTask(async scope => + using (locker) { - await using var subScope = await ShellScope.CreateChildScopeAsync(); - - var contentManager = subScope.ServiceProvider.GetRequiredService(); - var sitemapUpdateHandler = subScope.ServiceProvider.GetRequiredService(); + var contentManager = scope.ServiceProvider.GetRequiredService(); + var sitemapUpdateHandler = scope.ServiceProvider.GetRequiredService(); var updateContext = new SitemapUpdateContext { @@ -51,9 +45,11 @@ private async Task UpdateSitemapDeferredAsync(ContentContextBase context) }; await sitemapUpdateHandler.UpdateSitemapAsync(updateContext); - }); + } } - } + }); + + return Task.CompletedTask; } } } From 95d35c2e8055b0788311a861750690d451e8030f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 02:28:03 +0200 Subject: [PATCH 03/10] Trying to open a new transaction, but this doesn't help --- .../Sitemaps/ContentTypesSitemapUpdateHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index cb8b5dd1835..f42d1a36530 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -6,6 +6,7 @@ using OrchardCore.Environment.Shell.Scope; using OrchardCore.Locking.Distributed; using OrchardCore.Sitemaps.Handlers; +using YesSql; namespace OrchardCore.Contents.Sitemaps { @@ -36,6 +37,7 @@ private static Task UpdateSitemapDeferredAsync(ContentContextBase context) { using (locker) { + await scope.ServiceProvider.GetRequiredService().BeginTransactionAsync(); var contentManager = scope.ServiceProvider.GetRequiredService(); var sitemapUpdateHandler = scope.ServiceProvider.GetRequiredService(); From a90b2bdd3732091a8747254e50910e214e73b6ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 02:36:20 +0200 Subject: [PATCH 04/10] Lock release in deferred task --- .../ContentTypesSitemapUpdateHandler.cs | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index f42d1a36530..be4e30e2d1c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using OrchardCore.ContentManagement; @@ -6,38 +7,42 @@ using OrchardCore.Environment.Shell.Scope; using OrchardCore.Locking.Distributed; using OrchardCore.Sitemaps.Handlers; -using YesSql; namespace OrchardCore.Contents.Sitemaps { public class ContentTypesSitemapUpdateHandler : ContentHandlerBase { + private readonly IDistributedLock _distributedLock; + + public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock) + { + _distributedLock = distributedLock; + } + public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); // Doing the update in a deferred and synchronized way makes sure that two simultaneous content item updates // don't cause a ConcurrencyException due to the same sitemap document being updated. - private static Task UpdateSitemapDeferredAsync(ContentContextBase context) + private async Task UpdateSitemapDeferredAsync(ContentContextBase context) { var contentItemId = context.ContentItem.ContentItemId; - ShellScope.AddDeferredTask(async scope => + var timeout = TimeSpan.FromMilliseconds(20_000); + (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); + Debug.WriteLine($"Lock acquired: {DateTime.Now}"); + if (!locked) + { + throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); + } + else { - var distributedLock = scope.ServiceProvider.GetRequiredService(); - - var timeout = TimeSpan.FromMilliseconds(20_000); - (var locker, var locked) = await distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); - if (!locked) - { - throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); - } - else + ShellScope.AddDeferredTask(async scope => { using (locker) { - await scope.ServiceProvider.GetRequiredService().BeginTransactionAsync(); var contentManager = scope.ServiceProvider.GetRequiredService(); var sitemapUpdateHandler = scope.ServiceProvider.GetRequiredService(); @@ -48,10 +53,8 @@ private static Task UpdateSitemapDeferredAsync(ContentContextBase context) await sitemapUpdateHandler.UpdateSitemapAsync(updateContext); } - } - }); - - return Task.CompletedTask; + }); + } } } } From ae6566d132924f5896bf161e88436d1306c75f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 02:42:04 +0200 Subject: [PATCH 05/10] Locking without deferred task --- .../ContentTypesSitemapUpdateHandler.cs | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index be4e30e2d1c..37a75464faa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -1,10 +1,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Handlers; -using OrchardCore.Environment.Shell.Scope; using OrchardCore.Locking.Distributed; using OrchardCore.Sitemaps.Handlers; @@ -13,10 +10,12 @@ namespace OrchardCore.Contents.Sitemaps public class ContentTypesSitemapUpdateHandler : ContentHandlerBase { private readonly IDistributedLock _distributedLock; + private readonly ISitemapUpdateHandler _sitemapUpdateHandler; - public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock) + public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock, ISitemapUpdateHandler sitemapUpdateHandler) { _distributedLock = distributedLock; + _sitemapUpdateHandler = sitemapUpdateHandler; } public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); @@ -27,8 +26,6 @@ public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock) // don't cause a ConcurrencyException due to the same sitemap document being updated. private async Task UpdateSitemapDeferredAsync(ContentContextBase context) { - var contentItemId = context.ContentItem.ContentItemId; - var timeout = TimeSpan.FromMilliseconds(20_000); (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); Debug.WriteLine($"Lock acquired: {DateTime.Now}"); @@ -38,22 +35,15 @@ private async Task UpdateSitemapDeferredAsync(ContentContextBase context) } else { - - ShellScope.AddDeferredTask(async scope => + using (locker) { - using (locker) + var updateContext = new SitemapUpdateContext { - var contentManager = scope.ServiceProvider.GetRequiredService(); - var sitemapUpdateHandler = scope.ServiceProvider.GetRequiredService(); - - var updateContext = new SitemapUpdateContext - { - UpdateObject = await contentManager.GetAsync(contentItemId), - }; + UpdateObject = context.ContentItem, + }; - await sitemapUpdateHandler.UpdateSitemapAsync(updateContext); - } - }); + await _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); + } } } } From e6551b53d3b44bfa3ab55796207ec20ebbba74ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 02:42:27 +0200 Subject: [PATCH 06/10] Removing debug code --- .../Sitemaps/ContentTypesSitemapUpdateHandler.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index 37a75464faa..98b6903823b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Threading.Tasks; using OrchardCore.ContentManagement.Handlers; using OrchardCore.Locking.Distributed; @@ -28,7 +27,7 @@ private async Task UpdateSitemapDeferredAsync(ContentContextBase context) { var timeout = TimeSpan.FromMilliseconds(20_000); (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); - Debug.WriteLine($"Lock acquired: {DateTime.Now}"); + if (!locked) { throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); From 6fab9ede8d5c6a9003baebcb1a20acd187bd112b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 02:43:53 +0200 Subject: [PATCH 07/10] Fixing comment --- .../Sitemaps/ContentTypesSitemapUpdateHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index 98b6903823b..9069d0d4a85 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -21,8 +21,8 @@ public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock, ISitem public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); - // Doing the update in a deferred and synchronized way makes sure that two simultaneous content item updates - // don't cause a ConcurrencyException due to the same sitemap document being updated. + // Doing the update in a synchronized way makes sure that two simultaneous content item updates don't cause a + // ConcurrencyException due to the same sitemap document being updated. private async Task UpdateSitemapDeferredAsync(ContentContextBase context) { var timeout = TimeSpan.FromMilliseconds(20_000); From 644d277c22b4a528eca186ce7c1e64c4f930954a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 19:39:10 +0200 Subject: [PATCH 08/10] Removing unnecessary else --- .../Sitemaps/ContentTypesSitemapUpdateHandler.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index 9069d0d4a85..7eecf5249b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -32,17 +32,15 @@ private async Task UpdateSitemapDeferredAsync(ContentContextBase context) { throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); } - else + + using (locker) { - using (locker) + var updateContext = new SitemapUpdateContext { - var updateContext = new SitemapUpdateContext - { - UpdateObject = context.ContentItem, - }; + UpdateObject = context.ContentItem, + }; - await _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); - } + await _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); } } } From 21b7862c1b6be6feebcf5f97ee0146f37d710b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 19:45:12 +0200 Subject: [PATCH 09/10] Moving locking one step deeper --- .../ContentTypesSitemapUpdateHandler.cs | 33 +++++-------------- .../Handlers/DefaultSitemapUpdateHandler.cs | 26 +++++++++++++-- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index 7eecf5249b4..e1ee930f5d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -1,47 +1,30 @@ -using System; using System.Threading.Tasks; using OrchardCore.ContentManagement.Handlers; -using OrchardCore.Locking.Distributed; using OrchardCore.Sitemaps.Handlers; namespace OrchardCore.Contents.Sitemaps { public class ContentTypesSitemapUpdateHandler : ContentHandlerBase { - private readonly IDistributedLock _distributedLock; private readonly ISitemapUpdateHandler _sitemapUpdateHandler; - public ContentTypesSitemapUpdateHandler(IDistributedLock distributedLock, ISitemapUpdateHandler sitemapUpdateHandler) + public ContentTypesSitemapUpdateHandler(ISitemapUpdateHandler sitemapUpdateHandler) { - _distributedLock = distributedLock; _sitemapUpdateHandler = sitemapUpdateHandler; } - public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); + public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapAsync(context); - public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapDeferredAsync(context); + public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapAsync(context); - // Doing the update in a synchronized way makes sure that two simultaneous content item updates don't cause a - // ConcurrencyException due to the same sitemap document being updated. - private async Task UpdateSitemapDeferredAsync(ContentContextBase context) + private async Task UpdateSitemapAsync(ContentContextBase context) { - var timeout = TimeSpan.FromMilliseconds(20_000); - (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); - - if (!locked) - { - throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); - } - - using (locker) + var updateContext = new SitemapUpdateContext { - var updateContext = new SitemapUpdateContext - { - UpdateObject = context.ContentItem, - }; + UpdateObject = context.ContentItem, + }; - await _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); - } + await _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs index 44aa2224ed0..e5fc591d66f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs @@ -1,22 +1,42 @@ +using System; using System.Collections.Generic; using System.Threading.Tasks; +using OrchardCore.Locking.Distributed; namespace OrchardCore.Sitemaps.Handlers { public class DefaultSitemapUpdateHandler : ISitemapUpdateHandler { private readonly IEnumerable _sitemapTypeUpdateHandlers; + private readonly IDistributedLock _distributedLock; - public DefaultSitemapUpdateHandler(IEnumerable sitemapTypeUpdateHandlers) + public DefaultSitemapUpdateHandler( + IEnumerable sitemapTypeUpdateHandlers, + IDistributedLock distributedLock) { _sitemapTypeUpdateHandlers = sitemapTypeUpdateHandlers; + _distributedLock = distributedLock; } public async Task UpdateSitemapAsync(SitemapUpdateContext context) { - foreach (var sitemapTypeUpdateHandler in _sitemapTypeUpdateHandlers) + // Doing the update in a synchronized way makes sure that two simultaneous content item updates don't cause + // a ConcurrencyException due to the same sitemap document being updated. + + var timeout = TimeSpan.FromMilliseconds(20_000); + (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); + + if (!locked) + { + throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); + } + + using (locker) { - await sitemapTypeUpdateHandler.UpdateSitemapAsync(context); + foreach (var sitemapTypeUpdateHandler in _sitemapTypeUpdateHandlers) + { + await sitemapTypeUpdateHandler.UpdateSitemapAsync(context); + } } } } From beff4b062abe77cc38fd5470a1b1673ffc622eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Wed, 17 Apr 2024 19:49:37 +0200 Subject: [PATCH 10/10] Async simplification --- .../Sitemaps/ContentTypesSitemapUpdateHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index e1ee930f5d8..5c6a4b20118 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -17,14 +17,14 @@ public ContentTypesSitemapUpdateHandler(ISitemapUpdateHandler sitemapUpdateHandl public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapAsync(context); - private async Task UpdateSitemapAsync(ContentContextBase context) + private Task UpdateSitemapAsync(ContentContextBase context) { var updateContext = new SitemapUpdateContext { UpdateObject = context.ContentItem, }; - await _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); + return _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); } } }