ehcache / ehcache3

Ehcache 3.x line
http://www.ehcache.org
Apache License 2.0
2.02k stars 580 forks source link

ClassCastException #2824

Closed concon121 closed 2 years ago

concon121 commented 4 years ago

I have a strange bug coming stemming from ehcache, and was hoping someone might be able to help explain how it is happening?

Where we have ehcache managing an in-memory cache of our static data set, we very occasionally see errors in the form:

Unable to process request 
java.lang.ClassCastException: cannot assign instance of com.project.Class1 to field com.project.Class2.stringyField of type java.lang.String in instance of com.project.Class2
    at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2287)
    at java.io.ObjectStreamClass.setObjFieldValues(ObjectStreamClass.java:1417)

My observations are that:

  1. Where we cache a static data set, this seems to only effect one (complex) object type.
  2. When this occurs, we have seen multiple different fields be affected by the ClassCastException. Not only a class being mapped to a String as in the example, but classes being mapped to an entirely different class which isn't related in any way!
  3. It is not a consistent problem we can easily reproduce. In a multi-node environment, usually only one node is effected.
  4. When the cached item is deleted and re-created, the problem resolves itself.

To me this seems like an issue de-serializing an object and putting everything back in the right place. How is it possible that this can happen? What other things should I consider as a potential source of this problem?

Happy to provide any extra information you might think help. Thanks!

chrisdennis commented 4 years ago

A couple of things worth trying (if you haven't already):

  1. Try pushing your values through Java serialization outside of the cache. If this doesn't work then the problem is between Java and your value types.
  2. Try configuring the serialization to explicitly use the JavaSerializer. This is the 'simplest' serialization scheme. If this works then there may be a bug in the CompactJavaSerializer.

If you can provide details of your configuration, what your types look like and what version of Ehcache you are using that would be useful too.

concon121 commented 4 years ago

We are using ehcache version 3.8.1, and I probably should have mentioned that we are using the JCache API.

I can see from the stack trace that the PlainJavaSerializer is being used, as opposed to the CompactJavaSerializer, so I suspect point 2 might not be the case?

java.lang.ClassCastException: cannot assign instance of com.project.Class1 to field com.project.Class2.stringyField of type java.lang.String in instance of com.project.Class2
    at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2287)
    at java.io.ObjectStreamClass.setObjFieldValues(ObjectStreamClass.java:1417)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2348)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2266)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2124)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1625)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2342)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2266)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2124)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1625)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:465)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:423)
    at org.ehcache.impl.serialization.PlainJavaSerializer.read(PlainJavaSerializer.java:62)
    at org.ehcache.impl.internal.store.heap.holders.SerializedOnHeapValueHolder.get(SerializedOnHeapValueHolder.java:66)
    at org.ehcache.core.EhcacheBase.get(EhcacheBase.java:135)
    at org.ehcache.jsr107.Eh107Cache.get(Eh107Cache.java:90)
        etc...

I will give point 1 a go today and see what happens, thanks!

chrisdennis commented 4 years ago

The other thing to note is that the JSR-107 specification declares that cache must be 'by-value' by default and not 'by-reference'. This is why you're seeing a serialization pass on read from the cache to avoid by-reference semantics. In almost all cases this isn't what you want and it's one of the many places where I believe the spec is in-error (or at least misguided). You should be able to configure 'storeByReference' in your JCache setup to avoid this. This may side-step the whole serialization issue.

concon121 commented 4 years ago

Thanks @chrisdennis, sounds like store by reference would work for us. Aiming to get this tested by the end of the week, so i'll keep you updated, cheers!

chrisdennis commented 2 years ago

Assume this is all good now.