public void CallsOnErrorOnException() { // Arrange var mre = new ManualResetEventSlim(false); var onErrorCalled = false; var dbProviderFactory = new MockDbProviderFactory(); var dbBehavior = new Mock<IDbBehavior>(); var logger = new Mock<ILogger>(); dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(_defaultRetryDelays); dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(false); dbProviderFactory.MockDataReader.Setup(r => r.Read()).Throws(new ApplicationException("test")); var operation = new ObservableDbOperation("test", "test", logger.Object, dbProviderFactory, dbBehavior.Object); operation.Faulted += _ => { onErrorCalled = true; mre.Set(); }; // Act ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((record, o) => { })); mre.Wait(); operation.Dispose(); // Assert Assert.True(onErrorCalled); }
public void UseSqlNotificationsIfAvailable(bool supportSqlNotifications) { // Arrange var sqlDependencyAdded = false; var retryLoopCount = 0; var mre = new ManualResetEventSlim(); var dbProviderFactory = new MockDbProviderFactory(); var dbBehavior = new Mock<IDbBehavior>(); dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(_defaultRetryDelays); dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(supportSqlNotifications); dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>())) .Callback(() => { sqlDependencyAdded = true; mre.Set(); }); var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object); operation.Faulted += _ => mre.Set(); operation.Queried += () => { retryLoopCount++; if (retryLoopCount > 1) { mre.Set(); } }; // Act ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((record, o) => { })); mre.Wait(); operation.Dispose(); // Assert Assert.Equal(supportSqlNotifications, sqlDependencyAdded); }
public void DoesRetryLoopConfiguredNumberOfTimes(int? length1, int? length2, int? length3) { // Arrange var retryLoopCount = 0; var mre = new ManualResetEventSlim(); var retryLoopArgs = new List<int?>(new[] { length1, length2, length3 }).Where(l => l.HasValue); var retryLoopTotal = retryLoopArgs.Sum().Value; var retryLoopDelays = new List<Tuple<int, int>>(retryLoopArgs.Select(l => new Tuple<int, int>(0, l.Value))); var sqlDependencyCreated = false; var dbProviderFactory = new MockDbProviderFactory(); var dbBehavior = new Mock<IDbBehavior>(); dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays); dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true); dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>())) .Callback(() => { sqlDependencyCreated = true; mre.Set(); }); var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object); operation.Faulted += _ => mre.Set(); operation.Queried += () => { if (!sqlDependencyCreated) { // Only update the loop count if the SQL dependency hasn't been created yet (we're still in the loop) retryLoopCount++; } if (retryLoopCount == retryLoopTotal) { mre.Set(); } }; // Act ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((record, o) => { })); mre.Wait(); operation.Dispose(); // Assert Assert.Equal(retryLoopTotal, retryLoopCount); }
private void Receive(object state) { var tcs = (DispatchingTaskCompletionSource <object>)state; if (!_lastPayloadId.HasValue) { var lastPayloadIdOperation = new DbOperation(_connectionString, _maxIdSql, _trace) { TracePrefix = _tracePrefix }; try { _lastPayloadId = (long?)lastPayloadIdOperation.ExecuteScalar(); Queried(); _trace.TraceVerbose("{0}SqlReceiver started, initial payload id={1}", _tracePrefix, _lastPayloadId); // Complete the StartReceiving task as we've successfully initialized the payload ID tcs.TrySetResult(null); } catch (Exception ex) { _trace.TraceError("{0}SqlReceiver error starting: {1}", _tracePrefix, ex); tcs.TrySetException(ex); return; } } // NOTE: This is called from a BG thread so any uncaught exceptions will crash the process lock (this) { if (_disposed) { return; } var parameter = _dbProviderFactory.CreateParameter(); parameter.ParameterName = "PayloadId"; parameter.Value = _lastPayloadId.Value; _dbOperation = new ObservableDbOperation(_connectionString, _selectSql, _trace, parameter) { TracePrefix = _tracePrefix }; } _dbOperation.Queried += () => Queried(); _dbOperation.Faulted += ex => Faulted(ex); _dbOperation.Changed += () => { _trace.TraceInformation("{0}Starting receive loop again to process updates", _tracePrefix); _dbOperation.ExecuteReaderWithUpdates(ProcessRecord); }; _trace.TraceVerbose("{0}Executing receive reader, initial payload ID parameter={1}", _tracePrefix, _dbOperation.Parameters[0].Value); _dbOperation.ExecuteReaderWithUpdates(ProcessRecord); _trace.TraceInformation("{0}SqlReceiver.Receive returned", _tracePrefix); }
public void ExecuteReaderSetsNotificationStateCorrectlyWhenNotificationReceivedBeforeChangingStateToAwaitingNotification() { // Arrange var mre = new ManualResetEventSlim(false); var retryLoopDelays = new[] { Tuple.Create(0, 1) }; var dbProviderFactory = new MockDbProviderFactory(); var sqlDependencyAdded = false; var dbBehavior = new Mock<IDbBehavior>(); dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays); dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true); dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>())) .Callback(() => sqlDependencyAdded = true); var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object); dbProviderFactory.MockDataReader.Setup(r => r.Read()).Returns(() => { if (sqlDependencyAdded) { // Fake the SQL dependency firing while we're setting it up operation.CurrentNotificationState = ObservableDbOperation.NotificationState.NotificationReceived; sqlDependencyAdded = false; } return false; }); long? stateOnLoopRestart = null; var queriedCount = 0; operation.Queried += () => { queriedCount++; if (queriedCount == 3) { // First query after the loop starts again, capture the state stateOnLoopRestart = operation.CurrentNotificationState; mre.Set(); } }; // Act ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((__, ___) => { })); mre.Wait(); Assert.True(stateOnLoopRestart.HasValue); Assert.Equal(ObservableDbOperation.NotificationState.ProcessingUpdates, stateOnLoopRestart.Value); operation.Dispose(); }
public void ExecuteReaderSetsNotificationStateCorrectlyUpToAwaitingNotification() { // Arrange var retryLoopDelays = new [] { Tuple.Create(0, 1) }; var dbProviderFactory = new MockDbProviderFactory(); var dbBehavior = new Mock<IDbBehavior>(); dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays); dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true); dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>())); var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object); operation.Queried += () => { // Currently in the query loop Assert.Equal(ObservableDbOperation.NotificationState.ProcessingUpdates, operation.CurrentNotificationState); }; // Act operation.ExecuteReaderWithUpdates((_, __) => { }); // Assert Assert.Equal(ObservableDbOperation.NotificationState.AwaitingNotification, operation.CurrentNotificationState); operation.Dispose(); }
private void Receive(object state) { var tcs = (TaskCompletionSource<object>)state; if (!_lastPayloadId.HasValue) { var lastPayloadIdOperation = new DbOperation(_connectionString, _maxIdSql, _logger) { LoggerPrefix = _loggerPrefix }; try { _lastPayloadId = (long?)lastPayloadIdOperation.ExecuteScalar(); Queried(); _logger.LogVerbose(String.Format("{0}SqlReceiver started, initial payload id={1}", _loggerPrefix, _lastPayloadId)); // Complete the StartReceiving task as we've successfully initialized the payload ID tcs.TrySetResult(null); } catch (Exception ex) { _logger.LogError(String.Format("{0}SqlReceiver error starting: {1}", _loggerPrefix, ex)); tcs.TrySetException(ex); return; } } // NOTE: This is called from a BG thread so any uncaught exceptions will crash the process lock (this) { if (_disposed) { return; } var parameter = _dbProviderFactory.CreateParameter(); parameter.ParameterName = "PayloadId"; parameter.Value = _lastPayloadId.Value; _dbOperation = new ObservableDbOperation(_connectionString, _selectSql, _logger, parameter) { LoggerPrefix = _loggerPrefix }; } _dbOperation.Queried += () => Queried(); _dbOperation.Faulted += ex => Faulted(ex); #if DNX451 _dbOperation.Changed += () => { _logger.LogInformation("{0}Starting receive loop again to process updates", _loggerPrefix); _dbOperation.ExecuteReaderWithUpdates(ProcessRecord); }; #endif _logger.LogVerbose(String.Format("{0}Executing receive reader, initial payload ID parameter={1}", _loggerPrefix, _dbOperation.Parameters[0].Value)); _dbOperation.ExecuteReaderWithUpdates(ProcessRecord); _logger.LogInformation("{0}SqlReceiver.Receive returned", _loggerPrefix); }
private void Receive(object state) { var tcs = (TaskCompletionSource<object>)state; if (!_lastPayloadId.HasValue) { var lastPayloadIdOperation = new DbOperation(_connectionString, _maxIdSql, _trace) { TracePrefix = _tracePrefix }; try { _lastPayloadId = (long?)lastPayloadIdOperation.ExecuteScalar(); OnQuery(); // Complete the StartReceiving task as we've successfully initialized the payload ID tcs.TrySetResult(null); } catch (Exception ex) { tcs.TrySetException(ex); return; } } // NOTE: This is called from a BG thread so any uncaught exceptions will crash the process lock (this) { if (_disposed) { return; } var parameter = _dbProviderFactory.CreateParameter(); parameter.ParameterName = "PayloadId"; parameter.Value = _lastPayloadId.Value; _dbOperation = new ObservableDbOperation(_connectionString, _selectSql, _trace, parameter) { OnError = ex => _onError(ex), OnQuery = () => OnQuery(), TracePrefix = _tracePrefix }; } _dbOperation.ExecuteReaderWithUpdates((rdr, o) => ProcessRecord(rdr, o)); _trace.TraceWarning("{0}SqlReceiver.Receive returned", _tracePrefix); }
public void ExecuteReaderSetsNotificationStateCorrectlyWhenRecordsReceivedWhileSettingUpSqlDependency() { // Arrange var mre = new ManualResetEventSlim(false); var retryLoopDelays = new[] { Tuple.Create(0, 1) }; var dbProviderFactory = new MockDbProviderFactory(); var readCount = 0; var sqlDependencyAddedCount = 0; dbProviderFactory.MockDataReader.Setup(r => r.Read()).Returns(() => ++readCount == 2 && sqlDependencyAddedCount == 1); var dbBehavior = new Mock<IDbBehavior>(); var logger = new Mock<ILogger>(); dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays); dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true); dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>())).Callback(() => sqlDependencyAddedCount++); var operation = new ObservableDbOperation("test", "test", logger.Object, dbProviderFactory, dbBehavior.Object); long? stateOnLoopRestart = null; var queriedCount = 0; operation.Queried += () => { queriedCount++; if (queriedCount == 3) { // First query after the loop starts again, check the state is reset stateOnLoopRestart = operation.CurrentNotificationState; mre.Set(); } }; // Act ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((__, ___) => { })); mre.Wait(); Assert.True(stateOnLoopRestart.HasValue); Assert.Equal(ObservableDbOperation.NotificationState.ProcessingUpdates, stateOnLoopRestart.Value); operation.Dispose(); }