Skip to content

Commit 1f24fa6

Browse files
rvansagalderz
authored andcommitted
HHH-9868, HHH-9881 Implementation for non-transactional caches and non-JTA transactions.
1 parent fa7265f commit 1f24fa6

21 files changed

+1007
-185
lines changed

hibernate-infinispan/hibernate-infinispan.gradle

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ def osgiDescription() {
3636
return mavenPom.description
3737
}
3838

39+
classes.doLast {
40+
javaexec {
41+
classpath = project.sourceSets.main.runtimeClasspath
42+
main = "org.infinispan.factories.components.ComponentMetadataPersister"
43+
args = [
44+
project.sourceSets.main.output.classesDir,
45+
project.sourceSets.main.output.resourcesDir.toPath().resolve("hibernate-infinispan-component-metadata.dat").toString()
46+
].toList()
47+
standardOutput = { def f = File.createTempFile('metadata-log', null ); f.deleteOnExit(); f.newOutputStream() }()
48+
}
49+
}
50+
3951
test {
4052
systemProperties['java.net.preferIPv4Stack'] = true
4153
systemProperties['jgroups.ping.timeout'] = 500

hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.hibernate.cache.infinispan.tm.HibernateTransactionManagerLookup;
3535
import org.hibernate.cache.infinispan.util.CacheCommandFactory;
3636
import org.hibernate.cache.infinispan.util.Caches;
37+
import org.hibernate.cache.infinispan.util.Externalizers;
3738
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
3839
import org.hibernate.cache.internal.SimpleCacheKeysFactory;
3940
import org.hibernate.cache.spi.CacheDataDescription;
@@ -475,6 +476,7 @@ public EmbeddedCacheManager doWork(ClassLoader classLoader) {
475476
.globalJmxStatistics()
476477
.enabled( Boolean.parseBoolean( globalStats ) );
477478
}
479+
holder.getGlobalConfigurationBuilder().serialization().addAdvancedExternalizer(Externalizers.ALL_EXTERNALIZERS);
478480

479481
return createCacheManager( holder );
480482
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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.infinispan.util.CacheCommandInitializer;
10+
import org.infinispan.commands.CommandsFactory;
11+
import org.infinispan.commands.FlagAffectedCommand;
12+
import org.infinispan.commands.write.ClearCommand;
13+
import org.infinispan.commands.write.InvalidateCommand;
14+
import org.infinispan.commands.write.PutKeyValueCommand;
15+
import org.infinispan.commands.write.PutMapCommand;
16+
import org.infinispan.commands.write.RemoveCommand;
17+
import org.infinispan.commands.write.ReplaceCommand;
18+
import org.infinispan.commands.write.WriteCommand;
19+
import org.infinispan.commons.util.InfinispanCollections;
20+
import org.infinispan.context.Flag;
21+
import org.infinispan.context.InvocationContext;
22+
import org.infinispan.factories.annotations.Inject;
23+
import org.infinispan.factories.annotations.Start;
24+
import org.infinispan.interceptors.InvalidationInterceptor;
25+
import org.infinispan.interceptors.base.BaseRpcInterceptor;
26+
import org.infinispan.jmx.JmxStatisticsExposer;
27+
import org.infinispan.jmx.annotations.DataType;
28+
import org.infinispan.jmx.annotations.MBean;
29+
import org.infinispan.jmx.annotations.ManagedAttribute;
30+
import org.infinispan.jmx.annotations.ManagedOperation;
31+
import org.infinispan.jmx.annotations.MeasurementType;
32+
import org.infinispan.jmx.annotations.Parameter;
33+
import org.infinispan.util.logging.Log;
34+
import org.infinispan.util.logging.LogFactory;
35+
36+
import java.util.concurrent.atomic.AtomicLong;
37+
38+
/**
39+
* This interceptor should completely replace default InvalidationInterceptor.
40+
* We need to send custom invalidation commands with transaction identifier (as the invalidation)
41+
* since we have to do a two-phase invalidation (releasing the locks as JTA synchronization),
42+
* although the cache itself is non-transactional.
43+
*
44+
* @author Radim Vansa &lt;[email protected]&gt;
45+
46+
* @author Galder Zamarreño
47+
*/
48+
@MBean(objectName = "Invalidation", description = "Component responsible for invalidating entries on remote caches when entries are written to locally.")
49+
public class NonTxInvalidationInterceptor extends BaseRpcInterceptor implements JmxStatisticsExposer {
50+
private final AtomicLong invalidations = new AtomicLong(0);
51+
private final PutFromLoadValidator putFromLoadValidator;
52+
private CommandsFactory commandsFactory;
53+
private CacheCommandInitializer commandInitializer;
54+
private boolean statisticsEnabled;
55+
56+
private static final Log log = LogFactory.getLog(InvalidationInterceptor.class);
57+
58+
public NonTxInvalidationInterceptor(PutFromLoadValidator putFromLoadValidator) {
59+
this.putFromLoadValidator = putFromLoadValidator;
60+
}
61+
62+
@Override
63+
protected Log getLog() {
64+
return log;
65+
}
66+
67+
@Inject
68+
public void injectDependencies(CommandsFactory commandsFactory, CacheCommandInitializer commandInitializer) {
69+
this.commandsFactory = commandsFactory;
70+
this.commandInitializer = commandInitializer;
71+
}
72+
73+
@Start
74+
private void start() {
75+
this.setStatisticsEnabled(cacheConfiguration.jmxStatistics().enabled());
76+
}
77+
78+
@Override
79+
public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable {
80+
if (!isPutForExternalRead(command)) {
81+
return handleInvalidate(ctx, command, command.getKey());
82+
}
83+
return invokeNextInterceptor(ctx, command);
84+
}
85+
86+
@Override
87+
public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable {
88+
return handleInvalidate(ctx, command, command.getKey());
89+
}
90+
91+
@Override
92+
public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command) throws Throwable {
93+
return handleInvalidate(ctx, command, command.getKey());
94+
}
95+
96+
@Override
97+
public Object visitClearCommand(InvocationContext ctx, ClearCommand command) throws Throwable {
98+
Object retval = invokeNextInterceptor(ctx, command);
99+
if (!isLocalModeForced(command)) {
100+
// just broadcast the clear command - this is simplest!
101+
if (ctx.isOriginLocal()) {
102+
rpcManager.invokeRemotely(null, command, rpcManager.getDefaultRpcOptions(defaultSynchronous));
103+
}
104+
}
105+
return retval;
106+
}
107+
108+
@Override
109+
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);
112+
}
113+
114+
private Object handleInvalidate(InvocationContext ctx, WriteCommand command, Object... keys) throws Throwable {
115+
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+
}
122+
}
123+
return retval;
124+
}
125+
126+
private void invalidateAcrossCluster(boolean synchronous, Object[] keys, InvocationContext ctx) throws Throwable {
127+
// increment invalidations counter if statistics maintained
128+
incrementInvalidations();
129+
InvalidateCommand invalidateCommand;
130+
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+
}
141+
142+
rpcManager.invokeRemotely(null, invalidateCommand, rpcManager.getDefaultRpcOptions(synchronous));
143+
}
144+
145+
private void incrementInvalidations() {
146+
if (statisticsEnabled) {
147+
invalidations.incrementAndGet();
148+
}
149+
}
150+
151+
private boolean isPutForExternalRead(FlagAffectedCommand command) {
152+
if (command.hasFlag(Flag.PUT_FOR_EXTERNAL_READ)) {
153+
log.trace("Put for external read called. Suppressing clustered invalidation.");
154+
return true;
155+
}
156+
return false;
157+
}
158+
159+
@ManagedOperation(
160+
description = "Resets statistics gathered by this component",
161+
displayName = "Reset statistics"
162+
)
163+
public void resetStatistics() {
164+
invalidations.set(0);
165+
}
166+
167+
@ManagedAttribute(
168+
displayName = "Statistics enabled",
169+
description = "Enables or disables the gathering of statistics by this component",
170+
dataType = DataType.TRAIT,
171+
writable = true
172+
)
173+
public boolean getStatisticsEnabled() {
174+
return this.statisticsEnabled;
175+
}
176+
177+
public void setStatisticsEnabled(@Parameter(name = "enabled", description = "Whether statistics should be enabled or disabled (true/false)") boolean enabled) {
178+
this.statisticsEnabled = enabled;
179+
}
180+
181+
@ManagedAttribute(
182+
description = "Number of invalidations",
183+
displayName = "Number of invalidations",
184+
measurementType = MeasurementType.TRENDSUP
185+
)
186+
public long getInvalidations() {
187+
return invalidations.get();
188+
}
189+
190+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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.infinispan.util.BeginInvalidationCommand;
10+
import org.hibernate.cache.infinispan.util.CacheCommandInitializer;
11+
import org.hibernate.cache.infinispan.util.EndInvalidationCommand;
12+
import org.infinispan.commands.write.InvalidateCommand;
13+
import org.infinispan.context.InvocationContext;
14+
import org.infinispan.factories.annotations.Inject;
15+
import org.infinispan.interceptors.base.BaseCustomInterceptor;
16+
import org.infinispan.remoting.inboundhandler.DeliverOrder;
17+
import org.infinispan.remoting.rpc.RpcManager;
18+
19+
/**
20+
* Non-transactional counterpart of {@link TxPutFromLoadInterceptor}.
21+
* Invokes {@link PutFromLoadValidator#beginInvalidatingKey(Object, Object)} for each invalidation from
22+
* remote node ({@link BeginInvalidationCommand} and sends {@link EndInvalidationCommand} after the transaction
23+
* is complete, with help of {@link Synchronization};
24+
*
25+
* @author Radim Vansa &lt;[email protected]&gt;
26+
*/
27+
public class NonTxPutFromLoadInterceptor extends BaseCustomInterceptor {
28+
private final String cacheName;
29+
private final PutFromLoadValidator putFromLoadValidator;
30+
private CacheCommandInitializer commandInitializer;
31+
private RpcManager rpcManager;
32+
33+
public NonTxPutFromLoadInterceptor(PutFromLoadValidator putFromLoadValidator, String cacheName) {
34+
this.putFromLoadValidator = putFromLoadValidator;
35+
this.cacheName = cacheName;
36+
}
37+
38+
@Inject
39+
public void injectDependencies(CacheCommandInitializer commandInitializer, RpcManager rpcManager) {
40+
this.commandInitializer = commandInitializer;
41+
this.rpcManager = rpcManager;
42+
}
43+
44+
@Override
45+
public Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable {
46+
if (!ctx.isOriginLocal() && command instanceof BeginInvalidationCommand) {
47+
for (Object key : command.getKeys()) {
48+
putFromLoadValidator.beginInvalidatingKey(key, ((BeginInvalidationCommand) command).getLockOwner());
49+
}
50+
}
51+
return invokeNextInterceptor(ctx, command);
52+
}
53+
54+
public void broadcastEndInvalidationCommand(Object[] keys, Object lockOwner) {
55+
assert lockOwner != null;
56+
EndInvalidationCommand endInvalidationCommand = commandInitializer.buildEndInvalidationCommand(
57+
cacheName, keys, lockOwner);
58+
rpcManager.invokeRemotely(null, endInvalidationCommand, rpcManager.getDefaultRpcOptions(false, DeliverOrder.NONE));
59+
}
60+
}

0 commit comments

Comments
 (0)