use of org.hibernate.cache.spi.access.SoftLock in project hibernate-orm by hibernate.
the class AbstractRegionAccessStrategyTest method evictOrRemoveAllTest.
protected void evictOrRemoveAllTest(final boolean evict) throws Exception {
final Object KEY = generateNextKey();
assertEquals(0, localRegion.getCache().size());
assertEquals(0, remoteRegion.getCache().size());
SharedSessionContractImplementor s1 = mockedSession();
assertNull("local is clean", localAccessStrategy.get(s1, KEY, s1.getTimestamp()));
SharedSessionContractImplementor s2 = mockedSession();
assertNull("remote is clean", remoteAccessStrategy.get(s2, KEY, s2.getTimestamp()));
CountDownLatch localPutFromLoadLatch = expectRemotePutFromLoad(remoteRegion.getCache(), localRegion.getCache());
CountDownLatch remotePutFromLoadLatch = expectRemotePutFromLoad(localRegion.getCache(), remoteRegion.getCache());
SharedSessionContractImplementor s3 = mockedSession();
localAccessStrategy.putFromLoad(s3, KEY, VALUE1, s3.getTimestamp(), 1);
SharedSessionContractImplementor s5 = mockedSession();
remoteAccessStrategy.putFromLoad(s5, KEY, VALUE1, s5.getTimestamp(), 1);
// putFromLoad is applied on local node synchronously, but if there's a concurrent update
// from the other node it can silently fail when acquiring the loc . Then we could try to read
// before the update is fully applied.
assertTrue(localPutFromLoadLatch.await(1, TimeUnit.SECONDS));
assertTrue(remotePutFromLoadLatch.await(1, TimeUnit.SECONDS));
SharedSessionContractImplementor s4 = mockedSession();
SharedSessionContractImplementor s6 = mockedSession();
assertEquals(VALUE1, localAccessStrategy.get(s4, KEY, s4.getTimestamp()));
assertEquals(VALUE1, remoteAccessStrategy.get(s6, KEY, s6.getTimestamp()));
CountDownLatch endInvalidationLatch;
if (invalidation && !evict) {
// removeAll causes transactional remove commands which trigger EndInvalidationCommands on the remote side
// if the cache is non-transactional, PutFromLoadValidator.registerRemoteInvalidations cannot find
// current session nor register tx synchronization, so it falls back to simple InvalidationCommand.
endInvalidationLatch = new CountDownLatch(1);
if (transactional) {
PutFromLoadValidator originalValidator = PutFromLoadValidator.removeFromCache(remoteRegion.getCache());
assertEquals(PutFromLoadValidator.class, originalValidator.getClass());
PutFromLoadValidator mockValidator = spy(originalValidator);
doAnswer(invocation -> {
try {
return invocation.callRealMethod();
} finally {
endInvalidationLatch.countDown();
}
}).when(mockValidator).endInvalidatingKey(any(), any());
PutFromLoadValidator.addToCache(remoteRegion.getCache(), mockValidator);
cleanup.add(() -> {
PutFromLoadValidator.removeFromCache(remoteRegion.getCache());
PutFromLoadValidator.addToCache(remoteRegion.getCache(), originalValidator);
});
} else {
ExpectingInterceptor.get(remoteRegion.getCache()).when((ctx, cmd) -> cmd instanceof InvalidateCommand).countDown(endInvalidationLatch);
cleanup.add(() -> ExpectingInterceptor.cleanup(remoteRegion.getCache()));
}
} else {
endInvalidationLatch = new CountDownLatch(0);
}
withTx(localEnvironment, mockedSession(), () -> {
if (evict) {
localAccessStrategy.evictAll();
} else {
SoftLock softLock = localAccessStrategy.lockRegion();
localAccessStrategy.removeAll();
localAccessStrategy.unlockRegion(softLock);
}
return null;
});
SharedSessionContractImplementor s7 = mockedSession();
assertNull(localAccessStrategy.get(s7, KEY, s7.getTimestamp()));
assertEquals(0, localRegion.getCache().size());
SharedSessionContractImplementor s8 = mockedSession();
assertNull(remoteAccessStrategy.get(s8, KEY, s8.getTimestamp()));
assertEquals(0, remoteRegion.getCache().size());
// Wait for async propagation of EndInvalidationCommand before executing naked put
assertTrue(endInvalidationLatch.await(1, TimeUnit.SECONDS));
TIME_SERVICE.advance(1);
CountDownLatch lastPutFromLoadLatch = expectRemotePutFromLoad(remoteRegion.getCache(), localRegion.getCache());
// Test whether the get above messes up the optimistic version
SharedSessionContractImplementor s9 = mockedSession();
assertTrue(remoteAccessStrategy.putFromLoad(s9, KEY, VALUE1, s9.getTimestamp(), 1));
SharedSessionContractImplementor s10 = mockedSession();
assertEquals(VALUE1, remoteAccessStrategy.get(s10, KEY, s10.getTimestamp()));
assertEquals(1, remoteRegion.getCache().size());
assertTrue(lastPutFromLoadLatch.await(1, TimeUnit.SECONDS));
SharedSessionContractImplementor s11 = mockedSession();
assertEquals((isUsingInvalidation() ? null : VALUE1), localAccessStrategy.get(s11, KEY, s11.getTimestamp()));
SharedSessionContractImplementor s12 = mockedSession();
assertEquals(VALUE1, remoteAccessStrategy.get(s12, KEY, s12.getTimestamp()));
}
use of org.hibernate.cache.spi.access.SoftLock in project hibernate-orm by hibernate.
the class AbstractLockUpgradeEventListener method upgradeLock.
/**
* Performs a pessimistic lock upgrade on a given entity, if needed.
*
* @param object The entity for which to upgrade the lock.
* @param entry The entity's EntityEntry instance.
* @param lockOptions contains the requested lock mode.
* @param source The session which is the source of the event being processed.
*/
protected void upgradeLock(Object object, EntityEntry entry, LockOptions lockOptions, EventSource source) {
LockMode requestedLockMode = lockOptions.getLockMode();
if (requestedLockMode.greaterThan(entry.getLockMode())) {
if (entry.getStatus() != Status.MANAGED) {
throw new ObjectDeletedException("attempted to lock a deleted instance", entry.getId(), entry.getPersister().getEntityName());
}
final EntityPersister persister = entry.getPersister();
if (log.isTraceEnabled()) {
log.tracev("Locking {0} in mode: {1}", MessageHelper.infoString(persister, entry.getId(), source.getFactory()), requestedLockMode);
}
final boolean cachingEnabled = persister.canWriteToCache();
SoftLock lock = null;
Object ck = null;
try {
if (cachingEnabled) {
EntityDataAccess cache = persister.getCacheAccessStrategy();
ck = cache.generateCacheKey(entry.getId(), persister, source.getFactory(), source.getTenantIdentifier());
lock = cache.lockItem(source, ck, entry.getVersion());
}
if (persister.isVersioned() && requestedLockMode == LockMode.FORCE) {
// todo : should we check the current isolation mode explicitly?
Object nextVersion = persister.forceVersionIncrement(entry.getId(), entry.getVersion(), source);
entry.forceLocked(object, nextVersion);
} else {
persister.lock(entry.getId(), entry.getVersion(), object, lockOptions, source);
}
entry.setLockMode(requestedLockMode);
} finally {
// so release the soft lock
if (cachingEnabled) {
persister.getCacheAccessStrategy().unlockItem(source, ck, lock);
}
}
}
}
use of org.hibernate.cache.spi.access.SoftLock in project hibernate-orm by hibernate.
the class DefaultRefreshEventListener method onRefresh.
/**
* Handle the given refresh event.
*
* @param event The refresh event to be handled.
*/
public void onRefresh(RefreshEvent event, Map refreshedAlready) {
final EventSource source = event.getSession();
boolean isTransient;
if (event.getEntityName() != null) {
isTransient = !source.contains(event.getEntityName(), event.getObject());
} else {
isTransient = !source.contains(event.getObject());
}
if (source.getPersistenceContext().reassociateIfUninitializedProxy(event.getObject())) {
if (isTransient) {
source.setReadOnly(event.getObject(), source.isDefaultReadOnly());
}
return;
}
final Object object = source.getPersistenceContext().unproxyAndReassociate(event.getObject());
if (refreshedAlready.containsKey(object)) {
LOG.trace("Already refreshed");
return;
}
final EntityEntry e = source.getPersistenceContext().getEntry(object);
final EntityPersister persister;
final Serializable id;
if (e == null) {
persister = source.getEntityPersister(event.getEntityName(), object);
// refresh() does not pass an entityName
id = persister.getIdentifier(object, event.getSession());
if (LOG.isTraceEnabled()) {
LOG.tracev("Refreshing transient {0}", MessageHelper.infoString(persister, id, source.getFactory()));
}
final EntityKey key = source.generateEntityKey(id, persister);
if (source.getPersistenceContext().getEntry(key) != null) {
throw new PersistentObjectException("attempted to refresh transient instance when persistent instance was already associated with the Session: " + MessageHelper.infoString(persister, id, source.getFactory()));
}
} else {
if (LOG.isTraceEnabled()) {
LOG.tracev("Refreshing ", MessageHelper.infoString(e.getPersister(), e.getId(), source.getFactory()));
}
if (!e.isExistsInDatabase()) {
throw new UnresolvableObjectException(e.getId(), "this instance does not yet exist as a row in the database");
}
persister = e.getPersister();
id = e.getId();
}
// cascade the refresh prior to refreshing this entity
refreshedAlready.put(object, object);
Cascade.cascade(CascadingActions.REFRESH, CascadePoint.BEFORE_REFRESH, source, persister, object, refreshedAlready);
if (e != null) {
final EntityKey key = source.generateEntityKey(id, persister);
source.getPersistenceContext().removeEntity(key);
if (persister.hasCollections()) {
new EvictVisitor(source, object).process(object, persister);
}
}
if (persister.canWriteToCache()) {
Object previousVersion = null;
if (persister.isVersionPropertyGenerated()) {
// we need to grab the version value from the entity, otherwise
// we have issues with generated-version entities that may have
// multiple actions queued during the same flush
previousVersion = persister.getVersion(object);
}
final EntityDataAccess cache = persister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey(id, persister, source.getFactory(), source.getTenantIdentifier());
final SoftLock lock = cache.lockItem(source, ck, previousVersion);
cache.remove(source, ck);
source.getActionQueue().registerProcess((success, session) -> cache.unlockItem(session, ck, lock));
}
evictCachedCollections(persister, id, source);
String previousFetchProfile = source.getLoadQueryInfluencers().getInternalFetchProfile();
source.getLoadQueryInfluencers().setInternalFetchProfile("refresh");
Object result = persister.load(id, object, event.getLockOptions(), source);
// If it was transient, then set it to the default for the source.
if (result != null) {
if (!persister.isMutable()) {
// this is probably redundant; it should already be read-only
source.setReadOnly(result, true);
} else {
source.setReadOnly(result, (e == null ? source.isDefaultReadOnly() : e.isReadOnly()));
}
}
source.getLoadQueryInfluencers().setInternalFetchProfile(previousFetchProfile);
UnresolvableObjectException.throwIfNull(result, id, persister.getEntityName());
}
use of org.hibernate.cache.spi.access.SoftLock in project hibernate-orm by hibernate.
the class AbstractReadWriteEhcacheAccessStrategy method lockItem.
/**
* Soft-lock a cache item.
*
* @see RegionAccessStrategy#lockItem(SharedSessionContractImplementor, Object, Object)
* @see RegionAccessStrategy#lockItem(SharedSessionContractImplementor, Object, Object)
*/
public final SoftLock lockItem(SharedSessionContractImplementor session, Object key, Object version) throws CacheException {
region().writeLock(key);
try {
final Lockable item = (Lockable) region().get(key);
final long timeout = region().nextTimestamp() + region().getTimeout();
final Lock lock = (item == null) ? new Lock(timeout, uuid, nextLockId(), version) : item.lock(timeout, uuid, nextLockId());
region().put(key, lock);
return lock;
} finally {
region().writeUnlock(key);
}
}
use of org.hibernate.cache.spi.access.SoftLock in project hibernate-orm by hibernate.
the class CollectionRegionAccessStrategyTest method doUpdate.
@Override
protected void doUpdate(CollectionRegionAccessStrategy strategy, SharedSessionContractImplementor session, Object key, Object value, Object version) throws javax.transaction.RollbackException, javax.transaction.SystemException {
SoftLock softLock = strategy.lockItem(session, key, version);
strategy.remove(session, key);
session.getTransactionCoordinator().getLocalSynchronizations().registerSynchronization(new TestSynchronization.UnlockItem(strategy, session, key, softLock));
}
Aggregations