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