Exemplo n.º 1
0
        public void DoSyncAsync(bool force = false)
        {
            Task.Factory.StartNew(() =>
            {
                using (ReadWriteLock.UpgradeableReadLock())
                {
                    var settings = new JsonSerializerSettings()
                    {
                        MissingMemberHandling = MissingMemberHandling.Ignore
                    };

                    Dictionary <long, Work> items;
                    Dictionary <long, Work> current;
                    long time;
                    long old_sync;

                    Task <HttpResponseMessage> responseTask;
                    HttpResponseMessage response;
                    Task <string> contentTask;
                    string content;

                    using (ReadWriteLock.WriteLock())
                    {
                        if (serversync == SyncState.Disabled)
                        {
                            EndSyncEvent?.Invoke(this, new EventArgs <bool>(false));
                            //console.warn("dosync: FAILED. No credentials");
                            return;
                        }

                        // if we have waiting readers do it now
                        if (ReadWriteLock.WaitingReadCount > 0)
                        {
                            force = true;
                        }

                        // Enforce 5 minutes gap between server sync. Don't want to hammer the server while scrolling through a fic
                        var now = DateTime.UtcNow.Ticks / 10000;
                        if (!force && now < no_sync_until)
                        {
                            //console.log("dosync: have to wait %i for timeout", no_sync_until - now);
                            DelayedSyncAsync((int)(no_sync_until - now));
                            return;
                        }

                        serversync = SyncState.Syncing; // set to syncing!

                        if (DelayedSyncCTS != null)
                        {
                            DelayedSyncCTS.Cancel();
                            DelayedSyncCTS = null;
                        }
                        no_sync_until = now + 5 * 60 * 1000;

                        BeginSyncEvent?.Invoke(this, EventArgs.Empty);

                        //console.log("dosync: sending GET request");

                        responseTask = HttpClient.GetAsync(new Uri(url_base, "Values?after=" + last_sync));
                        responseTask.TryWait();
                        response = responseTask.IsFaulted ? null : responseTask.Result;

                        if (response == null || !response.IsSuccessStatusCode)
                        {
                            using (ReadWriteLock.WriteLock())
                            {
                                //console.error("dosync: FAILED %s", response.ReasonPhrase);
                                if (response != null && response.StatusCode == HttpStatusCode.Forbidden)
                                {
                                    serversync = SyncState.Disabled;
                                }
                                else
                                {
                                    serversync = SyncState.Ready;
                                }
                                EndSyncEvent?.Invoke(this, new EventArgs <bool>(false));
                                return;
                            }
                        }

                        contentTask = response.Content.ReadAsStringAsync();
                        contentTask.Wait();
                        content = contentTask.Result;

                        old_sync = last_sync;

                        try
                        {
                            items = JsonConvert.DeserializeObject <Dictionary <long, Work> >(content, settings);
                        }
                        catch (Exception e)
                        {
                            App.Log(e);
                            serversync = SyncState.Disabled;
                            EndSyncEvent?.Invoke(this, new EventArgs <bool>(false));
                            return;
                        }

                        Dictionary <long, Work> newitems = new Dictionary <long, Work>();
                        foreach (var item in items)
                        {
                            item.Value.workid = item.Key;
                            // Highest time value of incoming item is our new sync time
                            if (item.Value.Timestamp > last_sync)
                            {
                                last_sync = item.Value.Timestamp;
                            }

                            Work old = null;
                            if (!storage.ContainsKey(item.Key) || (old = storage[item.Key]).LessThanOrEqual(item.Value))
                            {
                                // Remove from unsynced list (if it exists)
                                if (unsynced.ContainsKey(item.Key))
                                {
                                    unsynced.Remove(item.Key);
                                }
                                // Grab the new details
                                newitems[item.Key] = storage[item.Key] = item.Value;

                                if (old == null || item.Value.location == null || item.Value.location == 0 || (old != null && item.Value.number > old.number))
                                {
                                    WorkEvents.TryGetEvent(item.Key)?.OnChapterNumChanged(this, item.Value);
                                }
                                else
                                {
                                    WorkEvents.TryGetEvent(item.Key)?.OnLocationChanged(this, item.Value);
                                }
                            }
                            // This kinda shouldn't happen, but apparently it did... we can deal with it though
                            else
                            {
                                // Update the timestamp to newer than newest
                                if (storage[item.Key].Timestamp <= item.Value.Timestamp)
                                {
                                    storage[item.Key].Timestamp = item.Value.Timestamp + 1;
                                }
                                else
                                {
                                    item.Value.Timestamp += 1;
                                }
                                // set as unsynced
                                unsynced[item.Key] = storage[item.Key];
                            }
                        }
                        App.Database.SaveItems(newitems.Values.ToReadOnly());

                        current  = unsynced;
                        unsynced = new Dictionary <long, Work>();
                        time     = last_sync;

                        if (current.Count == 0)
                        {
                            App.Database.SaveVariable("last_sync", last_sync);
                            serversync = SyncState.Ready;
                            EndSyncEvent?.Invoke(this, new EventArgs <bool>(true));
                            return;
                        }
                    }

                    foreach (var item in current.Values)
                    {
                        if (item.Timestamp > time)
                        {
                            time = item.Timestamp;
                        }
                    }

                    var json     = JsonConvert.SerializeObject(current);
                    var postBody = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
                    responseTask = HttpClient.PostAsync(new Uri(url_base, "Values"), postBody);
                    responseTask.TryWait();
                    response = responseTask.IsFaulted ? null : responseTask.Result;

                    if (response == null || !response.IsSuccessStatusCode)
                    {
                        using (ReadWriteLock.WriteLock())
                        {
                            //console.error("dosync: FAILED %s", response.ReasonPhrase);
                            if (response != null && response.StatusCode == HttpStatusCode.Forbidden)
                            {
                                last_sync  = 0;
                                serversync = SyncState.Disabled;
                            }
                            else
                            {
                                last_sync  = old_sync;
                                serversync = SyncState.Ready;
                                unsynced   = current;
                            }
                            App.Database.SaveVariable("last_sync", last_sync);
                            EndSyncEvent?.Invoke(this, new EventArgs <bool>(false));
                            return;
                        }
                    }

                    contentTask = response.Content.ReadAsStringAsync();
                    contentTask.Wait();
                    content = contentTask.Result;

                    try
                    {
                        items = JsonConvert.DeserializeObject <Dictionary <long, Work> >(content, settings);
                    }
                    catch (Exception e)
                    {
                        App.Log(e);
                        using (ReadWriteLock.WriteLock())
                        {
                            serversync = SyncState.Disabled;
                            App.Database.SaveVariable("last_sync", 0L);
                            last_sync = 0;
                            EndSyncEvent?.Invoke(this, new EventArgs <bool>(false));
                            return;
                        }
                    }

                    //console.log("dosync: SUCCESS. %i conflicted items", Object.keys(items).length);
                    using (ReadWriteLock.WriteLock())
                    {
                        if (items.Count > 0)
                        {
                            App.Database.SaveVariable("last_sync", 0L);
                            last_sync = 0;
                            DoSyncAsync(true);
                            return;
                        }
                        if (time > last_sync)
                        {
                            last_sync = time;
                            App.Database.SaveVariable("last_sync", time);
                        }

                        if (unsynced.Count > 0)
                        {
                            DoSyncAsync(true);
                        }
                        else
                        {
                            serversync = SyncState.Ready;
                            EndSyncEvent?.Invoke(this, new EventArgs <bool>(true));
                        }
                    }
                }
            }, TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning);
        }