none
Is there a design issue in Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges?

    问题

  • We are testing the Sync Framework with multiple clients on different networks (some mobile "unreliable" ones too).

    Our setup is with batching enabled with 100KB approximate batch size.

    If for some reason the initial synchronization is interrupted after the first batch is processed on the client and the client resumes the synchronization using the last saved anchor sent by the server, on the server side there seems a to be a logic error, which results in an invalid deserialization attempt, which of course throws an exception:

    Here is the sequence of the calls for when the client retries the sync process:

     

    Microsoft.Synchronization.Services.SyncService<T>.ProcessRequestForMessage(Stream messageBody)

    ...

                    _outgoingMessage = _requestProcessor.ProcessRequest(_requestDescription);

    ...

     

    Microsoft.Synchronization.Services.DownloadChangesRequestProcessor.ProcessRequest(Request incomingRequest)

    ...

                _getChangesResponse = providerService.GetChanges(incomingRequest.SyncBlob);

    ...

     

     

    Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges(byte[] serverBlob)

    ...

                        // This is a batched request, so handle it separately.

                        return GetChanges(incomingBlob.ClientKnowledge, incomingBlob.BatchCode.Value, incomingBlob.NextBatch.Value);

                        // Patch proposal: if it returns null then let treat it as if the BatchCode or NextBatch is null ... so get all the changes ...

                        // var batchResponse = GetChanges(incomingBlob.ClientKnowledge, incomingBlob.BatchCode.Value, incomingBlob.NextBatch.Value);

                        // if (null != batchResponse) return batchResponse;

    ...

     

     

    // Here the call to the GetNextBatch will return null because the batch file was already deleted (or not saved if it was the first one)!!!

    // So it calls again the GetChanges with the "serverBlob" which in this case is the actual "incomingBlob.ClientKnowledge" !!!

    Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges(byte[] serverBlob, Guid batchCode, Guid nextBatchSequenceNumber)

    ...

                // Get the next batch using the batch handler implementation.

                Batch batch = _batchHandler.GetNextBatch(batchCode, nextBatchSequenceNumber);

     

                if (null == batch)

                {

                    // Since we did'nt get a batch, default to the full get changes call.

                    return GetChanges(serverBlob);

                    // Patch proposal:

                    // return null;

                }

    ...

     

     

    Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges(byte[] serverBlob)

    ...

                if (null == serverBlob || 0 == serverBlob.Length)

                {

    ...

                }

                else

                {

                    SyncBlob incomingBlob = SyncBlob.DeSerialize(serverBlob); <===== THROWS an EXCEPTION !!! Because here the serverBlob is actually a serialized SyncKnowledge and NOT a SyncBlob !!!

    // Exception message:

    // The input stream is not a valid binary format. The starting contents (in bytes) are: 00-00-00-05-00-00-00-00-00-00-00-01-00-00-00-00-00 ...

    // Stack trace:

       at System.Runtime.Serialization.Formatters.Binary.SerializationHeaderRecord.Read(__BinaryParser input)

       at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadSerializationHeaderRecord()

       at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()

       at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)

       at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)

       at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)

       at Microsoft.Synchronization.Services.SyncBlob.DeSerialize(Byte[] syncBlob) in E:\Install\Microsoft\SyncFramework\Microsoft Sync Framework Toolkit\C#\SyncServiceLib\SyncBlob.cs:line 65

       at Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges(Byte[] serverBlob) in E:\Install\Microsoft\SyncFramework\Microsoft Sync Framework Toolkit\C#\SyncServiceLib\SqlProvider\SqlSyncProviderService.cs:line 300

       at Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges(Byte[] serverBlob, Guid batchCode, Guid nextBatchSequenceNumber) in E:\Install\Microsoft\SyncFramework\Microsoft Sync Framework Toolkit\C#\SyncServiceLib\SqlProvider\SqlSyncProviderService.cs:line 235

       at Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges(Byte[] serverBlob) in E:\Install\Microsoft\SyncFramework\Microsoft Sync Framework Toolkit\C#\SyncServiceLib\SqlProvider\SqlSyncProviderService.cs:line 311

       at Microsoft.Synchronization.Services.DownloadChangesRequestProcessor.ProcessRequest(Request incomingRequest) in E:\Install\Microsoft\SyncFramework\Microsoft Sync Framework Toolkit\C#\SyncServiceLib\RequestProcessor\DownloadChangesRequestProcessor.cs:line 112

       at Microsoft.Synchronization.Services.SyncService`1.ProcessRequestForMessage(Stream messageBody) in E:\Install\Microsoft\SyncFramework\Microsoft Sync Framework Toolkit\C#\SyncServiceLib\SyncService.cs:line 154

    ...

     

    So my question is if this is a design issue or not and what would be the best way of handling it? I proposed a patch but I don't know if there are other implications to it or not.

    Regards,
    Robert


    RobertG
    2012年2月6日 14:23

全部回复

  • I have come across the exact same scenario syncing a large dataset over a dodgy connection with batching enabled. Occasionally, the sync would continue after the connection dropped but most of the time this exception was raised and the only way to continue was to resync the entire data. Not ideal...

    I have tried your patch and it appears to work, it will create the batch files again rather than picking up from where it left off with those already generated. However, it does generate them correctly based upon the client knowledge that was passed in.

    I believe this is a bug in the framework, so thanks for the workaround!

     
    2012年4月13日 14:36