Search This Blog

Friday 20 December 2013

Update Timestamps Cache in Action

We saw the query cache in our previous post. We know that UpdateTimestampsCache is only used for query caching. I decided to try and bring it into play in this post.
public static void testQuery() {
      Session session = sessionFactory.openSession();
      Query query1 = session.createQuery("from BookType where name like :type");
      query1.setString("type", "%");
      query1.setCacheable(true);
      System.out.println("Query execute once");
      List<BookType> bookTypes = query1.list();
      for (BookType bookType : bookTypes) {
         System.out.println(bookType.getName());
      }
      session.close();
      
      System.out.println("A record has been Updated !!");
      session = sessionFactory.openSession();
      Transaction transaction = session.beginTransaction();
      BookType bookType1 = (BookType) session.get(BookType.class, 1);
      bookType1.setName(bookType1.getName().toUpperCase());
      session.save(bookType1);
      transaction.commit();
      session.close();
      
      session = sessionFactory.openSession();
      Query query2 = session.createQuery("from BookType where name like :type");
      query2.setString("type", "%");
      query2.setCacheable(true);
      System.out.println("Query executed once again...");
      bookTypes = query2.list();
      for (BookType bookType : bookTypes) {
         System.out.println(bookType.getName());
      }
      session.close();
   }
The code :
  1.  Executes a cacheable query, thus adding records to the second level and query cache. The cache setting is also changed to support updateable records.
    <class name="BookType" table="BOOK_TYPE_MASTER">
     <cache usage="read-write" />
    
  2. It then updates the BookType entity with id 1. This record was a part of the query cache.
  3. The query is again fired to fetch the records. All three operations have been done in separate sessions (Irrelevant though as we are working with second level not first level cache).
The logs are as below:
Query execute once
392  [main] DEBUG net.sf.ehcache.Cache  - org.hibernate.cache.StandardQueryCache
 cache - Miss
392  [main] DEBUG org.hibernate.cache.StandardQueryCache  - query results were n
ot found in cache
    select
        booktype0_.ID as ID0_,
        booktype0_.NAME as NAME0_ 
    from
        BOOK_TYPE_MASTER booktype0_ 
    where
        booktype0_.NAME like ?
...
420  [main] DEBUG org.hibernate.cache.ReadWriteCache  - Caching: com.object.cach
e.BookType#1
...
423  [main] DEBUG org.hibernate.cache.StandardQueryCache  - caching query result
s in region: org.hibernate.cache.StandardQueryCache; timestamp=5678792592117760
...
A record has been Updated !!
440  [main] DEBUG org.hibernate.cache.ReadWriteCache  - Cache hit: com.object.ca
che.BookType#1
440  [main] DEBUG org.hibernate.cache.UpdateTimestampsCache  - Pre-invalidating 
space [BOOK_TYPE_MASTER]
440  [main] DEBUG org.hibernate.cache.ReadWriteCache  - Invalidating: com.object
.cache.BookType#1
 update
            BOOK_TYPE_MASTER 
        set
            NAME=? 
        where
            ID=?
430  [main] DEBUG org.hibernate.cache.ReadWriteCache  - Updating: com.object.cac
he.BookType#1
...
Query executed once again...
462  [main] DEBUG org.hibernate.cache.StandardQueryCache  - checking cached quer
y results in region: org.hibernate.cache.StandardQueryCache
...
463  [main] DEBUG net.sf.ehcache.store.MemoryStore  - org.hibernate.cache.Update
TimestampsCacheCache: org.hibernate.cache.UpdateTimestampsCacheMemoryStore hit f
or BOOK_TYPE_MASTER
463  [main] DEBUG org.hibernate.cache.UpdateTimestampsCache  - [BOOK_TYPE_MASTER
] last update timestamp: 5678792592941057, result set timestamp: 5678792592117760
463  [main] DEBUG org.hibernate.cache.StandardQueryCache  - cached query results
 were not up to date
    select
        booktype0_.ID as ID0_,
        booktype0_.NAME as NAME0_ 
    from
        BOOK_TYPE_MASTER booktype0_ 
    where
        booktype0_.NAME like ?
465  [main] DEBUG org.hibernate.cache.ReadWriteCache  - Caching: com.object.cach
e.BookType#1
...
465  [main] DEBUG net.sf.ehcache.store.MemoryStore  - com.object.cache.BookTypeC
ache: com.object.cache.BookTypeMemoryStore hit for com.object.cache.BookType#2
465  [main] DEBUG org.hibernate.cache.ReadWriteCache  - Item was already cached:
 com.object.cache.BookType#2
465  [main] DEBUG net.sf.ehcache.store.MemoryStore  - com.object.cache.BookTypeC
ache: com.object.cache.BookTypeMemoryStore hit for com.object.cache.BookType#3
465  [main] DEBUG org.hibernate.cache.ReadWriteCache  - Item was already cached:
 com.object.cache.BookType#3
465  [main] DEBUG org.hibernate.cache.StandardQueryCache  - caching query result
s in region: org.hibernate.cache.StandardQueryCache; timestamp=5678792592941058
The conclusions from the above is as below:
  1. Once the record is added to the Query cache, a corresponding entry for the entity was made in the Timestamps cache.
  2. When the update was performed a new value was again added to the Time stamp cache to indicate the last update time.
  3. For the query, the timestamp of the cached result was found to be older than the last update time. Hence the result was invalidated.
  4. The query was executed again, and the new timestamp was updated in the Timestamps cache.
  5. Interestingly, the secondary level cache was smart enough to identify that the first record changed. Accordingly it updated the data in the second level cache. Rest of the records were used from the secondary level cache as is, and the result set data was ignored.
Thus the QueryCache is the only one affected by the  UpdateTimestamps Cache. The secondary level cache has its own logic based on the cache type used to evict stale data.

No comments:

Post a Comment