1 Reply Latest reply on Apr 16, 2013 9:34 AM by nate.h

    Objects are not added to the cache or removed after an update

    nate.h

      We currently have some problems with our Infinispan 2nd level cache. We've prepared two test cases:

       

      1) Newly created objects are not put in the 2nd level Cache.
      We create a new object (TestEntity) in a first transaction. We see that it is not put in the 2nd level cache, even if we flush it. When we close the transaction (container managed) and create a second transaction, the newly created object still isn't found in the 2nd level cache, but if we load it we find it in the database. So we are sure that it is created and committed in the first transaction. But why isn't the newly created object put into the 2nd level cache after the first transaction is closed?

       

      2) updated objects are removed from the 2nd level cache after flush
      We are loading an entity from the database, so it is in the 2nd level cache. Now we are updating an attribute of this loaded entity and do a flush. If we check the 2nd level cache again, the entity is not cached anymore. Why is it removed from the cache after updating + flushing and how can we prevent this?

       

      Case 1:
      Code:

      private static String tmpInsId = " ";
      public void onMessages(final Message[] messageBulk) {
       boolean isInCache = this.entityManager.getEntityManagerFactory().getCache().contains(TestEntity.class, tmpInsId);
       System.out.println("+++++ new TestEntity (" + tmpInsId + "): " + isInCache);
       if (!isInCache) {
        final TestEntity ri = this.entityManager.find(TestEntity.class, tmpInsId);
        if (ri != null) {
         System.out.println("+++++ new TestEntity (" + tmpInsId + "): Loaded!");
         isInCache = this.entityManager.getEntityManagerFactory().getCache().contains(TestEntity.class, tmpInsId);
         if (!isInCache) {
          System.out.println("+++++ new TestEntity (" + tmpInsId + "): Not in cache!"); // shouldn't be possible
         }
         else {
          System.out.println("+++++ new TestEntity (" + tmpInsId + "): Cached!");
         }
        }
        else {
         tmpInsId = ("TMP" + String.valueOf(System.currentTimeMillis()) + "0000000000").substring(0, 16);
         final TestEntity tmp = new TestEntity(tmpInsId, 0L, "N", "N", new Date());
         this.entityManager.persist(tmp);
         System.out.println("+++++ new TestEntity (" + tmpInsId + "): "
           + this.entityManager.getEntityManagerFactory().getCache().contains(TestEntity.class, tmpInsId));
         this.entityManager.flush();
         System.out.println("+++++ new TestEntity (" + tmpInsId + "): "
           + this.entityManager.getEntityManagerFactory().getCache().contains(TestEntity.class, tmpInsId));
         this.entityManager.find(TestEntity.class, tmpInsId);
         System.out.println("+++++ new TestEntity (" + tmpInsId + "): "
           + this.entityManager.getEntityManagerFactory().getCache().contains(TestEntity.class, tmpInsId));
        }
       }
      
      

       

      Log output 1nd Transaction:

      17:54:30,730 INFO [stdout] (default-threads - 2) +++++ new TestEntity ( ): false
      17:54:30,745 INFO [stdout] (default-threads - 2) +++++ new TestEntity (TMP1366041270730): false
      17:54:30,761 INFO [stdout] (default-threads - 2) +++++ new TestEntity (TMP1366041270730): false
      17:54:30,761 INFO [stdout] (default-threads - 2) +++++ new TestEntity (TMP1366041270730): false

       

      Log output 2nd Transaction:

      17:55:00,058 INFO [stdout] (default-threads - 2) +++++ new TestEntity (TMP1366041270730): false
      17:55:00,058 INFO [stdout] (default-threads - 2) +++++ new TestEntity (TMP1366041270730): Loaded!
      17:55:00,058 INFO [stdout] (default-threads - 2) +++++ new TestEntity (TMP1366041270730): Cached!

       

       

       

      Case 2:
      Preparation: PreLoad of entity "TMP0000000000043" which is already in DB

       

      Code:

       System.out.println(" TestEntity          : " + this.entityManager.getEntityManagerFactory().getCache().contains(TestEntity.class, "TMP0000000000043"));
       testEntity().setLstUpdTmstmp(currentTimestamp);
       this.entityManager.flush();
       System.out.println(" TestEntity          : " + this.entityManager.getEntityManagerFactory().getCache().contains(TestEntity.class, "TMP0000000000043"));
      


      Log output:
      17:21:17,685 INFO [stdout] (default-threads - 6) LifecycleEngine TestEntity : true
      17:21:17,857 INFO [stdout] (default-threads - 6) LifecycleEngine TestEntity : false
       
       
      We are using the following setup:
      - EAP-6.0.1
      - Hibernate 4.2
      - Infinispan as provided by EAP-6.0.1 as 2nd level cache

       

      Subsystem configuration:

       

      <subsystem xmlns="urn:jboss:domain:infinispan:1.3"> 
      <cache-container name="hibernate" default-cache="local-query" module="org.jboss.as.jpa.hibernate:4">
      <local-cache name="entity">
      <transaction mode="NON_XA"/>
      <eviction strategy="LRU" max-entries="10000"/>
      <expiration max-idle="100000"/>
      </local-cache>
      <local-cache name="local-query">
      <transaction mode="NONE"/>
      <eviction strategy="LRU" max-entries="10000"/>
      <expiration max-idle="100000"/>
      </local-cache>
      <local-cache name="timestamps">
      <transaction mode="NONE"/>
      <eviction strategy="NONE"/>
      </local-cache>
      </cache-container>
      </subsystem>
      

       

      Enabling of level 2 cache:
      <property name="hibernate.cache.use_second_level_cache" value="true" />
      <property name="hibernate.cache.use_query_cache" value="true" />
      <property name="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE" />
      <property name="hibernate.cache.default_cache_concurrency_strategy" value="transactional" <property
      <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>

       

      The annotations on the entities are :
      @Cacheable
      @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)

        • 1. Re: Objects are not added to the cache or removed after an update
          nate.h

          I just wanted to give an update, since we solved the problem. The reason was, that the entities which were not cached or removed after a flush, was that they had an additional “@DynamicUpdate” annotation. Hibernate was checking if the entities were dynamically updated and if yes, then they were invalidated. After we removed this annotation, the entities are found in the cache.

           

          If someone wants to debug this behaviour, you’ll find it in the AbstractEntityPersister in Hibernate.

           

          /**
           * Basic functionality for persisting an entity via JDBC
           * through either generated or custom SQL
           *
           * @author Gavin King
           */
          public abstract class AbstractEntityPersister  {
           /**
            * We can't immediately add to the cache if we have formulas
            * which must be evaluated, or if we have the possibility of
            * two concurrent updates to the same item being merged on
            * the database. This can happen if (a) the item is not
            * versioned and either (b) we have dynamic update enabled
            * or (c) we have multiple tables holding the state of the
            * item.
            */
           public boolean isCacheInvalidationRequired() {
            return hasFormulaProperties() ||
              ( !isVersioned() && ( entityMetamodel.isDynamicUpdate() || getTableSpan() > 1 ) );
           }
          }
          


          Maybe it would be helpful to add this to the Infinispan or Hibernate documentation.
          A property setting to enable merging the entity and updating the entity in the cache before it gets persisted in the database would also very helpful.