package org.infinispan.upgrade;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.infinispan.Cache;
import org.infinispan.commons.util.ServiceFinder;
import org.infinispan.commons.util.Util;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.SurvivesRestarts;
import org.infinispan.factories.scopes.Scope;
import org.infinispan.factories.scopes.Scopes;
import org.infinispan.jmx.annotations.MBean;
import org.infinispan.jmx.annotations.ManagedOperation;
import org.infinispan.jmx.annotations.Parameter;
import org.infinispan.util.TimeService;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* This component handles the control hooks to handle migrating from one version of Infinispan to
* another.
*
* @author Manik Surtani
* @author Tristan Tarrant
* @since 5.2
*/
@MBean(objectName = "RollingUpgradeManager", description = "This component handles the control hooks to handle migrating data from one version of Infinispan to another")
@Scope(value = Scopes.NAMED_CACHE)
@SurvivesRestarts
public class RollingUpgradeManager {
private static final Log log = LogFactory.getLog(RollingUpgradeManager.class);
private final Set<SourceMigrator> sourceMigrators = new HashSet<SourceMigrator>(2);
private Cache<Object, Object> cache;
private TimeService timeService;
@Inject
public void initialize(final Cache<Object, Object> cache, TimeService timeService) {
this.cache = cache;
this.timeService = timeService;
}
@ManagedOperation(
description = "Dumps the global known keyset to a well-known key for retrieval by the upgrade process",
displayName = "Dumps the global known keyset"
)
public void recordKnownGlobalKeyset() {
for (SourceMigrator m : sourceMigrators)
m.recordKnownGlobalKeyset();
}
@ManagedOperation(
description = "Synchronizes data from the old cluster to this using the specified migrator",
displayName = "Synchronizes data from the old cluster to this using the specified migrator"
)
public long synchronizeData(@Parameter(name="migratorName", description="The name of the migrator to use") String migratorName) throws Exception {
TargetMigrator migrator = getMigrator(migratorName);
long start = timeService.time();
long count = migrator.synchronizeData(cache);
log.entriesMigrated(count, cache.getName(), Util.prettyPrintTime(timeService.timeDuration(start, TimeUnit.MILLISECONDS)));
return count;
}
@ManagedOperation(
description = "Synchronizes data from the old cluster to this using the specified migrator",
displayName = "Synchronizes data from the old cluster to this using the specified migrator"
)
public long synchronizeData(@Parameter(name = "migratorName", description = "The name of the migrator to use") String migratorName,
@Parameter(name = "readBatch", description = "Numbers of entries transferred at a time from the old cluster") int readBatch,
@Parameter(name = "threads", description = "Number of threads per node used to write data to the new cluster") int threads) throws Exception {
TargetMigrator migrator = getMigrator(migratorName);
long start = timeService.time();
long count = migrator.synchronizeData(cache, readBatch, threads);
log.entriesMigrated(count, cache.getName(), Util.prettyPrintTime(timeService.timeDuration(start, TimeUnit.MILLISECONDS)));
return count;
}
@ManagedOperation(
description = "Disconnects the target cluster from the source cluster according to the specified migrator",
displayName = "Disconnects the target cluster from the source cluster"
)
public void disconnectSource(@Parameter(name="migratorName", description="The name of the migrator to use") String migratorName) throws Exception {
TargetMigrator migrator = getMigrator(migratorName);
migrator.disconnectSource(cache);
}
private TargetMigrator getMigrator(String name) throws Exception {
ClassLoader cl = cache.getCacheManager().getCacheManagerConfiguration().classLoader();
for (TargetMigrator m : ServiceFinder.load(TargetMigrator.class, cl)) {
if (name.equalsIgnoreCase(m.getName())) {
return m;
}
}
throw log.unknownMigrator(name);
}
/**
* Registers a migrator for a specific data format or endpoint. In the Infinispan ecosystem, we'd
* typically have one Migrator implementation for Hot Rod, one for memcached, one for REST and
* one for embedded/in-VM mode, and these would typically be added to the upgrade manager on
* first access via any of these protocols.
*
* @param migrator
*/
public void addSourceMigrator(SourceMigrator migrator) {
sourceMigrators.add(migrator);
}
}