private void ListeningLoop() { while (true) { // poll every 1 second Thread.Sleep(1000); if (!_dbFactory.Configured) { // if we aren't configured, we just keep looping since we can't query the db continue; } lock (_locker) { // If cancellation has been requested we will just exit. Depending on timing of the shutdown, // we will have already flagged _mainDomChanging = true, or we're shutting down faster than // the other MainDom is taking to startup. In this case the db row will just be deleted and the // new MainDom will just take over. if (_cancellationTokenSource.IsCancellationRequested) { return; } var db = GetDatabase(); try { db.BeginTransaction(IsolationLevel.ReadCommitted); // get a read lock _sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom); // TODO: We could in theory just check if the main dom row doesn't exist, that could indicate that // we are still the maindom. An empty value might be better because then we won't have any orphan rows // if the app is terminated. Could that work? if (!IsMainDomValue(_lockId)) { // we are no longer main dom, another one has come online, exit _mainDomChanging = true; _logger.Debug <SqlMainDomLock>("Detected new booting application, releasing MainDom lock."); return; } } catch (Exception ex) { ResetDatabase(); // unexpected _logger.Error <SqlMainDomLock>(ex, "Unexpected error, listening is canceled."); _hasError = true; return; } finally { db?.CompleteTransaction(); } } } }
private void ListeningLoop() { while (true) { // poll every couple of seconds // local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO Thread.Sleep(2000); if (!_dbFactory.Configured) { // if we aren't configured, we just keep looping since we can't query the db continue; } lock (_locker) { // If cancellation has been requested we will just exit. Depending on timing of the shutdown, // we will have already flagged _mainDomChanging = true, or we're shutting down faster than // the other MainDom is taking to startup. In this case the db row will just be deleted and the // new MainDom will just take over. if (_cancellationTokenSource.IsCancellationRequested) { _logger.Debug <SqlMainDomLock>("Task canceled, exiting loop"); return; } IUmbracoDatabase db = null; try { db = _dbFactory.CreateDatabase(); if (!_hasTable) { // re-check if its still false, we don't want to re-query once we know its there since this // loop needs to use minimal resources _hasTable = db.HasTable(Constants.DatabaseSchema.Tables.KeyValue); if (!_hasTable) { // the Db does not contain the required table, we just keep looping since we can't query the db continue; } } db.BeginTransaction(IsolationLevel.ReadCommitted); // get a read lock _sqlServerSyntax.ReadLock(db, _lockTimeout, Constants.Locks.MainDom); if (!IsMainDomValue(_lockId, db)) { // we are no longer main dom, another one has come online, exit _mainDomChanging = true; _logger.Debug <SqlMainDomLock>("Detected new booting application, releasing MainDom lock."); return; } } catch (Exception ex) { _logger.Error <SqlMainDomLock>(ex, "Unexpected error during listening."); // We need to keep on listening unless we've been notified by our own AppDomain to shutdown since // we don't want to shutdown resources controlled by MainDom inadvertently. We'll just keep listening otherwise. if (_cancellationTokenSource.IsCancellationRequested) { _logger.Debug <SqlMainDomLock>("Task canceled, exiting loop"); return; } } finally { // Even if any of the above fail like BeginTransaction, or even a query after the // Transaction is started, the calls below will not throw. I've tried all sorts of // combinations to see if I can make this throw but I can't. In any case, we'll be // extra safe and try/catch/log try { db?.CompleteTransaction(); } catch (Exception ex) { _logger.Error <SqlMainDomLock>(ex, "Unexpected error completing transaction."); } try { db?.Dispose(); } catch (Exception ex) { _logger.Error <SqlMainDomLock>(ex, "Unexpected error completing disposing."); } } } } }
private void ListeningLoop() { while (true) { // poll every couple of seconds // local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO Thread.Sleep(_globalSettings.Value.MainDomReleaseSignalPollingInterval); if (!_dbFactory.Configured) { // if we aren't configured, we just keep looping since we can't query the db continue; } lock (_locker) { // If cancellation has been requested we will just exit. Depending on timing of the shutdown, // we will have already flagged _mainDomChanging = true, or we're shutting down faster than // the other MainDom is taking to startup. In this case the db row will just be deleted and the // new MainDom will just take over. if (_cancellationTokenSource.IsCancellationRequested) { _logger.LogDebug("Task canceled, exiting loop"); return; } IUmbracoDatabase db = null; try { db = _dbFactory.CreateDatabase(); if (!_hasTable) { // re-check if its still false, we don't want to re-query once we know its there since this // loop needs to use minimal resources _hasTable = db.HasTable(Cms.Core.Constants.DatabaseSchema.Tables.KeyValue); if (!_hasTable) { // the Db does not contain the required table, we just keep looping since we can't query the db continue; } } // In case we acquired the main dom doing install when there was no database. We therefore have to insert our lockId now, but only handle this once. if (_acquireWhenTablesNotAvailable) { _acquireWhenTablesNotAvailable = false; InsertLockRecord(_lockId, db); } db.BeginTransaction(IsolationLevel.ReadCommitted); // get a read lock _sqlServerSyntax.ReadLock(db, Cms.Core.Constants.Locks.MainDom); if (!IsMainDomValue(_lockId, db)) { // we are no longer main dom, another one has come online, exit _mainDomChanging = true; _logger.LogDebug("Detected new booting application, releasing MainDom lock."); return; } } catch (Exception ex) { _logger.LogError(ex, "Unexpected error during listening."); // We need to keep on listening unless we've been notified by our own AppDomain to shutdown since // we don't want to shutdown resources controlled by MainDom inadvertently. We'll just keep listening otherwise. if (_cancellationTokenSource.IsCancellationRequested) { _logger.LogDebug("Task canceled, exiting loop"); return; } } finally { db?.CompleteTransaction(); db?.Dispose(); } } } }
private void ListeningLoop() { while (true) { // poll every couple of seconds // local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO Thread.Sleep(2000); if (!_dbFactory.Configured) { // if we aren't configured, we just keep looping since we can't query the db continue; } if (!_dbFactory.Configured) { // if we aren't configured, we just keep looping since we can't query the db continue; } lock (_locker) { // If cancellation has been requested we will just exit. Depending on timing of the shutdown, // we will have already flagged _mainDomChanging = true, or we're shutting down faster than // the other MainDom is taking to startup. In this case the db row will just be deleted and the // new MainDom will just take over. if (_cancellationTokenSource.IsCancellationRequested) { return; } using var db = _dbFactory.CreateDatabase(); using var transaction = db.GetTransaction(IsolationLevel.ReadCommitted); try { // get a read lock _sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom); if (!IsMainDomValue(_lockId, db)) { // we are no longer main dom, another one has come online, exit _mainDomChanging = true; _logger.Debug <SqlMainDomLock>("Detected new booting application, releasing MainDom lock."); return; } } catch (Exception ex) { _logger.Error <SqlMainDomLock>(ex, "Unexpected error during listening."); // We need to keep on listening unless we've been notified by our own AppDomain to shutdown since // we don't want to shutdown resources controlled by MainDom inadvertently. We'll just keep listening otherwise. if (_cancellationTokenSource.IsCancellationRequested) { return; } } finally { transaction.Complete(); } } } }