diff --git a/src/Umbraco.Core/Runtime/MainDom.cs b/src/Umbraco.Core/Runtime/MainDom.cs index e6780ec876..02f37f654e 100644 --- a/src/Umbraco.Core/Runtime/MainDom.cs +++ b/src/Umbraco.Core/Runtime/MainDom.cs @@ -144,8 +144,16 @@ namespace Umbraco.Core.Runtime _logger.Info("Acquiring."); - // Get the lock - var acquired = _mainDomLock.AcquireLockAsync(LockTimeoutMilliseconds).GetAwaiter().GetResult(); + // Get the lock + var acquired = false; + try + { + acquired = _mainDomLock.AcquireLockAsync(LockTimeoutMilliseconds).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + _logger.Error(ex, "Error while acquiring"); + } if (!acquired) { diff --git a/src/Umbraco.Core/Runtime/SqlMainDomLock.cs b/src/Umbraco.Core/Runtime/SqlMainDomLock.cs index 04abc83ba2..9793426c0b 100644 --- a/src/Umbraco.Core/Runtime/SqlMainDomLock.cs +++ b/src/Umbraco.Core/Runtime/SqlMainDomLock.cs @@ -1,4 +1,5 @@ -using System; +using NPoco; +using System; using System.Data; using System.Data.SqlClient; using System.Diagnostics; @@ -49,7 +50,9 @@ namespace Umbraco.Core.Runtime } if (!(_dbFactory.SqlContext.SqlSyntax is SqlServerSyntaxProvider sqlServerSyntaxProvider)) + { throw new NotSupportedException("SqlMainDomLock is only supported for Sql Server"); + } _sqlServerSyntax = sqlServerSyntaxProvider; @@ -57,11 +60,13 @@ namespace Umbraco.Core.Runtime var tempId = Guid.NewGuid().ToString(); - using var db = _dbFactory.CreateDatabase(); - using var transaction = db.GetTransaction(IsolationLevel.ReadCommitted); + IUmbracoDatabase db = null; try { + db = _dbFactory.CreateDatabase(); + db.BeginTransaction(IsolationLevel.ReadCommitted); + try { // wait to get a write lock @@ -102,7 +107,8 @@ namespace Umbraco.Core.Runtime } finally { - transaction.Complete(); + db?.CompleteTransaction(); + db?.Dispose(); } @@ -161,11 +167,11 @@ namespace Umbraco.Core.Runtime // new MainDom will just take over. if (_cancellationTokenSource.IsCancellationRequested) return; - - using var db = _dbFactory.CreateDatabase(); - using var transaction = db.GetTransaction(IsolationLevel.ReadCommitted); + IUmbracoDatabase db = null; try { + db = _dbFactory.CreateDatabase(); + db.BeginTransaction(IsolationLevel.ReadCommitted); // get a read lock _sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom); @@ -189,7 +195,8 @@ namespace Umbraco.Core.Runtime } finally { - transaction.Complete(); + db?.CompleteTransaction(); + db?.Dispose(); } } @@ -208,34 +215,47 @@ namespace Umbraco.Core.Runtime return Task.Run(() => { - using var db = _dbFactory.CreateDatabase(); - - var watch = new Stopwatch(); - watch.Start(); - while (true) + try { - // poll very often, we need to take over as fast as we can - // local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO - Thread.Sleep(1000); + using var db = _dbFactory.CreateDatabase(); - var acquired = TryAcquire(db, tempId, updatedTempId); - if (acquired.HasValue) - return acquired.Value; - - if (watch.ElapsedMilliseconds >= millisecondsTimeout) + var watch = new Stopwatch(); + watch.Start(); + while (true) { - return AcquireWhenMaxWaitTimeElapsed(db); + // poll very often, we need to take over as fast as we can + // local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO + Thread.Sleep(1000); + + var acquired = TryAcquire(db, tempId, updatedTempId); + if (acquired.HasValue) + return acquired.Value; + + if (watch.ElapsedMilliseconds >= millisecondsTimeout) + { + return AcquireWhenMaxWaitTimeElapsed(db); + } } } + catch (Exception ex) + { + _logger.Error(ex, "An error occurred trying to acquire and waiting for existing SqlMainDomLock to shutdown"); + return false; + } + }, _cancellationTokenSource.Token); } private bool? TryAcquire(IUmbracoDatabase db, string tempId, string updatedTempId) { - using var transaction = db.GetTransaction(IsolationLevel.ReadCommitted); + // Creates a separate transaction to the DB instance so we aren't allocating tons of new DB instances for each transaction + // since this is executed in a tight loop + + ITransaction transaction = null; try { + transaction = db.GetTransaction(IsolationLevel.ReadCommitted); // get a read lock _sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom); @@ -281,7 +301,8 @@ namespace Umbraco.Core.Runtime } finally { - transaction.Complete(); + transaction?.Complete(); + transaction?.Dispose(); } return null; // continue @@ -289,6 +310,9 @@ namespace Umbraco.Core.Runtime private bool AcquireWhenMaxWaitTimeElapsed(IUmbracoDatabase db) { + // Creates a separate transaction to the DB instance so we aren't allocating tons of new DB instances for each transaction + // since this is executed in a tight loop + // if the timeout has elapsed, it either means that the other main dom is taking too long to shutdown, // or it could mean that the previous appdomain was terminated and didn't clear out the main dom SQL row // and it's just been left as an orphan row. @@ -298,10 +322,12 @@ namespace Umbraco.Core.Runtime _logger.Debug("Timeout elapsed, assuming orphan row, acquiring MainDom."); - using var transaction = db.GetTransaction(IsolationLevel.ReadCommitted); + ITransaction transaction = null; try { + transaction = db.GetTransaction(IsolationLevel.ReadCommitted); + _sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom); // so now we update the row with our appdomain id @@ -324,7 +350,8 @@ namespace Umbraco.Core.Runtime } finally { - transaction.Complete(); + transaction?.Complete(); + transaction?.Dispose(); } } @@ -375,11 +402,12 @@ namespace Umbraco.Core.Runtime if (_dbFactory.Configured) { - using var db = _dbFactory.CreateDatabase(); - using var transaction = db.GetTransaction(IsolationLevel.ReadCommitted); - + IUmbracoDatabase db = null; try { + db = _dbFactory.CreateDatabase(); + db.BeginTransaction(IsolationLevel.ReadCommitted); + // get a write lock _sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom); @@ -406,7 +434,15 @@ namespace Umbraco.Core.Runtime } finally { - transaction.Complete(); + try + { + db?.CompleteTransaction(); + db?.Dispose(); + } + catch (Exception ex) + { + _logger.Error(ex, "Unexpected error during dispose when completing transaction."); + } } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html index d534cd77ed..022c140a2f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -1,6 +1,6 @@ 
-
+
{{ group.label }}
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/elementeditor/umb-element-editor-content.component.html b/src/Umbraco.Web.UI.Client/src/views/components/elementeditor/umb-element-editor-content.component.html index fae639562f..ecbe880eee 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/elementeditor/umb-element-editor-content.component.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/elementeditor/umb-element-editor-content.component.html @@ -2,7 +2,7 @@
+ ng-repeat="group in vm.model.variants[0].tabs track by group.id">
{{ group.label }}
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html index 0dceeb4c26..5162caf13c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html @@ -1,7 +1,7 @@
- +
{{ group.label }}
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html index 633eccdf62..816ca987f7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/apps/content/content.html @@ -1,5 +1,5 @@
-
+
{{ group.label }}
diff --git a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html index 1256c2d234..29051cd855 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html @@ -1,6 +1,6 @@
-
+
{{ group.label }}