public TimeoutsBatch GetTimeoutsBatch() { var retval = new TimeoutsBatch { DueTimeouts = new List <TimeoutData>() }; IMongoCollection <TimeoutData> collection = _mongoDatabase.GetCollection <TimeoutData>(TimeoutsCollectionName); DateTime utcNow = DateTime.UtcNow; // Find all the due timeouts and put a lock on each one to prevent multiple threads/processes getting hold of the same data. bool doQuery = true; while (doQuery) { var filter1 = Builders <TimeoutData> .Filter.Eq(_ => _.Locked, false) & Builders <TimeoutData> .Filter.Lte(_ => _.Time, utcNow); var update = Builders <TimeoutData> .Update.Set(_ => _.Locked, true); var result = collection.UpdateOne(filter1, update); if (result.ModifiedCount == 0) { doQuery = false; } else { var filter2 = Builders <TimeoutData> .Filter.Eq(s => s.Id, result.UpsertedId); retval.DueTimeouts.Add(collection.Find(filter2).First()); } } // Get next query time var nextQueryTime = DateTime.MaxValue; var filter = Builders <TimeoutData> .Filter.Eq(_ => _.Time, utcNow); var upcomingTimeoutsRes = collection.Find(filter); foreach (TimeoutData upcomingTimeout in upcomingTimeoutsRes.ToList()) { if (upcomingTimeout.Time < nextQueryTime) { nextQueryTime = upcomingTimeout.Time; } } if (nextQueryTime == DateTime.MaxValue) { nextQueryTime = utcNow.AddMinutes(1); } retval.NextQueryTime = nextQueryTime; return(retval); }
public TimeoutsBatch GetTimeoutsBatch() { var retval = new TimeoutsBatch { DueTimeouts = new List <TimeoutData>() }; MongoCollection <TimeoutData> collection = _mongoDatabase.GetCollection <TimeoutData>(TimeoutsCollectionName); DateTime utcNow = DateTime.UtcNow; // Find all the due timeouts and put a lock on each one to prevent multiple threads/processes getting hold of the same data. bool doQuery = true; while (doQuery) { var args = new FindAndModifyArgs { Query = Query.And(Query.EQ("Locked", false), Query.LTE("Time", utcNow)), Update = Update <TimeoutData> .Set(c => c.Locked, true), Upsert = false, VersionReturned = FindAndModifyDocumentVersion.Original }; FindAndModifyResult result = collection.FindAndModify(args); if (result.ModifiedDocument == null) { doQuery = false; } else { retval.DueTimeouts.Add(result.GetModifiedDocumentAs <TimeoutData>()); } } // Get next query time var nextQueryTime = DateTime.MaxValue; var upcomingTimeoutsRes = collection.Find(Query.GT("Time", utcNow)); foreach (TimeoutData upcomingTimeout in upcomingTimeoutsRes) { if (upcomingTimeout.Time < nextQueryTime) { nextQueryTime = upcomingTimeout.Time; } } if (nextQueryTime == DateTime.MaxValue) { nextQueryTime = utcNow.AddMinutes(1); } retval.NextQueryTime = nextQueryTime; return(retval); }
public TimeoutsBatch GetTimeoutsBatch() { var retval = new TimeoutsBatch { DueTimeouts = new List <TimeoutData>() }; DateTime utcNow = DateTime.UtcNow; var nextQueryTime = DateTime.MaxValue; lock (_memoryCacheLock) { //var cacheItems = (from n in Cache.AsParallel() where n.Value.GetType() == typeof (TimeoutData) select new { n.Value, n.Key }); IDictionary <string, object> cacheItems = new Dictionary <string, object>(); foreach (var key in _provider.Keys()) { var value = _provider.Get <string, object>(key.ToString()); if (value.GetType() == typeof(MemoryData <IProcessManagerData>)) { cacheItems.Add(key.ToString(), value); } } foreach (var data in cacheItems) { var timeoutData = (TimeoutData)data.Value; if (timeoutData.Time <= utcNow) { retval.DueTimeouts.Add(timeoutData); } if (timeoutData.Time > utcNow && timeoutData.Time < nextQueryTime) { nextQueryTime = timeoutData.Time; } } } if (nextQueryTime == DateTime.MaxValue) { nextQueryTime = utcNow.AddMinutes(1); } retval.NextQueryTime = nextQueryTime; return(retval); }
private void SetupProcessManagerFinderMock(DateTime nextTimeoutQueryTime) { var timeoutsBatch = new TimeoutsBatch { DueTimeouts = new List <TimeoutData> { new TimeoutData { Time = DateTime.UtcNow.AddSeconds(-30), ProcessManagerId = _pmId, Id = _tdId, Destination = "TestDest" } }, NextQueryTime = nextTimeoutQueryTime }; _mockProcessManagerFinder.Setup(i => i.GetTimeoutsBatch()).Returns(timeoutsBatch); }
public void InnerPoll(CancellationToken cancellationToken) { var utcNow = DateTime.UtcNow; if (NextQueryUtc > utcNow || cancellationToken.IsCancellationRequested) { return; } // connect to the data store and get all the expired timeouts TimeoutsBatch timeoutsBatch = _processManagerFinder.GetTimeoutsBatch(); foreach (var timeoutData in timeoutsBatch.DueTimeouts) { if (cancellationToken.IsCancellationRequested) { return; } // dispatch the timeout message var timeoutMsg = new TimeoutMessage(timeoutData.ProcessManagerId); _bus.Send(timeoutData.Destination, timeoutMsg); // remove dispatch timeout _processManagerFinder.RemoveDispatchedTimeout(timeoutData.Id); } lock (_locker) { var nextQueryTime = timeoutsBatch.NextQueryTime; // ensure to poll at least every minute var maxNextQuery = utcNow.AddMinutes(1); NextQueryUtc = (nextQueryTime > maxNextQuery) ? maxNextQuery : nextQueryTime; Logger.DebugFormat("Polling next query is at {0}.", NextQueryUtc.ToLocalTime()); } }
public TimeoutsBatch GetTimeoutsBatch() { var retval = new TimeoutsBatch { DueTimeouts = new List <TimeoutData>() }; using (var connection = new SqlConnection(_connectionString)) { connection.Open(); // Make sure table exists using (var cmd = new SqlCommand()) { cmd.Connection = connection; cmd.CommandText = string.Format("IF NOT EXISTS( SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{0}') ", TimeoutsTableName) + string.Format("CREATE TABLE {0} (Id uniqueidentifier NOT NULL, ProcessManagerId uniqueidentifier NOT NULL, Destination varchar(250) NOT NULL, Time DateTime NOT NULL, Locked bit, Headers text);", TimeoutsTableName); cmd.ExecuteNonQuery(); } using (var dbTran = connection.BeginTransaction(IsolationLevel.Serializable)) { var utcNow = DateTime.UtcNow; // Get timeouts due string querySql = string.Format("SELECT Id, ProcessManagerId, Destination, Time, Locked, Headers FROM [dbo].[{0}] WHERE Locked = 0 AND Time <= @Time", TimeoutsTableName); using (var cmd = new SqlCommand(querySql, connection, dbTran)) { cmd.Parameters.AddWithValue("@Time", utcNow); using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { var td = new TimeoutData { Destination = reader["Destination"].ToString(), Id = Guid.Parse(reader["Id"].ToString()), Headers = JsonConvert.DeserializeObject <IDictionary <string, object> >(reader["Headers"].ToString()), ProcessManagerId = Guid.Parse(reader["ProcessManagerId"].ToString()), Time = (DateTime)reader["Time"] }; retval.DueTimeouts.Add(td); } } } // Lock records with timout due string updateSql = string.Format("UPDATE [dbo].[{0}] SET Locked = 1 WHERE Locked = 0 AND Time <= @Time", TimeoutsTableName); using (var cmd = new SqlCommand(updateSql, connection, dbTran)) { cmd.Parameters.AddWithValue("@Time", utcNow); cmd.ExecuteNonQuery(); } // Get next query time var nextQueryTime = DateTime.MaxValue; string nextQueryTimeSql = string.Format("SELECT Time FROM [dbo].[{0}] WHERE Time > @Time", TimeoutsTableName); using (var cmd = new SqlCommand(nextQueryTimeSql, connection, dbTran)) { cmd.Parameters.AddWithValue("@Time", utcNow); using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { if ((DateTime)reader["Time"] < nextQueryTime) { nextQueryTime = (DateTime)reader["Time"]; } } } } if (nextQueryTime == DateTime.MaxValue) { nextQueryTime = utcNow.AddMinutes(1); } retval.NextQueryTime = nextQueryTime; dbTran.Commit(); } } return(retval); }