Skip to content

Commit 93d39fa

Browse files
rvansagalderz
authored andcommitted
HHH-9868, HHH-9881 Must not write into non-transactional caches during transactional write
* The write can only invalidate (remove) the entry and block further PFERs of that entry * After successful DB update, if there have not been any concurrent updates the value can be PFERed into the cache
1 parent 19c14ce commit 93d39fa

17 files changed

+423
-173
lines changed

hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/NonTxInvalidationInterceptor.java

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,19 @@ private void start() {
7878
@Override
7979
public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable {
8080
if (!isPutForExternalRead(command)) {
81-
return handleInvalidate(ctx, command, command.getKey());
81+
return handleInvalidate(ctx, command, new Object[] { command.getKey() });
8282
}
8383
return invokeNextInterceptor(ctx, command);
8484
}
8585

8686
@Override
8787
public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable {
88-
return handleInvalidate(ctx, command, command.getKey());
88+
return handleInvalidate(ctx, command, new Object[] { command.getKey() });
8989
}
9090

9191
@Override
9292
public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command) throws Throwable {
93-
return handleInvalidate(ctx, command, command.getKey());
93+
return handleInvalidate(ctx, command, new Object[] { command.getKey() });
9494
}
9595

9696
@Override
@@ -107,39 +107,39 @@ public Object visitClearCommand(InvocationContext ctx, ClearCommand command) thr
107107

108108
@Override
109109
public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command) throws Throwable {
110-
Object[] keys = command.getMap() == null ? null : command.getMap().keySet().toArray();
111-
return handleInvalidate(ctx, command, keys);
110+
if (!isPutForExternalRead(command)) {
111+
return handleInvalidate(ctx, command, command.getMap().keySet().toArray());
112+
}
113+
return invokeNextInterceptor(ctx, command);
112114
}
113115

114-
private Object handleInvalidate(InvocationContext ctx, WriteCommand command, Object... keys) throws Throwable {
116+
private Object handleInvalidate(InvocationContext ctx, WriteCommand command, Object[] keys) throws Throwable {
115117
Object retval = invokeNextInterceptor(ctx, command);
116-
if (command.isSuccessful() && !ctx.isInTxScope()) {
117-
if (keys != null && keys.length != 0) {
118-
if (!isLocalModeForced(command)) {
119-
invalidateAcrossCluster(isSynchronous(command), keys, ctx);
120-
}
121-
}
118+
if (command.isSuccessful() && keys != null && keys.length != 0) {
119+
invalidateAcrossCluster(command, keys);
122120
}
123121
return retval;
124122
}
125123

126-
private void invalidateAcrossCluster(boolean synchronous, Object[] keys, InvocationContext ctx) throws Throwable {
124+
private void invalidateAcrossCluster(FlagAffectedCommand command, Object[] keys) throws Throwable {
127125
// increment invalidations counter if statistics maintained
128126
incrementInvalidations();
129127
InvalidateCommand invalidateCommand;
130128
Object lockOwner = putFromLoadValidator.registerRemoteInvalidations(keys);
131-
if (lockOwner == null) {
132-
invalidateCommand = commandsFactory.buildInvalidateCommand(InfinispanCollections.<Flag>emptySet(), keys);
133-
}
134-
else {
135-
invalidateCommand = commandInitializer.buildBeginInvalidationCommand(
136-
InfinispanCollections.<Flag>emptySet(), keys, lockOwner);
137-
}
138-
if (log.isDebugEnabled()) {
139-
log.debug("Cache [" + rpcManager.getAddress() + "] replicating " + invalidateCommand);
140-
}
129+
if (!isLocalModeForced(command)) {
130+
if (lockOwner == null) {
131+
invalidateCommand = commandsFactory.buildInvalidateCommand(InfinispanCollections.<Flag>emptySet(), keys);
132+
}
133+
else {
134+
invalidateCommand = commandInitializer.buildBeginInvalidationCommand(
135+
InfinispanCollections.<Flag>emptySet(), keys, lockOwner);
136+
}
137+
if (log.isDebugEnabled()) {
138+
log.debug("Cache [" + rpcManager.getAddress() + "] replicating " + invalidateCommand);
139+
}
141140

142-
rpcManager.invokeRemotely(null, invalidateCommand, rpcManager.getDefaultRpcOptions(synchronous));
141+
rpcManager.invokeRemotely(null, invalidateCommand, rpcManager.getDefaultRpcOptions(isSynchronous(command)));
142+
}
143143
}
144144

145145
private void incrementInvalidations() {

hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/NonTxPutFromLoadInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public void injectDependencies(CacheCommandInitializer commandInitializer, RpcMa
4545
public Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable {
4646
if (!ctx.isOriginLocal() && command instanceof BeginInvalidationCommand) {
4747
for (Object key : command.getKeys()) {
48-
putFromLoadValidator.beginInvalidatingKey(key, ((BeginInvalidationCommand) command).getLockOwner());
48+
putFromLoadValidator.beginInvalidatingKey(((BeginInvalidationCommand) command).getLockOwner(), key);
4949
}
5050
}
5151
return invokeNextInterceptor(ctx, command);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.cache.infinispan.access;
8+
9+
import org.hibernate.cache.CacheException;
10+
import org.hibernate.cache.infinispan.impl.BaseRegion;
11+
import org.hibernate.engine.spi.SessionImplementor;
12+
import org.hibernate.resource.transaction.TransactionCoordinator;
13+
import org.hibernate.resource.transaction.spi.TransactionStatus;
14+
15+
/**
16+
* Delegate for non-transactional caches
17+
*
18+
* @author Radim Vansa &lt;[email protected]&gt;
19+
*/
20+
public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegate {
21+
public NonTxTransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
22+
super(region, validator);
23+
}
24+
25+
@Override
26+
@SuppressWarnings("UnusedParameters")
27+
public boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
28+
if ( !region.checkValid() ) {
29+
return false;
30+
}
31+
32+
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
33+
// (or any other invalidation), naked put that was started after the eviction ended but before this insert
34+
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
35+
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) {
36+
throw new CacheException(
37+
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
38+
);
39+
}
40+
putValidator.setCurrentSession(session);
41+
try {
42+
writeCache.remove(key);
43+
}
44+
finally {
45+
putValidator.resetCurrentSession();
46+
}
47+
return true;
48+
}
49+
50+
@Override
51+
@SuppressWarnings("UnusedParameters")
52+
public boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
53+
throws CacheException {
54+
// We update whether or not the region is valid. Other nodes
55+
// may have already restored the region so they need to
56+
// be informed of the change.
57+
58+
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
59+
// (or any other invalidation), naked put that was started after the eviction ended but before this update
60+
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
61+
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) {
62+
throw new CacheException(
63+
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
64+
);
65+
}
66+
putValidator.setCurrentSession(session);
67+
try {
68+
writeCache.remove(key);
69+
}
70+
finally {
71+
putValidator.resetCurrentSession();
72+
}
73+
return true;
74+
}
75+
76+
@Override
77+
public void unlockItem(SessionImplementor session, Object key) throws CacheException {
78+
TransactionCoordinator tc = session.getTransactionCoordinator();
79+
boolean doPFER = tc != null && tc.getTransactionDriverControl().getStatus() == TransactionStatus.COMMITTED;
80+
if ( !putValidator.endInvalidatingKey(session, key, doPFER) ) {
81+
// TODO: localization
82+
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
83+
+ region.getName() + "; the key won't be cached until invalidation expires.");
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)