
org.infinispan.client.hotrod.impl.RemoteCacheImpl Maven / Gradle / Ivy
package org.infinispan.client.hotrod.impl;
import static org.infinispan.client.hotrod.filter.Filters.makeFactoryParams;
import static org.infinispan.client.hotrod.impl.Util.await;
import static org.infinispan.client.hotrod.logging.Log.HOTROD;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.infinispan.api.async.AsyncCacheEntryProcessor;
import org.infinispan.api.common.CacheEntry;
import org.infinispan.api.common.CacheEntryVersion;
import org.infinispan.api.common.CacheOptions;
import org.infinispan.api.common.CacheWriteOptions;
import org.infinispan.api.common.events.cache.CacheEntryEvent;
import org.infinispan.api.common.events.cache.CacheEntryEventType;
import org.infinispan.api.common.events.cache.CacheListenerOptions;
import org.infinispan.api.common.process.CacheEntryProcessorResult;
import org.infinispan.api.common.process.CacheProcessorOptions;
import org.infinispan.api.configuration.CacheConfiguration;
import org.infinispan.client.hotrod.CacheTopologyInfo;
import org.infinispan.client.hotrod.DataFormat;
import org.infinispan.client.hotrod.Flag;
import org.infinispan.client.hotrod.MetadataValue;
import org.infinispan.client.hotrod.ProtocolVersion;
import org.infinispan.client.hotrod.RemoteCacheContainer;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.ServerStatistics;
import org.infinispan.client.hotrod.StreamingRemoteCache;
import org.infinispan.client.hotrod.configuration.Configuration;
import org.infinispan.client.hotrod.configuration.StatisticsConfiguration;
import org.infinispan.client.hotrod.event.impl.ClientListenerNotifier;
import org.infinispan.client.hotrod.exceptions.RemoteCacheManagerNotStartedException;
import org.infinispan.client.hotrod.filter.Filters;
import org.infinispan.client.hotrod.impl.cache.CacheEntryImpl;
import org.infinispan.client.hotrod.impl.cache.CacheEntryMetadataImpl;
import org.infinispan.client.hotrod.impl.cache.CacheEntryVersionImpl;
import org.infinispan.client.hotrod.impl.iteration.RemotePublisher;
import org.infinispan.client.hotrod.impl.operations.AddClientListenerOperation;
import org.infinispan.client.hotrod.impl.operations.AdvancedHotRodOperation;
import org.infinispan.client.hotrod.impl.operations.CacheOperationsFactory;
import org.infinispan.client.hotrod.impl.operations.GetAllBulkOperation;
import org.infinispan.client.hotrod.impl.operations.GetWithMetadataOperation;
import org.infinispan.client.hotrod.impl.operations.HotRodOperation;
import org.infinispan.client.hotrod.impl.operations.PingResponse;
import org.infinispan.client.hotrod.impl.operations.PutAllBulkOperation;
import org.infinispan.client.hotrod.impl.protocol.Codec30;
import org.infinispan.client.hotrod.impl.query.RemoteQueryFactory;
import org.infinispan.client.hotrod.impl.transport.netty.ChannelRecord;
import org.infinispan.client.hotrod.impl.transport.netty.OperationDispatcher;
import org.infinispan.client.hotrod.logging.Log;
import org.infinispan.client.hotrod.logging.LogFactory;
import org.infinispan.client.hotrod.near.NearCacheService;
import org.infinispan.commons.api.query.ContinuousQuery;
import org.infinispan.commons.api.query.Query;
import org.infinispan.commons.dataconversion.MediaType;
import org.infinispan.commons.time.TimeService;
import org.infinispan.commons.util.CloseableIterator;
import org.infinispan.commons.util.CloseableIteratorCollection;
import org.infinispan.commons.util.CloseableIteratorSet;
import org.infinispan.commons.util.Closeables;
import org.infinispan.commons.util.EnumUtil;
import org.infinispan.commons.util.IntSet;
import org.infinispan.commons.util.IteratorMapper;
import org.infinispan.commons.util.concurrent.CompletableFutures;
import org.reactivestreams.FlowAdapters;
import org.reactivestreams.Publisher;
import io.netty.channel.Channel;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.Single;
/**
* @author [email protected]
* @since 4.1
*/
public class RemoteCacheImpl extends RemoteCacheSupport implements InternalRemoteCache {
private static final Log log = LogFactory.getLog(RemoteCacheImpl.class, Log.class);
protected final String name;
protected final byte[] nameBytes;
protected final RemoteCacheManager remoteCacheManager;
protected final CacheOperationsFactory operationsFactory;
protected final ClientListenerNotifier clientListenerNotifier;
protected final int flagInt;
protected int batchSize;
protected DataFormat dataFormat;
protected ClientStatistics clientStatistics;
protected ObjectName mbeanObjectName;
protected OperationDispatcher dispatcher;
protected RemoteQueryFactory queryFactory;
public RemoteCacheImpl(RemoteCacheManager rcm, String name, TimeService timeService,
Function, CacheOperationsFactory> factoryFunction) {
this(rcm, name, timeService, null, factoryFunction);
}
public RemoteCacheImpl(RemoteCacheManager rcm, String name, TimeService timeService, NearCacheService nearCacheService,
Function, CacheOperationsFactory> factoryFunction) {
if (log.isTraceEnabled()) {
log.tracef("Creating remote cache: %s", name);
}
this.name = name;
this.nameBytes = name.getBytes(StandardCharsets.UTF_8);
this.remoteCacheManager = rcm;
this.dataFormat = DataFormat.builder().build();
this.clientStatistics = new ClientStatistics(timeService, nearCacheService, rcm.getConfiguration().metricRegistry().withCache(name));
this.operationsFactory = factoryFunction.apply(this);
this.clientListenerNotifier = rcm.getListenerNotifier();
this.flagInt = rcm.getConfiguration().forceReturnValues() ? Flag.FORCE_RETURN_VALUE.getFlagInt() : 0;
}
protected RemoteCacheImpl(RemoteCacheImpl, ?> other, int flagInt) {
if (log.isTraceEnabled()) {
log.tracef("Creating remote cache: %s with flags %d", other.name, flagInt);
}
this.name = other.name;
this.nameBytes = other.nameBytes;
this.remoteCacheManager = other.remoteCacheManager;
this.dataFormat = other.dataFormat;
this.clientStatistics = other.clientStatistics;
this.operationsFactory = other.operationsFactory.newFactoryFor(this);
this.flagInt = flagInt;
this.clientListenerNotifier = other.clientListenerNotifier;
// set the values for init
this.batchSize = other.batchSize;
this.dispatcher = other.dispatcher;
this.queryFactory = other.queryFactory;
}
@Override
public void init(Configuration configuration, OperationDispatcher dispatcher, ObjectName jmxParent) {
init(configuration, dispatcher);
if (jmxParent != null) {
registerMBean(jmxParent);
}
}
@Override
public OperationDispatcher getDispatcher() {
return dispatcher;
}
/**
* Inititalize without mbeans
*/
public void init(Configuration configuration, OperationDispatcher dispatcher) {
init(configuration.batchSize(), dispatcher);
}
private void init(int batchSize, OperationDispatcher dispatcher) {
this.batchSize = batchSize;
this.dispatcher = dispatcher;
// try cautiously
try {
this.queryFactory = new RemoteQueryFactory(this);
} catch (Throwable e) {
log.queryDisabled();
}
}
private void registerMBean(ObjectName jmxParent) {
StatisticsConfiguration configuration = getRemoteCacheContainer().getConfiguration().statistics();
if (configuration.jmxEnabled()) {
try {
MBeanServer mbeanServer = configuration.mbeanServerLookup().getMBeanServer();
String cacheName = name.isEmpty() ? "org.infinispan.default" : name;
mbeanObjectName = new ObjectName(String.format("%s:type=HotRodClient,name=%s,cache=%s", jmxParent.getDomain(), configuration.jmxName(), cacheName));
mbeanServer.registerMBean(clientStatistics, mbeanObjectName);
} catch (Exception e) {
throw HOTROD.jmxRegistrationFailure(e);
}
}
}
private synchronized void unregisterMBean() {
if (mbeanObjectName != null) {
try {
MBeanServer mBeanServer = getRemoteCacheContainer().getConfiguration().statistics()
.mbeanServerLookup().getMBeanServer();
if (mBeanServer.isRegistered(mbeanObjectName)) {
mBeanServer.unregisterMBean(mbeanObjectName);
} else {
HOTROD.debugf("MBean not registered: %s", mbeanObjectName);
}
mbeanObjectName = null;
} catch (Exception e) {
throw HOTROD.jmxUnregistrationFailure(e);
}
}
}
@Override
public RemoteCacheContainer getRemoteCacheContainer() {
return remoteCacheManager;
}
@Override
public CompletableFuture removeWithVersionAsync(final K key, final long version) {
assertRemoteCacheManagerIsStarted();
HotRodOperation> op = operationsFactory.newRemoveIfUnmodifiedOperation(key, version);
return dispatcher.execute(op)
.thenApply(response -> response.getCode().isUpdated())
.toCompletableFuture();
}
@Override
public CompletableFuture mergeAsync(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
throw new UnsupportedOperationException();
}
public CompletableFuture replaceWithVersionAsync(K key, V newValue, long version, long lifespan, TimeUnit lifespanTimeUnit, long maxIdle, TimeUnit maxIdleTimeUnit) {
assertRemoteCacheManagerIsStarted();
HotRodOperation> op = operationsFactory.newReplaceIfUnmodifiedOperation(
key, newValue, lifespan, lifespanTimeUnit, maxIdle, maxIdleTimeUnit, version);
return dispatcher.execute(op)
.thenApply(response -> response.getCode().isUpdated())
.toCompletableFuture();
}
@Override
public CloseableIterator> retrieveEntries(String filterConverterFactory, Object[] filterConverterParams, Set segments, int batchSize) {
Publisher> remotePublisher = publishEntries(filterConverterFactory, filterConverterParams, segments, batchSize);
//noinspection unchecked
return Closeables.iterator((Publisher) remotePublisher, batchSize);
}
@Override
public Publisher> publishEntries(String filterConverterFactory, Object[] filterConverterParams, Set segments, int batchSize) {
assertRemoteCacheManagerIsStarted();
if (segments != null && segments.isEmpty()) {
return Flowable.empty();
}
return new RemotePublisher<>(operationsFactory, dispatcher, filterConverterFactory, filterConverterParams, segments,
batchSize, false, dataFormat);
}
@Override
public CloseableIterator> retrieveEntriesByQuery(Query> filterQuery, Set segments, int batchSize) {
Publisher> remotePublisher = publishEntriesByQuery(filterQuery, segments, batchSize);
//noinspection unchecked
return Closeables.iterator((Publisher) remotePublisher, batchSize);
}
@Override
public Publisher> publishEntriesByQuery(Query> filterQuery, Set segments, int batchSize) {
Object[] factoryParams = makeFactoryParams(filterQuery);
return publishEntries(Filters.ITERATION_QUERY_FILTER_CONVERTER_FACTORY_NAME, factoryParams, segments, batchSize);
}
@Override
public CloseableIterator>> retrieveEntriesWithMetadata(Set segments, int batchSize) {
Publisher>> remotePublisher = publishEntriesWithMetadata(segments, batchSize);
//noinspection unchecked
return Closeables.iterator((Publisher) remotePublisher, batchSize);
}
@Override
public Publisher>> publishEntriesWithMetadata(Set segments, int batchSize) {
return new RemotePublisher<>(operationsFactory, dispatcher, null, null, segments,
batchSize, true, dataFormat);
}
@Override
public CompletableFuture> getWithMetadataAsync(K key) {
assertRemoteCacheManagerIsStarted();
var op = operationsFactory.newGetWithMetadataOperation(key, null);
return dispatcher.execute(op)
.thenApply(GetWithMetadataOperation.GetWithMetadataResult::value)
.toCompletableFuture();
}
@Override
public CompletionStage> getWithMetadataAsync(K key, Channel channel) {
assertRemoteCacheManagerIsStarted();
var op = operationsFactory.newGetWithMetadataOperation(key, channel);
return channel != null ?
dispatcher.executeOnSingleAddress(op, ChannelRecord.of(channel)) :
dispatcher.execute(op);
}
@Override
public CompletableFuture putAllAsync(Map extends K, ? extends V> map, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
assertRemoteCacheManagerIsStarted();
if (log.isTraceEnabled()) {
log.tracef("About to putAll entries (%s) lifespan:%d (%s), maxIdle:%d (%s)", map, lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
}
var op = new PutAllBulkOperation(map, dataFormat,
serialized -> operationsFactory.newPutAllBytesOperation(serialized, lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit));
return dispatcher.executeBulk(name, op).toCompletableFuture();
}
@Override
public CompletableFuture sizeAsync() {
assertRemoteCacheManagerIsStarted();
HotRodOperation op = operationsFactory.newSizeOperation();
return dispatcher.execute(op)
.toCompletableFuture()
.thenApply(Integer::longValue);
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public ClientStatistics clientStatistics() {
return clientStatistics;
}
@Override
public ServerStatistics serverStatistics() {
return await(serverStatisticsAsync());
}
@Override
public CompletionStage serverStatisticsAsync() {
assertRemoteCacheManagerIsStarted();
HotRodOperation op = operationsFactory.newStatsOperation();
return dispatcher.execute(op).toCompletableFuture();
}
@Override
public CompletableFuture putAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
assertRemoteCacheManagerIsStarted();
if (log.isTraceEnabled()) {
log.tracef("About to add (K,V): (%s, %s) lifespan:%d, maxIdle:%d", key, value, lifespan, maxIdleTime);
}
HotRodOperation> op = operationsFactory.newPutKeyValueOperation(key, value, lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
return dispatcher.execute(op)
.thenApply(CacheEntryConversion.extractValue())
.toCompletableFuture();
}
@Override
public CompletableFuture clearAsync() {
assertRemoteCacheManagerIsStarted();
HotRodOperation op = operationsFactory.newClearOperation();
return dispatcher.execute(op).toCompletableFuture();
}
@Override
public CompletableFuture computeAsync(K key, BiFunction super K, ? super V, ? extends V> remappingFunction, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
CompletableFuture> cf = getWithMetadataAsync(key);
return cf.thenCompose(metadataValue -> {
V newValue;
V oldValue;
long version;
if (metadataValue != null) {
oldValue = metadataValue.getValue();
version = metadataValue.getVersion();
} else {
oldValue = null;
version = -1;
}
newValue = remappingFunction.apply(key, oldValue);
CompletionStage doneStage;
if (newValue != null) {
if (oldValue != null) {
doneStage = replaceWithVersionAsync(key, newValue, version, lifespan, lifespanUnit, maxIdle, maxIdleUnit);
} else {
doneStage = putIfAbsentAsync(key, newValue, lifespan, lifespanUnit, maxIdle, maxIdleUnit, Flag.FORCE_RETURN_VALUE)
.thenApply(Objects::isNull);
}
} else {
if (oldValue != null) {
doneStage = removeWithVersionAsync(key, version);
} else {
// Nothing to remove
doneStage = CompletableFuture.completedFuture(Boolean.TRUE);
}
}
return doneStage.thenCompose(done -> {
if (done) {
return CompletableFuture.completedFuture(newValue);
}
// Retry if one of the operations failed
return computeAsync(key, remappingFunction, lifespan, lifespanUnit, maxIdle, maxIdleUnit);
});
});
}
@Override
public CompletableFuture computeIfAbsentAsync(K key, Function super K, ? extends V> mappingFunction, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
CompletableFuture cf = getAsync(key);
return cf.thenCompose(oldValue -> {
if (oldValue != null) return CompletableFuture.completedFuture(oldValue);
V newValue = mappingFunction.apply(key);
if (newValue == null) return CompletableFutures.completedNull();
return putIfAbsentAsync(key, newValue, lifespan, lifespanUnit, maxIdle, maxIdleUnit)
.thenApply(v -> v == null ? newValue : v);
});
}
@Override
public CompletableFuture computeIfPresentAsync(K key, BiFunction super K, ? super V, ? extends V> remappingFunction, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
CompletableFuture> cf = getWithMetadataAsync(key);
return cf.thenCompose(metadata -> {
if (metadata == null || metadata.getValue() == null) return CompletableFutures.completedNull();
V newValue = remappingFunction.apply(key, metadata.getValue());
CompletableFuture done;
if (newValue == null) {
done = removeWithVersionAsync(key, metadata.getVersion());
} else {
done = replaceWithVersionAsync(key, newValue, metadata.getVersion(), lifespan, lifespanUnit, maxIdle, maxIdleUnit);
}
return done.thenCompose(success -> {
if (success) {
return CompletableFuture.completedFuture(newValue);
}
return computeIfPresentAsync(key, remappingFunction, lifespan, lifespanUnit, maxIdle, maxIdleUnit);
});
});
}
@Override
public void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture putIfAbsentAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
assertRemoteCacheManagerIsStarted();
HotRodOperation> op = operationsFactory.newPutIfAbsentOperation(key, value, lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
return dispatcher.execute(op)
.thenApply(CacheEntryConversion.extractValue())
.toCompletableFuture();
}
private CompletableFuture putIfAbsentAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit, Flag ... flags) {
assertRemoteCacheManagerIsStarted();
HotRodOperation> op = operationsFactory.newPutIfAbsentOperation(key, value, lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit, flags);
return dispatcher.execute(op)
.thenApply(CacheEntryConversion.extractValue())
.toCompletableFuture();
}
@Override
public CompletableFuture replaceAsync(K key, V oldValue, V newValue, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
Objects.requireNonNull(oldValue);
Objects.requireNonNull(newValue);
CompletionStage> stage = getWithMetadataAsync(key);
return stage.thenCompose(metadataValue -> {
if (metadataValue != null) {
V prevValue = metadataValue.getValue();
if (oldValue.equals(prevValue)) {
return replaceWithVersionAsync(key, newValue, metadataValue.getVersion(), lifespan, lifespanUnit, maxIdle, maxIdleUnit)
.thenCompose(replaced -> {
if (replaced) {
return CompletableFuture.completedFuture(replaced);
}
// Concurrent modification - the value could still equal - we need to retry
return replaceAsync(key, oldValue, newValue, lifespan, lifespanUnit, maxIdle, maxIdleUnit);
});
}
}
return CompletableFuture.completedFuture(Boolean.FALSE);
}).toCompletableFuture();
}
@Override
public CompletableFuture removeAsync(Object key) {
assertRemoteCacheManagerIsStarted();
HotRodOperation> removeOperation = operationsFactory.newRemoveOperation(key);
// TODO: It sucks that you need the prev value to see if it works...
// We need to find a better API for RemoteCache...
return dispatcher.execute(removeOperation)
.thenApply(CacheEntryConversion.extractValue())
.toCompletableFuture();
}
@Override
public CompletableFuture removeAsync(Object key, Object value) {
Objects.requireNonNull(value);
CompletionStage> stage = getWithMetadataAsync((K) key);
return stage.thenCompose(metadataValue -> {
if (metadataValue == null || !value.equals(metadataValue.getValue())) {
return CompletableFuture.completedFuture(Boolean.FALSE);
}
return removeWithVersionAsync((K) key, metadataValue.getVersion())
.thenCompose(removed -> {
if (removed) {
return CompletableFuture.completedFuture(Boolean.TRUE);
}
// Concurrent operation - need to retry
return removeAsync(key, value);
});
}).toCompletableFuture();
}
@Override
public CompletableFuture replaceAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime,
TimeUnit maxIdleTimeUnit) {
assertRemoteCacheManagerIsStarted();
HotRodOperation op = operationsFactory.newReplaceOperation(key, value, lifespan, lifespanUnit, maxIdleTime,
maxIdleTimeUnit);
return dispatcher.execute(op).toCompletableFuture();
}
@Override
public CompletableFuture containsKeyAsync(K key) {
assertRemoteCacheManagerIsStarted();
HotRodOperation op = operationsFactory.newContainsKeyOperation(key);
return dispatcher.execute(op).toCompletableFuture();
}
@Override
public boolean containsValue(Object value) {
Objects.requireNonNull(value);
return values().contains(value);
}
@Override
public CompletableFuture
© 2015 - 2025 Weber Informatics LLC | Privacy Policy