Is there a design issue in Microsoft.Synchronization.Services.SqlProvider.SqlSyncProviderService.GetChanges?
-
06 Februari 2012 14:23
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
Semua Balasan
-
13 April 2012 14:36
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!