substantial refactoring

This commit is contained in:
2024-11-13 18:02:19 -05:00
parent f29a0e6e3f
commit 8be29fc37d
50 changed files with 1550 additions and 642 deletions

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.1-SNAPSHOT</version> <version>1.2-SNAPSHOT</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.1-SNAPSHOT</version> <version>1.2-SNAPSHOT</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>

View File

@@ -9,6 +9,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback; import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
@@ -23,10 +24,11 @@ import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi; import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.alfresco.asie.model.NodeParameterSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript; import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript;
import com.inteligr8.alfresco.asie.service.ShardBackupService; import com.inteligr8.alfresco.asie.rest.model.NodeParameterSet;
import com.inteligr8.alfresco.asie.spi.ShardStateService; import com.inteligr8.alfresco.asie.spi.ShardBackupService;
import com.inteligr8.alfresco.asie.enterprise.service.ShardStateService;
import com.inteligr8.solr.model.CoreMetadata; import com.inteligr8.solr.model.CoreMetadata;
import com.inteligr8.solr.model.core.StatusRequest; import com.inteligr8.solr.model.core.StatusRequest;
import com.inteligr8.solr.model.core.StatusResponse; import com.inteligr8.solr.model.core.StatusResponse;
@@ -99,7 +101,9 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
} }
} finally { } finally {
this.sss.remove(matchingCore.getKey()); this.sss.remove(matchingCore.getKey());
this.sbs.forget(shardNode);
Shard shard = shardNode.getShardInstance().getShard();
this.sbs.forget(ShardSet.from(shard.getFloc(), shardNode), shard.getInstance());
} }
} }
} finally { } finally {

View File

@@ -4,8 +4,8 @@ import org.alfresco.repo.index.shard.ShardState;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.NodeShardParameterSet;
import com.inteligr8.alfresco.asie.model.ShardSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.rest.model.NodeShardParameterSet;
@Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.delete") @Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.delete")
public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<NodeShardParameterSet> { public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<NodeShardParameterSet> {
@@ -20,7 +20,7 @@ public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<No
@Override @Override
protected boolean matches(NodeShardParameterSet params, ShardState shardState) { protected boolean matches(NodeShardParameterSet params, ShardState shardState) {
if (!params.getShardSet().isFor(shardState)) if (!params.getShardSet().contains(shardState))
return false; return false;
if (params.getShardId() != shardState.getShardInstance().getShard().getInstance()) if (params.getShardId() != shardState.getShardInstance().getShard().getInstance())
return false; return false;

View File

@@ -3,7 +3,7 @@ package com.inteligr8.alfresco.asie.enterprise.rest;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.NodeParameterSet; import com.inteligr8.alfresco.asie.rest.model.NodeParameterSet;
@Component(value = "webscript.com.inteligr8.alfresco.asie.node.delete") @Component(value = "webscript.com.inteligr8.alfresco.asie.node.delete")
public class UnloadNodeWebScript extends AbstractUnregisterNodeWebScript<NodeParameterSet> { public class UnloadNodeWebScript extends AbstractUnregisterNodeWebScript<NodeParameterSet> {

View File

@@ -0,0 +1,404 @@
package com.inteligr8.alfresco.asie.enterprise.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardRegistry;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.util.Pair;
import org.alfresco.util.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
@Component
public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.ShardDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private ShardRegistry shardRegistry;
@Override
public ShardSet findSetByCore(String core) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return null;
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
for (Entry<Shard, Set<ShardState>> flocShard : floc.getValue().entrySet()) {
for (ShardState shardState : flocShard.getValue()) {
ShardSet shardSet = ShardSet.from(floc.getKey(), shardState);
if (shardSet.getCore().equals(core))
return shardSet;
}
}
}
return null;
}
@Override
public SolrHost findNode(String nodeHostname, int nodePort) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return null;
this.logger.trace("Found {} shard sets", flocs.size());
Map<String, InetAddress> resolvedAddresses = new HashMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
for (Entry<Shard, Set<ShardState>> flocShard : floc.getValue().entrySet()) {
for (ShardState shardState : flocShard.getValue()) {
ShardInstance shardInstance = shardState.getShardInstance();
if (!nodeHostname.equalsIgnoreCase(shardInstance.getHostName())) {
if (!resolvedAddresses.containsKey(nodeHostname))
resolvedAddresses.put(nodeHostname, this.resolve(nodeHostname));
InetAddress nodeAddress = resolvedAddresses.get(nodeHostname);
this.logger.trace("Resolved: {} => {}", nodeHostname, nodeAddress);
if (nodeAddress == null)
continue;
if (!resolvedAddresses.containsKey(shardInstance.getHostName()))
resolvedAddresses.put(shardInstance.getHostName(), this.resolve(shardInstance.getHostName()));
InetAddress shardInstanceAddress = resolvedAddresses.get(shardInstance.getHostName());
this.logger.trace("Resolved: {} => {}", shardInstance.getHostName(), shardInstanceAddress);
if (!nodeAddress.equals(shardInstanceAddress))
continue;
}
if (nodePort == shardInstance.getPort()) {
SolrHost node = SolrHost.from(shardInstance);
this.logger.debug("Found node: {}", node);
return node;
}
}
}
}
return null;
}
@Override
public Map<ShardSet, Map<Integer, ShardInstanceState>> findByNode(SolrHost node) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyMap();
this.logger.trace("Found {} shard sets", flocs.size());
Map<ShardSet, Map<Integer, ShardInstanceState>> setShardStates = new HashMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet())
setShardStates.putAll(this.findByNode(node, floc.getKey(), floc.getValue()));
return setShardStates;
}
private Map<ShardSet, Map<Integer, ShardInstanceState>> findByNode(SolrHost node, Floc floc, Map<Shard, Set<ShardState>> shards) {
ShardSet shardSet = null;
Map<ShardSet, Map<Integer, ShardInstanceState>> setShardStates = new HashMap<>();
int shardStateCount = 0;
for (Entry<Shard, Set<ShardState>> flocShard : shards.entrySet()) {
for (ShardState shardState : flocShard.getValue()) {
if (shardSet == null)
shardSet = ShardSet.from(floc, shardState);
ShardInstance shardInstance = shardState.getShardInstance();
if (node.equals(SolrHost.from(shardInstance))) {
Map<Integer, ShardInstanceState> shardStates = setShardStates.get(shardSet);
if (shardStates == null)
setShardStates.put(shardSet, shardStates = new HashMap<>());
shardStates.put(flocShard.getKey().getInstance(), ShardInstanceState.from(shardState));
shardStateCount++;
}
}
}
this.logger.debug("Found {} shard states for node: {}", shardStateCount, node);
return setShardStates;
}
@Override
public Set<ShardSet> findSetsByShardMethod(ShardMethodEnum... shardMethods) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
Set<ShardMethodEnum> shardMethodSet = CollectionUtils.asSet(shardMethods);
Set<ShardSet> shardSets = new HashSet<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
if (shardMethodSet.contains(floc.getKey().getShardMethod())) {
ShardState shardState = this.extractAnyShardState(floc.getValue());
shardSets.add(ShardSet.from(floc.getKey(), shardState));
}
}
this.logger.debug("Found {} shard sets of methods: {}", flocs.size(), shardMethods);
return shardSets;
}
@Override
public Set<SolrHost> findNodes(ShardSet shardSet) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Set<SolrHost> nodes = this.findNodes(shardSet, null, floc.getKey(), floc.getValue());
if (nodes != null) {
this.logger.debug("Found {} nodes for set: {}", nodes.size(), shardSet);
return nodes;
}
}
this.logger.debug("Found {} nodes for set: {}", 0, shardSet);
return Collections.emptySet();
}
@Override
public Set<SolrHost> findNodesByShard(ShardSet shardSet, int shardId) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Set<SolrHost> nodes = this.findNodes(shardSet, shardId, floc.getKey(), floc.getValue());
if (nodes != null) {
this.logger.debug("Found {} nodes for shard #{} in set: {}", nodes.size(), shardId, shardSet);
return nodes;
}
}
this.logger.debug("Found {} nodes for shard #{} in set: {}", 0, shardId, shardSet);
return Collections.emptySet();
}
private Set<SolrHost> findNodes(ShardSet shardSet, Integer shardId, Floc floc, Map<Shard, Set<ShardState>> shards) {
Set<SolrHost> nodes = new HashSet<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
if (shardId == null || shardId.intValue() == shard.getKey().getInstance()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
SolrHost node = SolrHost.from(shardInstance);
nodes.add(node);
}
}
}
return nodes;
}
@Override
public Map<Integer, Pair<SolrHost, ShardInstanceState>> findLatestNodeStates(ShardSet shardSet) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyMap();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Map<Integer, Pair<SolrHost, ShardInstanceState>> shardNodeStates = this.findLatestNodeStates(shardSet, floc.getKey(), floc.getValue());
if (shardNodeStates != null) {
this.logger.debug("Found {} shard node states for set: {}", shardNodeStates.size(), shardSet);
return shardNodeStates;
}
}
this.logger.debug("Found {} shard node states for set: {}", 0, shardSet);
return Collections.emptyMap();
}
private Map<Integer, Pair<SolrHost, ShardInstanceState>> findLatestNodeStates(ShardSet shardSet, Floc floc, Map<Shard, Set<ShardState>> shards) {
Map<Integer, Pair<SolrHost, ShardInstanceState>> shardNodeStates = new HashMap<>();
boolean checked = false;
com.inteligr8.alfresco.asie.spi.ShardDiscoveryService.ShardedNodeShardStateComparator comparator = new com.inteligr8.alfresco.asie.spi.ShardDiscoveryService.ShardedNodeShardStateComparator();
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
int shardId = shard.getKey().getInstance();
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
SolrHost node = SolrHost.from(shardInstance);
ShardInstanceState nodeShardState = ShardInstanceState.from(shardState);
Pair<SolrHost, ShardInstanceState> pair = new Pair<>(node, nodeShardState);
if (comparator.compare(pair, shardNodeStates.get(shardId)) < 0)
shardNodeStates.put(shardId, pair);
}
}
return shardNodeStates;
}
@Override
public List<Pair<SolrHost, ShardInstanceState>> findNodeStatesByShard(ShardSet shardSet, int shardId) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyList();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
List<Pair<SolrHost, ShardInstanceState>> nodeStates = this.findNodeStates(shardSet, shardId, floc.getKey(), floc.getValue());
if (nodeStates != null) {
this.logger.debug("Found {} node states for shard #{} in set: {}", nodeStates.size(), shardId, shardSet);
return nodeStates;
}
}
this.logger.debug("Found {} node states for shard #{} in set: {}", 0, shardId, shardSet);
return Collections.emptyList();
}
private List<Pair<SolrHost, ShardInstanceState>> findNodeStates(ShardSet shardSet, Integer shardId, Floc floc, Map<Shard, Set<ShardState>> shards) {
List<Pair<SolrHost, ShardInstanceState>> nodeStates = new LinkedList<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
if (shardId == null || shardId.intValue() == shard.getKey().getInstance()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
SolrHost node = SolrHost.from(shardInstance);
ShardInstanceState nodeShardState = ShardInstanceState.from(shardState);
nodeStates.add(new Pair<>(node, nodeShardState));
}
}
}
return nodeStates;
}
@Override
public Set<Integer> findIdsByNode(ShardSet shardSet, SolrHost node) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Set<Integer> shardIds = this.findIdsByNode(shardSet, node, floc.getKey(), floc.getValue());
if (shardIds != null) {
this.logger.debug("Found {} shards for node '{}' in set: {}", shardIds.size(), node, shardSet);
return shardIds;
}
}
this.logger.debug("Found {} node states for node '{}' in set: {}", 0, node, shardSet);
return Collections.emptySet();
}
private Set<Integer> findIdsByNode(ShardSet shardSet, SolrHost node, Floc floc, Map<Shard, Set<ShardState>> shards) {
Set<Integer> shardIds = new HashSet<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
if (node.equals(SolrHost.from(shardInstance)))
shardIds.add(shard.getKey().getInstance());
}
}
return shardIds;
}
@Override
public Map<Integer, ShardInstanceState> findStatesByNode(ShardSet shardSet, SolrHost node) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyMap();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Map<Integer, ShardInstanceState> shardStates = this.findStatesByNode(shardSet, node, floc.getKey(), floc.getValue());
if (shardStates != null) {
this.logger.debug("Found {} shard states for node '{}' in set: {}", shardStates.size(), node, shardSet);
return shardStates;
}
}
this.logger.debug("Found {} shard states for node '{}' in set: {}", 0, node, shardSet);
return Collections.emptyMap();
}
private Map<Integer, ShardInstanceState> findStatesByNode(ShardSet shardSet, SolrHost node, Floc floc, Map<Shard, Set<ShardState>> shards) {
Map<Integer, ShardInstanceState> shardStates = new HashMap<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
if (node.equals(SolrHost.from(shardInstance)))
shardStates.put(shard.getKey().getInstance(), ShardInstanceState.from(shardState));
}
}
return shardStates;
}
private ShardState extractAnyShardState(Map<Shard, Set<ShardState>> shards) {
if (shards.isEmpty())
return null;
for (Set<ShardState> shardStates : shards.values())
for (ShardState shardState : shardStates)
return shardState;
return null;
}
private InetAddress resolve(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException uhe) {
return null;
}
}
}

View File

@@ -35,6 +35,7 @@ public class ShardStateService implements com.inteligr8.alfresco.asie.spi.ShardS
@Qualifier(Constants.BEAN_SHARD_GUID_CACHE) @Qualifier(Constants.BEAN_SHARD_GUID_CACHE)
private SimpleCache<ShardInstance, String> shardToGuidCache; private SimpleCache<ShardInstance, String> shardToGuidCache;
@Override
public void clear() { public void clear() {
this.logger.info("Removing all nodes/shards from the shard registry"); this.logger.info("Removing all nodes/shards from the shard registry");

View File

@@ -5,7 +5,7 @@
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.1-SNAPSHOT</version> <version>1.2-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>ASIE Platform Module Parent</name> <name>ASIE Platform Module Parent</name>

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.1-SNAPSHOT</version> <version>1.2-SNAPSHOT</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>

View File

@@ -6,20 +6,15 @@ public interface Constants {
static final String QUALIFIER_ASIE = "asie"; static final String QUALIFIER_ASIE = "asie";
// OOTB // defined OOTB
static final String BEAN_SHARD_STATE_CACHE = "shardStateCache"; static final String BEAN_SHARD_STATE_CACHE = "shardStateCache";
static final String BEAN_SHARD_GUID_CACHE = "shardToGuidCache"; static final String BEAN_SHARD_GUID_CACHE = "shardToGuidCache";
static final String BEAN_OFFILINE_SHARD_STATE_CACHE = "offlineShardStateCache"; static final String BEAN_SHARD_REGISTRY = "asie.ShardRegistry";
static final String BEAN_CORE_EXPLICIT_CACHE = "coreExplicitIdCache";
static final String BEAN_OBJECT_MAPPER = "asie.ObjectMapper"; static final String BEAN_OBJECT_MAPPER = "asie.ObjectMapper";
static final String BEAN_ATTRIBUTE_SERVICE = "asie.AttributeService"; static final String BEAN_ATTRIBUTE_SERVICE = "asie.AttributeService";
static final String BEAN_SHARD_REGISTRY = "asie.ShardRegistry";
static final String ATTR_ASIE = "inteligr8.asie"; static final String ATTR_ASIE = "inteligr8.asie";
static final String ATTR_ASIE_NODE_SHARD = "inteligr8.asie.nodeShard";
static final String ATTR_STATE = "state";
static final String ATTR_ONLINE = "online";
static final String ATTR_UNLOADED = "unloadedNode.cores"; static final String ATTR_UNLOADED = "unloadedNode.cores";
} }

View File

@@ -0,0 +1,48 @@
package com.inteligr8.alfresco.asie;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class SimpleCaster {
public static <T> T transform(String str, Class<T> returnType) {
if (str == null)
return null;
if (returnType.isAssignableFrom(String.class)) {
@SuppressWarnings("unchecked")
T t = (T) str;
return t;
}
try {
Constructor<T> constructor = returnType.getConstructor(String.class);
return constructor.newInstance(str);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
// suppress
}
for (String staticMethod : Arrays.asList("from", "valueOf")) {
try {
return invoke(returnType, staticMethod, str);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
// suppress
}
}
throw new IllegalArgumentException();
}
private static <T> T invoke(Class<T> returnType, String staticMethodName, Object... arguments) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = returnType.getDeclaredMethod(staticMethodName, String.class);
if (!returnType.isAssignableFrom(method.getReturnType()))
throw new NoSuchMethodException();
@SuppressWarnings("unchecked")
T t = (T) method.invoke(null, arguments);
return t;
}
}

View File

@@ -1,56 +0,0 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import org.alfresco.repo.index.shard.ShardInstance;
public class Node implements Serializable {
private static final long serialVersionUID = -8834744746109388928L;
private final String id;
private final ShardInstance shardNode;
public Node(ShardInstance shardNode) {
this.shardNode = shardNode;
this.id = this.getHostname() + ":" + this.getPort() + this.getPath();
}
public String getId() {
return this.id;
}
public String getHostname() {
return this.shardNode.getHostName();
}
public int getPort() {
return this.shardNode.getPort();
}
public String getPath() {
// baseUrl is to the shard; we want to the node, so exclude the core
int lastSlash = this.shardNode.getBaseUrl().lastIndexOf('/');
return this.shardNode.getBaseUrl().substring(0, lastSlash);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Node))
return false;
Node node = (Node) obj;
return this.id.equals(node.id);
}
@Override
public int hashCode() {
return this.id.hashCode();
}
@Override
public String toString() {
return this.id;
}
}

View File

@@ -1,5 +0,0 @@
package com.inteligr8.alfresco.asie.model;
public interface RequestParameterSet {
}

View File

@@ -0,0 +1,68 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import org.alfresco.repo.index.shard.Floc;
public class Shard implements Serializable {
private static final long serialVersionUID = 5683743181748541736L;
public static Shard from(ShardSet shardSet, int shardId) {
return new Shard(shardSet, shardId);
}
public static Shard from(String spec) {
return new Shard(spec);
}
private final String spec;
protected Shard(ShardSet shardSet, int shardId) {
this.spec = shardSet.getCore() + "~" + shardId;
}
protected Shard(String spec) {
this.spec = spec;
}
public org.alfresco.repo.index.shard.Shard toAlfrescoModel(Floc floc) {
org.alfresco.repo.index.shard.Shard shard = new org.alfresco.repo.index.shard.Shard();
shard.setFloc(floc);
shard.setInstance(this.extractShardId());
return shard;
}
public String getSpec() {
return spec;
}
public String extractShardSetCore() {
int pos = this.spec.indexOf('~');
return this.spec.substring(0, pos);
}
public int extractShardId() {
int pos = this.spec.indexOf('~');
return Integer.parseInt(this.spec.substring(pos+1));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Shard))
return false;
Shard shard = (Shard) obj;
return this.spec.equals(shard.spec);
}
@Override
public int hashCode() {
return this.spec.hashCode();
}
@Override
public String toString() {
return this.spec;
}
}

View File

@@ -0,0 +1,64 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
public class ShardInstance implements Serializable {
private static final long serialVersionUID = 7455521296197234581L;
public static ShardInstance from(Shard shard, SolrHost node) {
return new ShardInstance(shard, node);
}
private final String spec;
protected ShardInstance(Shard shard, SolrHost node) {
this.spec = shard.getSpec() + "~" + node.getSpec();
}
public org.alfresco.repo.index.shard.ShardInstance toAlfrescoModel(org.alfresco.repo.index.shard.Shard shard) {
SolrHost node = this.extractNode();
String core = shard.getFloc().getPropertyBag().get("coreName");
org.alfresco.repo.index.shard.ShardInstance shardInstance = new org.alfresco.repo.index.shard.ShardInstance();
shardInstance.setHostName(node.getHostname());
shardInstance.setPort(node.getPort());
shardInstance.setBaseUrl(node.getPath() + "/" + core + "-" + shard.getInstance());
shardInstance.setShard(shard);
return shardInstance;
}
public String getSpec() {
return spec;
}
public Shard extractShard() {
int pos = this.spec.indexOf('~');
return Shard.from(this.spec.substring(0, pos));
}
public SolrHost extractNode() {
int pos = this.spec.indexOf('~');
return SolrHost.from(this.spec.substring(pos+1));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ShardInstance))
return false;
ShardInstance shard = (ShardInstance) obj;
return this.spec.equals(shard.spec);
}
@Override
public int hashCode() {
return this.spec.hashCode();
}
@Override
public String toString() {
return this.spec;
}
}

View File

@@ -0,0 +1,102 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Map.Entry;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class ShardInstanceState implements Serializable, Comparable<ShardInstanceState> {
private static final long serialVersionUID = 2893797002689889478L;
public static ShardInstanceState from(ShardState shardState) {
return new ShardInstanceState(shardState);
}
private final OffsetDateTime lastUpdated;
private final long lastIndexedChangeSetId;
private final OffsetDateTime lastIndexedChangeSetTime;
private final long lastIndexedTxId;
private final OffsetDateTime lastIndexedTxTime;
private transient Integer hash = null;
private ShardInstanceState(ShardState shardState) {
this.lastUpdated = Instant.ofEpochMilli(shardState.getLastUpdated()).atOffset(ZoneOffset.UTC);
this.lastIndexedChangeSetId = shardState.getLastIndexedChangeSetId();
this.lastIndexedChangeSetTime = Instant.ofEpochMilli(shardState.getLastIndexedChangeSetCommitTime()).atOffset(ZoneOffset.UTC);
this.lastIndexedTxId = shardState.getLastIndexedTxId();
this.lastIndexedTxTime = Instant.ofEpochMilli(shardState.getLastIndexedTxCommitTime()).atOffset(ZoneOffset.UTC);
}
public ShardState toAlfrescoModel(ShardInstance shardInstance) {
ShardState state = new ShardState();
state.setLastIndexedChangeSetCommitTime(this.lastIndexedChangeSetTime.toInstant().toEpochMilli());
state.setLastIndexedChangeSetId(this.lastIndexedChangeSetId);
state.setLastIndexedTxCommitTime(this.lastIndexedTxTime.toInstant().toEpochMilli());
state.setLastIndexedTxId(this.lastIndexedTxId);
state.setLastUpdated(this.lastUpdated.toInstant().toEpochMilli());
state.setShardInstance(shardInstance);
for (Entry<String, String> prop : shardInstance.getShard().getFloc().getPropertyBag().entrySet())
if (prop.getKey().startsWith("shard."))
state.getPropertyBag().put(prop.getKey(), prop.getValue());
String core = shardInstance.getShard().getFloc().getPropertyBag().get("coreName");
if (core != null)
state.getPropertyBag().put("coreName", core + "-" + shardInstance.getShard().getInstance());
return state;
}
public OffsetDateTime getLastUpdated() {
return lastUpdated;
}
public long getLastIndexedChangeSetId() {
return lastIndexedChangeSetId;
}
public OffsetDateTime getLastIndexedChangeSetTime() {
return lastIndexedChangeSetTime;
}
public long getLastIndexedTxId() {
return lastIndexedTxId;
}
public OffsetDateTime getLastIndexedTxTime() {
return lastIndexedTxTime;
}
@Override
public int compareTo(ShardInstanceState o) {
return -this.lastUpdated.compareTo(o.lastUpdated);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ShardInstanceState))
return false;
ShardInstanceState snss = (ShardInstanceState) obj;
return this.lastIndexedChangeSetId == snss.lastIndexedChangeSetId &&
this.lastIndexedTxId == snss.lastIndexedTxId;
}
@Override
public int hashCode() {
if (this.hash == null) {
this.hash = new HashCodeBuilder()
.append(this.lastIndexedTxId)
.append(this.lastIndexedChangeSetId)
.build();
}
return this.hash.intValue();
}
}

View File

@@ -1,56 +1,194 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.model;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.HashSet;
import java.util.Map; import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.ShardMethodEnum; import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.collections.CollectionUtils;
import org.apache.commons.collections4.map.CompositeMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.inteligr8.alfresco.asie.SimpleCaster;
public class ShardSet implements Serializable { public class ShardSet implements Serializable {
private static final long serialVersionUID = -8891094367429601316L; private static final long serialVersionUID = -8891094367429601316L;
private static final String DEFAULT_SOLR_TEMPLATE = "rerank";
/** public static ShardSet from(Floc floc, ShardState anyShardState) {
* Examples: return new ShardSet(floc, anyShardState);
*
* MOD_ACL_ID
* ACL_ID
* DB_ID
* DB_ID_RANGE;range:0-20000
* DATE;key:cm:created
* DATE;key:cm:created;date.grouping:3
* PROPERTY;key:cm:created;regex:^d{4}
*/
private static final Pattern shardSetPattern = Pattern.compile("([A-Z]+)(;fulltext)?(;([a-z]+):([^;]+))?(;([a-z]+):([^;]+))?");
private final ShardMethodEnum method;
private final boolean hasContent;
private final Map<String, String> config;
private transient Integer hash;
public ShardSet(Floc floc, ShardState anyShardNode) {
this.method = floc.getShardMethod();
this.hasContent = floc.hasContent();
this.config = (floc.getPropertyBag().isEmpty() && anyShardNode != null) ? anyShardNode.getPropertyBag() : floc.getPropertyBag();
} }
public ShardSet(String shardSetSpec) { public static ShardSet from(String coreName, String spec) {
Matcher matcher = shardSetPattern.matcher(shardSetSpec); return new ShardSet(coreName, spec);
if (!matcher.find()) }
throw new IllegalArgumentException("The shard set '" + shardSetSpec + "' is not properly formatted");
this.method = ShardMethodEnum.valueOf(matcher.group(1)); private final String core;
this.hasContent = ";fulltext".equals(matcher.group(2)); private final ShardMethodEnum method;
this.config = new HashMap<>(); private final boolean hasContent;
for (int g = 3; g < matcher.groupCount(); g += 3) private final String template;
if (matcher.group(g) != null) private final Set<StoreRef> storeRefs;
this.config.put("shard." + matcher.group(g+1), matcher.group(g+2)); private final Short shards;
private final Pair<Long, Long> range;
private final Byte dateGrouping;
private final String prefixedProperty;
private final Pattern regex;
private transient String spec;
private ShardSet(Floc floc, ShardState anyShardNode) {
String shardCoreName = anyShardNode.getPropertyBag().get("coreName");
int lastDash = shardCoreName.lastIndexOf('-');
this.core = shardCoreName.substring(0, lastDash);
this.method = floc.getShardMethod();
this.hasContent = floc.hasContent();
this.template = floc.getTemplate();
this.storeRefs = floc.getStoreRefs();
CompositeMap<String, String> propbag = new CompositeMap<>(floc.getPropertyBag(), anyShardNode.getPropertyBag());
Short shards = null;
Pair<Long, Long> range = null;
Byte dateGrouping = null;
String prefixedProperty = null;
String regex = null;
switch (this.method) {
case DB_ID_RANGE:
range = this.strToRange(propbag.get("shard.range"));
break;
case DATE:
dateGrouping = SimpleCaster.transform(propbag.get("shard.date.grouping"), Byte.class);
case PROPERTY:
case EXPLICIT_ID:
prefixedProperty = StringUtils.trimToNull(propbag.get("shard.key"));
regex = StringUtils.trimToNull(propbag.get("shard.regex"));
default:
shards = (short) floc.getNumberOfShards();
}
this.range = range;
this.shards = shards;
this.dateGrouping = dateGrouping;
this.prefixedProperty = prefixedProperty;
this.regex = regex == null ? null : Pattern.compile(regex);
}
public Floc toAlfrescoModel() {
Floc floc = new Floc();
floc.setShardMethod(this.method);
floc.setHasContent(this.hasContent);
floc.setTemplate(this.template);
floc.setStoreRefs(new HashSet<>(this.storeRefs));
floc.getPropertyBag().put("coreName", this.core);
switch (this.method) {
case DB_ID_RANGE:
floc.getPropertyBag().put("shard.range", this.range.getLeft() + "-" + this.range.getRight());
break;
case DATE:
if (this.dateGrouping != null)
floc.getPropertyBag().put("shard.date.grouping", this.dateGrouping.toString());
case PROPERTY:
case EXPLICIT_ID:
floc.getPropertyBag().put("shard.key", this.prefixedProperty);
if (this.regex != null)
floc.getPropertyBag().put("shard.regex", this.regex.pattern());
default:
if (this.shards != null)
floc.setNumberOfShards(this.shards.intValue());
}
return floc;
}
private ShardSet(String coreName, String shardSetSpec) {
String[] parts = shardSetSpec.split("[;|_]");
if (parts.length == 0)
throw new IllegalArgumentException();
this.core = coreName;
this.method = ShardMethodEnum.valueOf(parts[0].toUpperCase());
boolean hasContent = false;
String template = DEFAULT_SOLR_TEMPLATE;
StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
Pair<Long, Long> range = null;
Short shards = null;
Byte dateGrouping = null;
String prefixedProperty = null;
String regex = null;
for (int i = 1; i < parts.length; i++) {
int colon = parts[i].indexOf(":");
String fieldName = colon < 0 ? parts[i] : parts[i].substring(0, colon);
String fieldValue = colon < 0 ? null : parts[i].substring(colon+1);
switch (fieldName) {
case "txt":
case "text":
case "fulltext":
case "content":
case "hasContent":
case "hasText":
hasContent = true;
break;
case "t":
case "template":
template = fieldValue;
break;
case "sref":
case "storeRef":
storeRef = new StoreRef(fieldValue);
break;
case "s":
case "shards":
shards = SimpleCaster.transform(fieldValue, Short.class);
break;
case "range":
range = this.strToRange(fieldValue);
break;
case "date.grouping":
dateGrouping = SimpleCaster.transform(fieldValue, Byte.class);
break;
case "k":
case "key":
prefixedProperty = fieldValue;
break;
case "regex":
regex = fieldValue;
break;
default:
throw new IllegalArgumentException();
}
}
this.hasContent = hasContent;
this.template = template;
this.storeRefs = CollectionUtils.asSet(storeRef);
this.shards = shards;
this.range = range;
this.dateGrouping = dateGrouping;
this.prefixedProperty = prefixedProperty;
this.regex = regex == null ? null : Pattern.compile(regex);
}
private Pair<Long, Long> strToRange(String str) {
str = StringUtils.trimToNull(str);
if (str == null)
return null;
String[] rangeValues = str.split("-");
return Pair.of(Long.valueOf(rangeValues[0]), Long.valueOf(rangeValues[1]));
}
public String getCore() {
return core;
} }
public ShardMethodEnum getMethod() { public ShardMethodEnum getMethod() {
@@ -61,35 +199,70 @@ public class ShardSet implements Serializable {
return hasContent; return hasContent;
} }
public String getTemplate() {
return template;
}
public Set<StoreRef> getStoreRefs() {
return storeRefs;
}
public Short getShards() {
return shards;
}
public Pair<Long, Long> getRange() {
return range;
}
public Byte getDateGrouping() {
return dateGrouping;
}
public String getPrefixedProperty() {
return prefixedProperty;
}
public Pattern getRegex() {
return regex;
}
public String toSpec() { public String toSpec() {
if (this.spec == null) {
StringBuilder spec = new StringBuilder(this.method.toString()); StringBuilder spec = new StringBuilder(this.method.toString());
if (this.hasContent) if (this.hasContent)
spec.append(";fulltext"); spec.append(";txt");
for (Entry<String, String> c : this.config.entrySet()) { spec.append(";t:").append(this.template);
if (!c.getKey().startsWith("shard.")) spec.append(";sref:").append(StringUtils.join(this.storeRefs, ','));
continue; if (this.shards != null)
spec.append(';').append(c.getKey().substring(6)).append(':').append(c.getValue()); spec.append(";s:").append(this.shards);
} if (this.range != null)
return spec.toString(); spec.append(";range:").append(this.range.getLeft()).append('-').append(this.range.getRight());
if (this.dateGrouping != null)
spec.append(";date.grouping:").append(this.dateGrouping);
if (this.prefixedProperty != null)
spec.append(";k:").append(this.prefixedProperty);
if (this.regex != null)
spec.append(";regex:").append(this.regex);
this.spec = spec.toString();
} }
public Map<String, String> getConfig() { return spec;
return config;
} }
public boolean isFor(ShardState shardState) { public boolean contains(ShardState shardState) {
return this.method.equals(shardState.getShardInstance().getShard().getFloc().getShardMethod()) && Floc floc = shardState.getShardInstance().getShard().getFloc();
this.hasContent == shardState.getShardInstance().getShard().getFloc().hasContent() && CompositeMap<String, String> propbag = new CompositeMap<>(shardState.getPropertyBag(), floc.getPropertyBag());
this.isConfigurationFor(shardState.getPropertyBag());
}
public boolean isConfigurationFor(Map<String, String> propertyBag) { return this.method.equals(floc.getShardMethod()) &&
for (Entry<String, String> config : this.config.entrySet()) { this.hasContent == floc.hasContent() &&
if (config.getValue() == null || !config.getValue().equals(propertyBag.get(config.getKey()))) StringUtils.equals(this.template, floc.getTemplate()) &&
return false; this.storeRefs.equals(floc.getStoreRefs()) &&
} this.equals(this.shards, floc.getNumberOfShards()) &&
this.equals(this.range, this.strToRange(propbag.get("shard.range"))) &&
return true; this.equals(this.dateGrouping, propbag.get("shard.date.grouping")) &&
StringUtils.equals(this.prefixedProperty, propbag.get("shard.key")) &&
this.equals(this.regex, propbag.get("shard.regex"));
} }
@Override @Override
@@ -98,21 +271,34 @@ public class ShardSet implements Serializable {
return false; return false;
ShardSet shardSet = (ShardSet) obj; ShardSet shardSet = (ShardSet) obj;
return this.method.equals(shardSet.method) && this.config.equals(shardSet.config); return this.core.equals(shardSet.core);
}
private <T> boolean equals(Pattern p1, String s2) {
s2 = StringUtils.trimToNull(s2);
if (p1 == null) {
return s2 == null;
} else {
return p1.pattern().equals(s2);
}
}
private <T> boolean equals(T t1, T t2) {
if (t1 == null) {
return t2 == null;
} else {
return t1.equals(t2);
}
} }
@Override @Override
public int hashCode() { public int hashCode() {
if (this.hash == null) { return this.core.hashCode();
this.hash = new HashCodeBuilder().append(this.method).append(this.hasContent).append(this.config).build();
}
return this.hash;
} }
@Override @Override
public String toString() { public String toString() {
return this.toSpec(); return this.core;
} }
} }

View File

@@ -0,0 +1,92 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.repo.index.shard.ShardInstance;
public class SolrHost implements Serializable {
private static final long serialVersionUID = -8834744746109388928L;
private static final Pattern PATTERN = Pattern.compile("([^:]+):([0-9]+)([^0-9]?.*)");
public static SolrHost from(ShardInstance shardNode) {
return new SolrHost(shardNode);
}
public static SolrHost from(String spec) {
return new SolrHost(spec);
}
private final String spec;
private final String hostname;
private final int port;
private final String path;
protected SolrHost(ShardInstance shardNode) {
this.hostname = shardNode.getHostName();
this.port = shardNode.getPort();
// baseUrl is to the shard; we want to the node, so exclude the core
int lastSlash = shardNode.getBaseUrl().lastIndexOf('/');
this.path = shardNode.getBaseUrl().substring(0, lastSlash);
this.spec = this.hostname + ":" + this.port + this.path;
}
protected SolrHost(String spec) {
this.spec = spec;
Matcher matcher = PATTERN.matcher(spec);
this.hostname = matcher.group(1);
this.port = Integer.parseInt(matcher.group(2));
this.path = matcher.group(3);
}
public String getSpec() {
return this.spec;
}
public String getHostname() {
return this.hostname;
}
public int getPort() {
return this.port;
}
public String getPath() {
return this.path;
}
public URL toUrl(String protocol) {
try {
return new URL(protocol + "://" + this.hostname + ':' + this.port + this.path);
} catch (MalformedURLException mue) {
throw new IllegalArgumentException(mue.getMessage());
}
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SolrHost))
return false;
SolrHost shardNode = (SolrHost) obj;
return this.spec.equals(shardNode.spec);
}
@Override
public int hashCode() {
return this.spec.hashCode();
}
@Override
public String toString() {
return this.spec;
}
}

View File

@@ -1,9 +1,8 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.Map;
import org.alfresco.repo.index.shard.ShardState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -12,7 +11,10 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
public abstract class AbstractAsieNodeWebScript extends AbstractAsieShardableWebScript { public abstract class AbstractAsieNodeWebScript extends AbstractAsieShardableWebScript {
@@ -37,15 +39,16 @@ public abstract class AbstractAsieNodeWebScript extends AbstractAsieShardableWeb
protected void execute(WebScriptRequest req, WebScriptResponse res, String nodeHostname, int nodePort) throws IOException { protected void execute(WebScriptRequest req, WebScriptResponse res, String nodeHostname, int nodePort) throws IOException {
this.logger.trace("execute({}, {})", nodeHostname, nodePort); this.logger.trace("execute({}, {})", nodeHostname, nodePort);
Set<ShardState> shardsOnNode = this.sds.findByNode(nodeHostname, nodePort); SolrHost node = this.sds.findNode(nodeHostname, nodePort);
Map<ShardSet, Map<Integer, ShardInstanceState>> shardsOnNode = this.sds.findByNode(node);
if (shardsOnNode == null || shardsOnNode.isEmpty()) if (shardsOnNode == null || shardsOnNode.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE node could not be found"); throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE node could not be found");
this.execute(req, res, shardsOnNode); this.execute(req, res, node, shardsOnNode);
} }
protected void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredNodeShards) throws IOException { protected void execute(WebScriptRequest req, WebScriptResponse res, SolrHost node, Map<ShardSet, Map<Integer, ShardInstanceState>> shardSetShardStates) throws IOException {
this.logger.trace("execute({})", registeredNodeShards.size()); this.logger.trace("execute({})", shardSetShardStates.size());
// made to be optionally overridden // made to be optionally overridden
} }

View File

@@ -1,9 +1,9 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.List;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.util.Pair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -13,7 +13,9 @@ import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.model.ShardSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService; import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
public abstract class AbstractAsieShardWebScript extends AbstractAsieShardableWebScript { public abstract class AbstractAsieShardWebScript extends AbstractAsieShardableWebScript {
@@ -26,21 +28,24 @@ public abstract class AbstractAsieShardWebScript extends AbstractAsieShardableWe
public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException { public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()"); this.logger.trace("execute()");
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class); String coreName = this.getRequiredPathParameter(req, "shardCore");
this.logger.debug("Parsed shard set: {}", shardSet);
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class); int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
try { try {
Set<ShardState> registeredShardNodes = this.sds.findByShard(shardSet, shardId); ShardSet shardSet = this.sds.findSetByCore(coreName);
if (registeredShardNodes == null || registeredShardNodes.isEmpty()) if (shardSet == null)
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard set or shard could not be found"); throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard set could not be found");
this.execute(req, res, registeredShardNodes); List<Pair<SolrHost, ShardInstanceState>> nodeShardStates = this.sds.findNodeStatesByShard(shardSet, shardId);
if (nodeShardStates == null || nodeShardStates.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard could not be found");
this.execute(req, res, shardId, shardSet, nodeShardStates);
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), iae.getMessage()); throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), iae.getMessage());
} }
} }
protected abstract void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredShardNodes) throws IOException; protected abstract void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException;
} }

View File

@@ -57,7 +57,7 @@ public abstract class AbstractAsieShardableWebScript extends AbstractAsieWebScri
try { try {
SolrShardHashSampleType type = SolrShardHashSampleType.valueOf(matcher.group(1)); SolrShardHashSampleType type = SolrShardHashSampleType.valueOf(matcher.group(1));
int shards = Integer.parseInt(matcher.group(2)); short shards = Short.parseShort(matcher.group(2));
return this.createSampleHashTable(type, shards); return this.createSampleHashTable(type, shards);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
// this should never happen, because of the regex // this should never happen, because of the regex
@@ -67,7 +67,7 @@ public abstract class AbstractAsieShardableWebScript extends AbstractAsieWebScri
} }
} }
protected SolrShardHashTable<?> createSampleHashTable(SolrShardHashSampleType sampleType, int shards) { protected SolrShardHashTable<?> createSampleHashTable(SolrShardHashSampleType sampleType, short shards) {
int thisYear = Year.now().getValue(); int thisYear = Year.now().getValue();
switch (sampleType) { switch (sampleType) {

View File

@@ -1,19 +1,21 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.Collections;
import java.util.List;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.util.Pair;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.service.ShardBackupService; import com.inteligr8.alfresco.asie.service.ShardBackupService;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.backupNode.get") @Component(value = "webscript.com.inteligr8.alfresco.asie.backupNode.get")
public class GetBackupNodeWebScript extends AbstractAsieShardWebScript { public class GetBackupNodeWebScript extends AbstractAsieShardWebScript {
@@ -22,15 +24,15 @@ public class GetBackupNodeWebScript extends AbstractAsieShardWebScript {
private ShardBackupService sbs; private ShardBackupService sbs;
@Override @Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> shardNodes) throws IOException { public void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException {
if (shardNodes.isEmpty()) Collections.sort(nodeShardStates, new ShardDiscoveryService.ShardedNodeShardStateComparator());
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found"); SolrHost mostRecentNode = nodeShardStates.iterator().next().getFirst();
Node node = this.sbs.fetchNode(shardNodes); SolrHost backupNode = this.sbs.selectNode(shardSet, shardId, mostRecentNode);
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), node.getId()); this.getObjectMapper().writeValue(res.getWriter(), backupNode.getSpec());
} }
} }

View File

@@ -1,10 +1,9 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.List;
import org.alfresco.repo.index.shard.ShardInstance; import org.alfresco.util.Pair;
import org.alfresco.repo.index.shard.ShardState;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
@@ -13,7 +12,9 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService; import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.leadNode.get") @Component(value = "webscript.com.inteligr8.alfresco.asie.leadNode.get")
@@ -23,17 +24,14 @@ public class GetLeadNodeWebScript extends AbstractAsieShardWebScript {
private ShardDiscoveryService sds; private ShardDiscoveryService sds;
@Override @Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> shardNodesCache) throws IOException { public void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException {
if (shardNodesCache.isEmpty()) SolrHost latestNode = this.sds.computeLeadNode(nodeShardStates);
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found");
ShardInstance latestNode = this.sds.computeLeadShard(shardNodesCache);
if (latestNode == null) if (latestNode == null)
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found"); throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE node could not be found");
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), new Node(latestNode).getId()); this.getObjectMapper().writeValue(res.getWriter(), latestNode.getSpec());
} }
} }

View File

@@ -1,18 +1,17 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.util.Map;
import java.time.OffsetDateTime; import java.util.Map.Entry;
import java.time.ZoneOffset;
import java.util.Set;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo; import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
@@ -22,32 +21,32 @@ import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
public class GetNodeWebScript extends AbstractAsieNodeWebScript { public class GetNodeWebScript extends AbstractAsieNodeWebScript {
@Override @Override
protected void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredNodeShards) throws IOException { protected void execute(WebScriptRequest req, WebScriptResponse res, SolrHost node, Map<ShardSet, Map<Integer, ShardInstanceState>> shardSetShardStates) throws IOException {
ShardState anyRegisteredNodeShard = registeredNodeShards.iterator().next();
ShardInstance registeredNode = anyRegisteredNodeShard.getShardInstance();
int maxShards = registeredNode.getShard().getFloc().getNumberOfShards();
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
NodeInfo node = new NodeShardInfo(registeredNode); NodeInfo nodeResponse = NodeShardInfo.from(node);
for (ShardState registeredNodeShard : registeredNodeShards) { for (Entry<ShardSet, Map<Integer, ShardInstanceState>> shardSet : shardSetShardStates.entrySet()) {
ShardInfo shard = new ShardInfo(); Short maxShards = shardSet.getKey().getShards();
shard.setId(registeredNodeShard.getShardInstance().getShard().getInstance()); SolrShardHashTable<?> sampleHashTable = null;
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredNodeShard.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); if (sampleHashType != null && maxShards != null)
shard.setTxsCompleted(registeredNodeShard.getLastIndexedTxId()); sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
shard.setShardSet(new ShardSetInfo(registeredNodeShard.getShardInstance().getShard().getFloc(), registeredNodeShard)); ShardSetInfo shardSetResponse = ShardSetInfo.from(shardSet.getKey());
for (Entry<Integer, ShardInstanceState> shard : shardSet.getValue().entrySet()) {
ShardInfo shardResponse = ShardInfo.from(shard.getKey(), shard.getValue());
shardResponse.setShardSet(shardSetResponse);
if (sampleHashTable != null) if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable); this.addShardHashSamples(shardResponse, sampleHashTable);
node.getShards().put(shard.getId(), shard); nodeResponse.getShards().put(shardResponse.getId(), shardResponse);
}
} }
res.setContentType("application/json"); res.setContentType("application/json");
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), node); this.getObjectMapper().writeValue(res.getWriter(), nodeResponse);
} }
} }

View File

@@ -8,6 +8,7 @@ import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard; import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -18,7 +19,9 @@ import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo; import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
@@ -39,34 +42,44 @@ public class GetNodesWebScript extends AbstractAsieShardableWebScript {
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
Map<String, NodeInfo> nodes = new TreeMap<>(); Map<String, NodeInfo> nodesResponse = new TreeMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) { for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
int maxShards = floc.getKey().getNumberOfShards(); Short maxShards = floc.getKey().getShardMethod().equals(ShardMethodEnum.DB_ID_RANGE) ? null : (short) floc.getKey().getNumberOfShards();
SolrShardHashTable<?> sampleHashTable = null;
if (sampleHashType != null && maxShards != null)
sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards); ShardSetInfo shardSetResponse = null;
for (Entry<Shard, Set<ShardState>> registeredShards : floc.getValue().entrySet()) { for (Entry<Shard, Set<ShardState>> registeredShards : floc.getValue().entrySet()) {
for (ShardState registeredShardNode : registeredShards.getValue()) { for (ShardState registeredShardNode : registeredShards.getValue()) {
String nodeId = new Node(registeredShardNode.getShardInstance()).getId(); if (shardSetResponse == null) {
NodeInfo node = nodes.get(nodeId); ShardSet shardSet = ShardSet.from(floc.getKey(), registeredShardNode);
if (node == null) { shardSetResponse = ShardSetInfo.from(shardSet);
node = new NodeShardInfo(registeredShardNode.getShardInstance());
nodes.put(node.getId(), node);
} }
ShardInfo shard = new ShardInfo(registeredShardNode); SolrHost node = SolrHost.from(registeredShardNode.getShardInstance());
shard.setShardSet(new ShardSetInfo(floc.getKey(), registeredShardNode)); String nodeSpec = node.getSpec();
NodeInfo nodeResponse = nodesResponse.get(nodeSpec);
if (nodeResponse == null) {
nodeResponse = NodeShardInfo.from(node);
nodesResponse.put(nodeResponse.getId(), nodeResponse);
}
ShardInstanceState nodeShardState = ShardInstanceState.from(registeredShardNode);
ShardInfo shardResponse = ShardInfo.from(registeredShards.getKey().getInstance(), nodeShardState);
shardResponse.setShardSet(shardSetResponse);
if (sampleHashTable != null) if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable); this.addShardHashSamples(shardResponse, sampleHashTable);
node.getShards().put(shard.getId(), shard); nodeResponse.getShards().put(shardResponse.getId(), shardResponse);
} }
} }
} }
res.setContentType("application/json"); res.setContentType("application/json");
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), nodes); this.getObjectMapper().writeValue(res.getWriter(), nodesResponse);
} }
} }

View File

@@ -1,11 +1,7 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -14,10 +10,7 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardMethodEnum; import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -30,11 +23,14 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.PropertyHashShardSetInfo; import com.inteligr8.alfresco.asie.rest.model.PropertyHashShardSetInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo; import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService; import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.propertyHashShards.get") @Component(value = "webscript.com.inteligr8.alfresco.asie.propertyHashShards.get")
public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScript { public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScript {
@@ -55,19 +51,18 @@ public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScri
List<String> values = this.getOptionalQueryParameterAsList(req); List<String> values = this.getOptionalQueryParameterAsList(req);
this.validateParameters(min, max, values); this.validateParameters(min, max, values);
List<PropertyHashShardSetInfo> shardSets = new LinkedList<>(); List<PropertyHashShardSetInfo> shardSetsResponse = new LinkedList<>();
Collection<Pair<Floc, Map<Shard, Set<ShardState>>>> flocs = this.sds.findByShardMethod(ShardMethodEnum.PROPERTY); Set<ShardSet> shardSets = this.sds.findSetsByShardMethod(ShardMethodEnum.PROPERTY);
if (flocs.isEmpty()) if (shardSets.isEmpty())
throw new WebScriptException(HttpStatus.NO_CONTENT.value(), "There are no property-based shards"); throw new WebScriptException(HttpStatus.NO_CONTENT.value(), "There are no property-based shards");
for (Pair<Floc, Map<Shard, Set<ShardState>>> floc : flocs) { for (ShardSet shardSet : shardSets) {
ShardState anyShardNode = this.getAnyShardNode(floc.getSecond()); PropertyHashShardSetInfo shardSetResponse = PropertyHashShardSetInfo.from(shardSet);
PropertyHashShardSetInfo shardSet = new PropertyHashShardSetInfo(floc.getFirst(), anyShardNode); shardSetResponse.setShards(new TreeMap<>());
shardSet.setShards(new TreeMap<>());
int maxShards = floc.getFirst().getNumberOfShards(); Short shardCount = shardSet.getShards();
SolrShardHashTable<?> sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards); SolrShardHashTable<?> sampleHashTable = this.createSampleHashTable(sampleHashType, shardCount);
Map<Integer, List<Object>> shardToHashMap = new HashMap<>(); Map<Integer, List<Object>> shardToHashMap = new HashMap<>();
@@ -83,39 +78,24 @@ public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScri
} }
} }
for (Entry<Shard, Set<ShardState>> shardCache : floc.getSecond().entrySet()) { for (Entry<Integer, Pair<SolrHost, ShardInstanceState>> shard : this.sds.findLatestNodeStates(shardSet).entrySet()) {
ShardInfo shard = new ShardInfo(); ShardInfo shardResponse = ShardInfo.from(shard.getKey(), shard.getValue().getSecond());
shard.setId(shardCache.getKey().getInstance()); shardResponse.setNodes(new HashMap<>());
shard.setNodes(new HashMap<>());
for (ShardState shardNodeCache : shardCache.getValue()) { NodeInfo nodeResponse = ShardNodeInfo.from(shard.getValue().getFirst(), shard.getValue().getSecond());
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < shardNodeCache.getLastIndexedTxId()) { shardResponse.getNodes().put(nodeResponse.getId(), nodeResponse);
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shardNodeCache.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
shard.setTxsCompleted(shardNodeCache.getLastIndexedTxId());
}
NodeInfo node = new ShardNodeInfo(shardNodeCache); List<Object> hashedValues = shardToHashMap.get(shardResponse.getId());
shard.getNodes().put(node.getId(), node);
}
List<Object> hashedValues = shardToHashMap.get(shard.getId());
if (hashedValues != null) for (Object hashedValue : hashedValues) if (hashedValues != null) for (Object hashedValue : hashedValues)
shardSet.getShards().put(hashedValue, shard); shardSetResponse.getShards().put(hashedValue, shardResponse);
} }
shardSets.add(shardSet); shardSetsResponse.add(shardSetResponse);
} }
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shardSets); this.getObjectMapper().writeValue(res.getWriter(), shardSetsResponse);
}
private ShardState getAnyShardNode(Map<Shard, Set<ShardState>> shards) {
for (Set<ShardState> shardNodes : shards.values())
for (ShardState shardNode : shardNodes)
return shardNode;
return null;
} }
private List<String> getOptionalQueryParameterAsList(WebScriptRequest req) { private List<String> getOptionalQueryParameterAsList(WebScriptRequest req) {

View File

@@ -1,20 +1,19 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.util.List;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.Shard; import org.alfresco.util.Pair;
import org.alfresco.repo.index.shard.ShardState;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo; import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
@@ -24,34 +23,35 @@ import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
public class GetShardWebScript extends AbstractAsieShardWebScript { public class GetShardWebScript extends AbstractAsieShardWebScript {
@Override @Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredShardNodes) throws IOException { public void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException {
ShardState aRegisteredShardNode = registeredShardNodes.iterator().next();
Shard registeredShard = aRegisteredShardNode.getShardInstance().getShard();
int maxShards = registeredShard.getFloc().getNumberOfShards();
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
ShardInfo shard = new ShardInfo(); Short maxShards = shardSet.getShards();
shard.setId(registeredShard.getInstance()); SolrShardHashTable<?> sampleHashTable = null;
shard.setShardSet(new ShardSetInfo(registeredShard.getFloc(), aRegisteredShardNode)); if (sampleHashType != null && maxShards != null)
shard.setNodes(new TreeMap<>()); sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
ShardSetInfo shardSetResponse = ShardSetInfo.from(shardSet);
ShardInfo shardResponse = ShardInfo.from(shardId);
shardResponse.setShardSet(shardSetResponse);
shardResponse.setNodes(new TreeMap<>());
if (sampleHashTable != null) if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable); this.addShardHashSamples(shardResponse, sampleHashTable);
for (ShardState registeredShardNode : registeredShardNodes) { for (Pair<SolrHost, ShardInstanceState> nodeShardState : nodeShardStates) {
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) { if (shardResponse.getTxsCompleted() == null || shardResponse.getTxsCompleted().longValue() < nodeShardState.getSecond().getLastIndexedTxId()) {
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); shardResponse.setLatestTx(nodeShardState.getSecond().getLastIndexedTxTime());
shard.setTxsCompleted(registeredShardNode.getLastIndexedTxId()); shardResponse.setTxsCompleted(nodeShardState.getSecond().getLastIndexedTxId());
} }
NodeInfo node = new ShardNodeInfo(registeredShardNode); NodeInfo node = ShardNodeInfo.from(nodeShardState.getFirst(), nodeShardState.getSecond());
shard.getNodes().put(node.getId(), node); shardResponse.getNodes().put(node.getId(), node);
} }
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shard); this.getObjectMapper().writeValue(res.getWriter(), shardResponse);
} }
} }

View File

@@ -22,6 +22,9 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo; import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
@@ -42,42 +45,46 @@ public class GetShardsWebScript extends AbstractAsieShardableWebScript {
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
Map<String, ShardSetInfo> shardSets = new TreeMap<>(); Map<String, ShardSetInfo> shardSetsResponse = new TreeMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) { for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
int maxShards = floc.getKey().getNumberOfShards();
ShardState anyShardNode = this.getAnyShardNode(floc.getValue()); ShardState anyShardNode = this.getAnyShardNode(floc.getValue());
ShardSetInfo shardSet = new ShardSetInfo(floc.getKey(), anyShardNode); ShardSet shardSet = ShardSet.from(floc.getKey(), anyShardNode);
shardSet.setShards(new TreeMap<>()); ShardSetInfo shardSetResponse = ShardSetInfo.from(shardSet);
shardSetResponse.setShards(new TreeMap<>());
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards); Short maxShards = shardSet.getShards();
SolrShardHashTable<?> sampleHashTable = null;
if (sampleHashType != null && maxShards != null)
sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
for (Entry<Shard, Set<ShardState>> registeredShard : floc.getValue().entrySet()) { for (Entry<Shard, Set<ShardState>> registeredShard : floc.getValue().entrySet()) {
ShardInfo shard = new ShardInfo(); ShardInfo shardResponse = ShardInfo.from(registeredShard.getKey().getInstance());
shard.setId(registeredShard.getKey().getInstance()); shardResponse.setNodes(new TreeMap<>());
shard.setNodes(new TreeMap<>());
for (ShardState registeredShardNode : registeredShard.getValue()) { for (ShardState registeredShardNode : registeredShard.getValue()) {
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) { if (shardResponse.getTxsCompleted() == null || shardResponse.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) {
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); shardResponse.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
shard.setTxsCompleted(registeredShardNode.getLastIndexedTxId()); shardResponse.setTxsCompleted(registeredShardNode.getLastIndexedTxId());
} }
NodeInfo node = new ShardNodeInfo(registeredShardNode); SolrHost node = SolrHost.from(registeredShardNode.getShardInstance());
shard.getNodes().put(node.getId(), node); ShardInstanceState nodeShardState = ShardInstanceState.from(registeredShardNode);
NodeInfo nodeResponse = ShardNodeInfo.from(node, nodeShardState);
shardResponse.getNodes().put(nodeResponse.getId(), nodeResponse);
} }
if (sampleHashTable != null) if (sampleHashTable != null)
this.addShardHashSamples(shardSet, shard, sampleHashTable); this.addShardHashSamples(shardSetResponse, shardResponse, sampleHashTable);
shardSet.getShards().put(shard.getId(), shard); shardSetResponse.getShards().put(shardResponse.getId(), shardResponse);
} }
shardSets.put(shardSet.getMethodSpec(), shardSet); shardSetsResponse.put(shardSetResponse.getSpec(), shardSetResponse);
} }
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shardSets); this.getObjectMapper().writeValue(res.getWriter(), shardSetsResponse);
} }
private ShardState getAnyShardNode(Map<Shard, Set<ShardState>> shards) { private ShardState getAnyShardNode(Map<Shard, Set<ShardState>> shards) {

View File

@@ -3,12 +3,10 @@ package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.ShardInstance;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.SolrHost;
public abstract class NodeInfo { public abstract class NodeInfo implements ResponseInfo {
@JsonProperty @JsonProperty
private String id; private String id;
@@ -19,8 +17,8 @@ public abstract class NodeInfo {
public NodeInfo() { public NodeInfo() {
} }
public NodeInfo(ShardInstance nodeCache) { protected NodeInfo(SolrHost node) {
this.setId(new Node(nodeCache).getId()); this.id = node.getSpec();
} }
public String getId() { public String getId() {

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.rest.model;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;

View File

@@ -2,25 +2,28 @@ package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.ShardInstance;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.SolrHost;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class NodeShardInfo extends NodeInfo { public class NodeShardInfo extends NodeInfo {
public static NodeShardInfo from(SolrHost node) {
return new NodeShardInfo(node);
}
@JsonProperty @JsonProperty
private Map<Integer, ShardInfo> shards; private Map<Integer, ShardInfo> shards;
public NodeShardInfo() { public NodeShardInfo() {
} }
public NodeShardInfo(ShardInstance nodeCache) { protected NodeShardInfo(SolrHost node) {
super(nodeCache); super(node);
} }
} }

View File

@@ -1,4 +1,6 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.rest.model;
import com.inteligr8.alfresco.asie.model.ShardSet;
public class NodeShardParameterSet extends NodeParameterSet { public class NodeShardParameterSet extends NodeParameterSet {

View File

@@ -2,24 +2,25 @@ package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.ShardState;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.ShardSet;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class PropertyHashShardSetInfo { public class PropertyHashShardSetInfo implements ResponseInfo {
public static PropertyHashShardSetInfo from(ShardSet shardSet) {
return new PropertyHashShardSetInfo(shardSet);
}
@JsonProperty @JsonProperty
private String methodSpec; private String core;
@JsonProperty @JsonProperty
private int shardCount; private String spec;
@JsonProperty @JsonProperty
private Map<Object, ShardInfo> shards; private Map<Object, ShardInfo> shards;
@@ -27,26 +28,25 @@ public class PropertyHashShardSetInfo {
public PropertyHashShardSetInfo() { public PropertyHashShardSetInfo() {
} }
public PropertyHashShardSetInfo(Floc floc, ShardState anyShardNode) { protected PropertyHashShardSetInfo(ShardSet shardSet) {
ShardSet shardSet = new ShardSet(floc, anyShardNode); this.core = shardSet.getCore();
this.setMethodSpec(shardSet.toSpec()); this.spec = shardSet.toSpec();
this.setShardCount(floc.getNumberOfShards());
} }
public String getMethodSpec() { public String getCore() {
return this.methodSpec; return core;
} }
public void setMethodSpec(String methodSpec) { public void setCore(String core) {
this.methodSpec = methodSpec; this.core = core;
} }
public int getShardCount() { public String getSpec() {
return shardCount; return this.spec;
} }
public void setShardCount(int shardCount) { public void setSpec(String spec) {
this.shardCount = shardCount; this.spec = spec;
} }
public Map<Object, ShardInfo> getShards() { public Map<Object, ShardInfo> getShards() {

View File

@@ -0,0 +1,5 @@
package com.inteligr8.alfresco.asie.rest.model;
public interface RequestParameterSet {
}

View File

@@ -0,0 +1,5 @@
package com.inteligr8.alfresco.asie.rest.model;
public interface ResponseInfo {
}

View File

@@ -1,22 +1,27 @@
package com.inteligr8.alfresco.asie.rest.model; package com.inteligr8.alfresco.asie.rest.model;
import java.time.Instant;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.ShardState;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ShardInfo { public class ShardInfo implements ResponseInfo {
public static ShardInfo from(int shardId) {
return new ShardInfo(shardId, null);
}
public static ShardInfo from(int shardId, ShardInstanceState nodeShardState) {
return new ShardInfo(shardId, nodeShardState);
}
@JsonProperty @JsonProperty
private int id; private int id;
@@ -40,10 +45,10 @@ public class ShardInfo {
public ShardInfo() { public ShardInfo() {
} }
public ShardInfo(ShardState shard) { protected ShardInfo(int shardId, ShardInstanceState nodeShardState) {
this.setId(shard.getShardInstance().getShard().getInstance()); this.id = shardId;
this.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shard.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); this.latestTx = nodeShardState == null ? null : nodeShardState.getLastIndexedTxTime();
this.setTxsCompleted(shard.getLastIndexedTxId()); this.txsCompleted = nodeShardState == null ? null : nodeShardState.getLastIndexedTxId();
} }
public int getId() { public int getId() {

View File

@@ -1,10 +1,6 @@
package com.inteligr8.alfresco.asie.rest.model; package com.inteligr8.alfresco.asie.rest.model;
import java.time.Instant;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import org.alfresco.repo.index.shard.ShardState;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape; import com.fasterxml.jackson.annotation.JsonFormat.Shape;
@@ -12,11 +8,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ShardNodeInfo extends NodeInfo { public class ShardNodeInfo extends NodeInfo {
public static ShardNodeInfo from(SolrHost node, ShardInstanceState nodeShardState) {
return new ShardNodeInfo(node, nodeShardState);
}
@JsonProperty @JsonProperty
private Long txsCompleted; private Long txsCompleted;
@@ -24,13 +26,16 @@ public class ShardNodeInfo extends NodeInfo {
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") @JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime latestTx; private OffsetDateTime latestTx;
/**
* For Spring deserialization
*/
public ShardNodeInfo() { public ShardNodeInfo() {
} }
public ShardNodeInfo(ShardState shard) { protected ShardNodeInfo(SolrHost node, ShardInstanceState nodeShardState) {
super(shard.getShardInstance()); super(node);
this.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shard.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); this.latestTx = nodeShardState.getLastIndexedTxTime();
this.setTxsCompleted(shard.getLastIndexedTxId()); this.txsCompleted = nodeShardState.getLastIndexedTxId();
} }
public Long getTxsCompleted() { public Long getTxsCompleted() {

View File

@@ -1,4 +1,6 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.rest.model;
import com.inteligr8.alfresco.asie.model.ShardSet;
public class ShardParameterSet implements RequestParameterSet { public class ShardParameterSet implements RequestParameterSet {

View File

@@ -4,24 +4,40 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.service.cmr.repository.StoreRef;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.ShardSet;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ShardSetInfo { public class ShardSetInfo implements ResponseInfo {
public static ShardSetInfo from(ShardSet shardSet) {
return new ShardSetInfo(shardSet);
}
@JsonProperty @JsonProperty
private String methodSpec; private String spec;
@JsonProperty @JsonProperty
private int shardCount; private ShardMethodEnum method;
@JsonProperty
private boolean fulltextEnabled;
@JsonProperty
private String template;
@JsonProperty
private Set<StoreRef> storeRefs;
@JsonProperty
private Short shardCount;
@JsonProperty @JsonProperty
private Map<Integer, ShardInfo> shards; private Map<Integer, ShardInfo> shards;
@@ -32,25 +48,60 @@ public class ShardSetInfo {
public ShardSetInfo() { public ShardSetInfo() {
} }
public ShardSetInfo(Floc floc, ShardState anyShardNode) { protected ShardSetInfo(ShardSet shardSet) {
ShardSet shardSet = new ShardSet(floc, anyShardNode); this.spec = shardSet.toSpec();
this.methodSpec = shardSet.toSpec(); this.method = shardSet.getMethod();
this.setShardCount(floc.getNumberOfShards()); this.fulltextEnabled = shardSet.hasContent();
this.template = shardSet.getTemplate();
this.storeRefs = shardSet.getStoreRefs();
this.shardCount = shardSet.getShards();
} }
public String getMethodSpec() { public String getSpec() {
return this.methodSpec; return this.spec;
} }
public void setMethodSpec(String methodSpec) { public void setSpec(String spec) {
this.methodSpec = methodSpec; this.spec = spec;
} }
public int getShardCount() { public ShardMethodEnum getMethod() {
return method;
}
public void setMethod(ShardMethodEnum method) {
this.method = method;
}
public boolean isFulltextEnabled() {
return fulltextEnabled;
}
public void setFulltextEnabled(boolean fulltextEnabled) {
this.fulltextEnabled = fulltextEnabled;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
public Set<StoreRef> getStoreRefs() {
return storeRefs;
}
public void setStoreRefs(Set<StoreRef> storeRefs) {
this.storeRefs = storeRefs;
}
public Short getShardCount() {
return shardCount; return shardCount;
} }
public void setShardCount(int shardCount) { public void setShardCount(Short shardCount) {
this.shardCount = shardCount; this.shardCount = shardCount;
} }

View File

@@ -1,11 +1,7 @@
package com.inteligr8.alfresco.asie.service; package com.inteligr8.alfresco.asie.service;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.attributes.AttributeService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -15,8 +11,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService; import com.inteligr8.alfresco.asie.model.SolrHost;
@Component @Component
public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.ShardBackupService { public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.ShardBackupService {
@@ -24,9 +20,6 @@ public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.Shard
private static final String ATTR_BACKUP_NODE = "backupNode"; private static final String ATTR_BACKUP_NODE = "backupNode";
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ShardDiscoveryService sds;
@Autowired @Autowired
@Qualifier(Constants.QUALIFIER_ASIE) @Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attributeService; private AttributeService attributeService;
@@ -34,21 +27,14 @@ public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.Shard
@Value("${inteligr8.asie.backup.persistTimeMinutes}") @Value("${inteligr8.asie.backup.persistTimeMinutes}")
private int persistTimeMinutes; private int persistTimeMinutes;
public Node fetchNode(Collection<ShardState> shardNodes) { public SolrHost selectNode(ShardSet shardSet, int shardId, SolrHost node) {
if (shardNodes.isEmpty()) String shardKey = shardSet.getCore() + "-" + shardId;
return null;
ShardState shardNode0 = shardNodes.iterator().next();
ShardInstance node0Shard = shardNode0.getShardInstance();
Shard shard = node0Shard.getShard();
String shardKey = shard.getFloc().getShardMethod().name() + "~" + shard.getFloc().getNumberOfShards() + "~" + shard.getInstance();
PersistedNode backupNode = (PersistedNode) this.attributeService.getAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey); PersistedNode backupNode = (PersistedNode) this.attributeService.getAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey);
this.logger.debug("Found backup node: {}", backupNode); this.logger.debug("Found backup node: {}", backupNode);
if (backupNode == null || backupNode.isExpired()) { if (backupNode == null || backupNode.isExpired()) {
ShardInstance backupShardInstance = this.sds.computeLeadShard(shardNodes); backupNode = new PersistedNode(node);
backupNode = new PersistedNode(new Node(backupShardInstance));
this.attributeService.setAttribute(backupNode, Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey); this.attributeService.setAttribute(backupNode, Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey);
} }
@@ -59,11 +45,8 @@ public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.Shard
this.attributeService.removeAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE); this.attributeService.removeAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE);
} }
public void forget(ShardState shardNode) { public void forget(ShardSet shardSet, int shardId) {
ShardInstance nodeShard = shardNode.getShardInstance(); String shardKey = shardSet.getCore() + "-" + shardId;
Shard shard = nodeShard.getShard();
String shardKey = shard.getFloc().getShardMethod().name() + "~" + shard.getFloc().getNumberOfShards() + "~" + shard.getInstance();
this.attributeService.removeAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey); this.attributeService.removeAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey);
} }
@@ -73,10 +56,10 @@ public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.Shard
private static final long serialVersionUID = 4105196543023419818L; private static final long serialVersionUID = 4105196543023419818L;
private final Node node; private final SolrHost node;
private long expireTimeMillis; private long expireTimeMillis;
PersistedNode(Node node) { PersistedNode(SolrHost node) {
this.node = node; this.node = node;
this.reset(); this.reset();
} }
@@ -89,7 +72,7 @@ public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.Shard
return this.expireTimeMillis < System.currentTimeMillis(); return this.expireTimeMillis < System.currentTimeMillis();
} }
Node getNode() { SolrHost getNode() {
return this.node; return this.node;
} }

View File

@@ -1,159 +0,0 @@
package com.inteligr8.alfresco.asie.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardRegistry;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.model.ShardSet;
@Component
public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.ShardDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private ShardRegistry shardRegistry;
public ShardInstance computeLeadShard(Collection<ShardState> shardNodesCache) {
if (shardNodesCache.isEmpty())
return null;
long latestTime = 0L;
ShardInstance latestNode = null;
for (ShardState shardNodeCache : shardNodesCache) {
if (latestTime < shardNodeCache.getLastIndexedTxCommitTime()) {
latestNode = shardNodeCache.getShardInstance();
latestTime = shardNodeCache.getLastIndexedTxCommitTime();
}
}
return latestNode;
}
public Set<ShardState> findByNode(String nodeHostname, int nodePort) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
Set<ShardState> shards = new HashSet<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
for (Entry<Shard, Set<ShardState>> flocShard : floc.getValue().entrySet()) {
for (ShardState shardState : flocShard.getValue()) {
ShardInstance shardInstance = shardState.getShardInstance();
if (!nodeHostname.equalsIgnoreCase(shardInstance.getHostName())) {
InetAddress nodeAddress = this.resolve(nodeHostname);
if (nodeAddress == null)
continue;
InetAddress shardInstanceAddress = this.resolve(shardInstance.getHostName());
if (!nodeAddress.equals(shardInstanceAddress))
continue;
}
if (nodePort == shardInstance.getPort())
shards.add(shardState);
}
}
}
return shards;
}
public Map<Shard, Set<ShardState>> findByShardSet(ShardSet shardSet) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyMap();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
if (!floc.getKey().getShardMethod().equals(shardSet.getMethod()))
continue;
if (!shardSet.getConfig().isEmpty()) {
if (floc.getValue().isEmpty())
continue;
Shard firstShard = floc.getValue().keySet().iterator().next();
Set<ShardState> firstShardStates = floc.getValue().get(firstShard);
if (firstShardStates == null || firstShardStates.isEmpty())
continue;
ShardState firstShardState = firstShardStates.iterator().next();
Map<String, String> firstShardProps = firstShardState.getPropertyBag();
if (!shardSet.isConfigurationFor(firstShardProps))
continue;
}
return floc.getValue();
}
return Collections.emptyMap();
}
public Collection<Pair<Floc, Map<Shard, Set<ShardState>>>> findByShardMethod(ShardMethodEnum shardMethod) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyList();
this.logger.trace("Found {} shard sets", flocs.size());
List<Pair<Floc, Map<Shard, Set<ShardState>>>> filteredFlocs = new LinkedList<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
if (!floc.getKey().getShardMethod().equals(shardMethod))
continue;
filteredFlocs.add(new Pair<>(floc.getKey(), floc.getValue()));
}
return filteredFlocs;
}
public <T> Set<T> filterByShard(Map<Shard, Set<T>> shards, int shardId) {
if (shards == null)
return null;
for (Entry<Shard, Set<T>> shard : shards.entrySet()) {
if (shard.getKey().getInstance() == shardId)
return shard.getValue();
}
return Collections.emptySet();
}
public Set<ShardState> findByShard(ShardSet shardSet, int shardId) {
Map<Shard, Set<ShardState>> shards = this.findByShardSet(shardSet);
return this.filterByShard(shards, shardId);
}
private InetAddress resolve(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException uhe) {
return null;
}
}
}

View File

@@ -1,17 +1,14 @@
package com.inteligr8.alfresco.asie.spi; package com.inteligr8.alfresco.asie.spi;
import java.util.Collection; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import org.alfresco.repo.index.shard.ShardState;
import com.inteligr8.alfresco.asie.model.Node;
public interface ShardBackupService { public interface ShardBackupService {
Node fetchNode(Collection<ShardState> shardNodes); SolrHost selectNode(ShardSet shardSet, int shardId, SolrHost bestNode);
void forget(); void forget();
void forget(ShardState shardNode); void forget(ShardSet shardSet, int shardId);
} }

View File

@@ -1,72 +1,132 @@
package com.inteligr8.alfresco.asie.spi; package com.inteligr8.alfresco.asie.spi;
import java.time.OffsetDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardMethodEnum; import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import com.inteligr8.alfresco.asie.model.ShardSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
public interface ShardDiscoveryService { public interface ShardDiscoveryService {
/** /**
* Determine the lead shard in the specified node/shard snapshot metadata. * Determine the lead shard in the specified node/shard snapshot metadata.
* *
* @param shardNodes A collection of snapshot metadata. * @param shardNodeStates A collection of shard node/hosts and snapshot metadata.
* @return A single node/shard holding the latest snapshot metadata. * @return A single node/shard holding the latest snapshot metadata.
*/ */
ShardInstance computeLeadShard(Collection<ShardState> shardNodes); default SolrHost computeLeadNode(Collection<Pair<SolrHost, ShardInstanceState>> shardNodeStates) {
if (shardNodeStates.isEmpty())
return null;
OffsetDateTime latestTime = OffsetDateTime.MIN;
SolrHost latestNode = null;
for (Pair<SolrHost, ShardInstanceState> shardNodeState : shardNodeStates) {
if (latestTime.isBefore(shardNodeState.getSecond().getLastIndexedTxTime())) {
latestNode = shardNodeState.getFirst();
latestTime = shardNodeState.getSecond().getLastIndexedTxTime();
}
}
return latestNode;
}
/**
* Find the shard set by the specified core name.
*
* @param core A core name (without the shard identifier suffix).
* @return A shard set; null if not found.
*/
ShardSet findSetByCore(String core);
/**
* Find the node with the specified hostname/port.
*
* @param nodeHostname The hostname of a ASIE node.
* @param nodePort The port of an ASIE node.
* @return A sharded node/host; null if not found.
*/
SolrHost findNode(String nodeHostname, int nodePort);
/** /**
* Find the latest snapshot of each shard on the specified node. * Find the latest snapshot of each shard on the specified node.
* *
* @param nodeHostname The hostname of a ASIE node. * @param node A sharded node/host.
* @param nodePort The port of an ASIE node.
* @return A set of the latest snapshot metadata of shards. * @return A set of the latest snapshot metadata of shards.
*/ */
Set<ShardState> findByNode(String nodeHostname, int nodePort); Map<ShardSet, Map<Integer, ShardInstanceState>> findByNode(SolrHost node);
/** /**
* Find the shards, their nodes, and the latest snapshot of each within the * Find all shard sets that support the specified shard methods.
* specified shard set. *
* @param shardMethods An array of shard methods.
* @return A set of shard sets.
*/
Set<ShardSet> findSetsByShardMethod(ShardMethodEnum... shardMethods);
/**
* Find the shard node/hosts for the specified shard set.
* *
* @param shardSet A shard set. * @param shardSet A shard set.
* @return A map of shards to sets of the latest snapshot metadata of those shards and their nodes. * @return A set of shard node/hosts.
*/ */
Map<Shard, Set<ShardState>> findByShardSet(ShardSet shardSet); Set<SolrHost> findNodes(ShardSet shardSet);
/** /**
* Find the shards, their nodes, and the latest snapshot of each using the * Find the shard node/hosts for the specified shard set and identifier.
* specified shard method.
*
* @param shardMethod A shard method.
* @return A collection of maps of shards to sets of the latest snapshot metadata of those shards and their nodes.
*/
Collection<Pair<Floc, Map<Shard, Set<ShardState>>>> findByShardMethod(ShardMethodEnum shardMethod);
/**
* Filter the latest snapshot of each shard.
*
* @param shards A map of shards to sets of the latest snapshot metadata of those shards and their nodes.
* @param shardId A 0-based index of a shard.
* @return A set of the latest snapshot metadata of shards.
*/
<T> Set<T> filterByShard(Map<Shard, Set<T>> shards, int shardId);
/**
* Find the latest snapshot of each shard and their nodes within the
* specified shard set.
* *
* @param shardSet A shard set. * @param shardSet A shard set.
* @param shardId A 0-based index of a shard. * @param shardId A shard identifier (e.g. 0).
* @return A set of the latest snapshot metadata of shards. * @return A set of shard node/hosts.
*/ */
Set<ShardState> findByShard(ShardSet shardSet, int shardId); Set<SolrHost> findNodesByShard(ShardSet shardSet, int shardId);
Map<Integer, Pair<SolrHost, ShardInstanceState>> findLatestNodeStates(ShardSet shardSet);
/**
* Find the shard node/hosts and their states for the specified shard set
* and identifier. The list is left in an unknown order, but it is a list
* for easy use of a Comparator for sorting.
*
* @param shardSet A shard set.
* @param shardId A shard identifier (e.g. 0).
* @return A list of shard node/hosts and their latest state.
*/
List<Pair<SolrHost, ShardInstanceState>> findNodeStatesByShard(ShardSet shardSet, int shardId);
/**
* Find the shard identifiers for the specified shard set and node/host.
*
* @param shardSet A shard set.
* @param node A shard ndoe/host.
* @return A set of shard identifiers.
*/
Set<Integer> findIdsByNode(ShardSet shardSet, SolrHost node);
/**
* Find the shards and their states for the specified shard set and node/host.
*
* @param shardSet A shard set.
* @param node A shard ndoe/host.
* @return A map of shards and their states.
*/
Map<Integer, ShardInstanceState> findStatesByNode(ShardSet shardSet, SolrHost node);
public class ShardedNodeShardStateComparator implements Comparator<Pair<SolrHost, ShardInstanceState>> {
@Override
public int compare(Pair<SolrHost, ShardInstanceState> p1, Pair<SolrHost, ShardInstanceState> p2) {
return - Long.compare(p1.getSecond().getLastIndexedTxId(), p2.getSecond().getLastIndexedTxId());
}
}
} }

View File

@@ -0,0 +1,9 @@
package com.inteligr8.alfresco.asie.spi;
import org.alfresco.repo.index.shard.ShardInstance;
public interface ShardRegistry extends org.alfresco.repo.index.shard.ShardRegistry {
void unregisterShardInstance(ShardInstance shardInstance);
}

View File

@@ -1,18 +1,13 @@
package com.inteligr8.alfresco.asie.spi; package com.inteligr8.alfresco.asie.spi;
import java.io.Serializable;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
public interface ShardStateService { public interface ShardStateService {
/** /**
* Clears the shard state. * Clears the shard state.
*
* This allows the framework to provide an alternative implementation from
* what is provided by Alfresco Enterprise, which sometimes fails.
*/ */
void clear(); void clear();
void remove(Serializable... keys);
void iterate(AttributeQueryCallback callback);
} }

View File

@@ -12,12 +12,10 @@
<dl> <dl>
<dt>nodeEndpoint</dt> <dt>nodeEndpoint</dt>
<dd>A hostname or hostname:port for the ASIE node</dd> <dd>A hostname or hostname:port for the ASIE node</dd>
<dt>shardSet</dt> <dt>shardCore</dt>
<dd>A shard method combined with its distinguishing properties; <dd>A core name (prefix) for the ASIE shard (e.g. alfresco)</dd>
methods: MOD_ACL_ID, ACL_ID, DB_ID, DB_ID_RANGE, DATE, PROPERTY, EXPLICIT_ID;
e.g. PROPERTY;key:cm:created;regex:^d{4} or DB_ID</dd>
<dt>shardId</dt> <dt>shardId</dt>
<dd>A number starting at 1</dd> <dd>A numeric shard ID for the ASIE shard (e.g. 0)</dd>
</dl> </dl>
<p>The following status codes should be expected:</p> <p>The following status codes should be expected:</p>
<dl> <dl>
@@ -31,7 +29,7 @@
]]></description> ]]></description>
<!-- Endpoint Configuration --> <!-- Endpoint Configuration -->
<url>/inteligr8/asie/node/{nodeEndpoint}/shard/{shardSet}/{shardId}</url> <url>/inteligr8/asie/node/{nodeEndpoint}/shard/{shardCore}/{shardId}</url>
<!-- Security --> <!-- Security -->
<authentication>admin</authentication> <authentication>admin</authentication>

View File

@@ -0,0 +1,41 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieves ASIE Node Shard Status</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieves meta-data about all shards on a single ASIE node as registred with ACS.</p>
<p>The following query parameter is supported:</p>
<dl>
<dt>nodeEndpoint</dt>
<dd>A hostname or hostname:port for the ASIE node; dots are not allowed, you may use _ (underscore) instead</dd>
<dt>shardCore</dt>
<dd>A core name (prefix) for the ASIE shard (e.g. alfresco)</dd>
<dt>shardId</dt>
<dd>A numeric shard ID for the ASIE shard (e.g. 0)</dd>
</dl>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>400</dt>
<dd>The path parameters are invalid</dd>
<dt>404</dt>
<dd>The specified ASIE node/shard could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/node/{nodeEndpoint}/shard/{shardCore}/{shardId}</url>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -2,7 +2,7 @@
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt"> xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization --> <!-- Naming & Organization -->
<shortname>Adds ASIE Node to Registry</shortname> <shortname>Adds ASIE Node/Shard to Registry</shortname>
<family>Inteligr8 ASIE</family> <family>Inteligr8 ASIE</family>
<description><![CDATA[ <description><![CDATA[
<p>Loads an ASIE shard on a single ASIE node, which will eventually register with ACS.</p> <p>Loads an ASIE shard on a single ASIE node, which will eventually register with ACS.</p>

View File

@@ -8,12 +8,10 @@
<p>Retrieve meta-data about the specified ASIE shard registered with ACS.</p> <p>Retrieve meta-data about the specified ASIE shard registered with ACS.</p>
<p>The following path and query parameters are expected or supported:</p> <p>The following path and query parameters are expected or supported:</p>
<dl> <dl>
<dt>shardSet</dt> <dt>shardCore</dt>
<dd>A shard method combined with its distinguishing properties; <dd>A core name (prefix) for the ASIE shard (e.g. alfresco)</dd>
methods: MOD_ACL_ID, ACL_ID, DB_ID, DB_ID_RANGE, DATE, PROPERTY, EXPLICIT_ID;
e.g. PROPERTY;key:cm:created;regex:^d{4} or DB_ID</dd>
<dt>shardId</dt> <dt>shardId</dt>
<dd>A number starting at 1</dd> <dd>A numeric shard ID for the ASIE shard (e.g. 0)</dd>
<dt>sampleHashType</dt> <dt>sampleHashType</dt>
<dd>A sample hash type; Sample hash types: PropertyYear, PropertyQuarter, PropertyMonth, PropertyWeek</dd> <dd>A sample hash type; Sample hash types: PropertyYear, PropertyQuarter, PropertyMonth, PropertyWeek</dd>
</dl> </dl>
@@ -58,7 +56,7 @@
]]></description> ]]></description>
<!-- Endpoint Configuration --> <!-- Endpoint Configuration -->
<url>/inteligr8/asie/shard/{shardSet}/{shardId}?includeSampleHashes={includeSampleHashes?}</url> <url>/inteligr8/asie/shard/{shardCore}/{shardId}?includeSampleHashes={includeSampleHashes?}</url>
<format default="json">any</format> <format default="json">any</format>
<!-- Security --> <!-- Security -->

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.1-SNAPSHOT</version> <version>1.2-SNAPSHOT</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>