You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ma...@apache.org on 2020/08/22 18:42:21 UTC
[lucene-solr] 02/22: @582 Keep making Schema threadsafe.
This is an automated email from the ASF dual-hosted git repository.
markrmiller pushed a commit to branch reference_impl
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
commit f19e31cecd4fecbec3193a877c8b125fca673982
Author: markrmiller@gmail.com <ma...@gmail.com>
AuthorDate: Thu Aug 20 11:51:46 2020 -0500
@582 Keep making Schema threadsafe.
---
.../handler/component/SpellCheckComponent.java | 6 +-
.../java/org/apache/solr/schema/IndexSchema.java | 73 +++++++++++++---------
.../org/apache/solr/schema/ManagedIndexSchema.java | 19 +++---
.../org/apache/solr/spelling/SolrSpellChecker.java | 2 +-
4 files changed, 60 insertions(+), 40 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java b/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java
index 3599718..c301826 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java
@@ -754,7 +754,11 @@ public class SpellCheckComponent extends SearchComponent implements SolrCoreAwar
queryConverter = queryConverters.values().iterator().next();
IndexSchema schema = core.getLatestSchema();
String fieldTypeName = (String) initParams.get("queryAnalyzerFieldType");
- FieldType fieldType = schema.getFieldTypes().get(fieldTypeName);
+
+ FieldType fieldType = null;
+ if (fieldTypeName != null) {
+ fieldType = schema.getFieldTypes().get(fieldTypeName);
+ }
Analyzer analyzer = fieldType == null ? new WhitespaceAnalyzer()
: fieldType.getQueryAnalyzer();
//TODO: There's got to be a better way! Where's Spring when you need it?
diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
index 935b2c8..2691239 100644
--- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
@@ -140,12 +140,14 @@ public class IndexSchema {
private static XPathExpression defaultSearchFieldExp;
private static XPathExpression solrQueryParserDefaultOpExp;
private static XPathExpression schemaUniqueKeyExp;
-
-
+ private static XPathExpression fieldTypeXPathExpressionsExp;
+ private static XPathExpression copyFieldsExp;
static String schemaNamePath = stepsToPath(SCHEMA, AT + NAME);
static String schemaVersionPath = "/schema/@version";
+ static String copyFieldPath = "//" + COPY_FIELD;
+
static String fieldTypeXPathExpressions = getFieldTypeXPathExpressions();
static String schemaSimPath = stepsToPath(SCHEMA, SIMILARITY); // /schema/similarity
@@ -202,47 +204,59 @@ public class IndexSchema {
} catch (XPathExpressionException e) {
log.error("", e);
}
+ try {
+ fieldTypeXPathExpressionsExp = xpath.compile(fieldTypeXPathExpressions);
+ } catch (XPathExpressionException e) {
+ log.error("", e);
+ }
+
+ try {
+ copyFieldsExp = xpath.compile(copyFieldPath);
+ } catch (XPathExpressionException e) {
+ log.error("", e);
+ }
+
}
- protected String resourceName;
- protected String name;
+ protected volatile String resourceName;
+ protected volatile String name;
protected final Version luceneVersion;
protected float version;
protected final SolrResourceLoader loader;
protected final Properties substitutableProperties;
// some code will add fields after construction, needs to be thread safe
- protected final Map<String,SchemaField> fields = new ConcurrentHashMap<>(128);
+ protected volatile Map<String,SchemaField> fields = new ConcurrentHashMap<>(128);
- protected Map<String,FieldType> fieldTypes = new HashMap<>(64);
+ protected volatile Map<String,FieldType> fieldTypes = new ConcurrentHashMap<>(64);
- protected List<SchemaField> fieldsWithDefaultValue = new ArrayList<>(64);
- protected Collection<SchemaField> requiredFields = new HashSet<>(32);
- protected DynamicField[] dynamicFields = new DynamicField[] {};
+ protected volatile Set<SchemaField> fieldsWithDefaultValue = ConcurrentHashMap.newKeySet(64);
+ protected volatile Collection<SchemaField> requiredFields = ConcurrentHashMap.newKeySet(32);
+ protected volatile DynamicField[] dynamicFields = new DynamicField[] {};
public DynamicField[] getDynamicFields() { return dynamicFields; }
protected final Cache<String, SchemaField> dynamicFieldCache = new ConcurrentLRUCache(10000, 8000, 9000,100, false,false, null);
- private Analyzer indexAnalyzer;
- private Analyzer queryAnalyzer;
+ private volatile Analyzer indexAnalyzer;
+ private volatile Analyzer queryAnalyzer;
- protected List<SchemaAware> schemaAware = new ArrayList<>(64);
+ protected volatile Set<SchemaAware> schemaAware = ConcurrentHashMap.newKeySet(64);
- protected Map<String,Set<CopyField>> copyFieldsMap = new ConcurrentHashMap<>(64);
+ protected volatile Map<String,Set<CopyField>> copyFieldsMap = new ConcurrentHashMap<>(64);
public Map<String,Set<CopyField>> getCopyFieldsMap() { return Collections.unmodifiableMap(copyFieldsMap); }
- protected DynamicCopy[] dynamicCopyFields = new DynamicCopy[] {};
+ protected volatile DynamicCopy[] dynamicCopyFields = new DynamicCopy[] {};
public DynamicCopy[] getDynamicCopyFields() { return dynamicCopyFields; }
- private Map<FieldType, PayloadDecoder> decoders = new ConcurrentHashMap<>(); // cache to avoid scanning token filters repeatedly, unnecessarily
+ private final Map<FieldType, PayloadDecoder> decoders = new ConcurrentHashMap<>(); // cache to avoid scanning token filters repeatedly, unnecessarily
/**
* keys are all fields copied to, count is num of copyField
* directives that target them.
*/
- protected Map<SchemaField, Integer> copyFieldTargetCounts = new HashMap<>();
+ protected volatile Map<SchemaField, Integer> copyFieldTargetCounts = new ConcurrentHashMap<>(32);
/**
* Constructs a schema using the specified resource name and stream.
@@ -341,7 +355,7 @@ public class IndexSchema {
/**
* Provides direct access to the List containing all fields with a default value
*/
- public List<SchemaField> getFieldsWithDefaultValue() { return fieldsWithDefaultValue; }
+ public Set<SchemaField> getFieldsWithDefaultValue() { return fieldsWithDefaultValue; }
/**
* Provides direct access to the List containing all required fields. This
@@ -499,15 +513,15 @@ public class IndexSchema {
}
private class SolrIndexAnalyzer extends DelegatingAnalyzerWrapper {
- protected final HashMap<String, Analyzer> analyzers;
+ protected final Map<String, Analyzer> analyzers;
SolrIndexAnalyzer() {
super(PER_FIELD_REUSE_STRATEGY);
analyzers = analyzerCache();
}
- protected HashMap<String, Analyzer> analyzerCache() {
- HashMap<String, Analyzer> cache = new HashMap<>();
+ protected Map<String, Analyzer> analyzerCache() {
+ Map<String, Analyzer> cache = new ConcurrentHashMap<>();
for (SchemaField f : getFields().values()) {
Analyzer analyzer = f.getType().getIndexAnalyzer();
cache.put(f.getName(), analyzer);
@@ -527,8 +541,8 @@ public class IndexSchema {
SolrQueryAnalyzer() {}
@Override
- protected HashMap<String, Analyzer> analyzerCache() {
- HashMap<String, Analyzer> cache = new HashMap<>();
+ protected Map<String, Analyzer> analyzerCache() {
+ Map<String, Analyzer> cache = new ConcurrentHashMap<>();
for (SchemaField f : getFields().values()) {
Analyzer analyzer = f.getType().getQueryAnalyzer();
cache.put(f.getName(), analyzer);
@@ -554,7 +568,6 @@ public class IndexSchema {
// in the current case though, the stream is valid so we wont load the resource by name
XmlConfigFile schemaConf = new XmlConfigFile(loader, SCHEMA, is, SLASH+SCHEMA+SLASH, substitutableProperties);
Document document = schemaConf.getDocument();
- final XPath xpath = XmlConfigFile.getXpath();
Node nd = (Node) schemaNameExp.evaluate(document, XPathConstants.NODE);
StringBuilder sb = new StringBuilder();
@@ -585,11 +598,11 @@ public class IndexSchema {
// load the Field Types
final FieldTypePluginLoader typeLoader = new FieldTypePluginLoader(this, fieldTypes, schemaAware);
- NodeList nodes = (NodeList) xpath.evaluate(fieldTypeXPathExpressions, document, XPathConstants.NODESET);
+ NodeList nodes = (NodeList) fieldTypeXPathExpressionsExp.evaluate(document, XPathConstants.NODESET);
typeLoader.load(loader, nodes);
// load the fields
- Map<String,Boolean> explicitRequiredProp = loadFields(document, xpath);
+ Map<String,Boolean> explicitRequiredProp = loadFields(document);
Node node = (Node) schemaSimExp.evaluate(document, XPathConstants.NODE);
@@ -687,7 +700,7 @@ public class IndexSchema {
// expression = "/schema/copyField";
dynamicCopyFields = new DynamicCopy[] {};
- loadCopyFields(document, xpath);
+ loadCopyFields(document);
postReadInform();
@@ -722,7 +735,7 @@ public class IndexSchema {
*
* @return a map from field name to explicit required value
*/
- protected synchronized Map<String,Boolean> loadFields(Document document, XPath xpath) throws XPathExpressionException {
+ protected synchronized Map<String,Boolean> loadFields(Document document) throws XPathExpressionException {
// Hang on to the fields that say if they are required -- this lets us set a reasonable default for the unique key
Map<String,Boolean> explicitRequiredProp = new HashMap<>();
@@ -745,7 +758,7 @@ public class IndexSchema {
FieldType ft = fieldTypes.get(type);
if (ft==null) {
throw new SolrException
- (ErrorCode.BAD_REQUEST, "Unknown " + FIELD_TYPE + " '" + type + "' specified on field " + name);
+ (ErrorCode.BAD_REQUEST, "Unknown " + FIELD_TYPE + " '" + type + "' specified on field " + name + " types:" + fieldTypes);
}
Map<String,String> args = DOMUtil.toMapExcept(attrs, NAME, TYPE);
@@ -813,9 +826,9 @@ public class IndexSchema {
/**
* Loads the copy fields
*/
- protected synchronized void loadCopyFields(Document document, XPath xpath) throws XPathExpressionException {
+ protected synchronized void loadCopyFields(Document document) throws XPathExpressionException {
String expression = "//" + COPY_FIELD;
- NodeList nodes = (NodeList)xpath.evaluate(expression, document, XPathConstants.NODESET);
+ NodeList nodes = (NodeList)copyFieldsExp.evaluate(document, XPathConstants.NODESET);
for (int i=0; i<nodes.getLength(); i++) {
Node node = nodes.item(i);
diff --git a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
index 96be6fb..78c04a2 100644
--- a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
@@ -398,6 +398,12 @@ public final class ManagedIndexSchema extends IndexSchema {
}
newSchema = shallowCopy(true);
+ newSchema.fields = new ConcurrentHashMap<>(fields);
+ newSchema.requiredFields = ConcurrentHashMap.newKeySet(requiredFields.size());
+ newSchema.requiredFields.addAll(requiredFields);
+ newSchema.fieldsWithDefaultValue = ConcurrentHashMap.newKeySet(fieldsWithDefaultValue.size());
+ newSchema.fieldsWithDefaultValue.addAll(fieldsWithDefaultValue);
+
for (SchemaField newField : newFields) {
if (null != newSchema.fields.get(newField.getName())) {
String msg = "Field '" + newField.getName() + "' already exists.";
@@ -496,10 +502,10 @@ public final class ManagedIndexSchema extends IndexSchema {
newSchema = shallowCopy(true);
// clone data structures before modifying them
newSchema.copyFieldsMap = cloneCopyFieldsMap(copyFieldsMap);
- newSchema.copyFieldTargetCounts
- = (Map<SchemaField,Integer>)((HashMap<SchemaField,Integer>)copyFieldTargetCounts).clone();
+ newSchema.copyFieldTargetCounts = new ConcurrentHashMap<>(copyFieldTargetCounts);
newSchema.dynamicCopyFields = new DynamicCopy[dynamicCopyFields.length];
System.arraycopy(dynamicCopyFields, 0, newSchema.dynamicCopyFields, 0, dynamicCopyFields.length);
+ newSchema.fields = new ConcurrentHashMap<>(fields);
// Drop the old field
newSchema.fields.remove(fieldName);
@@ -711,8 +717,7 @@ public final class ManagedIndexSchema extends IndexSchema {
newSchema = shallowCopy(true);
// clone data structures before modifying them
- newSchema.copyFieldTargetCounts
- = (Map<SchemaField,Integer>)((HashMap<SchemaField,Integer>)copyFieldTargetCounts).clone();
+ newSchema.copyFieldTargetCounts = new ConcurrentHashMap<>(copyFieldTargetCounts);
newSchema.dynamicCopyFields = new DynamicCopy[dynamicCopyFields.length];
System.arraycopy(dynamicCopyFields, 0, newSchema.dynamicCopyFields, 0, dynamicCopyFields.length);
@@ -955,9 +960,7 @@ public final class ManagedIndexSchema extends IndexSchema {
// we shallow copied fieldTypes, but since we're changing them, we need to do a true
// deep copy before adding the new field types
- HashMap<String,FieldType> clone =
- (HashMap<String,FieldType>)((HashMap<String,FieldType>)newSchema.fieldTypes).clone();
- newSchema.fieldTypes = clone;
+ newSchema.fieldTypes = new ConcurrentHashMap<>(fieldTypes);
// do a first pass to validate the field types don't exist already
for (FieldType fieldType : fieldTypeList) {
@@ -1399,7 +1402,7 @@ public final class ManagedIndexSchema extends IndexSchema {
newSchema.requiredFields.addAll(requiredFields);
}
- // These don't need new collections - addFields() won't add members to them
+ // These don't need new collections - addFields() won't add members to them
newSchema.fieldTypes = fieldTypes;
newSchema.dynamicFields = dynamicFields;
newSchema.dynamicCopyFields = dynamicCopyFields;
diff --git a/solr/core/src/java/org/apache/solr/spelling/SolrSpellChecker.java b/solr/core/src/java/org/apache/solr/spelling/SolrSpellChecker.java
index 5543867..95079c5 100644
--- a/solr/core/src/java/org/apache/solr/spelling/SolrSpellChecker.java
+++ b/solr/core/src/java/org/apache/solr/spelling/SolrSpellChecker.java
@@ -67,7 +67,7 @@ public abstract class SolrSpellChecker {
analyzer = schema.getFieldType(field).getQueryAnalyzer();
}
fieldTypeName = (String) config.get(FIELD_TYPE);
- if (schema.getFieldTypes().containsKey(fieldTypeName)) {
+ if (fieldTypeName != null && schema.getFieldTypes().containsKey(fieldTypeName)) {
FieldType fieldType = schema.getFieldTypes().get(fieldTypeName);
analyzer = fieldType.getQueryAnalyzer();
}