Пример #1
0
        public void ChangeTrackerReceivedChange(IDictionary <string, object> change)
        {
            var lastSequence = change.Get("seq").ToString();
            var docID        = (string)change.Get("id");

            if (docID == null)
            {
                return;
            }

            if (!LocalDatabase.IsValidDocumentId(docID))
            {
                if (!docID.StartsWith("_user/", StringComparison.InvariantCultureIgnoreCase))
                {
                    Log.W(TAG, string.Format("{0}: Received invalid doc ID from _changes: {1} ({2})", this, docID, Manager.GetObjectMapper().WriteValueAsString(change)));
                }

                return;
            }

            var deleted = change.GetCast <bool>("deleted");
            var changes = change.Get("changes").AsList <object>();

            SafeAddToChangesCount(changes.Count);

            foreach (var changeObj in changes)
            {
                var changeDict = changeObj.AsDictionary <string, object>();
                var revID      = changeDict.GetCast <string>("rev");
                if (revID == null)
                {
                    continue;
                }

                var rev = new PulledRevision(docID, revID, deleted, LocalDatabase);
                rev.SetRemoteSequenceID(lastSequence);
                if (changes.Count > 1)
                {
                    rev.IsConflicted = true;
                }

                Log.D(TAG, "Adding rev to inbox " + rev);
                AddToInbox(rev);
            }

            PauseOrResume();

            while (_revsToPull != null && _revsToPull.Count > 1000)
            {
                try {
                    // Presumably we are letting 1 or more other threads do something while we wait.
                    Thread.Sleep(500);
                }
                catch (Exception e) {
                    Log.W(TAG, "Swalling exception while sleeping after receiving changetracker changes.", e);
                    // swallow
                }
            }
        }
Пример #2
0
        // Got a _changes feed entry from the ChangeTracker.
        public void ChangeTrackerReceivedChange(IDictionary <string, object> change)
        {
            var lastSequence = change.Get("seq").ToString();
            var docID        = (string)change.Get("id");

            if (docID == null)
            {
                return;
            }

            if (!LocalDatabase.IsValidDocumentId(docID))
            {
                if (!docID.StartsWith("_user/", StringComparison.InvariantCultureIgnoreCase))
                {
                    Log.W(Tag, string.Format("{0}: Received invalid doc ID from _changes: {1} ({2})", this, docID, JsonConvert.SerializeObject(change)));
                }
                return;
            }

            var deleted = (change.ContainsKey("deleted") && ((bool)change.Get("deleted")).Equals(true));

            var changesContainer = change.Get("changes") as JContainer;
            var changes          = changesContainer.ToArray();

            foreach (var changeObj in changes)
            {
                var changeDict = changeObj.ToObject <IDictionary <string, object> >();
                var revID      = (string)changeDict.Get("rev");
                if (revID == null)
                {
                    continue;
                }

                var rev = new PulledRevision(docID, revID, deleted, LocalDatabase);
                rev.SetRemoteSequenceID(lastSequence);

                Log.D(Tag, "adding rev to inbox " + rev);
                Log.V(Tag, "ChangeTrackerReceivedChange() incrementing changesCount by 1");

                SafeAddToChangesCount(changes.Length);

                AddToInbox(rev);
            }

            while (revsToPull != null && revsToPull.Count > 1000)
            {
                try
                {
                    // Presumably we are letting 1 or more other threads do something while we wait.
                    Thread.Sleep(500);
                }
                catch (Exception e)
                {
                    Log.W(Tag, "Swalling exception while sleeping after receiving changetracker changes.", e);
                    // swallow
                }
            }
        }
Пример #3
0
 public void OnCompletion(object result, Exception e)
 {
     try
     {
         // OK, now we've got the response revision:
         Log.D(Database.Tag, this + ": pullRemoteRevision got response for rev: " + rev);
         if (result != null)
         {
             IDictionary <string, object> properties = (IDictionary <string, object>)result;
             IList <string> history = Database.ParseCouchDBRevisionHistory(properties);
             if (history != null)
             {
                 PulledRevision gotRev = new PulledRevision(properties, this._enclosing.db);
                 // Add to batcher ... eventually it will be fed to -insertRevisions:.
                 IList <object> toInsert = new AList <object>();
                 toInsert.AddItem(gotRev);
                 toInsert.AddItem(history);
                 Log.D(Database.Tag, this + ": pullRemoteRevision add rev: " + rev + " to batcher"
                       );
                 this._enclosing.downloadsToInsert.QueueObject(toInsert);
                 Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread() + ": pullRemoteRevision.onCompletion() calling asyncTaskStarted()"
                       );
                 this._enclosing.AsyncTaskStarted();
             }
             else
             {
                 Log.W(Database.Tag, this + ": Missing revision history in response from " + pathInside
                       );
                 this._enclosing.SetCompletedChangesCount(this._enclosing.GetCompletedChangesCount
                                                              () + 1);
             }
         }
         else
         {
             if (e != null)
             {
                 Log.E(Database.Tag, "Error pulling remote revision", e);
                 this._enclosing.SetError(e);
             }
             this._enclosing.RevisionFailed();
             this._enclosing.SetCompletedChangesCount(this._enclosing.GetCompletedChangesCount
                                                          () + 1);
         }
     }
     finally
     {
         Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread() + ": pullRemoteRevision.onCompletion() calling asyncTaskFinished()"
               );
         this._enclosing.AsyncTaskFinished(1);
     }
     // Note that we've finished this task; then start another one if there
     // are still revisions waiting to be pulled:
     --this._enclosing.httpConnectionCount;
     this._enclosing.PullRemoteRevisions();
 }
Пример #4
0
        protected internal override void ProcessInbox(RevisionList inbox)
        {
            // Ask the local database which of the revs are not known to it:
            //Log.w(Database.TAG, String.format("%s: Looking up %s", this, inbox));
            string lastInboxSequence = ((PulledRevision)inbox[inbox.Count - 1]).GetRemoteSequenceID
                                           ();
            int total = GetChangesCount() - inbox.Count;

            if (!db.FindMissingRevisions(inbox))
            {
                Log.W(Database.Tag, string.Format("%s failed to look up local revs", this));
                inbox = null;
            }
            //introducing this to java version since inbox may now be null everywhere
            int inboxCount = 0;

            if (inbox != null)
            {
                inboxCount = inbox.Count;
            }
            if (GetChangesCount() != total + inboxCount)
            {
                SetChangesCount(total + inboxCount);
            }
            if (inboxCount == 0)
            {
                // Nothing to do. Just bump the lastSequence.
                Log.W(Database.Tag, string.Format("%s no new remote revisions to fetch", this));
                long seq = pendingSequences.AddValue(lastInboxSequence);
                pendingSequences.RemoveSequence(seq);
                SetLastSequence(pendingSequences.GetCheckpointedValue());
                return;
            }
            Log.V(Database.Tag, this + " fetching " + inboxCount + " remote revisions...");
            //Log.v(Database.TAG, String.format("%s fetching remote revisions %s", this, inbox));
            // Dump the revs into the queue of revs to pull from the remote db:
            lock (this)
            {
                if (revsToPull == null)
                {
                    revsToPull = new AList <RevisionInternal>(200);
                }
                for (int i = 0; i < inbox.Count; i++)
                {
                    PulledRevision rev = (PulledRevision)inbox[i];
                    // FIXME add logic here to pull initial revs in bulk
                    rev.SetSequence(pendingSequences.AddValue(rev.GetRemoteSequenceID()));
                    revsToPull.AddItem(rev);
                }
            }
            PullRemoteRevisions();
        }
Пример #5
0
        // Got a _changes feed entry from the ChangeTracker.
        public void ChangeTrackerReceivedChange(IDictionary <string, object> change)
        {
            var lastSequence = change.Get("seq").ToString();
            var docID        = (string)change.Get("id");

            if (docID == null)
            {
                return;
            }

            if (!LocalDatabase.IsValidDocumentId(docID))
            {
                Log.W(Tag, string.Format("{0}: Received invalid doc ID from _changes: {1}", this, change));
                return;
            }

            var deleted = (change.ContainsKey("deleted") && ((bool)change.Get("deleted")).Equals(true));

            var changesContainer = change.Get("changes") as JContainer;
            var changes          = changesContainer.ToArray();

            foreach (var changeObj in changes)
            {
                var changeDict = changeObj.ToObject <IDictionary <string, object> >();
                var revID      = (string)changeDict.Get("rev");
                if (revID == null)
                {
                    continue;
                }
                var rev = new PulledRevision(docID, revID, deleted, LocalDatabase);
                rev.SetRemoteSequenceID(lastSequence);
                Log.D(Tag, this + ": adding rev to inbox " + rev);
                AddToInbox(rev);
            }
            ChangesCount = ChangesCount + changes.Length;
            while (revsToPull != null && revsToPull.Count > 1000)
            {
                try
                {
                    Sharpen.Thread.Sleep(500);
                }
                catch (Exception)
                {
                    // swallow
                }
            }
        }
Пример #6
0
        public void ChangeTrackerReceivedChange(IDictionary <string, object> change)
        {
            string lastSequence = change.Get("seq").ToString();
            string docID        = (string)change.Get("id");

            if (docID == null)
            {
                return;
            }
            if (!Database.IsValidDocumentId(docID))
            {
                Log.W(Log.TagSync, "%s: Received invalid doc ID from _changes: %s", this, change);
                return;
            }
            bool deleted = (change.ContainsKey("deleted") && ((bool)change.Get("deleted")).Equals
                                (true));
            IList <IDictionary <string, object> > changes = (IList <IDictionary <string, object> >)
                                                            change.Get("changes");

            foreach (IDictionary <string, object> changeDict in changes)
            {
                string revID = (string)changeDict.Get("rev");
                if (revID == null)
                {
                    continue;
                }
                PulledRevision rev = new PulledRevision(docID, revID, deleted, db);
                rev.SetRemoteSequenceID(lastSequence);
                Log.D(Log.TagSync, "%s: adding rev to inbox %s", this, rev);
                Log.V(Log.TagSync, "%s: changeTrackerReceivedChange() incrementing changesCount by 1"
                      , this);
                // this is purposefully done slightly different than the ios version
                AddToChangesCount(1);
                AddToInbox(rev);
            }
            while (revsToPull != null && revsToPull.Count > 1000)
            {
                try
                {
                    Sharpen.Thread.Sleep(500);
                }
                catch (Exception)
                {
                }
            }
        }
Пример #7
0
        public void ChangeTrackerReceivedChange(IDictionary <string, object> change)
        {
            string lastSequence = change.Get("seq").ToString();
            string docID        = (string)change.Get("id");

            if (docID == null)
            {
                return;
            }
            if (!Database.IsValidDocumentId(docID))
            {
                Log.W(Database.Tag, string.Format("%s: Received invalid doc ID from _changes: %s"
                                                  , this, change));
                return;
            }
            bool deleted = (change.ContainsKey("deleted") && ((bool)change.Get("deleted")).Equals
                                (true));
            IList <IDictionary <string, object> > changes = (IList <IDictionary <string, object> >)
                                                            change.Get("changes");

            foreach (IDictionary <string, object> changeDict in changes)
            {
                string revID = (string)changeDict.Get("rev");
                if (revID == null)
                {
                    continue;
                }
                PulledRevision rev = new PulledRevision(docID, revID, deleted, db);
                rev.SetRemoteSequenceID(lastSequence);
                Log.D(Database.Tag, this + ": adding rev to inbox " + rev);
                AddToInbox(rev);
            }
            SetChangesCount(GetChangesCount() + changes.Count);
            while (revsToPull != null && revsToPull.Count > 1000)
            {
                try
                {
                    Sharpen.Thread.Sleep(500);
                }
                catch (Exception)
                {
                }
            }
        }
Пример #8
0
 public void OnCompletion(object result, Exception e)
 {
     try
     {
         if (e != null)
         {
             Log.E(Log.TagSync, "Error pulling remote revision", e);
             this._enclosing.SetError(e);
             this._enclosing.RevisionFailed();
             Log.D(Log.TagSync, "%s: pullRemoteRevision() updating completedChangesCount from %d  ->  due to error pulling remote revision"
                   , this, this._enclosing.GetCompletedChangesCount(), this._enclosing.GetCompletedChangesCount
                       () + 1);
             this._enclosing.AddToCompletedChangesCount(1);
         }
         else
         {
             IDictionary <string, object> properties = (IDictionary <string, object>)result;
             PulledRevision gotRev = new PulledRevision(properties, this._enclosing.db);
             gotRev.SetSequence(rev.GetSequence());
             // Add to batcher ... eventually it will be fed to -insertDownloads:.
             Log.V(Log.TagSync, "%s | %s: pullRemoteRevision.sendAsyncMultipartDownloaderRequest() calling asyncTaskStarted()"
                   , this, Sharpen.Thread.CurrentThread());
             this._enclosing.AsyncTaskStarted();
             // TODO: [gotRev.body compact];
             Log.D(Log.TagSync, "%s: pullRemoteRevision add rev: %s to batcher", this, gotRev);
             this._enclosing.downloadsToInsert.QueueObject(gotRev);
         }
     }
     finally
     {
         Log.V(Log.TagSync, "%s | %s: pullRemoteRevision.sendAsyncMultipartDownloaderRequest() calling asyncTaskFinished()"
               , this, Sharpen.Thread.CurrentThread());
         this._enclosing.AsyncTaskFinished(1);
     }
     // Note that we've finished this task; then start another one if there
     // are still revisions waiting to be pulled:
     --this._enclosing.httpConnectionCount;
     this._enclosing.PullRemoteRevisions();
 }
Пример #9
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        private void PullRemoteRevision(RevisionInternal rev)
        {
            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path = new StringBuilder($"/{Uri.EscapeUriString(rev.DocID)}?rev={Uri.EscapeUriString(rev.RevID.ToString())}&revs=true");

            var attachments = true;

            if (attachments)
            {
                // TODO: deferred attachments
                path.Append("&attachments=true");
            }

            // Include atts_since with a list of possible ancestor revisions of rev. If getting attachments,
            // this allows the server to skip the bodies of attachments that have not changed since the
            // local ancestor. The server can also trim the revision history it returns, to not extend past
            // the local ancestor (not implemented yet in SG but will be soon.)
            var knownRevs = default(IList <RevisionID>);
            ValueTypePtr <bool> haveBodies = false;

            try {
                knownRevs = LocalDatabase.Storage.GetPossibleAncestors(rev, MaxAttsSince, haveBodies)?.ToList();
            } catch (Exception e) {
                Log.To.Sync.W(TAG, "Error getting possible ancestors (probably database closed)", e);
            }

            if (knownRevs != null)
            {
                path.Append(haveBodies ? "&atts_since=" : "&revs_from=");
                path.Append(JoinQuotedEscaped(knownRevs.Select(x => x.ToString()).ToList()));
            }
            else
            {
                // If we don't have any revisions at all, at least tell the server how long a history we
                // can keep track of:
                var maxRevTreeDepth = LocalDatabase.GetMaxRevTreeDepth();
                if (rev.Generation > maxRevTreeDepth)
                {
                    path.AppendFormat("&revs_limit={0}", maxRevTreeDepth);
                }
            }

            var pathInside = path.ToString();

            Log.To.SyncPerf.I(TAG, "{0} getting {1}", this, rev);
            Log.To.Sync.V(TAG, "{0} GET {1}", this, new SecureLogString(pathInside, LogMessageSensitivity.PotentiallyInsecure));
            _remoteSession.SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                // OK, now we've got the response revision:
                Log.To.SyncPerf.I(TAG, "{0} got {1}", this, rev);

                if (e != null)
                {
                    Log.To.Sync.I(TAG, String.Format("{0} error pulling remote revision", this), e);
                    LastError = e;
                    RevisionFailed();
                    SafeIncrementCompletedChangesCount();
                    if (IsDocumentError(e))
                    {
                        // Make sure this document is skipped because it is not available
                        // even though the server is functioning
                        _pendingSequences.RemoveSequence(rev.Sequence);
                        LastSequence = _pendingSequences.GetCheckpointedValue();
                    }
                }
                else
                {
                    var properties  = result.AsDictionary <string, object>();
                    var gotRev      = new PulledRevision(properties);
                    gotRev.Sequence = rev.Sequence;

                    if (_downloadsToInsert != null)
                    {
                        if (!_downloadsToInsert.QueueObject(gotRev))
                        {
                            Log.To.Sync.W(TAG, "{0} failed to queue {1} for download because it is already queued, marking completed...", this, rev);
                            SafeIncrementCompletedChangesCount();
                        }
                    }
                    else
                    {
                        Log.To.Sync.E(TAG, "downloadsToInsert is null");
                    }
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                PullRemoteRevisions();
            });
        }
Пример #10
0
        public void ChangeTrackerReceivedChange(IDictionary <string, object> change)
        {
            if (ServerType == null)
            {
                ServerType = _remoteSession.ServerType;
            }

            var lastSequence = change.Get("seq").ToString();
            var docID        = (string)change.Get("id");

            if (docID == null)
            {
                Log.To.Sync.W(TAG, "{0} Change received with no id, ignoring...", this);
                return;
            }

            var removed = change.Get("removed") != null;

            if (removed)
            {
                Log.To.Sync.V(TAG, "Removed entry received, body may not be available");
            }

            if (!Document.IsValidDocumentId(docID))
            {
                if (!docID.StartsWith("_user/", StringComparison.InvariantCultureIgnoreCase))
                {
                    Log.To.Sync.W(TAG, "{0}: Received invalid doc ID from _changes: {1} ({2})",
                                  this, new SecureLogString(docID, LogMessageSensitivity.PotentiallyInsecure), new LogJsonString(change));
                }

                return;
            }

            var deleted = change.GetCast <bool>("deleted");
            var changes = change.Get("changes").AsList <object>();

            SafeAddToChangesCount(changes.Count);

            foreach (var changeObj in changes)
            {
                var changeDict = changeObj.AsDictionary <string, object>();
                var revID      = changeDict.GetCast <string>("rev").AsRevID();
                if (revID == null)
                {
                    Log.To.Sync.W(TAG, "{0} missing revID for entry, skipping...");
                    SafeIncrementCompletedChangesCount();
                    continue;
                }

                var rev = new PulledRevision(docID, revID, deleted, LocalDatabase);
                rev.SetRemoteSequenceID(lastSequence);
                if (changes.Count > 1)
                {
                    rev.IsConflicted = true;
                }

                Log.To.Sync.D(TAG, "Adding rev to inbox " + rev);
                if (!AddToInbox(rev))
                {
                    Log.To.Sync.W(TAG, "{0} Failed to add {1} to inbox, probably already added.  Marking completed", this, rev);
                    SafeIncrementCompletedChangesCount();
                }
            }

            PauseOrResume();

            while (_revsToPull != null && _revsToPull.Count > 1000)
            {
                try {
                    // Presumably we are letting 1 or more other threads do something while we wait.
                    Thread.Sleep(500);
                }
                catch (Exception e) {
                    Log.To.Sync.W(TAG, "Swallowing exception while sleeping after receiving changetracker changes.", e);
                    // swallow
                }
            }
        }
Пример #11
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        internal void PullRemoteRevision(RevisionInternal rev)
        {
            Log.D(Tag, "PullRemoteRevision with rev: {0}", rev);

            Log.D(Tag, "PullRemoteRevision() calling AsyncTaskStarted()");
            AsyncTaskStarted();

            httpConnectionCount++;

            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path      = new StringBuilder("/" + Uri.EscapeUriString(rev.GetDocId()) + "?rev=" + Uri.EscapeUriString(rev.GetRevId()) + "&revs=true&attachments=true");
            var knownRevs = KnownCurrentRevIDs(rev);

            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                Log.D(Tag, "PullRemoteRevision() calling AsyncTaskFinished()");
                AsyncTaskFinished(1);

                httpConnectionCount--;
                return;
            }

            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }

            //create a final version of this variable for the log statement inside
            //FIXME find a way to avoid this
            var pathInside = path.ToString();

            SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                try
                {
                    // OK, now we've got the response revision:
                    Log.D(Tag, "PullRemoteRevision got response for rev: " + rev);

                    if (e != null)
                    {
                        Log.E(Tag, "Error pulling remote revision", e);
                        SetLastError(e);
                        RevisionFailed();
                        Log.D(Tag, "PullRemoteRevision updating completedChangesCount from " +
                              CompletedChangesCount + " -> " + (CompletedChangesCount + 1)
                              + " due to error pulling remote revision");
                        SafeIncrementCompletedChangesCount();
                    }
                    else
                    {
                        var properties = result.AsDictionary <string, object>();
                        var gotRev     = new PulledRevision(properties);
                        gotRev.SetSequence(rev.GetSequence());
                        AsyncTaskStarted();
                        Log.D(Tag, "PullRemoteRevision add rev: " + gotRev + " to batcher");

                        if (downloadsToInsert != null)
                        {
                            downloadsToInsert.QueueObject(gotRev);
                        }
                        else
                        {
                            Log.E(Tag, "downloadsToInsert is null");
                        }
                    }
                }
                finally
                {
                    Log.D(Tag, "PullRemoteRevision.onCompletion() calling AsyncTaskFinished()");
                    AsyncTaskFinished(1);
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                --httpConnectionCount;
                PullRemoteRevisions();
            });
        }
Пример #12
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        private void PullRemoteRevision(RevisionInternal rev)
        {
            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path      = new StringBuilder("/" + Uri.EscapeUriString(rev.DocID) + "?rev=" + Uri.EscapeUriString(rev.RevID) + "&revs=true&attachments=true");
            var knownRevs = default(IList <string>);

            try {
                var tmp = LocalDatabase.Storage.GetPossibleAncestors(rev, MAX_ATTS_SINCE, true);
                knownRevs = tmp == null ? null : tmp.ToList();
            } catch (Exception e) {
                Log.To.Sync.W(TAG, "Error getting possible ancestors (probably database closed)", e);
            }

            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                return;
            }

            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }

            var pathInside = path.ToString();

            Log.To.SyncPerf.I(TAG, "{0} getting {1}", this, rev);
            Log.To.Sync.V(TAG, "{0} GET {1}", this, new SecureLogString(pathInside, LogMessageSensitivity.PotentiallyInsecure));
            SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                // OK, now we've got the response revision:
                Log.To.SyncPerf.I(TAG, "{0} got {1}", this, rev);

                if (e != null)
                {
                    Log.To.Sync.I(TAG, String.Format("{0} error pulling remote revision", this), e);
                    LastError = e;
                    RevisionFailed();
                    SafeIncrementCompletedChangesCount();
                    if (IsDocumentError(e))
                    {
                        // Make sure this document is skipped because it is not available
                        // even though the server is functioning
                        _pendingSequences.RemoveSequence(rev.Sequence);
                        LastSequence = _pendingSequences.GetCheckpointedValue();
                    }
                }
                else
                {
                    var properties  = result.AsDictionary <string, object>();
                    var gotRev      = new PulledRevision(properties);
                    gotRev.Sequence = rev.Sequence;

                    if (_downloadsToInsert != null)
                    {
                        _downloadsToInsert.QueueObject(gotRev);
                    }
                    else
                    {
                        Log.To.Sync.E(TAG, "downloadsToInsert is null");
                    }
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                PullRemoteRevisions();
            });
        }
Пример #13
0
        public void InsertRevisions(IList <IList <object> > revs)
        {
            Log.I(Database.Tag, this + " inserting " + revs.Count + " revisions...");
            //Log.v(Database.TAG, String.format("%s inserting %s", this, revs));
            revs.Sort(new _IComparer_402());
            if (db == null)
            {
                Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread() + ": insertRevisions() calling asyncTaskFinished() since db == null"
                      );
                AsyncTaskFinished(revs.Count);
                return;
            }
            db.BeginTransaction();
            bool success = false;

            try
            {
                foreach (IList <object> revAndHistory in revs)
                {
                    PulledRevision rev          = (PulledRevision)revAndHistory[0];
                    long           fakeSequence = rev.GetSequence();
                    IList <string> history      = (IList <string>)revAndHistory[1];
                    // Insert the revision:
                    try
                    {
                        Log.I(Database.Tag, this + ": db.forceInsert " + rev);
                        db.ForceInsert(rev, history, remote);
                        Log.I(Database.Tag, this + ": db.forceInsert succeeded " + rev);
                    }
                    catch (CouchbaseLiteException e)
                    {
                        if (e.GetCBLStatus().GetCode() == Status.Forbidden)
                        {
                            Log.I(Database.Tag, this + ": Remote rev failed validation: " + rev);
                        }
                        else
                        {
                            Log.W(Database.Tag, this + " failed to write " + rev + ": status=" + e.GetCBLStatus
                                      ().GetCode());
                            RevisionFailed();
                            SetError(new HttpResponseException(e.GetCBLStatus().GetCode(), null));
                            continue;
                        }
                    }
                    pendingSequences.RemoveSequence(fakeSequence);
                }
                Log.W(Database.Tag, this + " finished inserting " + revs.Count + " revisions");
                SetLastSequence(pendingSequences.GetCheckpointedValue());
                success = true;
            }
            catch (SQLException e)
            {
                Log.E(Database.Tag, this + ": Exception inserting revisions", e);
            }
            finally
            {
                db.EndTransaction(success);
                Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread() + ": insertRevisions() calling asyncTaskFinished()"
                      );
                AsyncTaskFinished(revs.Count);
            }
            SetCompletedChangesCount(GetCompletedChangesCount() + revs.Count);
        }
Пример #14
0
        protected internal override void ProcessInbox(RevisionList inbox)
        {
            if (canBulkGet == null)
            {
                canBulkGet = ServerIsSyncGatewayVersion("0.81");
            }
            // Ask the local database which of the revs are not known to it:
            string lastInboxSequence = ((PulledRevision)inbox[inbox.Count - 1]).GetRemoteSequenceID
                                           ();
            int numRevisionsRemoved = 0;

            try
            {
                // findMissingRevisions is the local equivalent of _revs_diff. it looks at the
                // array of revisions in ‘inbox’ and removes the ones that already exist. So whatever’s left in ‘inbox’
                // afterwards are the revisions that need to be downloaded.
                numRevisionsRemoved = db.FindMissingRevisions(inbox);
            }
            catch (SQLException e)
            {
                Log.E(Log.TagSync, string.Format("%s failed to look up local revs", this), e);
                inbox = null;
            }
            //introducing this to java version since inbox may now be null everywhere
            int inboxCount = 0;

            if (inbox != null)
            {
                inboxCount = inbox.Count;
            }
            if (numRevisionsRemoved > 0)
            {
                Log.V(Log.TagSync, "%s: processInbox() setting changesCount to: %s", this, GetChangesCount
                          () - numRevisionsRemoved);
                // May decrease the changesCount, to account for the revisions we just found out we don’t need to get.
                AddToChangesCount(-1 * numRevisionsRemoved);
            }
            if (inboxCount == 0)
            {
                // Nothing to do. Just bump the lastSequence.
                Log.W(Log.TagSync, "%s no new remote revisions to fetch", this);
                long seq = pendingSequences.AddValue(lastInboxSequence);
                pendingSequences.RemoveSequence(seq);
                SetLastSequence(pendingSequences.GetCheckpointedValue());
                return;
            }
            Log.V(Log.TagSync, "%s: fetching %s remote revisions...", this, inboxCount);
            // Dump the revs into the queue of revs to pull from the remote db:
            lock (this)
            {
                int numBulked = 0;
                for (int i = 0; i < inbox.Count; i++)
                {
                    PulledRevision rev = (PulledRevision)inbox[i];
                    //TODO: add support for rev isConflicted
                    if (canBulkGet || (rev.GetGeneration() == 1 && !rev.IsDeleted()))
                    {
                        // &&!rev.isConflicted)
                        //optimistically pull 1st-gen revs in bulk
                        if (bulkRevsToPull == null)
                        {
                            bulkRevsToPull = new AList <RevisionInternal>(100);
                        }
                        bulkRevsToPull.AddItem(rev);
                        ++numBulked;
                    }
                    else
                    {
                        QueueRemoteRevision(rev);
                    }
                    rev.SetSequence(pendingSequences.AddValue(rev.GetRemoteSequenceID()));
                }
            }
            PullRemoteRevisions();
        }
Пример #15
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        private void PullRemoteRevision(RevisionInternal rev)
        {
            Log.D(TAG, "PullRemoteRevision with rev: {0}", rev);
            _httpConnectionCount++;

            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path      = new StringBuilder("/" + Uri.EscapeUriString(rev.GetDocId()) + "?rev=" + Uri.EscapeUriString(rev.GetRevId()) + "&revs=true&attachments=true");
            var knownRevs = default(IList <string>);

            try {
                var tmp = LocalDatabase.Storage.GetPossibleAncestors(rev, MAX_ATTS_SINCE, true);
                knownRevs = tmp == null ? null : tmp.ToList();
            } catch (Exception) {
                Log.W(TAG, "Error getting possible ancestors (probably database closed)");
            }
            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                _httpConnectionCount--;
                return;
            }

            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }

            //create a final version of this variable for the log statement inside
            //FIXME find a way to avoid this
            var pathInside = path.ToString();

            SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                // OK, now we've got the response revision:
                Log.D(TAG, "PullRemoteRevision got response for rev: " + rev);

                if (e != null)
                {
                    Log.E(TAG, "Error pulling remote revision", e);
                    LastError = e;
                    RevisionFailed();
                    Log.D(TAG, "PullRemoteRevision updating completedChangesCount from " +
                          CompletedChangesCount + " -> " + (CompletedChangesCount + 1)
                          + " due to error pulling remote revision");
                    SafeIncrementCompletedChangesCount();
                    if (IsDocumentError(e as HttpResponseException))
                    {
                        // Make sure this document is skipped because it is not available
                        // even though the server is functioning
                        _pendingSequences.RemoveSequence(rev.GetSequence());
                        LastSequence = _pendingSequences.GetCheckpointedValue();
                    }
                }
                else
                {
                    var properties = result.AsDictionary <string, object>();
                    var gotRev     = new PulledRevision(properties);
                    gotRev.SetSequence(rev.GetSequence());
                    Log.D(TAG, "PullRemoteRevision add rev: " + gotRev + " to batcher");

                    if (_downloadsToInsert != null)
                    {
                        _downloadsToInsert.QueueObject(gotRev);
                    }
                    else
                    {
                        Log.E(TAG, "downloadsToInsert is null");
                    }
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                --_httpConnectionCount;
                PullRemoteRevisions();
            });
        }