package org.apache.solr.search.field; /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.apache.lucene.index.AtomicReaderContext; import org.apache.solr.core.RefCountBase; import org.apache.solr.search.CacheRegenerator; import org.apache.solr.search.QueryContext; import org.apache.solr.search.SolrIndexSearcher; import org.noggit.JSONUtil; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; public abstract class TopValues extends RefCountBase { protected FieldValues fieldValues; // pointer back to the "source" protected LeafValues[] leafValues; protected volatile int nSegs; // number of segments instantiated protected int carriedOver; // number of segments carried over // top terms? public TopValues(FieldValues fieldValues) { this.fieldValues = fieldValues; } public boolean allSegmentsLoaded() { return leafValues != null && leafValues.length == nSegs; } public LeafValues getLeafValues(QueryContext context, AtomicReaderContext readerContext) throws IOException { assert context != null; int readerOrd = readerContext.ord; if (allSegmentsLoaded()) { return leafValues[readerOrd]; } LeafValues leaf; synchronized (this) { if (leafValues == null) { leafValues = new LeafValues[readerContext.parent.leaves().size()]; } leaf = leafValues[readerOrd]; if (leaf == null) { leaf = new CreationLeafValue(fieldValues); leafValues[readerOrd] = leaf; // add any flags or creation commands to the creation value // since another thread may actually do the creation. } } if (leaf instanceof CreationLeafValue) { synchronized (leaf) { CreationLeafValue create = (CreationLeafValue)leaf; if (create.value == null) { create.value = createValue(this, create, readerContext); synchronized (this) { leafValues[readerOrd] = create.value; nSegs++; } } leaf = create.value; } } return leaf; } public abstract LeafValues createValue(TopValues topValues, CreationLeafValue create, AtomicReaderContext readerContext) throws IOException; @Override public String toString() { Map<String,Object> map = new LinkedHashMap<String,Object>(); addInfo(map); return JSONUtil.toJSON(map, 2); } public void addInfo(Map<String,Object> map) { Map<String,Object> sfMap = new LinkedHashMap<String,Object>(); // fieldValues.field.addInfo(sfMap); // map.put("field", sfMap); map.put("field", fieldValues.getFieldName()); map.put("class", this.getClass().getSimpleName()); map.put("refcount", getRefCount()); map.put("numSegments", nSegs); map.put("carriedOver", carriedOver); map.put("size", getSizeInBytes()); } public long getSizeInBytes() { LeafValues[] snapshot; synchronized (this) { if (leafValues == null) return 0; snapshot = leafValues.clone(); } long answer = 0; for (LeafValues leaf : snapshot) { if (leaf != null && !(leaf instanceof CreationLeafValue)) { answer += leaf.getSizeInBytes(); } } return answer; } @Override public void free() { // Free should never be called while this TopValues is still in active use, hence // we do not need to worry about concurrent access any longer. // A final read memory barrier isn't a bad idea though... synchronized (this) { if (leafValues == null) return; for (LeafValues leaf : leafValues) { if (leaf == null) continue; if (leaf instanceof CreationLeafValue) { // This should *never* happen... it means we are freeing concurrently with someone // trying to instantiate a leaf. Ref counting must be off. throw new RuntimeException("ERROR: Encountered " + leaf + " during free of " + this); } leaf.decref(); } } } static final class CreationLeafValue extends LeafValues { LeafValues value; CreationLeafValue(FieldValues fieldValues) { super(fieldValues); } @Override public long getSizeInBytes() { return 0; } @Override public FieldStats getFieldStats() { return null; } @Override public int getRefCount() { return 1; } @Override public int incref() { return 1; } @Override public int decref() { return 1; } @Override public boolean tryIncref() { return true; } @Override public boolean tryDecref() { return true; } @Override protected void free() { } } public static class Regenerator implements CacheRegenerator { @Override public boolean regenerateItem(SolrIndexSearcher.WarmContext warmContext, Object oldKey, Object oldVal) throws IOException { TopValues newValues = ((TopValues)oldVal).create(warmContext); warmContext.searcher.getnCache().put((String)oldKey, newValues); return true; } } // called on the old TopValues public abstract TopValues create(SolrIndexSearcher.WarmContext warmContext); // called on a newly created TopValues public void create(SolrIndexSearcher.WarmContext warmContext, TopValues oldTopValues) { LeafValues[] oldLeafValues = oldTopValues.leafValues; if (warmContext.segmentsShared == 0 || oldLeafValues == null) { return; } assert oldLeafValues.length == warmContext.oldToNewOrd.length; leafValues = new LeafValues[warmContext.searcher.getTopReaderContext().leaves().size()]; synchronized (oldTopValues) { // is this really necessary here? for (int i=0; i<warmContext.oldToNewOrd.length; i++) { int newOrd = warmContext.oldToNewOrd[i]; if (newOrd >= 0) { LeafValues lf = oldLeafValues[i]; if (lf != null && !(lf instanceof CreationLeafValue)) { lf.incref(); leafValues[newOrd] = lf; carriedOver++; nSegs++; } } } } } }