1 2 Previous Next 24 Replies Latest reply: Jun 11, 2012 8:53 AM by Saugat Manandhar RSS

Invalidation not working with LazyDeserialization on non-basic PK (clustered)

Saugat Manandhar Newbie

The test case here is two java processes spawned in a cluster in invalidation mode. Both java process will read the same entity into their cache. One process then reads from the cache in a loop (the Reading JVM) while the other will change the value (the Evicting JVM) some point later. What I am seeing is that the reading JVM will get the invalidation message after the evicting JVM changes the value, however, it will not actually evict the entity.

 

Stepping through the code, it seems like infinispan sees the evicted entity as a serialized object while the one in the Reading JVM as not serialized. When it attempts to do a comparison, it will now serialize the not-serialized key and then compare the byte array, but the byte array does not seem to be equal. So the question here is, why exactly are the byte array different? If I serialize two objects with the same values, would I always expect the same byte array?

 

The PK here is a basic class with just a string as the key. If I force the entity to use a string class as it's id, it will function properly. Here is the class just in case:

 

 

import java.io.Serializable;

public final class BasicPK implements Serializable {

    public BasicPK() {
        _name = "";
    }


    public BasicPK(String name) {
        _name = name;
    }
    @Override
    public boolean equals (Object other) {

        if (this == other) {

            return true;
        }
        if (other == null) {

            return false;
        }
        if (!(other instanceof BasicPK)) {

            return false;
        }
        if ("".equals(_name)) {

            return false;
        }


        final BasicPK otherPK = (BasicPK)other;


        if ("".equals(otherPK._name)) {

            return false;
        }
        boolean result =_name.equals(otherPK._name);
        return result;
    }


    public int hashCode() {


        int hashVal = _name.hashCode();


        return hashVal;
    }


    public String toString() {
        return "name=["+_name+"]";
    }



    //Getter/setters
    public String getName() {
        return _name;
    }
    public void setName(String name) {
        _name = name;
    }


    /**************************************************
     * Implementation
     */


    private static final long serialVersionUID = 1L;


    private String _name = "";


}

 

  • 1. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Galder Zamarreño Master

    Well, that equals() impl doesn't look right. You're assuming that two instances that have name as "" (empty) are not the same...

     

    You might wanna get yourself a modern IDE (i.e. IntelliJ) and let it generate the equals/hashCode impls for you. Alternatively, get yourself a copy of this book

  • 2. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Saugat Manandhar Newbie

    Galder Zamarreño wrote:

     

    Well, that equals() impl doesn't look right. You're assuming that two instances that have name as "" (empty) are not the same...

     

    You might wanna get yourself a modern IDE (i.e. IntelliJ) and let it generate the equals/hashCode impls for you. Alternatively, get yourself a copy of this book

    How would that equality effect serialization, or how infinispan equates two object? I understand that this is not conventional, but it's ok for the Object I am dealing with and what I want to do with it.  The hashcode looks fine to me, I don't see any errors, is there something I am missing that would make the class work?

     

    To emphasize my point, here is the code that should be returning true, but doesn't.

     

    public boolean equals(Object o) {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
    
    
          MarshalledValue that = (MarshalledValue) o;
          final boolean preferInstanceEquality = equalityPreferenceForInstance && that.equalityPreferenceForInstance;
    
    
          // if both versions are serialized or deserialized, just compare the relevant representations,
          // but attempt the operations in order to respect the value of equalityPreferenceForInstance
          Object thisInstance = this.instance;
          Object thatInstance = that.instance;
          //test the default equality first so we might skip some work:
          if (preferInstanceEquality && thisInstance != null && thatInstance != null) return thisInstance.equals(thatInstance);
    
          byte[] thisRaw = this.raw;
          byte[] thatRaw = that.raw;
          if (thisRaw != null && thatRaw != null) return Arrays.equals(thisRaw, thatRaw);
          if (thisInstance != null && thatInstance != null) return thisInstance.equals(thatInstance);
    
    
          // if conversion of one representation to the other is necessary, then see which we prefer converting.
          if (preferInstanceEquality) {
             if (thisInstance == null) {
                thisInstance = this.deserialize();
             }
             if (thatInstance == null) {
                thatInstance = that.deserialize();
             }
             return thisInstance.equals(thatInstance);
          } else {
             if (thisRaw == null) {
                thisRaw = this.serialize();
             }
             if (thatRaw == null) {
                thatRaw = that.serialize();
             }
         //This is the problem point
            return Arrays.equals(thisRaw, thatRaw);
          }
       }
    

     

     

     

  • 3. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Galder Zamarreño Master

    Wel, first of all, that code belongs to storeAsBinary or lazyDeserialization options. Do you really need this? As indicated in the documentation, this functionality is used to solve a particular use case. If you don't need this, disable it.

     

    Well, equality and serialization are important because you need to make sure that when objects are serialized, these will be equals.

     

    If you look at your equals, you have:

     

    if ("".equals(otherPK._name))

    {

    return false;

    }

     

    And

     

    String _name = "";

     

    So, it's pretty clear that two brand new instances of that class (without assignment of name) that are serialized and then deserialized will not be equals.

  • 4. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Saugat Manandhar Newbie

    I don't think you understood my question then. Here is what I am trying to do:

     

     

    public class Test{
         }
        /**
         * @param args
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
            //a server handles all the database transactions
    
    
            //These methods are ran in separate processes but are in the same cluster
            if (args.length == 0)
                 runDefault();
            else
                 runEvict();
    
    
        }
        //Run this concurrently with runDefault to evict that process's cache
        public static void runEvict() throws InterruptedException{
    
             TestDAO dao = new TestDAO();
             BasicPK pk = new BasicPK();
    
             pk.setName("ReadThis");
    
            for (int i = 0; i < 100;++i){
                 BasicClass change=dao.read(pk);
                 System.err.println("iteration : " + i + change.getParam());
                 change.setParam("Changed: " + (new Date()).toString());
                 server.commit();
                 Thread.sleep(200);
            }
    
            System.exit(0);
        }
    
    
        //Should be updating the value when ran concurrently with runEvict
      //All it does is read from the cache in a loop.
        public static void runDefault()  {
            TestDAO dao = new TestDAO();
    
            BasicPK = new BasicPK();
            pk.setName("ReadThis");
    
            dao.read(pk);
            server.commit();
    
    
            long begin = System.currentTimeMillis();
            for (int i = 0; i < 1000; ++i) {
                System.err.println("iteration : " + i);
                BasicClass def = dao.read(pk);
                System.err.println("iteration : " + def.getParam());
                server.commit();
                Thread.sleep(10);
                }
            }
            long total = System.currentTimeMillis() - begin;
    
    
            System.out.println("Took: " + total);
    
    
            System.exit(0);
        }
    }
    

     

     

    The problem, again, is that this works fine without lazyDeserialization or with basic objects like String/Integer with lazyDeserialization. The process that is just reading will evict those entities. However, as soon as I start using composite-pk through hibernate (embedded class if using annotation) with lazyDeserialization, I can't get it to work. I see no points where your comments about the equal methods  becomes relevant because it is not even being called. There is no point in this test where you will see an empty string as a PK (which I also validated with a test run afterwards).

     

    So my question, again, is why does this work without lazyDeserialization but not with?

  • 5. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Galder Zamarreño Master

    At this stage, it's probably best for you to attach something that we can run and have a closer look. Thanks

  • 6. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Saugat Manandhar Newbie

    So, my issue might be related to objects rather than keys. Here is my question:

     

    In the guide it says:

     

     

    Support For Non-Serializable Objects

    From a users perspective, a very common concern is whether Infinispan supports storing non-Serializable objects. In 4.0, an Infinispan cache instance can only store non-Serializable key or value objects if, and only if:

    • cache is configured to be a local cache and...
    • cache is not configured with lazy serialization and...
    • cache is not configured with any write-behind cache store

     

    So, if my objects are not serializable, but they are implemented using Hibernate and put in the 2nd level cache, can I expect it to work properly or not? The reason I am asking is because I don't get a "unmarshallable" error which you would if you didn't have hibernate.

  • 7. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Galder Zamarreño Master

    Hibernate dehydrates the entities to avoid storing them as such and hence requiring them to be serializable, so that's why Hibernate works. Infinispan works as indicated in the guide.

  • 8. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Saugat Manandhar Newbie

    So I have been trying to replicate this issue without using any company code, but I am running into this issue while attaching it through hibernate (4.1.3) and infinispan 5.1.4. If I implement a transaction manager lookup, I get this error:

     

     

    org.hibernate.cache.CacheException: java.lang.IllegalStateException: This is a tx cache!

    at org.hibernate.cache.infinispan.util.CacheAdapterImpl.put(CacheAdapterImpl.java:110)

    at org.hibernate.cache.infinispan.access.TransactionalAccessDelegate.update(TransactionalAccessDelegate.java:138)

    at org.hibernate.cache.infinispan.entity.TransactionalAccess.update(TransactionalAccess.java:64)

    at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:197)

    at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)

    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)

    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:276)

    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)

    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)

    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1214)

    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:403)

    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)

    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)

    at org.testinfinispan.Test.testCache(Test.java:94)

    at org.testinfinispan.Evict.main(Evict.java:12)

    Caused by: java.lang.IllegalStateException: This is a tx cache!

    at org.infinispan.context.TransactionalInvocationContextContainer.createInvocationContext(TransactionalInvocationContextContainer.java:80)

    at org.infinispan.CacheImpl.getInvocationContext(CacheImpl.java:473)

    at org.infinispan.CacheImpl.getInvocationContextWithImplicitTransaction(CacheImpl.java:457)

    at org.infinispan.CacheImpl.put(CacheImpl.java:700)

    at org.infinispan.CacheImpl.put(CacheImpl.java:694)

    at org.infinispan.CacheSupport.put(CacheSupport.java:53)

    at org.infinispan.AbstractDelegatingCache.put(AbstractDelegatingCache.java:308)

    at org.hibernate.cache.infinispan.util.CacheAdapterImpl.put(CacheAdapterImpl.java:108)

    ... 14 more

     

     

    and if I don't, and just setup a transactionmanagerlookup through infinispan.xml then I get this issue:

    Initial SessionFactory creation failed.org.infinispan.CacheException: This is transactional cache but no transaction manager could be found. Configure the transaction manager lookup properly.

    java.lang.ExceptionInInitializerError

              at org.testinfinispan.util.HibernateUtil.buildSessionFactory(HibernateUtil.java:54)

              at org.testinfinispan.util.HibernateUtil.newSessionFactory(HibernateUtil.java:59)

              at org.testinfinispan.util.HibernateUtil.<clinit>(HibernateUtil.java:35)

              at org.testinfinispan.Test.testCache(Test.java:21)

              at org.testinfinispan.Evict.main(Evict.java:13)

    Caused by: org.infinispan.CacheException: This is transactional cache but no transaction manager could be found. Configure the transaction manager lookup properly.

              at org.infinispan.factories.TransactionManagerFactory.construct(TransactionManagerFactory.java:81)

              at org.infinispan.factories.AbstractComponentRegistry.getOrCreateComponent(AbstractComponentRegistry.java:286)

              at org.infinispan.factories.AbstractComponentRegistry.invokeInjectionMethod(AbstractComponentRegistry.java:246)

              at org.infinispan.factories.AbstractComponentRegistry.access$000(AbstractComponentRegistry.java:83)

              at org.infinispan.factories.AbstractComponentRegistry$Component.injectDependencies(AbstractComponentRegistry.java:792)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponentInternal(AbstractComponentRegistry.java:220)

              at org.infinispan.factories.ComponentRegistry.registerComponentInternal(ComponentRegistry.java:154)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponent(AbstractComponentRegistry.java:175)

              at org.infinispan.factories.AbstractComponentRegistry.getOrCreateComponent(AbstractComponentRegistry.java:291)

              at org.infinispan.factories.AbstractComponentRegistry.invokeInjectionMethod(AbstractComponentRegistry.java:246)

              at org.infinispan.factories.AbstractComponentRegistry.access$000(AbstractComponentRegistry.java:83)

              at org.infinispan.factories.AbstractComponentRegistry$Component.injectDependencies(AbstractComponentRegistry.java:792)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponentInternal(AbstractComponentRegistry.java:220)

              at org.infinispan.factories.ComponentRegistry.registerComponentInternal(ComponentRegistry.java:154)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponent(AbstractComponentRegistry.java:175)

              at org.infinispan.factories.AbstractComponentRegistry.getOrCreateComponent(AbstractComponentRegistry.java:291)

              at org.infinispan.factories.AbstractComponentRegistry.invokeInjectionMethod(AbstractComponentRegistry.java:246)

              at org.infinispan.factories.AbstractComponentRegistry.access$000(AbstractComponentRegistry.java:83)

              at org.infinispan.factories.AbstractComponentRegistry$Component.injectDependencies(AbstractComponentRegistry.java:792)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponentInternal(AbstractComponentRegistry.java:220)

              at org.infinispan.factories.ComponentRegistry.registerComponentInternal(ComponentRegistry.java:154)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponent(AbstractComponentRegistry.java:175)

              at org.infinispan.factories.AbstractComponentRegistry.getOrCreateComponent(AbstractComponentRegistry.java:291)

              at org.infinispan.factories.AbstractComponentRegistry.invokeInjectionMethod(AbstractComponentRegistry.java:246)

              at org.infinispan.factories.AbstractComponentRegistry.access$000(AbstractComponentRegistry.java:83)

              at org.infinispan.factories.AbstractComponentRegistry$Component.injectDependencies(AbstractComponentRegistry.java:792)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponentInternal(AbstractComponentRegistry.java:220)

              at org.infinispan.factories.ComponentRegistry.registerComponentInternal(ComponentRegistry.java:154)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponent(AbstractComponentRegistry.java:175)

              at org.infinispan.factories.AbstractComponentRegistry.getOrCreateComponent(AbstractComponentRegistry.java:291)

              at org.infinispan.factories.AbstractComponentRegistry.invokeInjectionMethod(AbstractComponentRegistry.java:246)

              at org.infinispan.factories.AbstractComponentRegistry.access$000(AbstractComponentRegistry.java:83)

              at org.infinispan.factories.AbstractComponentRegistry$Component.injectDependencies(AbstractComponentRegistry.java:792)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponentInternal(AbstractComponentRegistry.java:220)

              at org.infinispan.factories.ComponentRegistry.registerComponentInternal(ComponentRegistry.java:154)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponent(AbstractComponentRegistry.java:175)

              at org.infinispan.factories.AbstractComponentRegistry.getOrCreateComponent(AbstractComponentRegistry.java:291)

              at org.infinispan.factories.AbstractComponentRegistry.invokeInjectionMethod(AbstractComponentRegistry.java:246)

              at org.infinispan.factories.AbstractComponentRegistry.access$000(AbstractComponentRegistry.java:83)

              at org.infinispan.factories.AbstractComponentRegistry$Component.injectDependencies(AbstractComponentRegistry.java:792)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponentInternal(AbstractComponentRegistry.java:220)

              at org.infinispan.factories.ComponentRegistry.registerComponentInternal(ComponentRegistry.java:154)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponent(AbstractComponentRegistry.java:175)

              at org.infinispan.factories.AbstractComponentRegistry.getOrCreateComponent(AbstractComponentRegistry.java:291)

              at org.infinispan.factories.AbstractComponentRegistry.invokeInjectionMethod(AbstractComponentRegistry.java:246)

              at org.infinispan.factories.AbstractComponentRegistry.access$000(AbstractComponentRegistry.java:83)

              at org.infinispan.factories.AbstractComponentRegistry$Component.injectDependencies(AbstractComponentRegistry.java:792)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponentInternal(AbstractComponentRegistry.java:220)

              at org.infinispan.factories.ComponentRegistry.registerComponentInternal(ComponentRegistry.java:154)

              at org.infinispan.factories.AbstractComponentRegistry.registerComponent(AbstractComponentRegistry.java:175)

              at org.infinispan.factories.InternalCacheFactory.bootstrap(InternalCacheFactory.java:102)

              at org.infinispan.factories.InternalCacheFactory.createAndWire(InternalCacheFactory.java:78)

              at org.infinispan.factories.InternalCacheFactory.createCache(InternalCacheFactory.java:62)

              at org.infinispan.manager.DefaultCacheManager.wireCache(DefaultCacheManager.java:692)

              at org.infinispan.manager.DefaultCacheManager.createCache(DefaultCacheManager.java:649)

              at org.infinispan.manager.DefaultCacheManager.getCache(DefaultCacheManager.java:549)

              at org.hibernate.cache.infinispan.InfinispanRegionFactory.getCache(InfinispanRegionFactory.java:483)

              at org.hibernate.cache.infinispan.InfinispanRegionFactory.buildEntityRegion(InfinispanRegionFactory.java:209)

              at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:349)

              at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1744)

              at org.testinfinispan.util.HibernateUtil.buildSessionFactory(HibernateUtil.java:48)

              ... 4 more

  • 9. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Galder Zamarreño Master

    Hibernate access Infinispan in a transactional way, so before you call into Hibernate:

     

    1. you need to start a JTA transaction.

    2. open a session and start a transaction in the session

    // call hibernate...

    N-1. commit session transaction

    N. commit JTA transaction.

     

    Example: https://github.com/hibernate/hibernate-orm/blob/master/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java#L99

     

    Taking in account that withTx implementation is: https://github.com/infinispan/infinispan/blob/master/core/src/test/java/org/infinispan/test/TestingUtil.java#L1226

     

    The first exception you're getting is saying that you're accessing Infinispan outside of a transction.

  • 10. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Saugat Manandhar Newbie

    Edit: Ok actually, here it is in better form. I am basically borrowing your tests in hibernate-infinispan branch you linked right before me, so it will work if you overwrite that folder. The test names you want to run are NonBasicIdInvalidationTestCase (for composite-pk key) and IdInvalidationTestCase (for a basic Integer key). You will note that the one with Integer as it's key will evict while the one with the CitizenPK will not.

     

    I am not sure if I broke any of the other tests with that branch, so please only run those two specific tests.

  • 11. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Galder Zamarreño Master

    @Saugat, I've been looking into at NonBasicIdInvalidationTestCase, and at first glance the test looks good, and I can get it to fail. Something odd is going on with the generation of the payload for the key. I'll check whether IdInvalidationTestCase suffers the same exact issue. Needs more investigation. Disabling lazyDeserialization, or storeAsBinary, gets around the problem.

  • 12. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Galder Zamarreño Master

    Btw, looking at the payloads of these keys, I dunno what this @EmbeddedId and @Embeddable really does and the advantages it has, but it results in basic keys such as CitizenPK generating a payload of 3KB approx (3060 bytes per key). And that's only the key. There seems to be a variable parameter in the CacheKey that Hibernate creates which results in two equal PK instances resulting in different payloads. And every run these payloads are different to previous runs too.

  • 14. Re: Invalidation not working with LazyDeserialization on non-basic PK (clustered)
    Saugat Manandhar Newbie

    http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e2177

     

    I was using @EmbeddedId and @Embeddable as a way to specify that the entity is using composite-pk. It seems that in the example they have, you do not need to use both tags, but I am running into the same issue even when following their format. I am also having the same problem with using xml format, but I didn't post that here as I assumed the annotated class would be sufficient.

1 2 Previous Next