-
Type: Bug
-
Resolution: Cannot Reproduce
-
Priority: Major - P3
-
None
-
Affects Version/s: 3.10.2
-
Component/s: POJO
-
None
Problem
During a multi-threaded bulk write a NPE thrown due to null cachedCodec being set on the PropertyModel :-
Caused by: java.lang.NullPointerException at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91) at org.bson.codecs.pojo.PojoCodecImpl.encodeValue(PojoCodecImpl.java:180) at org.bson.codecs.pojo.PojoCodecImpl.encodeProperty(PojoCodecImpl.java:168) at org.bson.codecs.pojo.PojoCodecImpl.encode(PojoCodecImpl.java:105) at org.bson.codecs.pojo.LazyPojoCodec.encode(LazyPojoCodec.java:47) at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91) at org.bson.codecs.pojo.PojoCodecImpl.encodeValue(PojoCodecImpl.java:180) at org.bson.codecs.pojo.PojoCodecImpl.encodeProperty(PojoCodecImpl.java:168) at org.bson.codecs.pojo.PojoCodecImpl.encode(PojoCodecImpl.java:105) at org.bson.codecs.pojo.LazyPojoCodec.encode(LazyPojoCodec.java:47) at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91) at org.bson.codecs.pojo.PojoCodecImpl.encodeValue(PojoCodecImpl.java:180)
Cause
In PojoCodecImpl a race condition may occur in the specializedPojoCodec method between the containsKey and get call against the ConcurrentHashMap :-
ClassModel<S> specialized = getSpecializedClassModel(pojoCodec.getClassModel(), propertyModel); if (codecCache.containsKey(specialized)) { codec = (Codec<S>) codecCache.get(specialized); } else { codec = new LazyPojoCodec<S>(specialized, registry, propertyCodecRegistry, discriminatorLookup, codecCache); } } return codec; }
In this case the specialized keys ClassModel hashCode changes between the two calls resulting in a cache miss and the codec getting set to null
Solution
Replace the containsKey and get calls with a thread safe call to computeIfAbsent :-
ClassModel<S> specialized = getSpecializedClassModel(pojoCodec.getClassModel(), propertyModel);
codec = (Codec<S>) codecCache.computeIfAbsent( specialized, x-> new LazyPojoCodec<>((ClassModel<S>) x, registry, propertyCodecRegistry, discriminatorLookup, codecCache));
Further Suggestions
It would suggest the change to hashCode indicates ClassModel is not immutable. Its collections are directly exposed via getters and can be changed after construction. Can a copy of the collections be returned by the getters instead.
- duplicates
-
JAVA-3456 ClassModel needs to be immutable
- Closed