Session implementing CTC is a bit messy, IMO. Creating a separate object on session open would increase the number of objects allocated while CTC does not have to be used (especially if 2LC is off).
Having the Session implement CTC is not "clean", I definitely agree with that. The point of that suggestion was to handle your second point.. to minimize instantiations/allocations of some separate "no txn" CTC delegate. Personally I think that the performance gain here is going to be more important than the "messiness".
The timestamp should be acquired before the data is loaded from DB (this may be later used for putFromLoad) - is there a good place to initialize it? If not, session open would be preferred.
Right. The "Session is a CacheTransactionContext" option is maybe a bit hard to visualize, so perhaps some code would help:
/**
* ...
*
* @apiNote Note that Session implements CacheTransactionContext for cases where either
* L2C is not enabled or when L2C is enabled but we are not joined to a txn context. In this
* case, the Session itself will act as the CacheTransactionContext for calls to the cache. This
* is done to minimize instantiations of a separate CacheTransactionContext delegate for these
* cases which would get expensive resource-wise.
*/
class SessionImpl ... implements ..., CacheTransactionContext {
...
private CacheTransactionContext explicitCurrentCacheTransactionContext;
private long nonTransactedStartTimestamp;
public SessionImpl(...) {
...
resetNonTransactedStartTimestamp();
}
private void resetNonTransactedStartTimestamp() {
this.nonTransactedStartTimestamp = sessionFactory.getCacheEngine().getRegionFactory().nextTimestamp();
}
@Override
public CacheTransactionContext getCurrentCacheTransactionContext() {
return explicitCurrentCacheTransactionContext == null
? this
: explicitCurrentCacheTransactionContext;
}
/**
* ...
* @implNote this method is called whenever we join a txn context. This will happen when either
* a JDBC-based transaction is started via the Hibernate Transaction API or we join a JTA-based
* transaction.
*/
@Override
public void startTransactionBoundary() {
this.cacheTransactionContext = factory.getCache().getRegionFactory().startingTransaction( this );
}
@Override
public void beforeTransactionCompletion() {
getCurrentCacheTransactionContext().transactionCompleting();
...
}
@Override
public void afterTransactionCompletion(boolean successful, boolean delayed) {
...
getCurrentCacheTransactionContext().transactionCompleted(); this.explicitCurrentCacheTransactionContext = null;
resetNonTransactedStartTimestamp();
}
@Override
public long getCurrentTransactionStartTimestamp() {
return nonTransactedStartTimestamp;
}
@Override
public void beforeCompletion() {
}
@Override
public void afterCompletion() {
}
}
Again, I agree this is "messy" - it is a matter of performance versus generating a new CacheTransactionContext instance for every call to "resetNonTransactedStartTimestamp". The other option I mentioned is to have that single CacheTransactionContext instance be in effect for the entire Session lifecycle, something like:
class SessionImpl ... {
private CacheTransactionContext cacheTransactionContext;
public SessionImpl(...) {
this.cacheTransactionContext = factory.getCache().getRegionFactory().startingTransaction( this );
}
@Override
public CacheTransactionContext getCurrentCacheTransactionContext() {
return cacheTransactionContext;
}
@Override
public void startTransactionBoundary() {
getCurrentCacheTransactionContext().transactionJoined();
}
@Override
public void beforeTransactionCompletion() {
...
getCurrentCacheTransactionContext().transactionCompleting();
...
}
@Override
public void afterTransactionCompletion(boolean successful, boolean delayed) {
...
getCurrentCacheTransactionContext().transactionCompleted(); ...
}
}
here you can see that the CacheTransactionContext is given enough contextual callbacks to be aware of transaction boundaries.
When the transaction is completed CTC does not change. The next non-transactional operation would use the same instance - do we want CTC tracking it's transaction state internally, or will we dispose the instance? (and get it to the same state as if Session was just open).
Exactly - see above
Having rather a structured object is a good idea - as long as it does not increase allocation rates (but that shouldn't be the case, all instances should be created on startup).
As far as "real" caching calls, this is accurate. However, we'd also have to perform this when a user makes calls of the "caching engine" which happens:
- the various SessionFactory/EntityManagerFactory level cache eviction calls
- caching statistics accessed
In these cases we could either generate the Name each time, or we could decide to maintain a map of "raw" region name to its Name reference. The latter is probably a premature optimization though at this point. |