public void Test08DocRevisions()
        {
            const string TEST_NAME = "test8";
            if (!PerformanceTestsEnabled) {
                return;
            }

            var docs = new Document[GetNumberOfDocuments(TEST_NAME)];
            var success = database.RunInTransaction(() =>
            {
                for(int i = 0; i < GetNumberOfDocuments(TEST_NAME); i++) {
                    var props = new Dictionary<string, object> {
                        { "toggle", true }
                    };
                    var doc = database.CreateDocument();
                    try {
                        doc.PutProperties(props);
                        docs[i] = doc;
                    } catch(CouchbaseLiteException e) {
                        Log.E(TAG, "Document creation failed");
                        return false;
                    }
                }

                return true;
            });

            Assert.IsTrue(success);
            TimeBlock(String.Format("{0}, {1}", GetNumberOfDocuments(TEST_NAME), GetNumberOfUpdates(TEST_NAME)), () =>
            {
                foreach(var doc in docs) {
                    for(int i = 0; i < GetNumberOfUpdates(TEST_NAME); i++) {
                        var contents = new Dictionary<string, object>(doc.Properties);
                        var wasChecked = contents.GetCast<bool>("toggle");
                        contents["toggle"] = !wasChecked;
                        doc.PutProperties(contents);
                    }
                 }
            });
        }
        internal override void ProcessInbox(RevisionList inbox)
        {
            if (Status == ReplicationStatus.Offline) {
                Log.V(TAG, "Offline, so skipping inbox process");
                return;
            }

            if(_requests.Count > ManagerOptions.Default.MaxOpenHttpConnections) {
                Task.Delay(1000).ContinueWith(t => ProcessInbox(inbox), CancellationToken.None, TaskContinuationOptions.None, WorkExecutor.Scheduler);
                return;
            }

            // Generate a set of doc/rev IDs in the JSON format that _revs_diff wants:
            // <http://wiki.apache.org/couchdb/HttpPostRevsDiff>
            var diffs = new Dictionary<String, IList<String>>();
            foreach (var rev in inbox) {
                var docID = rev.GetDocId();
                var revs = diffs.Get(docID);
                if (revs == null) {
                    revs = new List<String>();
                    diffs[docID] = revs;
                }
                revs.Add(rev.GetRevId());
                AddPending(rev);
            }

            // Call _revs_diff on the target db:
            Log.D(TAG, "posting to /_revs_diff: {0}", String.Join(Environment.NewLine, new[] { Manager.GetObjectMapper().WriteValueAsString(diffs) }));
            SendAsyncRequest(HttpMethod.Post, "/_revs_diff", diffs, (response, e) =>
            {
                try {
                    if(!LocalDatabase.IsOpen) {
                        return;
                    }

                    var results = response.AsDictionary<string, object>();

                    Log.D(TAG, "/_revs_diff response: {0}\r\n{1}", response, results);

                    if (e != null) {
                        LastError = e;
                        RevisionFailed();
                    } else {
                        if (results.Count != 0)  {
                            // Go through the list of local changes again, selecting the ones the destination server
                            // said were missing and mapping them to a JSON dictionary in the form _bulk_docs wants:
                            var docsToSend = new List<object> ();
                            var revsToSend = new RevisionList();
                            foreach (var rev in inbox) {
                                // Is this revision in the server's 'missing' list?
                                IDictionary<string, object> properties = null;
                                var revResults = results.Get(rev.GetDocId()).AsDictionary<string, object>(); 
                                if (revResults == null) {
                                    //SafeIncrementCompletedChangesCount();
                                    continue;
                                }

                                var revs = revResults.Get("missing").AsList<string>();
                                if (revs == null || !revs.Any( id => id.Equals(rev.GetRevId(), StringComparison.OrdinalIgnoreCase))) {
                                    RemovePending(rev);
                                    //SafeIncrementCompletedChangesCount();
                                    continue;
                                }

                                // Get the revision's properties:
                                var contentOptions = DocumentContentOptions.IncludeAttachments;
                                if (!_dontSendMultipart && RevisionBodyTransformationFunction == null)
                                {
                                    contentOptions |= DocumentContentOptions.BigAttachmentsFollow;
                                }

                                RevisionInternal loadedRev;
                                try {
                                    loadedRev = LocalDatabase.LoadRevisionBody (rev);
                                    if(loadedRev == null) {
                                        throw new CouchbaseLiteException("DB is closed", StatusCode.DbError);
                                    }

                                    properties = new Dictionary<string, object>(rev.GetProperties());
                                } catch (Exception e1) {
                                    Log.W(TAG, String.Format("{0} Couldn't get local contents of", rev), e);
                                    RevisionFailed();
                                    continue;
                                }

                                var populatedRev = TransformRevision(loadedRev);
                                IList<string> possibleAncestors = null;
                                if (revResults.ContainsKey("possible_ancestors")) {
                                    possibleAncestors = revResults["possible_ancestors"].AsList<string>();
                                }

                                properties = new Dictionary<string, object>(populatedRev.GetProperties());

                                try {
                                    var history = LocalDatabase.GetRevisionHistory(populatedRev, possibleAncestors);
                                    if(history == null) {
                                        throw new CouchbaseLiteException("DB closed", StatusCode.DbError);
                                    }

                                    properties["_revisions"] = Database.MakeRevisionHistoryDict(history);
                                } catch(Exception e1) {
                                    Log.W(TAG, "Error getting revision history", e1);
                                    RevisionFailed();
                                    continue;
                                }

                                populatedRev.SetProperties(properties);
                                if(properties.GetCast<bool>("_removed")) {
                                    RemovePending(rev);
                                    continue;
                                }

                                // Strip any attachments already known to the target db:
                                if (properties.ContainsKey("_attachments")) {
                                    // Look for the latest common ancestor and stuf out older attachments:
                                    var minRevPos = FindCommonAncestor(populatedRev, possibleAncestors);
                                    try {
                                        LocalDatabase.ExpandAttachments(populatedRev, minRevPos + 1, !_dontSendMultipart, false);
                                    } catch(Exception ex) {
                                        Log.W(TAG, "Error expanding attachments!", ex);
                                        RevisionFailed();
                                        continue;
                                    }

                                    properties = populatedRev.GetProperties();
                                    if (!_dontSendMultipart && UploadMultipartRevision(populatedRev)) {
                                        continue;
                                    }
                                }

                                if (properties == null || !properties.ContainsKey("_id")) {
                                    throw new InvalidOperationException("properties must contain a document _id");
                                }

                                // Add the _revisions list:
                                revsToSend.Add(rev);

                                //now add it to the docs to send
                                docsToSend.Add (properties);
                            }

                            UploadBulkDocs(docsToSend, revsToSend);
                        } else {
                            foreach (var revisionInternal in inbox) {
                                RemovePending(revisionInternal);
                            }

                            SafeAddToCompletedChangesCount(inbox.Count);
                        }
                    }
                } catch (Exception ex) {
                    Log.E(TAG, "Unhandled exception in Pusher.ProcessInbox", ex);
                }
            });
        }
        private void UploadChanges(IList<RevisionInternal> changes, IDictionary<string, object> revsDiffResults)
        {

            // Go through the list of local changes again, selecting the ones the destination server
            // said were missing and mapping them to a JSON dictionary in the form _bulk_docs wants:
            var docsToSend = new List<object> ();
            var revsToSend = new RevisionList();
            IDictionary<string, object> revResults = null;
            foreach (var rev in changes) {
                // Is this revision in the server's 'missing' list?
                if (revsDiffResults != null) {
                    revResults = revsDiffResults.Get(rev.DocID).AsDictionary<string, object>(); 
                    if (revResults == null) {
                        continue;
                    }

                    var revs = revResults.Get("missing").AsList<string>();
                    if (revs == null || !revs.Any(id => id.Equals(rev.RevID.ToString()))) {
                        RemovePending(rev);
                        continue;
                    }
                }

                IDictionary<string, object> properties = null;
                RevisionInternal loadedRev;
                try {
                    loadedRev = LocalDatabase.LoadRevisionBody (rev);
                    if(loadedRev == null) {
                        throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.NotFound, TAG,
                            "Unable to load revision body");
                    }

                    properties = new Dictionary<string, object>(rev.GetProperties());
                } catch (Exception e1) {
                    Log.To.Sync.E(TAG, String.Format("Couldn't get local contents of {0}, marking revision failed",
                        rev), e1);
                    RevisionFailed();
                    continue;
                }

                if (properties.GetCast<bool> ("_removed")) {
                    RemovePending (rev);
                    continue;
                }

                var populatedRev = TransformRevision(loadedRev);
                var backTo = revResults?.Get("possible_ancestors")?.AsList<RevisionID>();

                try {
                    var history = LocalDatabase.GetRevisionHistory(populatedRev, backTo);
                    if(history == null) {
                        throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.DbError, TAG,
                            "Unable to load revision history");
                    }

                    properties["_revisions"] = TreeRevisionID.MakeRevisionHistoryDict(history);
                    populatedRev.SetPropertyForKey("_revisions", properties["_revisions"]);
                } catch(Exception e1) {
                    Log.To.Sync.E(TAG, "Error getting revision history, marking revision failed", e1);
                    RevisionFailed();
                    continue;
                }

                // Strip any attachments already known to the target db:
                if (properties.Get("_attachments") != null) {
                    // Look for the latest common ancestor and stuf out older attachments:
                    var minRevPos = FindCommonAncestor(populatedRev, backTo);
                    try {
                        LocalDatabase.ExpandAttachments(populatedRev, minRevPos + 1, !_dontSendMultipart, false);
                    } catch(Exception ex) {
                        Log.To.Sync.E(TAG, "Error expanding attachments, marking revision failed", ex);
                        RevisionFailed();
                        continue;
                    }

                    properties = populatedRev.GetProperties();
                    if (!_dontSendMultipart && UploadMultipartRevision(populatedRev)) {
                        continue;
                    }
                }

                if (properties == null || !properties.ContainsKey("_id")) {
                    throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.BadParam, TAG,
                        "properties must contain a document _id");
                }

                // Add the _revisions list:
                revsToSend.Add(rev);

                //now add it to the docs to send
                docsToSend.Add (properties);
            }

            UploadBulkDocs(docsToSend, revsToSend);
        }