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(); } } } })); }
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(); } } } })); }