コード例 #1
0
ファイル: MessagePuller.cs プロジェクト: octogonz/yamster
        /// <summary>
        /// FreshenThread() asks the MessagePuller to refresh the specified thread as soon
        /// as possible, ignoring its normal algorithm priorities.
        /// </summary>
        /// <remarks>
        /// Since this operation may require multiple REST service calls and is subject to
        /// Yammer rate limits, it may still take a while to process.  The returned
        /// FreshenThreadRequest object can be used to track the progress.  Only one
        /// FreshenThread() request can be active at a time; any previous requests are
        /// canceled by a new call to FreshenThread().  Note that no processing will occur
        /// unless MessagePuller.Enabled=true and MessagePuller.Process() is being called
        /// at regular intervals.
        /// </remarks>
        public FreshenThreadRequest FreshenThread(YamsterThread thread)
        {
            FreshenThreadRequest interruptedRequest = freshenThreadRequest;

            freshenThreadRequest = new FreshenThreadRequest(thread, this);
            if (interruptedRequest != null)
            {
                interruptedRequest.SetError(new Exception("The operation was interrupted by a more recent request"));
            }
            return(freshenThreadRequest);
        }
コード例 #2
0
ファイル: MessagePuller.cs プロジェクト: octogonz/yamster
        async Task ProcessAsync()
        {
            this.appContext.RequireForegroundThread();

            if (!this.enabled)
            {
                return;
            }

            DateTime nowUtc = DateTime.UtcNow;

            // Don't exceed the Yammer throttling limit.  For a FreshenThread() request,
            // we increase the priority.
            if (!yamsterApi.IsSafeToRequest(increasedPriority: freshenThreadRequest != null))
            {
                return;
            }

            // Start by assuming we're not up to date, unless proven otherwise
            UpToDate = false;

            // 1. Is there a request to freshen a specific thread?
            if (freshenThreadRequest != null)
            {
                var freshenedThread = yamsterCoreDb.SyncingThreads
                                      .Query("WHERE ThreadId = " + freshenThreadRequest.Thread.ThreadId)
                                      .FirstOrDefault();

                if (freshenThreadRequest.State == FreshenThreadState.Queued)
                {
                    freshenThreadRequest.SetState(FreshenThreadState.Processing);

                    // Is there already an existing gap for this thread?
                    if (freshenedThread != null)
                    {
                        // Yes, simply reopen it
                        freshenedThread.LastPulledMessageId = null;
                    }
                    else
                    {
                        // No, so create a new one
                        freshenedThread          = new DbSyncingThread();
                        freshenedThread.FeedId   = freshenThreadRequest.Thread.GroupId;
                        freshenedThread.ThreadId = freshenThreadRequest.Thread.ThreadId;

                        // NOTE: The thread is presumed to be contiguous at this point.
                        long latestMessageInDb = yamsterArchiveDb.Mapper.QueryScalar <long>(
                            "SELECT MAX(Id) FROM " + this.yamsterArchiveDb.ArchiveMessages.TableName
                            + " WHERE ThreadId = " + freshenThreadRequest.Thread.ThreadId.ToString());
                        freshenedThread.StopMessageId       = latestMessageInDb;
                        freshenedThread.LastPulledMessageId = null;

                        yamsterCoreDb.SyncingThreads.InsertRecord(freshenedThread);
                    }
                }

                if (freshenThreadRequest.State != FreshenThreadState.Processing)
                {
                    // This should be impossible
                    freshenThreadRequest.SetError(new Exception("State machine error"));
                    freshenThreadRequest = null;
                    return;
                }

                await ProcessGappedThreadAsync(freshenedThread);

                this.appContext.RequireForegroundThread();
                return;
            }

            // 2. Are there any syncing threads?  We must finish them before processing more spans
            var syncingThread = yamsterCoreDb.SyncingThreads
                                .QueryAll()
                                .FirstOrDefault();

            if (syncingThread != null)
            {
                // Start at the top of the list
                await ProcessGappedThreadAsync(syncingThread);

                this.appContext.RequireForegroundThread();
                return;
            }


            // Get the list of subscribed feeds
            List <DbGroupState> groupsToSync = yamsterCoreDb.GroupStates
                                               .Query("WHERE ShouldSync").ToList();
            bool forceCheckNew = false;
            List <JsonSyncingFeed> syncingFeeds = groupsToSync
                                                  .Select(
                groupState => {
                var syncingFeed = yamsterCoreDb.GetJsonSyncingFeed(groupState.GroupId)
                                  ?? new JsonSyncingFeed();
                syncingFeed.GroupState = groupState;
                syncingFeed.FeedId     = groupState.GroupId;
                return(syncingFeed);
            }
                ).ToList();

            if (yamsterCoreDb.Properties.SyncInbox)
            {
                // The Inbox is not a real group, so it doesn't have a DbGroupState record.
                var inboxSyncingFeed = yamsterCoreDb.GetJsonSyncingFeed(YamsterGroup.InboxFeedId)
                                       ?? new JsonSyncingFeed();
                inboxSyncingFeed.GroupState = null;
                inboxSyncingFeed.FeedId     = YamsterGroup.InboxFeedId;
                syncingFeeds.Insert(0, inboxSyncingFeed);
            }

            JsonSyncingFeed chosenSyncingFeed = null;

            // 3. Should we interrupt work on the history and instead check for new messages?
            if (Algorithm == MessagePullerAlgorithm.OptimizeReading)
            {
                TimeSpan longCheckNewDuration = TimeSpan.FromMinutes(7);

                chosenSyncingFeed = syncingFeeds
                                    // Choose a feed that wasn't synced recently, but only if it has already
                                    // done some work on its history
                                    .Where(x => x.SpanCyclesSinceCheckNew >= 2 &&
                                           (nowUtc - x.LastCheckNewUtc) > longCheckNewDuration)
                                    // Pick the feed that was synced least recently
                                    .OrderBy(x => x.LastCheckNewUtc)
                                    .FirstOrDefault();

                if (chosenSyncingFeed != null)
                {
                    forceCheckNew = true;
                }
            }

            // 4. Are there any incomplete histories?  If so, choose the feed who
            // made the least progress syncing so far
            if (chosenSyncingFeed == null)
            {
                // There are two kinds of feeds that need work:
                // 1. If it has gaps in the spans
                // 2. If we did not reach the beginning of the stream yet
                var nextHistoricalFeed = syncingFeeds
                                         .Where(x => x.HasSpanGaps || !x.ReachedEmptyResult)
                                         .OrderByDescending(x => x.GetNextOlderThanTime())
                                         .FirstOrDefault();

                if (nextHistoricalFeed != null)
                {
                    var time = nextHistoricalFeed.GetNextOlderThanTime();
                    if (time != DateTime.MaxValue)  // don't show this degenerate value in the UI
                    {
                        HistoryProgress = time;
                    }

                    if (HistoryLimitDays > 0)
                    {
                        // If HistoryLimitDays is enabled, then don't pull threads that are
                        // older than the historyLimit
                        DateTime historyLimit = DateTime.Now.Date.Subtract(TimeSpan.FromDays(HistoryLimitDays));
                        if (nextHistoricalFeed.GetNextOlderThanTime() >= historyLimit)
                        {
                            chosenSyncingFeed = nextHistoricalFeed;
                        }
                    }
                    else
                    {
                        chosenSyncingFeed = nextHistoricalFeed;
                    }
                }
            }

            // 5. If all the histories are complete, then check for new messages at periodic intervals
            if (chosenSyncingFeed == null)
            {
                TimeSpan shortCheckNewDuration = TimeSpan.FromMinutes(3);

                chosenSyncingFeed = syncingFeeds
                                    // Don't sync more often than shortCheckNewDuration
                                    .Where(x => (nowUtc - x.LastCheckNewUtc) > shortCheckNewDuration)
                                    // Pick the feed that was synced least recently
                                    .OrderBy(x => x.LastCheckNewUtc)
                                    .FirstOrDefault();

                if (chosenSyncingFeed != null)
                {
                    forceCheckNew = true;
                }
            }

            UpToDate = chosenSyncingFeed == null;
            if (!UpToDate)
            {
                await ProcessSpanAsync(chosenSyncingFeed, forceCheckNew);

                this.appContext.RequireForegroundThread();
            }
            else
            {
                Debug.WriteLine("Up to date.");
            }
        }