private static IEnumerable <bool> LoadBubblesInto(BubbleGroup group, List <VisualBubble> listToLoadInto,
                                                          LoadBubbles instance)
        {
            if (!listToLoadInto.Any())
            {
                yield break;
            }

            Utils.DebugPrint("Loading into convo " + group.ID);

            var unifiedGroup = group as UnifiedBubbleGroup;
            var groups       = unifiedGroup != null ? unifiedGroup.Groups : new List <BubbleGroup>()
            {
                group
            };

            const int MaxLoadPerGroup = 100;
            var       currentTime     = listToLoadInto.Min(x => x.Time);

            var bubbleGroupStates = new Dictionary <BubbleGroup, LoadBubblesIntoStateHolder>();

            foreach (var innerGroup in groups)
            {
                bubbleGroupStates[innerGroup] = new LoadBubblesIntoStateHolder();
            }

            EventHandler <Service> serviceStarted = (sender, e) =>
            {
                foreach (var state in bubbleGroupStates)
                {
                    if (state.Key.Service == e)
                    {
                        state.Value.Dead = false;
                    }
                }
            };

            ServiceEvents.Started  += serviceStarted;
            instance.ServiceStarted = serviceStarted;

            while (true)
            {
                var listToLoadIntoCount = listToLoadInto.Count;
                var bubbleLoads         = new Dictionary <BubbleGroup, List <Tuple <VisualBubble, bool> > >();

                foreach (var innerGroup in groups)
                {
                    var innerGroupState = bubbleGroupStates[innerGroup];

                    if (innerGroupState.Dead)
                    {
                        bubbleLoads[innerGroup] = null;
                        continue;
                    }

                    if (innerGroupState.LocalFinished)
                    {
                        var agentBubbles = LoadBubblesIntoDoAgent(innerGroup, currentTime, MaxLoadPerGroup, innerGroupState);
                        if (agentBubbles != null)
                        {
                            bubbleLoads[innerGroup] = agentBubbles.Select(x =>
                                                                          new Tuple <VisualBubble, bool>(x, true)).ToList();
                        }
                        else
                        {
                            bubbleLoads[innerGroup] = null;
                        }
                    }
                    else
                    {
                        var localBubbles = new List <VisualBubble>();
                        innerGroupState.LocalCursor = BubbleGroupDatabase.FetchBubblesAt(innerGroup,
                                                                                         currentTime, MaxLoadPerGroup, ref localBubbles, innerGroupState.LocalCursor);
                        if (innerGroupState.LocalCursor == 0 || localBubbles.Count == 0)
                        {
                            innerGroupState.LocalFinished = true;
                        }

                        if (localBubbles.Count == MaxLoadPerGroup)
                        {
                            bubbleLoads[innerGroup] = localBubbles.Select(x =>
                                                                          new Tuple <VisualBubble, bool>(x, false)).ToList();
                        }
                        else
                        {
                            var innerGroupComparer = innerGroup.Service as Comparer;

                            if (innerGroupComparer != null)
                            {
                                var agentBubbles = LoadBubblesIntoDoAgent(innerGroup, currentTime, MaxLoadPerGroup, innerGroupState);

                                if (agentBubbles == null)
                                {
                                    bubbleLoads[innerGroup] = localBubbles.Select(x =>
                                                                                  new Tuple <VisualBubble, bool>(x, false)).ToList();
                                }
                                else
                                {
                                    var innerGroupAgent = innerGroup.Service as Agent;
                                    var combined        = new List <Tuple <VisualBubble, bool> >();

                                    // combine them: take all agent bubbles, and then try to replace the clouds with the locals.
                                    // what can't be replaced becomes the inserts.

                                    for (int i = 0; i < agentBubbles.Count; i++)
                                    {
                                        var agentBubble = agentBubbles[i];
                                        var localBubble =
                                            localBubbles.FirstOrDefault(x => innerGroupComparer.LoadBubblesComparer(x, agentBubble));
                                        if (localBubble != null)
                                        {
                                            combined.Add(new Tuple <VisualBubble, bool>(localBubble, false));
                                        }
                                        else
                                        {
                                            combined.Add(new Tuple <VisualBubble, bool>(agentBubble, true));
                                        }
                                    }

                                    bubbleLoads[innerGroup] = combined;
                                }
                            }
                            else
                            {
                                bubbleLoads[innerGroup] = localBubbles.Select(x =>
                                                                              new Tuple <VisualBubble, bool>(x, false)).ToList();
                            }
                        }
                    }
                }

                // insert the bubbles into the disa bubble group with two conditions
                // a) must not be bubble retreived from disa bubble group
                // b) must not be a duplicate already in the list to load into
                var listToLoadIntoBubblesOnTime = listToLoadInto.Where(x => x.Time == currentTime).ToList();
                foreach (var bubbleLoad in bubbleLoads)
                {
                    if (bubbleLoad.Value == null)
                    {
                        continue;
                    }

                    var bubbleGroup     = bubbleLoad.Key;
                    var bubblesToInsert = LoadBubblesIntoRemoveDuplicates(bubbleLoad.Value.Where(x => x.Item2)
                                                                          .Select(x => x.Item1).ToList(), listToLoadIntoBubblesOnTime);
                    if (bubblesToInsert.Any())
                    {
                        BubbleGroupDatabase.InsertBubblesByTime(bubbleGroup,
                                                                bubblesToInsert.ToArray(), int.MaxValue, true, true);
                    }
                }

                // find the greatest minimum time of all the bubble loads
                // and merge the bubble loads against that

                var greatestMin = 0L;
                foreach (var bubbleLoad in bubbleLoads)
                {
                    if (bubbleLoad.Value == null || !bubbleLoad.Value.Any())
                    {
                        continue;
                    }

                    var min = bubbleLoad.Value.Min(x => x.Item1.Time);
                    if (min > greatestMin)
                    {
                        greatestMin = min;
                    }
                }

                var mergedBubbles = new List <VisualBubble>();
                foreach (var bubbleLoad in bubbleLoads)
                {
                    if (bubbleLoad.Value != null)
                    {
                        var bubblesToMerge = bubbleLoad.Value.Where(x =>
                                                                    x.Item1.Time >= greatestMin).Select(x => x.Item1).ToList();
                        foreach (var bubbleToMerge in bubblesToMerge)
                        {
                            bubbleToMerge.BubbleGroupReference = bubbleLoad.Key;
                        }
                        mergedBubbles.AddRange(bubblesToMerge);
                    }
                }
                mergedBubbles.TimSort((x, y) => x.Time.CompareTo(y.Time));

                // insert the merged bubbles into the list to load into, making sure to
                // remove and duplicates encountered.
                listToLoadInto.InsertRange(0, mergedBubbles);
                LoadBubblesIntoRemoveDuplicates(listToLoadInto, currentTime);

                if (mergedBubbles.Any())
                {
                    currentTime = mergedBubbles.First().Time;
                }

                // if the count wasn't altered, we've hit the end
                if (listToLoadIntoCount == listToLoadInto.Count)
                {
                    yield break;
                }

                yield return(true);
            }
        }
        public static Task Sync(BubbleGroup group, bool force = false)
        {
            return(Task.Factory.StartNew(() =>
            {
                Utils.DebugPrint("Syncing convo " + group.ID);

                lock (SyncLock)
                {
                    Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)");

                    var somethingSynced = false;

                    var groupsToSync = new List <BubbleGroup>();

                    var unifiedGroup = group as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        groupsToSync.AddRange(unifiedGroup.Groups);
                    }
                    else
                    {
                        groupsToSync.Add(group);
                    }

                    foreach (var groupToSync in groupsToSync)
                    {
                        if (!groupToSync.NeedsSync && !force)
                        {
                            continue;
                        }

                        var groupToSyncAgent = groupToSync.Service as Agent;

                        if (groupToSyncAgent == null)
                        {
                            continue;
                        }

                        if (!ServiceManager.IsRunning(groupToSync.Service))
                        {
                            continue;
                        }

                        somethingSynced = true;

                        try
                        {
                            ReSync:
                            var syncTime = Time.GetNowUnixTimestamp();
                            var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync));
                            syncTask.Wait();
                            var syncResult = syncTask.Result;
                            if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh)
                            {
                                lock (BubbleGroupDatabase.OperationLock)
                                {
                                    if (syncResult.ResultType == Result.Type.Purge)
                                    {
                                        if (BubbleGroupManager.LastBubbleSentTimestamps.ContainsKey(groupToSync.ID))
                                        {
                                            lock (BubbleGroupManager.LastBubbleSentTimestamps)
                                            {
                                                var lastBubbleSentTime = BubbleGroupManager.LastBubbleSentTimestamps[groupToSync.ID];
                                                if (lastBubbleSentTime >= syncTime)
                                                {
                                                    Utils.DebugPrint("Sync has detected that a bubble was sent during the time the sync was fetched. Redoing sync...");
                                                    goto ReSync;
                                                }
                                            }
                                        }
                                        Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID
                                                         + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleGroupDatabase.Kill(groupToSync);
                                    }
                                    if (!syncResult.EmptyInserts)
                                    {
                                        Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        if (syncResult.ResultType == Result.Type.Purge)
                                        {
                                            syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time));
                                            BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts);
                                            // If a purge is issued, then we need to carry over all sending bubbles to the new BubbleGroup being created.
                                            // Additionally, we need to remove any sending bubbles that are already on the insert list to avoid duplicates.
                                            var sendingBubbles = BubbleManager.FetchAllSending(groupToSync).ToList();
                                            var sendingBubblesFiltered = new List <VisualBubble>();
                                            foreach (var sendingBubble in sendingBubbles)
                                            {
                                                var hasInsertBubble = syncResult.Inserts.FirstOrDefault(x => x.ID == sendingBubble.ID) != null;
                                                if (!hasInsertBubble)
                                                {
                                                    sendingBubblesFiltered.Add(sendingBubble);
                                                }
                                            }
                                            if (sendingBubblesFiltered.Any())
                                            {
                                                BubbleGroupDatabase.InsertBubblesByTime(groupToSync, sendingBubblesFiltered.ToArray(), SearchDepth);
                                            }
                                        }
                                        else
                                        {
                                            BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth);
                                        }
                                    }
                                    if (!syncResult.EmptyUpdates)
                                    {
                                        Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth);
                                    }
                                }
                            }
                            else
                            {
                                Utils.DebugPrint("Sync for bubble group " +
                                                 groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName +
                                                 " returned an empty result (" + syncResult.ResultType.ToString() + ").");
                            }
                            if (!syncResult.NullActionId)
                            {
                                Database.SetActionId(groupToSync, syncResult.NewActionId);
                            }
                            groupToSync.NeedsSync = false;
                        }
                        catch (Exception ex)
                        {
                            Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID
                                             + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex);
                        }
                    }

                    if (somethingSynced)
                    {
                        lock (BubbleGroupDatabase.OperationLock)
                        {
                            // Here we need to replace all the sending or downloading bubbles with the 'real' ones.
                            // If this is never done, then on the UI, a 'sending' will never be set to 'sent', and any download
                            // progress will never be updated. Why? Different memory objects.
                            var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList();
                            BubbleGroupFactory.UnloadFullLoad(group);
                            BubbleGroupFactory.LoadFullyIfNeeded(group, true);
                            if (sendingBubbles.Any())
                            {
                                BubbleManager.Replace(group, sendingBubbles);
                            }
                            group.RaiseBubblesSynced();
                        }
                    }
                }
            }));
        }
Exemple #3
0
        public static Task Sync(BubbleGroup group, bool force = false)
        {
            return(Task.Factory.StartNew(() =>
            {
                Utils.DebugPrint("Syncing convo " + group.ID);

                lock (SyncLock)
                {
                    Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)");

                    var somethingSynced = true;

                    var groupsToSync = new List <BubbleGroup>();

                    var unifiedGroup = group as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        groupsToSync.AddRange(unifiedGroup.Groups);
                    }
                    else
                    {
                        groupsToSync.Add(group);
                    }

                    foreach (var groupToSync in groupsToSync)
                    {
                        if (!groupToSync.NeedsSync && !force)
                        {
                            continue;
                        }

                        var groupToSyncAgent = groupToSync.Service as Agent;

                        if (groupToSyncAgent == null)
                        {
                            continue;
                        }

                        if (!ServiceManager.IsRunning(groupToSync.Service))
                        {
                            continue;
                        }

                        try
                        {
                            var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync));
                            syncTask.Wait();
                            var syncResult = syncTask.Result;
                            if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh)
                            {
                                lock (BubbleGroupDatabase.OperationLock)
                                {
                                    if (syncResult.ResultType == Result.Type.Purge)
                                    {
                                        Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID
                                                         + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleGroupDatabase.Kill(groupToSync);
                                    }
                                    if (!syncResult.EmptyInserts)
                                    {
                                        Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        if (syncResult.ResultType == Result.Type.Purge)
                                        {
                                            syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time));
                                            BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts);
                                        }
                                        else
                                        {
                                            BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth);
                                        }
                                    }
                                    if (!syncResult.EmptyUpdates)
                                    {
                                        Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth);
                                    }
                                    Database.SetActionId(groupToSync, syncResult.NewActionId);
                                }
                            }
                            else
                            {
                                Utils.DebugPrint("Sync for bubble group " +
                                                 groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName +
                                                 " returned an empty result (" + syncResult.ResultType.ToString() + ").");
                                somethingSynced = syncResult.JustRefresh;
                            }
                            groupToSync.NeedsSync = false;
                        }
                        catch (Exception ex)
                        {
                            Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID
                                             + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex);
                            somethingSynced = false;
                        }
                    }

                    if (somethingSynced)
                    {
                        lock (BubbleGroupDatabase.OperationLock)
                        {
                            var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList();
                            BubbleGroupFactory.UnloadFullLoad(group);
                            BubbleGroupFactory.LoadFullyIfNeeded(group, true);
                            if (sendingBubbles.Any())
                            {
                                BubbleManager.Replace(group, sendingBubbles);
                            }
                            group.RaiseBubblesSynced();
                        }
                    }
                }
            }));
        }