moving shard determination to SolrShardHashService

This commit is contained in:
2026-01-12 15:29:03 -05:00
parent ed2251065d
commit fbf6c17206
2 changed files with 69 additions and 25 deletions
@@ -11,7 +11,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.Floc;
@@ -19,8 +18,6 @@ import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance; import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardRegistry; import org.alfresco.repo.index.shard.ShardRegistry;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
@@ -51,9 +48,6 @@ public abstract class AbstractNodeActionService {
@Autowired @Autowired
private NamespaceService namespaceService; private NamespaceService namespaceService;
@Autowired
private NodeService nodeService;
@Autowired @Autowired
private ApiService apiService; private ApiService apiService;
@@ -233,7 +227,7 @@ public abstract class AbstractNodeActionService {
if (!floc.getKey().getStoreRefs().contains(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) if (!floc.getKey().getStoreRefs().contains(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE))
continue; continue;
Integer shardHash = null; int shardHash = -1;
ShardSet shardset = null; ShardSet shardset = null;
for (Entry<Shard, Set<ShardState>> shard : floc.getValue().entrySet()) { for (Entry<Shard, Set<ShardState>> shard : floc.getValue().entrySet()) {
for (ShardState shardState : shard.getValue()) { for (ShardState shardState : shard.getValue()) {
@@ -242,26 +236,10 @@ public abstract class AbstractNodeActionService {
// so we are computing the shardHash once and caching it // so we are computing the shardHash once and caching it
if (shardset == null) { if (shardset == null) {
shardset = ShardSet.from(floc.getKey(), shardState); shardset = ShardSet.from(floc.getKey(), shardState);
switch (shardset.getMethod()) { shardHash = this.shardHashService.computeShardInstanceId(shardset, nodeDbId);
case PROPERTY:
this.logger.trace("Using property-based sharding method; discovering target shard ...");
NodeRef nodeRef = this.nodeService.getNodeRef(nodeDbId);
Object propValue = this.nodeService.getProperty(nodeRef, QName.createQName(shardset.getPrefixedProperty(), this.namespaceService));
if (propValue != null) {
this.logger.trace("Discovered node property for sharding: {} <=> {} => {}", nodeDbId, nodeRef, propValue);
Matcher matcher = shardset.getRegex().matcher(propValue.toString());
String hashable = matcher.group(1);
this.logger.trace("Extracted shardable value from node: {} <=> {} => {}", nodeDbId, propValue, hashable);
shardHash = this.shardHashService.hash(hashable, shardset.getShards().intValue());
this.logger.debug("Hash shardable value to shard instance ID: {} <=> {} => {}", nodeDbId, hashable, shardHash);
}
break;
default:
this.logger.trace("Despite sharding, considering all shards and nodes without optimization: {}: {}", nodeDbId, instances);
}
} }
if (shardHash != null) { if (shardHash >= 0) {
if (shard.getKey().getInstance() == shardHash) if (shard.getKey().getInstance() == shardHash)
instances.add(this.toModel(shardState.getShardInstance(), shardState)); instances.add(this.toModel(shardState.getShardInstance(), shardState));
} else { } else {
@@ -1,14 +1,32 @@
package com.inteligr8.alfresco.asie.service; package com.inteligr8.alfresco.asie.service;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.regex.Matcher;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.codec.digest.MurmurHash3; import org.apache.commons.codec.digest.MurmurHash3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.ShardSet;
@Component @Component
public class SolrShardHashService { public class SolrShardHashService {
private final Charset charset = Charset.forName("utf-8"); private final Charset charset = Charset.forName("utf-8");
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private NamespaceService namespaceService;
@Autowired
private NodeService nodeService;
public int hash(Object obj, int shardCount) { public int hash(Object obj, int shardCount) {
String str = obj.toString(); String str = obj.toString();
@@ -23,5 +41,53 @@ public class SolrShardHashService {
hash.add(bytes, 0, bytes.length); hash.add(bytes, 0, bytes.length);
return Math.abs(hash.end()) % shardCount; return Math.abs(hash.end()) % shardCount;
} }
/**
* @param shardset A ShardSet.
* @param nodeDbId The numeric database ID of an ACS node.
* @return A shard instance from 0 to 1 less than the number of shards; -1 if unable to hash.
*/
public int computeShardInstanceId(ShardSet shardset, long nodeDbId) {
NodeRef nodeRef = this.nodeService.getNodeRef(nodeDbId);
if (nodeRef == null)
throw new AlfrescoRuntimeException("The node " + nodeDbId + " does not exist");
return this.computeShardInstanceId(shardset, nodeRef);
}
/**
* @param shardset A ShardSet.
* @param nodeRef A reference to an ACS node.
* @return A shard instance from 0 to 1 less than the number of shards; -1 if unable to hash.
*/
public int computeShardInstanceId(ShardSet shardset, NodeRef nodeRef) {
switch (shardset.getMethod()) {
case PROPERTY:
QName hashableProperty = QName.createQName(shardset.getPrefixedProperty(), this.namespaceService);
Object fullPropertyValue = this.nodeService.getProperty(nodeRef, hashableProperty);
if (fullPropertyValue == null) {
this.logger.debug("Unable to determine shard instance ID because property does not exist on node: {}: {}", nodeRef, hashableProperty);
return -1;
}
this.logger.trace("Discovered node property for sharding: {} => {}", nodeRef, fullPropertyValue);
String hashableValue = fullPropertyValue.toString();
if (shardset.getRegex() != null) {
Matcher matcher = shardset.getRegex().matcher(hashableValue);
if (!matcher.find()) {
this.logger.debug("Unable to determine shard instance ID because regex pattern doesn't match the hashing property value: {}: {}: {}", nodeRef, shardset.getRegex(), hashableValue);
return -1;
}
hashableValue = matcher.group(1);
this.logger.trace("Extracted shardable value from node: {}: {} => {}", nodeRef, fullPropertyValue, hashableValue);
}
int shardHash = this.hash(hashableValue, shardset.getShards().intValue());
this.logger.debug("Hash shardable value to shard instance ID: {}: {} => {}", nodeRef, hashableValue, shardHash);
return shardHash;
default:
this.logger.trace("Unable to determine shard instance ID due to shard method: {}", shardset.getMethod());
return -1;
}
}
} }