{"record":{"id":"422fb8f24db477bb","repo":"OrchardCMS/OrchardCore","slug":"couldn-t-acquire-a-lock-to-update-the-sitemap-within-timeout","errorCode":null,"errorMessage":"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds.","messagePattern":"Couldn't acquire a lock to update the sitemap within (.+?) seconds\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs","lineNumber":28,"sourceCode":"    public DefaultSitemapUpdateHandler(\n        IEnumerable<ISitemapTypeUpdateHandler> sitemapTypeUpdateHandlers,\n        IDistributedLock distributedLock)\n    {\n        _sitemapTypeUpdateHandlers = sitemapTypeUpdateHandlers;\n        _distributedLock = distributedLock;\n    }\n\n    public async Task UpdateSitemapAsync(SitemapUpdateContext context)\n    {\n        // Doing the update in a synchronized way makes sure that two simultaneous content item updates don't cause\n        // a ConcurrencyException due to the same sitemap document being updated.\n\n        var timeout = TimeSpan.FromMilliseconds(20_000);\n        (var locker, var locked) = await _distributedLock.TryAcquireLockAsync(\"SITEMAPS_UPDATE_LOCK\", timeout, timeout);\n\n        if (!locked)\n        {\n            throw new TimeoutException($\"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds.\");\n        }\n\n        using (locker)\n        {\n            foreach (var sitemapTypeUpdateHandler in _sitemapTypeUpdateHandlers)\n            {\n                await sitemapTypeUpdateHandler.UpdateSitemapAsync(context);\n            }\n        }\n    }\n}\n","sourceCodeStart":10,"sourceCodeEnd":40,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs#L10-L40","documentation":"DefaultSitemapUpdateHandler.TryUpdateSitemapAsync acquires a distributed lock (SITEMAPS_UPDATE_LOCK) with a 20-second timeout before rebuilding sitemaps. If the lock cannot be acquired within that window — because another sitemap update is still running — it throws TimeoutException to prevent concurrent/overlapping sitemap rebuilds.","triggerScenarios":"Two or more sitemap update requests overlap (e.g. saving a sitemap while a scheduled/background rebuild holds the lock for more than 20 seconds); large sitemap generation exceeding the lock timeout; multi-node farms contending for the lock.","commonSituations":"Multi-server Orchard deployments with a shared distributed lock; very large sitemaps (tens of thousands of URLs) taking longer than 20s to build; rapid successive saves to sitemap entries triggering rebuilds.","solutions":["Reduce sitemap size/complexity (page via sitemap index, limit entries) so rebuilds finish under 20 seconds.","Retry the update after a delay — the error means the lock was busy, so re-trigger the sitemap rebuild.","Ensure only one node/rebuild process updates sitemaps at a time (stagger background tasks, avoid simultaneous saves).","Check whether another process is stuck holding the lock (hanging rebuild) and restart it; investigate slow sitemap builders."],"exampleFix":"// before (caller)\nawait _sitemapManager.TryUpdateSitemapAsync(sitemapId);\n// after (caller with retry)\ntry { await _sitemapManager.TryUpdateSitemapAsync(sitemapId); }\ncatch (TimeoutException) { _logger.LogWarning(\"Sitemap update locked; will retry.\"); await Task.Delay(5000); /* retry */ }","handlingStrategy":"retry","validationCode":"// no pre-call validation possible; check lock contention before retrying\nvar recent = await _distributedLockService.TryAcquireLockAsync(\"SITEMAPS_UPDATE_LOCK\", TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));\nif (!recent.locked) logger.LogWarning(\"Sitemap update lock is currently held; defer the rebuild.\");","typeGuard":null,"tryCatchPattern":"try {\n  await sitemapManager.TryUpdateSitemapAsync(sitemapId);\n} catch (TimeoutException ex) when (ex.Message.Contains(\"sitemap\")) {\n  await Task.Delay(TimeSpan.FromSeconds(30));\n  // retry once, or enqueue a background rebuild\n}","preventionTips":["Debounce sitemap rebuilds triggered by rapid content/sitemap saves.","Use sitemap index + caching to keep rebuilds under 20 seconds.","Stagger sitemap rebuilds across nodes in multi-server farms.","Run large rebuilds in a background task with retry."],"tags":["sitemaps","distributed-lock","timeout","concurrency"],"backgroundTag":"request-timeout","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}