Brief description: after parent and one of its child are updated, transaction commited, session closed, next session gets parent from L2 cache, but updated child is loaded from db. Parent is versioned, children are not.
Detailed description: as I mentioned I have parent with child collection. Parent is versioned, but children are not as my application and mappings guarantees all updates are done by saving parent. What I see that when I load parent with children, parent and most of children are loaded from L2 cache, but recently updated child is loaded from database. I did some research in ReadWriteCache details and found that after I saved parent and updated child the following code decides NOT to update cache:
public bool Put(CacheKey key, object value, long txTimestamp, object version, IComparer versionComparator, bool minimalPut)
{
bool CS$1$0000;
if (txTimestamp == -9223372036854775808L)
{
return false;
}
lock (this._lockObject)
{
if (log.IsDebugEnabled)
{
log.Debug("Caching: " + key);
}
try
{
this.cache.Lock(key);
ILockable lockable = (ILockable) this.cache.Get(key);
if ((lockable == null) || lockable.IsPuttable(txTimestamp, version, versionComparator))
{
this.cache.Put(key, new CachedItem(value, this.cache.NextTimestamp(), version));
if (log.IsDebugEnabled)
{
log.Debug("Cached: " + key);
}
return true;
}
if (log.IsDebugEnabled)
{
if (lockable.IsLock)
{
log.Debug("Item was locked: " + key);
}
else
{
log.Debug("Item was already cached: " + key);
}
}
CS$1$0000 = false;
}
finally
{
this.cache.Unlock(key);
}
}
return CS$1$0000;
}
In my case lockable is not null, but it is not puttable:
public bool IsPuttable(long txTimestamp, object newVersion, IComparer comparator)
{
// we really could refresh the item if it
// is not a lock, but it might be slower
//return freshTimestamp < txTimestamp
return version != null && comparator.Compare(version, newVersion) < 0;
}
As you can see from NHibernate source, earlier decision IsPuttable was made by comparing timestampts, but now version comes into play.
Now the question: should I version EVERY entity in my domain model? Before the problem arises I was sure I should version Aggregate Roots only.
I really expect to find another way of solving this problem because my parent-child hierarchy is much deeper (don't ask why :)), let's say it 3 levels deep (actually it's deeper): Root->Child->GrandChild
In scenario when only GrandChild was updated, I expected 2 UPDATE statements: one for GrandChild and another for Root to update its version. But if I version Child also it will cost me extra database updates and roundtrips :( ... BTW, should ping Ayende or somebody from NH - ADO.NET batching for NH works only for same entities, it won't put in the same batch update statements for GrandChild, Child and Root.