Skip to content

Commit e3fca7c

Browse files
committed
Added Cache Instructions lock, to avoid deadlocks
1 parent 64f466c commit e3fca7c

6 files changed

Lines changed: 50 additions & 1 deletion

File tree

src/Umbraco.Core/Persistence/Constants-Locks.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,10 @@ public static class Locks
7070
/// ScheduledPublishing job.
7171
/// </summary>
7272
public const int ScheduledPublishing = -341;
73+
74+
/// <summary>
75+
/// CacheInstructions service.
76+
/// </summary>
77+
public const int CacheInstructions = -342;
7378
}
7479
}

src/Umbraco.Core/Services/ContentTypeService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public ContentTypeService(
2929
// beware! order is important to avoid deadlocks
3030
protected override int[] ReadLockIds { get; } = { Constants.Locks.ContentTypes };
3131

32-
protected override int[] WriteLockIds { get; } = { Constants.Locks.ContentTree, Constants.Locks.ContentTypes };
32+
protected override int[] WriteLockIds { get; } = { Constants.Locks.ContentTree, Constants.Locks.ContentTypes, Constants.Locks.CacheInstructions};
3333

3434
protected override Guid ContainedObjectType => Constants.ObjectTypes.DocumentType;
3535

src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,8 @@ private void CreateLockData()
10151015
new LockDto { Id = Constants.Locks.Languages, Name = "Languages" });
10161016
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false,
10171017
new LockDto { Id = Constants.Locks.ScheduledPublishing, Name = "ScheduledPublishing" });
1018+
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false,
1019+
new LockDto { Id = Constants.Locks.CacheInstructions, Name = "CacheInstructions" });
10181020

10191021
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false,
10201022
new LockDto { Id = Constants.Locks.MainDom, Name = "MainDom" });

src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_2_0;
88
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_5_0;
99
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_7_0;
10+
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_9_0;
1011
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0;
1112
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_1;
1213
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0;
@@ -307,5 +308,8 @@ protected void DefinePlan()
307308

308309
// to 10.7.0
309310
To<MigrateTagsFromNVarcharToNText>("{EF93F398-1385-4F07-808A-D3C518984442}");
311+
312+
// to 10.9.0
313+
To<AddCacheInstructionLock>("{75AB5D21-4464-40F5-A9D1-3E8CE0D89E38}");
310314
}
311315
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Umbraco.Cms.Core;
2+
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
3+
using Umbraco.Extensions;
4+
5+
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_9_0;
6+
7+
internal class AddCacheInstructionLock : MigrationBase
8+
{
9+
public AddCacheInstructionLock(IMigrationContext context)
10+
: base(context)
11+
{
12+
}
13+
14+
protected override void Migrate()
15+
{
16+
var currentCount = Database
17+
.Fetch<int>(
18+
Sql()
19+
.SelectCount()
20+
.From<LockDto>()
21+
.Where<LockDto>(x => x.Id == Constants.Locks.CacheInstructions))
22+
.FirstOrDefault();
23+
if (currentCount == 0)
24+
{
25+
Database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false,
26+
new LockDto { Id = Constants.Locks.CacheInstructions, Name = "CacheInstructions" });
27+
}
28+
29+
}
30+
}

src/Umbraco.Infrastructure/Services/CacheInstructionService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Microsoft.Extensions.Options;
33
using Newtonsoft.Json;
44
using Newtonsoft.Json.Linq;
5+
using Umbraco.Cms.Core;
56
using Umbraco.Cms.Core.Cache;
67
using Umbraco.Cms.Core.Configuration.Models;
78
using Umbraco.Cms.Core.Events;
@@ -77,6 +78,7 @@ public CacheInstructionService(
7778
public bool IsColdBootRequired(int lastId)
7879
{
7980
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
81+
scope.ReadLock(Constants.Locks.CacheInstructions);
8082
if (lastId <= 0)
8183
{
8284
var count = _cacheInstructionRepository.CountAll();
@@ -103,6 +105,7 @@ public bool IsColdBootRequired(int lastId)
103105
public bool IsInstructionCountOverLimit(int lastId, int limit, out int count)
104106
{
105107
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
108+
scope.ReadLock(Constants.Locks.CacheInstructions);
106109
// Check for how many instructions there are to process, each row contains a count of the number of instructions contained in each
107110
// row so we will sum these numbers to get the actual count.
108111
count = _cacheInstructionRepository.CountPendingInstructions(lastId);
@@ -113,6 +116,7 @@ public bool IsInstructionCountOverLimit(int lastId, int limit, out int count)
113116
public int GetMaxInstructionId()
114117
{
115118
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
119+
scope.ReadLock(Constants.Locks.CacheInstructions);
116120
return _cacheInstructionRepository.GetMaxId();
117121
}
118122

@@ -123,6 +127,7 @@ public void DeliverInstructions(IEnumerable<RefreshInstruction> instructions, st
123127

124128
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
125129
{
130+
scope.WriteLock(Constants.Locks.CacheInstructions);
126131
_cacheInstructionRepository.Add(entity);
127132
scope.Complete();
128133
}
@@ -134,6 +139,7 @@ public void DeliverInstructionsInBatches(IEnumerable<RefreshInstruction> instruc
134139
// Write the instructions but only create JSON blobs with a max instruction count equal to MaxProcessingInstructionCount.
135140
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
136141
{
142+
scope.WriteLock(Constants.Locks.CacheInstructions);
137143
foreach (IEnumerable<RefreshInstruction> instructionsBatch in instructions.InGroupsOf(
138144
_globalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount))
139145
{
@@ -157,6 +163,7 @@ public ProcessInstructionsResult ProcessInstructions(
157163
using (_profilingLogger.DebugDuration<CacheInstructionService>("Syncing from database..."))
158164
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
159165
{
166+
scope.ReadLock(Constants.Locks.CacheInstructions);
160167
var numberOfInstructionsProcessed = ProcessDatabaseInstructions(cacheRefreshers, cancellationToken, localIdentity, ref lastId);
161168

162169
// Check for pruning throttling.
@@ -251,6 +258,7 @@ private int ProcessDatabaseInstructions(CacheRefresherCollection cacheRefreshers
251258
List<RefreshInstruction> instructionBatch = GetAllInstructions(jsonInstructions);
252259

253260
// Process as per-normal.
261+
254262
var success = ProcessDatabaseInstructions(cacheRefreshers, instructionBatch, instruction, processed, cancellationToken, ref lastId);
255263

256264
// If they couldn't be all processed (i.e. we're shutting down) then exit.

0 commit comments

Comments
 (0)