Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protobuf.net Exception - Timeout while inspecting metadata

I am sometimes receiving the following exception when attempting to deserialise an object using protobuf.net. I'm surprised as I never have more than a single thread deserialising the same object at the same time and the protobuf.net source does not seem to use any static objects for deserialising. The exception does suggest a solution but I am unsure as to how to implement so would welcome an example.

Base Exception Type:
System.TimeoutException: Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection
at ProtoBuf.Meta.RuntimeTypeModel.TakeLock(Boolean& lockTaken)
at ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(Type type, Boolean demand, Boolean addWithContractOnly, Boolean addEvenIfAutoDisabled)
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)

Inner Exception Type:
System.TimeoutException: Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection
at ProtoBuf.Meta.RuntimeTypeModel.TakeLock(Boolean& lockTaken)
at ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(Type type, Boolean demand, Boolean addWithContractOnly, Boolean addEvenIfAutoDisabled)
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)

Stack Trace: 
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)
at ProtoBuf.Meta.TypeModel.GetKey(Type& type)
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type)

Regards, Marc

edit to add: I define my serialisable objects thus:

[ProtoContract]
public class Job
{
    [ProtoMember(1)]
    public long JobId { get; private set; } 
}

It would be difficult for me to easily call PrepareSerialiser on each one of my serialisable objects as I have many in different namespaces. However thinking about it what happens if protobuf is asked to deserialise two objects of the same type, a type it has not seen before, at exactly the same time?

like image 855
MarcF Avatar asked Sep 10 '11 15:09

MarcF


2 Answers

Old question, but if somebody happens to get this error, please check the DLL version you are using. Chances for this exception to show up in portable version are very high.

There are a couple of PRs related to this issue with the Portable version that are https://github.com/mgravell/protobuf-net/pull/98 and https://github.com/mgravell/protobuf-net/pull/114.

like image 110
Krzysztof Cygan Avatar answered Oct 21 '22 01:10

Krzysztof Cygan


RuntimeTypeModel.Default (the default model) is static, and underpins the static Serializer class (re the comment that there wasn't any static state). Despite adding this check due to paranoia, I have never seen this error raised. I would absolutely love to see an example that will repro this. Are you sure you aren't threading? If not for threading, I can only wonder: is the type-model really really big?

Indeed, even with many threads hitting it aggressively at startup (i.e. here on stackoverflow) it is well behaved. As the error message hints, you might try calling Serializer.PrepareSerializer during app-startup, which will pre-initialize everything, avoiding any threading concerns.

But hey! At least it didn't deadlock!

The odd thing here, though, is that it still shouldn't be possible to deadlock - it deliberately uses a coarse lock to avoid issues from the order it takes lock. Again - I'd really love to see a sample.

like image 43
Marc Gravell Avatar answered Oct 21 '22 00:10

Marc Gravell