/// <summary> /// Returns the most recent (TWEETS_PER_REQUEST) tweets /// that match a given state and that are older than a given id /// </summary> /// <param name="currentState">tweet state to be matched</param> /// <param name="olderThanThisNumber">Results should include only tweets that have an id older than this</param> /// <returns>Array of tweet ids and their respective states</returns> public TweetIdAndStateVM[] GetSelectedTweetsByState(PublishingState currentState, ulong olderThanThisNumber = ulong.MaxValue) { //1) define the filter: filter by state and date Func <SelectedTweet, bool> filter = (x) => x.CurrentState == currentState && ulong.Parse(x.TweetId) < olderThanThisNumber; //2) apply the filter and return results return(GetSelectedTweets(filter)); }
/// <summary> /// Checks the state of the subscriptions. /// </summary> public void PublishTimerExpired() { List <Subscription> subscriptionsToDelete = new List <Subscription>(); lock (m_lock) { List <QueuedSubscription> liveSubscriptions = new List <QueuedSubscription>(m_queuedSubscriptions.Count); // check each available subscription. for (int ii = 0; ii < m_queuedSubscriptions.Count; ii++) { QueuedSubscription subscription = m_queuedSubscriptions[ii]; PublishingState state = subscription.Subscription.PublishTimerExpired(); // check for expired subscription. if (state == PublishingState.Expired) { subscriptionsToDelete.Add(subscription.Subscription); ((SubscriptionManager)m_server.SubscriptionManager).SubscriptionExpired(subscription.Subscription); continue; } liveSubscriptions.Add(subscription); // check if idle. if (state == PublishingState.Idle) { subscription.ReadyToPublish = false; continue; } // do nothing if subscription has already been flagged as available. if (subscription.ReadyToPublish) { if (subscription.ReadyToPublish && m_queuedRequests.Count == 0) { if (!m_subscriptionsWaiting) { m_subscriptionsWaiting = true; // TraceState("SUBSCRIPTIONS WAITING"); } } continue; } // assign subscription to request if one is available. if (!subscription.Publishing) { AssignSubscriptionToRequest(subscription); } } // only keep the live subscriptions. m_queuedSubscriptions = liveSubscriptions; // schedule cleanup on a background thread. SubscriptionManager.CleanupSubscriptions(m_server, subscriptionsToDelete); } }
/// <summary> /// Verifies the result of a publish /// </summary> private bool VerifyPublishResponse( ResponseHeader responseHeader, Subscription subscription, UInt32Collection availableSequenceNumbers, bool moreNotifications, NotificationMessage notificationMessage, StatusCodeCollection results, DiagnosticInfoCollection diagnosticInfos) { /* Utils.Trace( "PublishReceived: SubId={0} SeqNo={1}, PublishTime={2:mm:ss.fff}, Time={3:mm:ss.fff}", subscription.SubscriptionId, notificationMessage.SequenceNumber, notificationMessage.PublishTime, DateTime.UtcNow); */ // check if there is an odd delay. if (responseHeader.Timestamp > notificationMessage.PublishTime.AddMilliseconds(100)) { Log( "WARNING. Unexpected delay between PublishTime and ResponseTime. SeqNo={0}, PublishTime={1:hh:mm:ss.fff}, ResponseTime={2:hh:mm:ss.fff}", notificationMessage.SequenceNumber, notificationMessage.PublishTime, responseHeader.Timestamp); } // save results. subscription.AvailableSequenceNumbers = availableSequenceNumbers; if (notificationMessage.NotificationData.Count == 0) { // keep alives do not increment the sequence number. if (subscription.NextExpectedSequenceNumber != notificationMessage.SequenceNumber) { Log( "Incorrect sequence number for keep alive. SubscriptionId = {0}, Actual = {1}, Expected = {2}", subscription.SubscriptionId, notificationMessage.SequenceNumber, subscription.NextExpectedSequenceNumber); subscription.Failed = true; return false; } // save the message. DateTime timestamp = responseHeader.Timestamp; DateTime start = subscription.States[subscription.States.Count - 1].Start; // check if this is an old request being processed late. if (start > timestamp && subscription.States.Count > 1) { subscription.States[subscription.States.Count - 2].KeepAlives.Add(timestamp); } else { subscription.States[subscription.States.Count - 1].KeepAlives.Add(timestamp); } } else { // check for replays. if (subscription.NextExpectedSequenceNumber > notificationMessage.SequenceNumber) { // check for out of order responses. bool found = false; for (int ii = 0; ii < subscription.MissingSequenceNumbers.Count; ii++) { if (subscription.MissingSequenceNumbers[ii] == notificationMessage.SequenceNumber) { subscription.MissingSequenceNumbers.RemoveAt(ii); found = true; break; } } // oops - duplicate. if (!found) { Log( "Duplicate sequence number for message. SubscriptionId = {0}, Actual = {1}, Expected = {2}", subscription.SubscriptionId, notificationMessage.SequenceNumber, subscription.NextExpectedSequenceNumber); subscription.Failed = true; return false; } } // increment message counter. if (notificationMessage.SequenceNumber >= subscription.NextExpectedSequenceNumber) { for (uint ii = subscription.NextExpectedSequenceNumber; ii < notificationMessage.SequenceNumber; ii++) { if (!subscription.MissingSequenceNumbers.Contains(ii)) { subscription.MissingSequenceNumbers.Add(ii); } } subscription.NextExpectedSequenceNumber = notificationMessage.SequenceNumber+1; } // save the largest received message number (gap exist because of client side threading issues). if (subscription.LastReceivedSequenceNumber < notificationMessage.SequenceNumber) { subscription.LastReceivedSequenceNumber = notificationMessage.SequenceNumber; } // save the message. DateTime timestamp = responseHeader.Timestamp; DateTime start = subscription.States[subscription.States.Count-1].Start; // check if this is an old request being processed late. if (start > timestamp && subscription.States.Count > 1) { subscription.States[subscription.States.Count - 2].KeepAlives.Add(timestamp); } else { subscription.States[subscription.States.Count - 1].KeepAlives.Add(timestamp); } subscription.NotificationMessages.Add(notificationMessage); subscription.ReceiveTimes.Add(responseHeader.Timestamp); // change to keep alive mode. if (subscription.StaticData) { PublishingState state = new PublishingState(); state.KeepAliveCount = subscription.KeepAliveCount; state.PublishingInterval = subscription.PublishingInterval; state.Start = timestamp; state.KeepAliveMode = true; subscription.States[subscription.States.Count-1].End = state.Start; subscription.States.Add(state); } // save the acknowlegements. SaveAcknowledgement(subscription.SubscriptionId, notificationMessage.SequenceNumber); } return true; }
/// <summary> /// Creates a subscription. /// </summary> private bool CreateSubscription( double publishingInterval, uint lifetimeCount, uint keepAliveCount, uint maxNotificationsPerPublish, bool publishingEnabled, byte priority) { try { uint subscriptionId; double revisedPublishingInterval; uint revisedLifetimeCount; uint revisedKeepAliveCount; Subscription subscription = new Subscription(); subscription.MaxNotificationsPerPublish = maxNotificationsPerPublish; subscription.PublishingEnabled = publishingEnabled; subscription.Priority = priority; subscription.NextExpectedSequenceNumber = 1; subscription.StaticData = true; lock (m_subscriptions) { m_subscriptions.Add(subscription); } DateTime start = DateTime.UtcNow; ResponseHeader responseHeader = Session.CreateSubscription( null, publishingInterval, lifetimeCount, keepAliveCount, maxNotificationsPerPublish, publishingEnabled, priority, out subscriptionId, out revisedPublishingInterval, out revisedLifetimeCount, out revisedKeepAliveCount); double elapsedTime = (DateTime.UtcNow - start).TotalMilliseconds; if (elapsedTime > 300) { Log("WARNING: CreateSubscription took {0}ms. Timing errors may occur.", (DateTime.UtcNow - start).TotalMilliseconds); } subscription.SubscriptionId = subscriptionId; subscription.PublishingInterval = revisedPublishingInterval; subscription.LifetimeCount = revisedLifetimeCount; subscription.KeepAliveCount = revisedKeepAliveCount; while (m_outstandingPublishRequests < m_publishPipelineDepth) { BeginPublish(); } lock (subscription) { PublishingState state = new PublishingState(); state.KeepAliveCount = subscription.KeepAliveCount; state.PublishingInterval = subscription.PublishingInterval; state.Start = responseHeader.Timestamp; state.KeepAliveMode = true; subscription.States.Add(state); } return true; } catch (Exception e) { Log(e, "Error creating subscription.", null); return false; } }
/// <summary> /// Sets the publishing enable state for the subscriptions. /// </summary> private bool ModifySubscription(Subscription subscription, double publishingInterval) { try { double revisedPublishingInterval; uint revisedLifetimeCount; uint revisedKeepAliveCount; RequestHeader requestHeader = new RequestHeader(); requestHeader.ReturnDiagnostics = 0; DateTime start = DateTime.UtcNow; ResponseHeader responseHeader = Session.ModifySubscription( requestHeader, subscription.SubscriptionId, publishingInterval, subscription.LifetimeCount, subscription.KeepAliveCount, subscription.MaxNotificationsPerPublish, subscription.Priority, out revisedPublishingInterval, out revisedLifetimeCount, out revisedKeepAliveCount); double elapsedTime = (DateTime.UtcNow - start).TotalMilliseconds; if (elapsedTime > 300) { Log("WARNING: ModifySubscription took {0}ms. Timing errors may occur.", (DateTime.UtcNow - start).TotalMilliseconds); } PublishingState state = new PublishingState(); state.KeepAliveCount = revisedKeepAliveCount; state.PublishingInterval = revisedPublishingInterval; state.Start = responseHeader.Timestamp; state.KeepAliveMode = true; lock (subscription) { subscription.PublishingInterval = revisedPublishingInterval; subscription.KeepAliveCount = revisedKeepAliveCount; subscription.LifetimeCount = revisedLifetimeCount; subscription.States[subscription.States.Count-1].End = state.Start; subscription.States.Add(state); } return true; } catch (Exception e) { Log(e, "Error modifying state of subscription {0}.", subscription.SubscriptionId); return false; } }
/// <summary> /// Verifies the keep alive test results. /// </summary> private bool VerifyKeepAliveTestResults(Subscription subscription, PublishingState state, bool isFirst) { double keepAlivePeriod = state.KeepAliveCount*state.PublishingInterval; double elaspedTime = CalculateInterval(state.Start, state.End); double referencePeriod = (state.KeepAliveMode)?keepAlivePeriod:state.PublishingInterval; bool success = true; DateTime start = state.Start; for (int ii = 0; ii < state.KeepAlives.Count; ii++) { double referencePeriodToUse = referencePeriod; // the first keep alive comes with the first publishing interval. if (isFirst && ii == 0) { referencePeriodToUse = state.PublishingInterval; } double delta = CalculateInterval(start, state.KeepAlives[ii]); if (Math.Abs(referencePeriodToUse - delta) > m_idealTimingError && delta > m_idealTimingError) { bool fatal = Math.Abs(referencePeriodToUse - delta) > (referencePeriodToUse + (double)((isFirst)?500:0)); Log( "{0}: Notification received at the wrong time for subscription {1}. Index = {2}, Expected = {3}ms, Actual = {4}ms", "TIMING ERROR", subscription.SubscriptionId, ii, referencePeriodToUse, delta); if (fatal) { success = false; } } start = state.KeepAlives[ii]; } if ((Math.Truncate(elaspedTime/keepAlivePeriod) > state.KeepAlives.Count)) { double[] deltas = new double[state.KeepAlives.Count]; for (int ii = 0; ii < state.KeepAlives.Count-1; ii++) { deltas[ii] = CalculateInterval(state.KeepAlives[ii], state.KeepAlives[ii + 1]); } double timingError = Math.Abs((state.KeepAlives.Count + 1) * keepAlivePeriod - elaspedTime); // check if the keep alive is an exact multiple of the elapsed time. if (timingError > m_idealTimingError && timingError > state.PublishingInterval) { bool fatal = Math.Truncate(elaspedTime/keepAlivePeriod) > state.KeepAlives.Count+1; Log( "{0}: Keep alives not received for subscription {1}. , Expected = {2}, Actual = {3}, TimingError={4}", "TIMING ERROR", subscription.SubscriptionId, Math.Truncate(elaspedTime/keepAlivePeriod), state.KeepAlives.Count, timingError); if (fatal) { success = false; } } } return success; }