I'm updating the rankings of 35,000+ users in a game with batch processing as follows.
Note: Session and transaction are opened/closed before and after this code segment.
Code:
int batchSize = 100, count = 0;
User currentUser = null, lastUser = null;
ScrollableResults users =
session.getNamedQuery("User.findAllForRanking")
.setCacheMode(CacheMode.IGNORE)
.setFlushMode(FlushMode.MANUAL)
.scroll(ScrollMode.FORWARD_ONLY);
while (users.next()) {
// Don't run this the first time, since currentUser will be null
// The other times through the loop, the current user needs to be
// re-attached to the persistence context since it was evicted when
// the batch was flushed, but that one User object is still needed.
if (count > 0)
lastUser = (count % batchSize == 0) ? (User)session.merge(currentUser) : currentUser;
currentUser = (User)users.get(0);
count++;
//...
//Adjust the user's global ranking, basically just user.setCurrentRanking(x);
//...
if (count % batchSize == 0) {
session.flush(); // PROBLEM HAPPENS HERE
session.clear();
}
}
// Flush it once more, in case there were any unflushed entities at
// the end of the loop execution
session.flush();
session.clear();
// Close the ScrollableResults
users.close();
The Problem:
When calling session.flush() in the loop, I see this output in the log:
Quote:
An item was expired by the cache while it was locked (increase your cache timeout): com.stag.beans.User#3519
A couple notes:
1.) The NamedQuery referenced in the code above is just
SELECT u FROM User ORDER BY points DESC.
2.) I set
hibernate.jdbc.batch_size = 100, to match the size of the batch used above.
3.) Setting
hibernate.cache.use_second_level_cache = false fixes the problem.
Am I correct in interpreting that message as: One of the entities which I was referencing within the transaction was expired by the cache, despite the fact that I set the session to use CacheMode.IGNORE? This doesn't stop execution, nor prevent the flush from properly committing the modified entities, but I don't think it should be happening, ergo I want to make it stop.
Also, the total time required for one execution of the loop above is about 1 second, and the cache configured for the User entity has a timeToLive of 600 seconds, and a timeToIdle of 300 seconds.
I have faith in the hibernate developers, and I didn't find anything to this effect in a google search nor in the manual, so I must be doing something odd here.
Any ideas?