IChangeFeedObserver ProcessChangesAsync之后的奇怪异常

时间:2017-09-21 13:52:17

标签: azure-cosmosdb

我正在尝试从CosmosDB创建到本地CouchBase Lite数据库的复制。为此,我使用了Microsoft.Azure.Documents.ChangeFeedProcessor

但由于某种原因,总是在IChangeFeedObserver ProcessChangesAsync结束后我在控制台上得到以下异常:

"错误":"未满足指定的前提条件之一"]是什么意思?复制以其他方式成功结束,但这让我感到困扰。

使用system.diagnostics

更新了

ChangeFeedEventHost Information: 53 : 09/25/2017 08:27:54: Host 'TESTIkiosk' starting to check for available leases.
ChangeFeedEventHost Information: 54 : 09/25/2017 08:27:54: Host 'TESTIkiosk' starting to check for available leases.
ChangeFeedEventHost Information: 55 : 09/25/2017 08:27:54: Host 'TESTIkiosk' 1 partitions, 1 hosts, 0 available leases, target = 1, min = 0, max = 0, mine = 1, will try to take 0 lease(s) for myself'.
ChangeFeedEventHost Information: 56 : 09/25/2017 08:27:54: Host 'TESTIkiosk' 1 partitions, 1 hosts, 0 available leases, target = 1, min = 0, max = 0, mine = 1, will try to take 0 lease(s) for myself'.
ChangeFeedEventHost Information: 57 : 09/25/2017 08:28:06: Host 'TESTIkiosk' starting renewal of Leases.
ChangeFeedEventHost Information: 58 : 09/25/2017 08:28:06: Host 'TESTIkiosk' renewing lease for PartitionId '0' with lease token '"0000fa04-0000-0000-0000-59c893d40000"'
ChangeFeedEventHost Information: 59 : 09/25/2017 08:28:06: Host 'TESTIkiosk' starting renewal of Leases.
ChangeFeedEventHost Information: 60 : 09/25/2017 08:28:06: Host 'TESTIkiosk' renewing lease for PartitionId '0' with lease token '"0000fb04-0000-0000-0000-59c893d40000"'
ChangeFeedEventHost Information: 61 : 09/25/2017 08:28:06: Host 'TESTIkiosk' attempted to renew lease for PartitionId '0' and lease token '"0000fa04-0000-0000-0000-59c893d40000"' with result: 'True'
ChangeFeedEventHost Information: 62 : 09/25/2017 08:28:06: Host 'TESTIkiosk' attempted to renew lease for PartitionId '0' and lease token '"0000fb04-0000-0000-0000-59c893d40000"' with result: 'True'
ChangeFeedEventHost Information: 63 : 09/25/2017 08:28:07: Host 'TESTIkiosk' starting to check for available leases.
ChangeFeedEventHost Information: 64 : 09/25/2017 08:28:07: Host 'TESTIkiosk' starting to check for available leases.
ChangeFeedEventHost Information: 65 : 09/25/2017 08:28:07: Host 'TESTIkiosk' 1 partitions, 1 hosts, 0 available leases, target = 1, min = 0, max = 0, mine = 1, will try to take 0 lease(s) for myself'.
ChangeFeedEventHost Information: 66 : 09/25/2017 08:28:07: Host 'TESTIkiosk' 1 partitions, 1 hosts, 0 available leases, target = 1, min = 0, max = 0, mine = 1, will try to take 0 lease(s) for myself'.
[08:28:15 INF] Change feed in Ingredients: total 1 doc(s)
[08:28:15 INF] Replicated 2614
ChangeFeedEventHost Information: 67 : 09/25/2017 08:28:15: Partition '0' update failed because the lease with token '"0000e804-0000-0000-0000-59c893b20000"' was updated by same/this host with token '"00000005-0000-0000-0000-59c893e50000"'. Will retry, 4 retry(s) left.
ChangeFeedEventHost Information: 68 : 09/25/2017 08:28:15: Checkpoint: partition 0, new continuation '"81"'


DocDBTrace Error: 0 : DocumentClientException with status code PreconditionFailed, message: Message: {"Errors":["One of the specified pre-condition is not met"]}, inner exception: null, and response headers: {
"x-ms-last-state-change-utc": "Sun, 24 Sep 2017 21:03:48.392 GMT",
"lsn": "954",
"x-ms-schemaversion": "1.3",
"x-ms-quorum-acked-lsn": "954",
"x-ms-current-write-quorum": "3",
"x-ms-current-replica-set-size": "4",
"x-ms-documentdb-partitionkeyrangeid": "0",
"x-ms-xp-role": "1",
"x-ms-request-charge": "1.24",
"x-ms-serviceversion": " version=1.17.52.1",
"x-ms-activity-id": "b9877abb-6203-4408-b1c3-92c9f52aad67",
}
DocDBTrace Error: 0 : Operation will NOT be retried. Current attempt 0, Exception: Microsoft.Azure.Documents.PreconditionFailedException: Message: {"Errors":["One of the specified pre-condition is not met"]}
ActivityId: b9877abb-6203-4408-b1c3-92c9f52aad67, Request URI: /apps/c9c8f510-0ca7-4702-aa6c-9c596d797367/services/75cca430-a307-47a0-89aa-82aabf32e065/partitions/53fb63ca-95ae-4d13-90be-d32f1a88843c/replicas/131504697049132845p/
   at Microsoft.Azure.Documents.TransportClient.ThrowIfFailed(String resourceAddress, StoreResponse storeResponse, Uri physicalAddress, Guid activityId)
   at Microsoft.Azure.Documents.RntbdTransportClient.<InvokeStoreAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.ReplicatedResourceClient.<WriteAsync>d__1b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.ReplicatedResourceClient.<InvokeAsync>d__b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.ReplicatedResourceClient.<>c__DisplayClass1.<<InvokeAsync>b__0>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<>c__DisplayClassf`1.<<ExecuteAsync>b__d>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<ExecuteRetry>d__1b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<ExecuteRetry>d__1b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<ExecuteAsync>d__18`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.ReplicatedResourceClient.<InvokeAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.StoreClient.<ProcessMessageAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.Client.DocumentClient.<UpdateAsync>d__305.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.Client.DocumentClient.<ReplaceDocumentPrivateAsync>d__13d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.Client.DocumentClient.<ReplaceDocumentPrivateAsync>d__135.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<>c__DisplayClass2.<<ExecuteAsync>b__0>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<ExecuteRetry>d__1b.MoveNext() 

我的实施准则:

    using Microsoft.Azure.Documents.ChangeFeedProcessor;
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    namespace Solteq.Pos.Repositories.Feeds
    {
        internal class MyHostFeed : IChangeFeedObserver
        {
            private int s_totalDocs = 0;
            private Func<dynamic, bool> replicate;
            private string name;

            public Func<dynamic, bool> Replicate { get => replicate; set => replicate = value; }
            public string Name { get => name; set => name = value; }

            public Task OpenAsync(ChangeFeedObserverContext context)
            {
                Console.WriteLine("Worker opened for {0}, {1}", name, context.PartitionKeyRangeId);
                return Task.CompletedTask;  // Requires targeting .NET 4.6+.
            }
            public Task CloseAsync(ChangeFeedObserverContext context, ChangeFeedObserverCloseReason reason)
            {
                Console.WriteLine("Worker closed for  {0}, {1}", name, context.PartitionKeyRangeId);
                return Task.CompletedTask;
            }

            public Task ProcessChangesAsync(ChangeFeedObserverContext context, IReadOnlyList<Microsoft.Azure.Documents.Document> docs)
            {
                Console.WriteLine("Change feed in {0}: total {1} doc(s)", name, Interlocked.Add(ref s_totalDocs, docs.Count));
                foreach (var one in docs)
                {
                    Console.WriteLine("Replicated: " + one.Id);
                    Replicate(one);
                }
                return Task.CompletedTask;
            }
        }
    }

Observer的初始化:

        public async void StartReplication(string replicateFrom, string hostName, Func<dynamic, bool> replicate)
        {

            // Customizable change feed option and host options 
            ChangeFeedOptions feedOptions = new ChangeFeedOptions();

            // ie customize StartFromBeginning so change feed reads from beginning
            // can customize MaxItemCount, PartitonKeyRangeId, RequestContinuation, SessionToken and StartFromBeginning
            // feedOptions.StartFromBeginning = true;

            ChangeFeedHostOptions feedHostOptions = new ChangeFeedHostOptions();

            // ie. customizing lease renewal interval to 15 seconds
            // can customize LeaseRenewInterval, LeaseAcquireInterval, LeaseExpirationInterval, FeedPollDelay 
            //feedHostOptions.LeaseRenewInterval = TimeSpan.FromSeconds(15);
            feedHostOptions.LeasePrefix = replicateFrom;
            feedHostOptions.FeedPollDelay = TimeSpan.FromSeconds(60);

            try
            {
                DocumentCollectionInfo documentCollectionLocation = new DocumentCollectionInfo
                {
                    Uri = new Uri(_databaseServiceUri),
                    MasterKey = _databaseAuthKey,
                    DatabaseName = _databaseId,
                    CollectionName = replicateFrom
                };
                DocumentCollectionInfo leaseCollectionLocation = new DocumentCollectionInfo
                {
                    Uri = new Uri(_databaseServiceUri),
                    MasterKey = _databaseAuthKey,
                    DatabaseName = _databaseId,
                    CollectionName = "leases"
                };
                DocumentFeedObserverFactory docObserverFactory = new DocumentFeedObserverFactory(replicate, replicateFrom);
                ChangeFeedEventHost host = new ChangeFeedEventHost(hostName, documentCollectionLocation, leaseCollectionLocation, feedOptions, feedHostOptions);

                await host.RegisterObserverFactoryAsync(docObserverFactory);
                hosts.Add(host);
            }
            catch (Exception e)
            {
                Console.WriteLine("Replication process halted for (" + replicateFrom + ") reason: " + e.ToString());
                //throw e;
            }
        }

1 个答案:

答案 0 :(得分:2)

这是跟踪的一部分,而不是错误,它可以在Visual Studio中查看,因为跟踪已启用。

当ProcessChangesAsync完成时,处理器更新租约。但是有一个单独的线程也维护租约更新。很自然,两个线程都有可能尝试编写同一个文档并生成并发异常(前置条件消息),因此处理器会跟踪它并重试并继续前进。

这不是一个意外的错误,它是一个处理和预期的错误,库只是确保跟踪所有情况。