Skip to content

Commit 990d5a1

Browse files
committed
Query DSL: term, term, prefix, and range filter are now weakly cached, for more strong caching, set _cache to true, closes elastic#450.
1 parent 6c91c8a commit 990d5a1

File tree

12 files changed

+295
-8
lines changed

12 files changed

+295
-8
lines changed

modules/elasticsearch/src/main/java/org/apache/lucene/search/PublicTermsFilter.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@
1919

2020
package org.apache.lucene.search;
2121

22+
import org.apache.lucene.index.IndexReader;
2223
import org.apache.lucene.index.Term;
24+
import org.apache.lucene.index.TermDocs;
25+
import org.apache.lucene.util.OpenBitSet;
2326

27+
import java.io.IOException;
2428
import java.util.Set;
2529

2630
/**
@@ -31,4 +35,24 @@ public class PublicTermsFilter extends TermsFilter {
3135
public Set<Term> getTerms() {
3236
return terms;
3337
}
38+
39+
// override default impl here to use fastSet...
40+
41+
@Override
42+
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
43+
OpenBitSet result = new OpenBitSet(reader.maxDoc());
44+
TermDocs td = reader.termDocs();
45+
try {
46+
for (Term term : terms) {
47+
td.seek(term);
48+
while (td.next()) {
49+
result.fastSet(td.doc());
50+
}
51+
}
52+
}
53+
finally {
54+
td.close();
55+
}
56+
return result;
57+
}
3458
}

modules/elasticsearch/src/main/java/org/elasticsearch/common/lucene/search/TermFilter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
/**
3232
* A simple filter for a specific term.
3333
*
34-
* @author kimchy (Shay Banon)
34+
* @author kimchy (shay.banon)
3535
*/
3636
public class TermFilter extends Filter {
3737

@@ -51,7 +51,7 @@ public Term getTerm() {
5151
try {
5252
td.seek(term);
5353
while (td.next()) {
54-
result.set(td.doc());
54+
result.fastSet(td.doc());
5555
}
5656
}
5757
finally {

modules/elasticsearch/src/main/java/org/elasticsearch/index/cache/filter/FilterCache.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public interface FilterCache extends IndexComponent, CloseableComponent {
3333

3434
Filter cache(Filter filterToCache);
3535

36+
Filter weakCache(Filter filterToCache);
37+
3638
boolean isCached(Filter filter);
3739

3840
void clear(IndexReader reader);

modules/elasticsearch/src/main/java/org/elasticsearch/index/cache/filter/none/NoneFilterCache.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public class NoneFilterCache extends AbstractIndexComponent implements FilterCac
5050
return filterToCache;
5151
}
5252

53+
@Override public Filter weakCache(Filter filterToCache) {
54+
return filterToCache;
55+
}
56+
5357
@Override public boolean isCached(Filter filter) {
5458
return false;
5559
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/cache/filter/soft/SoftFilterCache.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import org.elasticsearch.common.lucene.docset.DocSet;
2626
import org.elasticsearch.common.settings.Settings;
2727
import org.elasticsearch.index.Index;
28-
import org.elasticsearch.index.cache.filter.support.AbstractConcurrentMapFilterCache;
28+
import org.elasticsearch.index.cache.filter.support.AbstractDoubleConcurrentMapFilterCache;
2929
import org.elasticsearch.index.settings.IndexSettings;
3030

3131
import java.util.concurrent.ConcurrentMap;
@@ -35,18 +35,24 @@
3535
*
3636
* @author kimchy (shay.banon)
3737
*/
38-
public class SoftFilterCache extends AbstractConcurrentMapFilterCache {
38+
public class SoftFilterCache extends AbstractDoubleConcurrentMapFilterCache {
3939

4040
@Inject public SoftFilterCache(Index index, @IndexSettings Settings indexSettings) {
4141
super(index, indexSettings);
4242
}
4343

44-
@Override protected ConcurrentMap<Filter, DocSet> buildFilterMap() {
44+
@Override protected ConcurrentMap<Filter, DocSet> buildCacheMap() {
4545
// DocSet are not really stored with strong reference only when searching on them...
4646
// Filter might be stored in query cache
4747
return new MapMaker().softValues().makeMap();
4848
}
4949

50+
@Override protected ConcurrentMap<Filter, DocSet> buildWeakCacheMap() {
51+
// DocSet are not really stored with strong reference only when searching on them...
52+
// Filter might be stored in query cache
53+
return new MapMaker().weakValues().makeMap();
54+
}
55+
5056
@Override public String type() {
5157
return "soft";
5258
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/cache/filter/support/AbstractConcurrentMapFilterCache.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ protected AbstractConcurrentMapFilterCache(Index index, @IndexSettings Settings
9595
return new FilterCacheFilterWrapper(filterToCache, this);
9696
}
9797

98+
@Override public Filter weakCache(Filter filterToCache) {
99+
return cache(filterToCache);
100+
}
101+
98102
@Override public boolean isCached(Filter filter) {
99103
return filter instanceof FilterCacheFilterWrapper;
100104
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Licensed to Elastic Search and Shay Banon under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. Elastic Search licenses this
6+
* file to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.index.cache.filter.support;
21+
22+
import org.apache.lucene.index.IndexReader;
23+
import org.apache.lucene.search.DocIdSet;
24+
import org.apache.lucene.search.Filter;
25+
import org.elasticsearch.common.collect.MapMaker;
26+
import org.elasticsearch.common.lucene.docset.DocSet;
27+
import org.elasticsearch.common.settings.Settings;
28+
import org.elasticsearch.index.AbstractIndexComponent;
29+
import org.elasticsearch.index.Index;
30+
import org.elasticsearch.index.cache.filter.FilterCache;
31+
import org.elasticsearch.index.settings.IndexSettings;
32+
33+
import java.io.IOException;
34+
import java.util.concurrent.ConcurrentMap;
35+
36+
import static org.elasticsearch.common.lucene.docset.DocSets.*;
37+
import static org.elasticsearch.common.util.concurrent.ConcurrentCollections.*;
38+
39+
/**
40+
* A base concurrent filter cache that accepts the actual cache to use.
41+
*
42+
* @author kimchy (shay.banon)
43+
*/
44+
public abstract class AbstractDoubleConcurrentMapFilterCache extends AbstractIndexComponent implements FilterCache {
45+
46+
final ConcurrentMap<Object, ConcurrentMap<Filter, DocSet>> cache;
47+
final ConcurrentMap<Object, ConcurrentMap<Filter, DocSet>> weakCache;
48+
49+
protected AbstractDoubleConcurrentMapFilterCache(Index index, @IndexSettings Settings indexSettings) {
50+
super(index, indexSettings);
51+
// weak keys is fine, it will only be cleared once IndexReader references will be removed
52+
// (assuming clear(...) will not be called)
53+
this.cache = new MapMaker().weakKeys().makeMap();
54+
this.weakCache = new MapMaker().weakKeys().makeMap();
55+
}
56+
57+
@Override public void close() {
58+
cache.clear();
59+
}
60+
61+
@Override public void clear() {
62+
cache.clear();
63+
}
64+
65+
@Override public void clear(IndexReader reader) {
66+
ConcurrentMap<Filter, DocSet> map = cache.remove(reader.getFieldCacheKey());
67+
// help soft/weak handling GC
68+
if (map != null) {
69+
map.clear();
70+
}
71+
map = weakCache.remove(reader.getFieldCacheKey());
72+
// help soft/weak handling GC
73+
if (map != null) {
74+
map.clear();
75+
}
76+
}
77+
78+
@Override public void clearUnreferenced() {
79+
}
80+
81+
@Override public Filter cache(Filter filterToCache) {
82+
if (isCached(filterToCache)) {
83+
return filterToCache;
84+
}
85+
return new FilterCacheFilterWrapper(filterToCache, this);
86+
}
87+
88+
@Override public Filter weakCache(Filter filterToCache) {
89+
if (isCached(filterToCache)) {
90+
return filterToCache;
91+
}
92+
return new FilterWeakCacheFilterWrapper(filterToCache, this);
93+
}
94+
95+
@Override public boolean isCached(Filter filter) {
96+
return filter instanceof CacheMarker;
97+
}
98+
99+
protected ConcurrentMap<Filter, DocSet> buildCacheMap() {
100+
return newConcurrentMap();
101+
}
102+
103+
protected ConcurrentMap<Filter, DocSet> buildWeakCacheMap() {
104+
return newConcurrentMap();
105+
}
106+
107+
static abstract class CacheMarker extends Filter {
108+
109+
}
110+
111+
// LUCENE MONITOR: Check next version Lucene for CachingWrapperFilter, consider using that logic
112+
// and not use the DeletableConstantScoreQuery, instead pass the DeletesMode enum to the cache method
113+
// see: https://issues.apache.org/jira/browse/LUCENE-2468
114+
115+
static class FilterCacheFilterWrapper extends CacheMarker {
116+
117+
private final Filter filter;
118+
119+
private final AbstractDoubleConcurrentMapFilterCache cache;
120+
121+
FilterCacheFilterWrapper(Filter filter, AbstractDoubleConcurrentMapFilterCache cache) {
122+
this.filter = filter;
123+
this.cache = cache;
124+
}
125+
126+
@Override public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
127+
ConcurrentMap<Filter, DocSet> cachedFilters = cache.cache.get(reader.getFieldCacheKey());
128+
if (cachedFilters == null) {
129+
cachedFilters = cache.buildCacheMap();
130+
cache.cache.putIfAbsent(reader.getFieldCacheKey(), cachedFilters);
131+
}
132+
DocSet docSet = cachedFilters.get(filter);
133+
if (docSet != null) {
134+
return docSet;
135+
}
136+
137+
// check if its in the weak cache, if so, move it from weak to soft
138+
ConcurrentMap<Filter, DocSet> weakCachedFilters = cache.weakCache.get(reader.getFieldCacheKey());
139+
if (weakCachedFilters != null) {
140+
docSet = weakCachedFilters.get(filter);
141+
if (docSet != null) {
142+
cachedFilters.put(filter, docSet);
143+
weakCachedFilters.remove(filter);
144+
return docSet;
145+
}
146+
}
147+
148+
DocIdSet docIdSet = filter.getDocIdSet(reader);
149+
docSet = cacheable(reader, docIdSet);
150+
cachedFilters.putIfAbsent(filter, docSet);
151+
return docIdSet;
152+
}
153+
154+
public String toString() {
155+
return "FilterCacheFilterWrapper(" + filter + ")";
156+
}
157+
158+
public boolean equals(Object o) {
159+
if (!(o instanceof FilterCacheFilterWrapper)) return false;
160+
return this.filter.equals(((FilterCacheFilterWrapper) o).filter);
161+
}
162+
163+
public int hashCode() {
164+
return filter.hashCode() ^ 0x1117BF25;
165+
}
166+
}
167+
168+
static class FilterWeakCacheFilterWrapper extends CacheMarker {
169+
170+
private final Filter filter;
171+
172+
private final AbstractDoubleConcurrentMapFilterCache cache;
173+
174+
FilterWeakCacheFilterWrapper(Filter filter, AbstractDoubleConcurrentMapFilterCache cache) {
175+
this.filter = filter;
176+
this.cache = cache;
177+
}
178+
179+
@Override public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
180+
DocSet docSet;
181+
// first check if its in the actual cache
182+
ConcurrentMap<Filter, DocSet> cachedFilters = cache.cache.get(reader.getFieldCacheKey());
183+
if (cachedFilters != null) {
184+
docSet = cachedFilters.get(filter);
185+
if (docSet != null) {
186+
return docSet;
187+
}
188+
}
189+
190+
// now, handle it in the weak cache
191+
ConcurrentMap<Filter, DocSet> weakCacheFilters = cache.weakCache.get(reader.getFieldCacheKey());
192+
if (weakCacheFilters == null) {
193+
weakCacheFilters = cache.buildWeakCacheMap();
194+
cache.weakCache.putIfAbsent(reader.getFieldCacheKey(), weakCacheFilters);
195+
}
196+
197+
docSet = weakCacheFilters.get(filter);
198+
if (docSet != null) {
199+
return docSet;
200+
}
201+
202+
DocIdSet docIdSet = filter.getDocIdSet(reader);
203+
docSet = cacheable(reader, docIdSet);
204+
weakCacheFilters.putIfAbsent(filter, docSet);
205+
return docIdSet;
206+
}
207+
208+
public String toString() {
209+
return "FilterCacheFilterWrapper(" + filter + ")";
210+
}
211+
212+
public boolean equals(Object o) {
213+
if (!(o instanceof FilterCacheFilterWrapper)) return false;
214+
return this.filter.equals(((FilterCacheFilterWrapper) o).filter);
215+
}
216+
217+
public int hashCode() {
218+
return filter.hashCode() ^ 0x1117BF25;
219+
}
220+
}
221+
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/query/xcontent/PrefixFilterParser.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class PrefixFilterParser extends AbstractIndexComponent implements XConte
5353
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
5454
XContentParser parser = parseContext.parser();
5555

56-
boolean cache = true; // default to true, make sense to cache prefix
56+
boolean cache = false;
5757
String fieldName = null;
5858
String value = null;
5959

@@ -88,9 +88,15 @@ public class PrefixFilterParser extends AbstractIndexComponent implements XConte
8888
}
8989

9090
Filter filter = new PrefixFilter(new Term(fieldName, value));
91+
92+
// we weak cache the filter if not cached, since in any case it builds an OpenBitSet
93+
// we might as well weak cache it...
9194
if (cache) {
9295
filter = parseContext.cacheFilter(filter);
96+
} else {
97+
filter = parseContext.cacheWeakFilter(filter);
9398
}
99+
94100
filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
95101
if (filterName != null) {
96102
parseContext.addNamedFilter(filterName, filter);

modules/elasticsearch/src/main/java/org/elasticsearch/index/query/xcontent/QueryParseContext.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ public Filter cacheFilter(Filter filter) {
112112
return indexQueryParser.indexCache.filter().cache(filter);
113113
}
114114

115+
public Filter cacheWeakFilter(Filter filter) {
116+
return indexQueryParser.indexCache.filter().weakCache(filter);
117+
}
118+
115119
public void addNamedFilter(String name, Filter filter) {
116120
namedFilters.put(name, filter);
117121
}

0 commit comments

Comments
 (0)