diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index 79b3ce3871..67f967f050 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -666,8 +666,8 @@ namespace Umbraco.Core.Persistence return ExecuteScalar(sql.SQL, sql.Arguments); } - Regex rxSelect = new Regex(@"\A\s*(SELECT|EXECUTE|CALL)\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline); - Regex rxFrom = new Regex(@"\A\s*FROM\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline); + static Regex rxSelect = new Regex(@"\A\s*(SELECT|EXECUTE|CALL)\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline); + static Regex rxFrom = new Regex(@"\A\s*FROM\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline); string AddSelectClause(string sql) { if (sql.StartsWith(";")) diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index bfee772872..ed23913719 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -374,7 +374,7 @@ namespace Umbraco.Core.Scoping } var parent = ParentScope; - _scopeProvider.AmbientScope = parent; + _scopeProvider.AmbientScope = parent; // might be null = this is how scopes are removed from context objects #if DEBUG_SCOPES _scopeProvider.Disposed(this); diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index dfcf7985ae..a9deb3465e 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -66,17 +66,22 @@ namespace Umbraco.Core.Scoping // tests, any other things (see https://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx), // but we don't want to make all of our objects serializable since they are *not* meant to be // used in cross-AppDomain scenario anyways. + // // in addition, whatever goes into the logical call context is serialized back and forth any - // time cross-AppDomain code executes, so if we put an "object" there, we'll can *another* - // "object" instance - and so we cannot use a random object as a key. + // time cross-AppDomain code executes, so if we put an "object" there, we'll get *another* + // "object" instance back - and so we cannot use a random object as a key. + // // so what we do is: we register a guid in the call context, and we keep a table mapping those // guids to the actual objects. the guid serializes back and forth without causing any issue, // and we can retrieve the actual objects from the table. - // only issue: how are we supposed to clear the table? we can't, really. objects should take - // care of de-registering themselves from context. - // everything we use does, except the NoScope scope, which just stays there // - // during tests, NoScope can to into call context... nothing much we can do about it + // so far, the only objects that go into this table are scopes (using ScopeItemKey) and + // scope contexts (using ContextItemKey). + // + // there is no automatic way to garbage-collect the table. Normal scopes and scope contexts + // takes great care removing themselves when disposed, so it is safe. OTOH the NoScope + // *CANNOT* remove itself, so it *WILL* leak, and there is nothing we can do about it. NoScope + // exists for backward compatibility reasons, but ... relying on it is greatly discouraged. private static readonly object StaticCallContextObjectsLock = new object(); private static readonly Dictionary StaticCallContextObjects diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs index 973ba341f2..357f36b60a 100644 --- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Sync; using Umbraco.Web.Routing; using Umbraco.Core.Logging; +using Umbraco.Core.Scoping; using Umbraco.Web.Scheduling; namespace Umbraco.Web @@ -21,9 +22,12 @@ namespace Umbraco.Web /// public class BatchedDatabaseServerMessenger : DatabaseServerMessenger { + private readonly ApplicationContext _appContext; + public BatchedDatabaseServerMessenger(ApplicationContext appContext, bool enableDistCalls, DatabaseServerMessengerOptions options) : base(appContext, enableDistCalls, options) { + _appContext = appContext; Scheduler.Initializing += Scheduler_Initializing; } @@ -42,7 +46,7 @@ namespace Umbraco.Web //start the background task runner for processing instructions const int delayMilliseconds = 60000; var instructionProcessingRunner = new BackgroundTaskRunner("InstructionProcessing", ApplicationContext.ProfilingLogger.Logger); - var instructionProcessingTask = new InstructionProcessing(instructionProcessingRunner, this, delayMilliseconds, Options.ThrottleSeconds * 1000); + var instructionProcessingTask = new InstructionProcessing(instructionProcessingRunner, this, _appContext.ScopeProvider, delayMilliseconds, Options.ThrottleSeconds * 1000); instructionProcessingRunner.TryAdd(instructionProcessingTask); e.Add(instructionProcessingTask); } @@ -73,18 +77,31 @@ namespace Umbraco.Web private class InstructionProcessing : RecurringTaskBase { private readonly DatabaseServerMessenger _messenger; + private readonly IScopeProvider _scopeProvider; public InstructionProcessing(IBackgroundTaskRunner runner, DatabaseServerMessenger messenger, + IScopeProvider scopeProvider, int delayMilliseconds, int periodMilliseconds) : base(runner, delayMilliseconds, periodMilliseconds) { _messenger = messenger; + _scopeProvider = scopeProvider; } public override bool PerformRun() { - _messenger.Sync(); + // beware! + // DatabaseServerMessenger uses _appContext.DatabaseContext.Database without creating + // scopes, and since we are running in a background task, there will be no ambient + // scope (as would be the case within a web request), and so we would end up creating + // (and leaking) a NoScope instance, which is bad - better make sure we have a true + // scope here! - see U4-11207 + using (var scope = _scopeProvider.CreateScope()) + { + _messenger.Sync(); + scope.Complete(); + } //return true to repeat return true; } @@ -191,4 +208,4 @@ namespace Umbraco.Web } } -} \ No newline at end of file +}