dotnet / orleans

Cloud Native application framework for .NET
https://docs.microsoft.com/dotnet/orleans
MIT License
10.04k stars 2.02k forks source link

Client stream subscription failing to deserialise model #3260

Closed chris-eaton closed 6 years ago

chris-eaton commented 7 years ago

Note

This may just be anecdotal, but I'm fairly sure this used to work [although I had only got it working in test / POC setups] and I feel like this only happened after I switched from using GrainClient to ClientBuilder...

Problem Description

I have a Silo and a Client that I am connecting to the Silo. The client is very lightweight and simply connects to the Silo and then subscribes to a stream like so:

private async Task Subscribe()
{
    var streamProvider = this.grainClient.GetStreamProvider("SMSProvider");
    var stream = streamProvider.GetStream<Plot>(15304.ToGuid(), "PlotDataStream");

    this.subscriptionHandle = await stream.SubscribeAsync(this.OnData);
}

private async Task OnData(Plot plot, StreamSequenceToken data)
{
    Console.WriteLine($"New Plot {plot?.Imei}");
}

When data is sent to the stream I get this error message

StreamClientTest.exe Error: 0 : [2017-07-27 13:50:43.004 GMT 5 ERROR 101030 Message 192.168.0.93:0] !!!!!!!!!! Exception deserializing message body Exc level 0: System.TypeAccessException: Named type "xxx.Common.Models.Plot" is invalid: Type string "xxx.Common.Models.Plot" cannot be resolved. at Orleans.Serialization.BinaryTokenStreamReader.ReadSpecifiedTypeHeader(SerializationManager serializationManager) at Orleans.Serialization.SerializationManager.DeserializeInner(Type expected, IDeserializationContext context) at Orleans.Serialization.BuiltInTypes.DeserializeImmutable[T](Type expected, IDeserializationContext context) at Orleans.Serialization.SerializationManager.DeserializeInner(Type expected, IDeserializationContext context) at Orleans.Serialization.BuiltInTypes.DeserializeInvokeMethodRequest(Type expected, IDeserializationContext context) at Orleans.Serialization.SerializationManager.DeserializeInner(Type expected, IDeserializationContext context) at Orleans.Serialization.SerializationManager.Deserialize(Type t, BinaryTokenStreamReader stream) at Orleans.Runtime.Message.DeserializeBody(SerializationManager serializationManager, List`1 bytes)

All of the properties on Plot are primitive except for DateTime. When this didn't work I tried another simpler model User but this also failed in the same way.

Interestingly if I subscribe to this stream from another grain within the Silo it works perfectly. So it seems to only be an issue to a client application connecting in.

If it helps here is the package.config for the test stream client

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="AutoMapper" version="5.1.1" targetFramework="net461" />
  <package id="CryptSharpOfficial" version="2.1.0.0" targetFramework="net461" />
  <package id="Desktop.Analyzers" version="1.1.0" targetFramework="net461" />
  <package id="EntityFramework" version="6.1.3" targetFramework="net461" />
  <package id="esendex-dotnet-sdk" version="3.2.0.0" targetFramework="net461" />
  <package id="Microsoft.AnalyzerPowerPack" version="1.0.1" targetFramework="net461" />
  <package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net461" />
  <package id="Microsoft.CodeAnalysis.Common" version="2.0.0" targetFramework="net461" />
  <package id="Microsoft.CodeAnalysis.CSharp" version="2.0.0" targetFramework="net461" />
  <package id="Microsoft.CodeAnalysis.FxCopAnalyzers" version="1.1.0" targetFramework="net461" />
  <package id="Microsoft.Extensions.DependencyInjection" version="1.1.1" targetFramework="net461" />
  <package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.1" targetFramework="net461" />
  <package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net461" />
  <package id="Microsoft.Orleans.Client" version="1.5.0" targetFramework="net461" />
  <package id="Microsoft.Orleans.Core" version="1.5.0" targetFramework="net461" />
  <package id="Microsoft.Orleans.OrleansCodeGenerator" version="1.5.0" targetFramework="net461" />
  <package id="Microsoft.Orleans.OrleansProviders" version="1.5.0" targetFramework="net461" />
  <package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net461" />
  <package id="NETStandard.Library" version="1.6.1" targetFramework="net461" />
  <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
  <package id="RestSharp" version="105.2.3" targetFramework="net461" />
  <package id="System.AppContext" version="4.3.0" targetFramework="net461" />
  <package id="System.Collections" version="4.3.0" targetFramework="net461" />
  <package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net461" />
  <package id="System.Collections.Immutable" version="1.3.1" targetFramework="net461" />
  <package id="System.ComponentModel" version="4.3.0" targetFramework="net461" />
  <package id="System.Console" version="4.3.0" targetFramework="net461" />
  <package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" />
  <package id="System.Diagnostics.DiagnosticSource" version="4.3.1" targetFramework="net461" />
  <package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net461" />
  <package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net461" />
  <package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net461" />
  <package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net461" />
  <package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net461" />
  <package id="System.Globalization" version="4.3.0" targetFramework="net461" />
  <package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net461" />
  <package id="System.IO" version="4.3.0" targetFramework="net461" />
  <package id="System.IO.Compression" version="4.3.0" targetFramework="net461" />
  <package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net461" />
  <package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" />
  <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" />
  <package id="System.Linq" version="4.3.0" targetFramework="net461" />
  <package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" />
  <package id="System.Net.Http" version="4.3.2" targetFramework="net461" />
  <package id="System.Net.Primitives" version="4.3.0" targetFramework="net461" />
  <package id="System.Net.Sockets" version="4.3.0" targetFramework="net461" />
  <package id="System.ObjectModel" version="4.3.0" targetFramework="net461" />
  <package id="System.Reflection" version="4.3.0" targetFramework="net461" />
  <package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net461" />
  <package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net461" />
  <package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net461" />
  <package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net461" />
  <package id="System.Runtime" version="4.3.0" targetFramework="net461" />
  <package id="System.Runtime.Analyzers" version="1.0.1" targetFramework="net461" />
  <package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" />
  <package id="System.Runtime.Handles" version="4.3.0" targetFramework="net461" />
  <package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net461" />
  <package id="System.Runtime.InteropServices.Analyzers" version="1.0.1" targetFramework="net461" />
  <package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" />
  <package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.Hashing.Algorithms.Analyzers" version="1.1.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
  <package id="System.Text.Encoding" version="4.3.0" targetFramework="net461" />
  <package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net461" />
  <package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net461" />
  <package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net461" />
  <package id="System.Threading" version="4.3.0" targetFramework="net461" />
  <package id="System.Threading.Tasks" version="4.3.0" targetFramework="net461" />
  <package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net461" />
  <package id="System.Threading.Thread" version="4.3.0" targetFramework="net461" />
  <package id="System.Threading.Timer" version="4.3.0" targetFramework="net461" />
  <package id="System.ValueTuple" version="4.3.0" targetFramework="net461" />
  <package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net461" />
  <package id="System.Xml.XDocument" version="4.3.0" targetFramework="net461" />
  <package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net461" />
  <package id="System.Xml.XPath" version="4.3.0" targetFramework="net461" />
  <package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net461" />
  <package id="Topshelf" version="4.0.3" targetFramework="net452" />
  <package id="xxx.Common" version="1.17.208.2" targetFramework="net461" />
  <package id="xxx.Data" version="1.17.208.2" targetFramework="net461" />
</packages>
ReubenBond commented 7 years ago

@chris-eaton what happens if you force the Common asm to be loaded at the entry point of the program by calling something like Console.WriteLine(typeof(Plot))?

chris-eaton commented 7 years ago

Hey @ReubenBond thats a good idea, and thanks for the help, but unfortunately it doesnt work.

Interestingly, I restarted it and out of nowhere got this error message in another client [one that sends Plots to Orleans]. Note that this client has worked without fail [and is working in production under the same code] for some time. Not that simply cleaning and building made this specific error go away but the streaming still fails to work

var grain = this.grainClient.GetGrain<IUnitGrain>(plot.Imei);
await grain.Process(plot.AsImmutable());

Orleans.Runtime.OrleansMessageRejectionException: 'Unexpected error serializing message to gateway gwy.tcp://127.0.0.1:40000/0. System.ArgumentException: No serializer found for object of type xxx.Common.Models.Plot. Perhaps you need to mark it [Serializable] or define a custom serializer for it?

at Orleans.Serialization.SerializationManager.SerializeInner(Object obj, ISerializationContext context, Type expected) at Orleans.Serialization.BuiltInTypes.SerializeImmutable[T](Object original, ISerializationContext context, Type expected) at Orleans.Serialization.BuiltInTypes.SerializeGenericImmutable(Object original, ISerializationContext context, Type expected) at Orleans.Serialization.SerializationManager.SerializeInner(Object obj, ISerializationContext context, Type expected) at Orleans.Serialization.BuiltInTypes.SerializeInvokeMethodRequest(Object obj, ISerializationContext context, Type expected) at Orleans.Serialization.SerializationManager.SerializeInner(Object obj, ISerializationContext context, Type expected) at Orleans.Serialization.SerializationManager.Serialize(Object raw, BinaryTokenStreamWriter stream) at Orleans.Runtime.Message.Serialize(SerializationManager serializationManager, Int32& headerLengthOut, Int32& bodyLengthOut) at Orleans.Messaging.OutgoingMessageSender.Process(Message msg)'

chris-eaton commented 7 years ago

Just to test, I changed the stream to accept a string and then manually serialize and deserialize the Plot myself into JSON [using NewtonSoft] and this instantly works and decodes correctly after reading the stream.

await this.plotPointDataStream.OnNextAsync(Newtonsoft.Json.JsonConvert.SerializeObject(plot));

Here is the encoded json:

"{ \"Id\": 2256638137, \"UtcHost\": \"2017-08-02T08: 59: 44.4755029Z\", \"Utc\": \"2017-08-02T08: 59: 42Z\", \"UtcQueueReader\": \"2017-08-02T08: 59: 44.6604392Z\", \"Odometer\": 619401521, \"Velocity\": 77, \"Satfix\": 1, \"Sats\": 8, \"Rssi\": 1000, \"Reason\": 1, \"Direction\": 323, \"PlaceName\": \"M1, Northampton NN6 7UH, UK\", \"District\": null, \"RoadName\": null, \"PostSector\": null, \"GeofenceName\": null, \"VehicleId\": 18387, \"DriverId\": null, \"Imei\": \"355060010829732\", \"ReasonTextLegacy\": null, \"MdtMessageLegacy\": null, \"LocationTextLegacy\": null, \"DirectionTextLegacy\": null, \"MaxVelocityInPeriod\": null, \"BatteryLevel\": null, \"VehicleBatteryLevel\": null, \"MessagingDeviceConnected\": false, \"PlotValidity\": 1, \"GeocodedAddressId\": 74576, \"IsIgnitionOn\": true, \"DriverLogonInfo\": null, \"GeocodedAddress\": { \"Id\": 74576, \"Center\": { \"Latitude\": 52.3188732, \"Longitude\": -1.1344806 }, \"Bounds\": [ { \"Latitude\": 52.3220709, \"Longitude\": -1.1306657 }, { \"Latitude\": 52.3220709, \"Longitude\": -1.1381608 }, { \"Latitude\": 52.3157345, \"Longitude\": -1.1381608 }, { \"Latitude\": 52.3157345, \"Longitude\": -1.1306657 }, { \"Latitude\": 52.3220709, \"Longitude\": -1.1306657 } ], \"North\": 52.3220709, \"East\": -1.1306657, \"South\": 52.3157345, \"West\": -1.1381608, \"PlaceId\": \"ChIJSzUW4QUVd0gRyWF7SXwetCo\", \"UtcUpdated\": \"2017-07-25T13: 42: 43.897Z\", \"FormattedAddress\": \"M1, Northampton NN6 7UH, UK\", \"PostCode\": \"NN6 7UH\", \"BoundsSource\": 1 }, \"CanParams\": null, \"Temperatures\": null, \"GarminMessage\": null, \"GarminJob\": null, \"JobEta\": null, \"Telematics\": null, \"Latitude\": 52.317308333333337, \"Longitude\": -1.1323722222222223 }"

ReubenBond commented 7 years ago

@chris-eaton is the type marked as [Serializable]? What happens if you enable build-time codegen by installing the Microsoft.Orleans.OrleansCodeGenerator.Build nuget package into the assembly containing that type?

chris-eaton commented 7 years ago

@ReubenBond No, but its able to be passed around grains easily enough and has been for months.

The type is actually contained in another nuget package [that we author] that I'd ideally like to keep free of any Orleans code. Unless you mean installing that package into my Orleans project? If so should it go into the project containing the grains or all projects that talk to Orleans?

ReubenBond commented 7 years ago

@chris-eaton no worries. In your Orleans project (which is loaded by the client), try adding this [assembly: KnownAssembly(typeof(Plot))] and installing the aforementioned nuget package.

chris-eaton commented 7 years ago

@ReubenBond

I've put [assembly: KnownAssembly(typeof(Plot))] into

I've also installed Microsoft.Orleans.OrleansCodeGenerator.Build into all projects. Note I also have Microsoft.Orleans.OrleansCodeGenerator installed

I still get no Plots sent via streaming and the same exception thrown in logs

System.TypeAccessException: Named type "xxx.Common.Models.Plot

ReubenBond commented 7 years ago

@chris-eaton is there a time we can skype/IM to solve this? One issue is that the type is not having a serializer generated for it because it's not marked as [Serializable]. You can change that behavior by adding an assembly-level attribute: [assembly: GenerateSerializer(typeof(Plot))].

I'm still unsure about the TypeAccessException, it seems that the assembly containing that type isn't being processed.

chris-eaton commented 7 years ago

@ReubenBond Hey Reuben, sorry I hadn't realised you'd written this. I'm not working on that project right now but as soon as I am I'll give you a shout if the offer is still available. Thank you!

ReubenBond commented 6 years ago

No worries, @chris-eaton :) Should we close this for now until you get a chance to revisit it?