Files
Umbraco-CMS/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs

160 lines
5.8 KiB
C#
Raw Normal View History

2017-07-20 11:21:28 +02:00
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Umbraco.Core;
2016-03-17 15:59:35 +01:00
using Umbraco.Core.Cache;
using Umbraco.Core.Sync;
using Umbraco.Web.Routing;
2015-07-29 15:22:22 +02:00
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
2017-12-28 09:06:33 +01:00
using Umbraco.Core.Persistence.Dtos;
2017-05-12 14:49:44 +02:00
using Umbraco.Core.Scoping;
using Umbraco.Core.Hosting;
namespace Umbraco.Web
{
/// <summary>
/// An <see cref="IServerMessenger"/> implementation that works by storing messages in the database.
/// </summary>
/// <remarks>
/// This binds to appropriate umbraco events in order to trigger the Boot(), Sync() & FlushBatch() calls
/// </remarks>
public class BatchedDatabaseServerMessenger : DatabaseServerMessenger, IBatchedDatabaseServerMessenger
{
2017-05-12 14:49:44 +02:00
private readonly IUmbracoDatabaseFactory _databaseFactory;
private readonly IRequestCache _requestCache;
2020-03-03 07:29:51 +01:00
private readonly IRequestAccessor _requestAccessor;
2017-05-12 14:49:44 +02:00
public BatchedDatabaseServerMessenger(
IMainDom mainDom,
2020-03-03 07:29:51 +01:00
IUmbracoDatabaseFactory databaseFactory,
IScopeProvider scopeProvider,
ISqlContext sqlContext,
IProfilingLogger proflog,
IServerRegistrar serverRegistrar,
2020-03-03 07:29:51 +01:00
DatabaseServerMessengerOptions options,
IHostingEnvironment hostingEnvironment,
CacheRefresherCollection cacheRefreshers,
IRequestCache requestCache,
IRequestAccessor requestAccessor)
: base(mainDom, scopeProvider, sqlContext, proflog, serverRegistrar, true, options, hostingEnvironment, cacheRefreshers)
2017-05-12 14:49:44 +02:00
{
_databaseFactory = databaseFactory;
_requestCache = requestCache;
2020-03-03 07:29:51 +01:00
_requestAccessor = requestAccessor;
2017-05-12 14:49:44 +02:00
}
// invoked by DatabaseServerRegistrarAndMessengerComponent
2020-03-03 07:29:51 +01:00
public void Startup()
{
2020-03-03 07:29:51 +01:00
_requestAccessor.EndRequest += UmbracoModule_EndRequest;
2017-05-12 14:49:44 +02:00
if (_databaseFactory.CanConnect == false)
{
2020-09-14 10:17:32 +02:00
Logger.LogWarning<BatchedDatabaseServerMessenger>("Cannot connect to the database, distributed calls will not be enabled for this server.");
}
else
{
Boot();
}
}
2016-11-18 13:48:18 +01:00
private void UmbracoModule_EndRequest(object sender, UmbracoRequestEventArgs e)
{
// will clear the batch - will remain in HttpContext though - that's ok
FlushBatch();
}
protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable<object> ids = null, string json = null)
{
var idsA = ids?.ToArray();
Type arrayType;
if (GetArrayType(idsA, out arrayType) == false)
throw new ArgumentException("All items must be of the same type, either int or Guid.", nameof(ids));
BatchMessage(refresher, messageType, idsA, arrayType, json);
}
public void FlushBatch()
{
var batch = GetBatch(false);
if (batch == null) return;
var instructions = batch.SelectMany(x => x.Instructions).ToArray();
batch.Clear();
//Write the instructions but only create JSON blobs with a max instruction count equal to MaxProcessingInstructionCount
Merge remote-tracking branch 'origin/dev-v7' into dev-v8 # Conflicts: # src/SolutionInfo.cs # src/Umbraco.Core/Configuration/UmbracoVersion.cs # src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs # src/Umbraco.Core/Persistence/PetaPoco.cs # src/Umbraco.Core/Scoping/NoScope.cs # src/Umbraco.Core/Scoping/ScopeProvider.cs # src/Umbraco.Core/Services/ContentService.cs # src/Umbraco.Core/Services/IContentService.cs # src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs # src/Umbraco.Tests/UI/LegacyDialogTests.cs # src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js # src/Umbraco.Web.UI/umbraco/config/create/UI.xml # src/Umbraco.Web.UI/umbraco/config/lang/zh.xml # src/Umbraco.Web/BatchedDatabaseServerMessenger.cs # src/Umbraco.Web/Editors/ContentController.cs # src/Umbraco.Web/Editors/MediaTypeController.cs # src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs # src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs # src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs # src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs # src/Umbraco.Web/Trees/ContentTreeControllerBase.cs # src/Umbraco.Web/umbraco.presentation/umbraco/create/MemberGroupTasks.cs
2018-04-19 23:41:35 +10:00
using (var scope = ScopeProvider.CreateScope())
{
2018-04-12 14:26:47 +02:00
foreach (var instructionsBatch in instructions.InGroupsOf(Options.MaxProcessingInstructionCount))
{
WriteInstructions(scope, instructionsBatch);
}
scope.Complete();
}
2017-07-20 11:21:28 +02:00
}
2018-04-12 14:26:47 +02:00
private void WriteInstructions(IScope scope, IEnumerable<RefreshInstruction> instructions)
{
var dto = new CacheInstructionDto
{
UtcStamp = DateTime.UtcNow,
Instructions = JsonConvert.SerializeObject(instructions, Formatting.None),
2018-03-27 10:04:07 +02:00
OriginIdentity = LocalIdentity,
InstructionCount = instructions.Sum(x => x.JsonIdCount)
};
2018-04-12 14:26:47 +02:00
scope.Database.Insert(dto);
}
protected ICollection<RefreshInstructionEnvelope> GetBatch(bool create)
{
2020-03-03 07:29:51 +01:00
var key = nameof(BatchedDatabaseServerMessenger);
if (!_requestCache.IsAvailable) return null;
// no thread-safety here because it'll run in only 1 thread (request) at a time
var batch = (ICollection<RefreshInstructionEnvelope>)_requestCache.Get(key);
if (batch == null && create)
{
batch = new List<RefreshInstructionEnvelope>();
_requestCache.Set(key, batch);
}
return batch;
}
protected void BatchMessage(
ICacheRefresher refresher,
MessageType messageType,
IEnumerable<object> ids = null,
Type idType = null,
string json = null)
{
var batch = GetBatch(true);
var instructions = RefreshInstruction.GetInstructions(refresher, messageType, ids, idType, json);
// batch if we can, else write to DB immediately
if (batch == null)
{
//only write the json blob with a maximum count of the MaxProcessingInstructionCount
Merge remote-tracking branch 'origin/dev-v7' into dev-v8 # Conflicts: # src/SolutionInfo.cs # src/Umbraco.Core/Configuration/UmbracoVersion.cs # src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs # src/Umbraco.Core/Persistence/PetaPoco.cs # src/Umbraco.Core/Scoping/NoScope.cs # src/Umbraco.Core/Scoping/ScopeProvider.cs # src/Umbraco.Core/Services/ContentService.cs # src/Umbraco.Core/Services/IContentService.cs # src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs # src/Umbraco.Tests/UI/LegacyDialogTests.cs # src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js # src/Umbraco.Web.UI/umbraco/config/create/UI.xml # src/Umbraco.Web.UI/umbraco/config/lang/zh.xml # src/Umbraco.Web/BatchedDatabaseServerMessenger.cs # src/Umbraco.Web/Editors/ContentController.cs # src/Umbraco.Web/Editors/MediaTypeController.cs # src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs # src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs # src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs # src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs # src/Umbraco.Web/Trees/ContentTreeControllerBase.cs # src/Umbraco.Web/umbraco.presentation/umbraco/create/MemberGroupTasks.cs
2018-04-19 23:41:35 +10:00
using (var scope = ScopeProvider.CreateScope())
{
2018-04-12 14:26:47 +02:00
foreach (var maxBatch in instructions.InGroupsOf(Options.MaxProcessingInstructionCount))
{
WriteInstructions(scope, maxBatch);
}
scope.Complete();
}
}
else
{
batch.Add(new RefreshInstructionEnvelope(refresher, instructions));
}
2017-07-20 11:21:28 +02:00
}
}
2017-07-20 11:21:28 +02:00
}