[ 
https://issues.apache.org/jira/browse/SOLR-8291?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16378465#comment-16378465
 ] 

Ron Ben-Yosef edited comment on SOLR-8291 at 3/12/18 9:28 AM:
--------------------------------------------------------------

Hi,

I've run into the same issue, or at least what I think is the same issue (I'm 
on 7.2.1, and the call stack leading up to the exception looks somewhat 
different, but reading everything written here I'm fairly sure it's the same 
issue. If it's not - let me know and I can open a separate bug for it).

The scenario is similar - querying the /export handler with the 
"useFilterForSortedQuery" configuration option enabled.

Here's the call stack leading up to the exception:

o.a.s.s.HttpSolrCall null:java.lang.NullPointerException
 at org.apache.lucene.util.BitSetIterator.<init>(BitSetIterator.java:61)
 at org.apache.solr.handler.ExportWriter.writeDocs(ExportWriter.java:243)
 at org.apache.solr.handler.ExportWriter.lambda$null$1(ExportWriter.java:222)
 at 
org.apache.solr.response.JSONWriter.writeIterator(JSONResponseWriter.java:523)
 at 
org.apache.solr.response.TextResponseWriter.writeVal(TextResponseWriter.java:180)
 at org.apache.solr.response.JSONWriter$2.put(JSONResponseWriter.java:559)
 at org.apache.solr.handler.ExportWriter.lambda$null$2(ExportWriter.java:222)
 at org.apache.solr.response.JSONWriter.writeMap(JSONResponseWriter.java:547)
 at 
org.apache.solr.response.TextResponseWriter.writeVal(TextResponseWriter.java:198)
 at org.apache.solr.response.JSONWriter$2.put(JSONResponseWriter.java:559)
 at org.apache.solr.handler.ExportWriter.lambda$write$3(ExportWriter.java:220)
 at org.apache.solr.response.JSONWriter.writeMap(JSONResponseWriter.java:547)
 at org.apache.solr.handler.ExportWriter.write(ExportWriter.java:218)
 at org.apache.solr.core.SolrCore$3.write(SolrCore.java:2627)
 at 
org.apache.solr.response.QueryResponseWriterUtil.writeQueryResponse(QueryResponseWriterUtil.java:49)
 at org.apache.solr.servlet.HttpSolrCall.writeResponse(HttpSolrCall.java:788)
 at org.apache.solr.servlet.HttpSolrCall.call(HttpSolrCall.java:525)
 at 
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:382)
 at 
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:326)
 at 
org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1751)
 at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
 at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
 at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
 at 
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
 at 
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1180)
 at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
 at 
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
 at 
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
 at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
 at 
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
 at 
org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119)
 at 
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
 at 
org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:335)
 at 
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
 at org.eclipse.jetty.server.Server.handle(Server.java:534)
 at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
 at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
 at 
org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:283)
 at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:108)
 at 
org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
 at 
org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
 at 
org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
 at 
org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
 at 
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
 at 
org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
 at java.lang.Thread.run(Thread.java:748)

 

Debugging the issue, it seems what's happening is that when there's more than 
one leaf (=segment?) then the per-leaf bitsets are being created only upto the 
highest leaf with matching documents, and if that's not the last leaf then 
you're going to have null bitsets for all the leaves higher than the highest 
one containing a matching document. This happens in SolrIndexSearcher.java in 
the function sortDocSet:
{code:java}
...
LeafCollector leafCollector = null;
while (iter.hasNext()) {
 int doc = iter.nextDoc();
 while (doc >= end) {
  LeafReaderContext leaf = leafContexts.get(readerIndex++);
  base = leaf.docBase;
  end = base + leaf.reader().maxDoc();
  leafCollector = topCollector.getLeafCollector(leaf);
  // we should never need to set the scorer given the settings for the collector
 }
 leafCollector.collect(doc - base);
}
...{code}
As can be seen in the code above, once there are no more matching documents the 
loop won't be entered anymore and so the function getLeafCollector will not be 
called for the remaining leaves. Since that is where ExportCollector sets the 
leaves' bitsets, the ones for which the function is not called will not get set 
and remain null, as can be seen in ExportQParserPlugin.java:
{code:java}
@Override
public LeafCollector getLeafCollector(LeafReaderContext context) throws 
IOException {
 final FixedBitSet set = new FixedBitSet(context.reader().maxDoc());
 this.sets[context.ord] = set;
 return new LeafCollector() {
 
  @Override
  public void setScorer(Scorer scorer) throws IOException {}

  @Override
  public void collect(int docId) throws IOException{
   ++totalHits;
   set.set(docId);
  }
 };
}{code}
Anyhow, this array of bitsets eventually gets propagated and ends up at the 
ExportWriter's writeDocs method where the bitsets are used to create instances 
of BitSetIterator, the constructor of which then throws the NPE when trying to 
call the length method on the bitset in the case that it's null:
{code:java}
...
for(int i=0; i<leaves.size(); i++) {
 sortDoc.setNextReader(leaves.get(i));
 DocIdSetIterator it = new BitSetIterator(sets[i], 0); // cost is not useful 
here
 int docId = -1;
 while((docId = it.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
  sortDoc.setValues(docId);
  if(top.lessThan(sortDoc)) {
   top.setValues(sortDoc);
   top = queue.updateTop();
  }
 }
}
...{code}
 

As for a fix - I tried two things, both of which seem to work:
 * One option is simply to not try iterate the null bitsets (their 
corresponding leaves don't include matching documents anyway, which is why the 
weren't set in the first place) , for instance by modifying the code from above 
(from ExportWriter::writeDocs) like this:

{code:java}
...
for(int i=0; i<leaves.size(); i++) {
 if (sets[i] == null) continue;
 sortDoc.setNextReader(leaves.get(i));
 DocIdSetIterator it = new BitSetIterator(sets[i], 0); // cost is not useful 
here
 int docId = -1;
 while((docId = it.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
  sortDoc.setValues(docId);
  if(top.lessThan(sortDoc)) {
   top.setValues(sortDoc);
   top = queue.updateTop();
  }
 }
}
...{code}
 * The second option is to modify SolrIndexSearcher::sortDocSet to create 
bitsets for the remaining leaves so that they're not null, for instance by 
calling getLeafCollector for them as well (even though there's no need for the 
collector since they include no matching documents) just for the bitsets to get 
created. For example:

{code:java}
...
LeafCollector leafCollector = null;
while (iter.hasNext()) {
 int doc = iter.nextDoc();
 while (doc >= end) {
  LeafReaderContext leaf = leafContexts.get(readerIndex++);
  base = leaf.docBase;
  end = base + leaf.reader().maxDoc();
  leafCollector = topCollector.getLeafCollector(leaf);
  // we should never need to set the scorer given the settings for the collector
 }
 leafCollector.collect(doc - base);
}

for (int leaves = leafContexts.size(); readerIndex < leaves; readerIndex++) {
 topCollector.getLeafCollector(leafContexts.get(readerIndex));
}
...{code}
 

The first option of course won't solve any other flows where there might be 
attempts to access the null bitsets, but I don't know if there are any such 
flows or if it's at all relevant...

 

Anyway, I don't really know how to go about fixing it, have never done it 
before. What do I need to do? Make a fork on github and then submit a pull 
request?

 

Sorry for the long comment.

 

Thanks,

Ron


was (Author: ronnnnnnn):
Hi,

I've run into the same issue, or at least what I think is the same issue (I'm 
on 7.2.1, and the call stack leading up to the exception looks somewhat 
different, but reading everything written here I'm fairly sure it's the same 
issue. If it's not - let me know and I can open a separate bug for it).

The scenario is similar - querying the /export handler with the 
"useFilterForSortedQuery" configuration option enabled.

Here's the call stack leading up to the exception:

o.a.s.s.HttpSolrCall null:java.lang.NullPointerException
 at org.apache.lucene.util.BitSetIterator.<init>(BitSetIterator.java:61)
 at org.apache.solr.handler.ExportWriter.writeDocs(ExportWriter.java:243)
 at org.apache.solr.handler.ExportWriter.lambda$null$1(ExportWriter.java:222)
 at 
org.apache.solr.response.JSONWriter.writeIterator(JSONResponseWriter.java:523)
 at 
org.apache.solr.response.TextResponseWriter.writeVal(TextResponseWriter.java:180)
 at org.apache.solr.response.JSONWriter$2.put(JSONResponseWriter.java:559)
 at org.apache.solr.handler.ExportWriter.lambda$null$2(ExportWriter.java:222)
 at org.apache.solr.response.JSONWriter.writeMap(JSONResponseWriter.java:547)
 at 
org.apache.solr.response.TextResponseWriter.writeVal(TextResponseWriter.java:198)
 at org.apache.solr.response.JSONWriter$2.put(JSONResponseWriter.java:559)
 at org.apache.solr.handler.ExportWriter.lambda$write$3(ExportWriter.java:220)
 at org.apache.solr.response.JSONWriter.writeMap(JSONResponseWriter.java:547)
 at org.apache.solr.handler.ExportWriter.write(ExportWriter.java:218)
 at org.apache.solr.core.SolrCore$3.write(SolrCore.java:2627)
 at 
org.apache.solr.response.QueryResponseWriterUtil.writeQueryResponse(QueryResponseWriterUtil.java:49)
 at org.apache.solr.servlet.HttpSolrCall.writeResponse(HttpSolrCall.java:788)
 at org.apache.solr.servlet.HttpSolrCall.call(HttpSolrCall.java:525)
 at 
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:382)
 at 
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:326)
 at 
org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1751)
 at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
 at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
 at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
 at 
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
 at 
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1180)
 at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
 at 
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
 at 
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
 at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
 at 
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
 at 
org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119)
 at 
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
 at 
org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:335)
 at 
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
 at org.eclipse.jetty.server.Server.handle(Server.java:534)
 at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
 at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
 at 
org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:283)
 at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:108)
 at 
org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
 at 
org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
 at 
org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
 at 
org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
 at 
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
 at 
org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
 at java.lang.Thread.run(Thread.java:748)

 

Debugging the issue, it seems what's happening is that when there's more than 
one leaf (=segment?) then the per-leaf bitsets are being created only upto the 
highest leaf with matching documents, and if that's not the last leaf then 
you're going to have null bitsets for all the leaves higher than the highest 
one containing a matching document. This happens in SolrIndexSearcher.java in 
the function sortDocSet:
{code:java}
...
LeafCollector leafCollector = null;
while (iter.hasNext()) {
 int doc = iter.nextDoc();
 while (doc >= end) {
  LeafReaderContext leaf = leafContexts.get(readerIndex++);
  base = leaf.docBase;
  end = base + leaf.reader().maxDoc();
  leafCollector = topCollector.getLeafCollector(leaf);
  // we should never need to set the scorer given the settings for the collector
 }
 leafCollector.collect(doc - base);
}
...{code}
As can be seen in the code above, once there are no more matching documents the 
loop won't be entered anymore and so the function getLeafCollector will not be 
called for the remaining leaves. Since that is where ExportCollector sets the 
leaves' bitsets, the ones for which the function is not called will nor get set 
and remain null, as can be seen in ExportQParserPlugin.java:
{code:java}
@Override
public LeafCollector getLeafCollector(LeafReaderContext context) throws 
IOException {
 final FixedBitSet set = new FixedBitSet(context.reader().maxDoc());
 this.sets[context.ord] = set;
 return new LeafCollector() {
 
  @Override
  public void setScorer(Scorer scorer) throws IOException {}

  @Override
  public void collect(int docId) throws IOException{
   ++totalHits;
   set.set(docId);
  }
 };
}{code}
Anyhow, this array of bitsets eventually gets propagated and ends up at the 
ExportWriter's writeDocs method where the bitsets are used to create instances 
of BitSetIterator, the constructor of which then throws the NPE when trying to 
call the length method on the bitset in the case that it's null:
{code:java}
...
for(int i=0; i<leaves.size(); i++) {
 sortDoc.setNextReader(leaves.get(i));
 DocIdSetIterator it = new BitSetIterator(sets[i], 0); // cost is not useful 
here
 int docId = -1;
 while((docId = it.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
  sortDoc.setValues(docId);
  if(top.lessThan(sortDoc)) {
   top.setValues(sortDoc);
   top = queue.updateTop();
  }
 }
}
...{code}
 

As for a fix - I tried two things, both of which seem to work:
 * One option is simply to not try iterate the null bitsets (their 
corresponding leaves don't include matching documents anyway, which is why the 
weren't set in the first place) , for instance by modifying the code from above 
(from ExportWriter::writeDocs) like this:

{code:java}
...
for(int i=0; i<leaves.size(); i++) {
 if (sets[i] == null) continue;
 sortDoc.setNextReader(leaves.get(i));
 DocIdSetIterator it = new BitSetIterator(sets[i], 0); // cost is not useful 
here
 int docId = -1;
 while((docId = it.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
  sortDoc.setValues(docId);
  if(top.lessThan(sortDoc)) {
   top.setValues(sortDoc);
   top = queue.updateTop();
  }
 }
}
...{code}
 * The second option is to modify SolrIndexSearcher::sortDocSet to create 
bitsets for the remaining leaves so that they're not null, for instance by 
calling getLeafCollector for them as well (even though there's no need for the 
collector since they include no matching documents) just for the bitsets to get 
created. For example:

{code:java}
...
LeafCollector leafCollector = null;
while (iter.hasNext()) {
 int doc = iter.nextDoc();
 while (doc >= end) {
  LeafReaderContext leaf = leafContexts.get(readerIndex++);
  base = leaf.docBase;
  end = base + leaf.reader().maxDoc();
  leafCollector = topCollector.getLeafCollector(leaf);
  // we should never need to set the scorer given the settings for the collector
 }
 leafCollector.collect(doc - base);
}

for (int leaves = leafContexts.size(); readerIndex < leaves; readerIndex++) {
 topCollector.getLeafCollector(leafContexts.get(readerIndex));
}
...{code}
 

The first option of course won't solve any other flows where there might be 
attempts to access the null bitsets, but I don't know if there are any such 
flows or if it's at all relevant...

 

Anyway, I don't really know how to go about fixing it, have never done it 
before. What do I need to do? Make a fork on github and then submit a pull 
request?

 

Sorry for the long comment.

 

Thanks,

Ron

> NPE calling export handler when useFilterForSortedQuery=true
> ------------------------------------------------------------
>
>                 Key: SOLR-8291
>                 URL: https://issues.apache.org/jira/browse/SOLR-8291
>             Project: Solr
>          Issue Type: Bug
>          Components: SolrCloud
>    Affects Versions: 5.2.1
>            Reporter: Ray
>            Priority: Major
>         Attachments: solr.log
>
>
> *Updated*: The stacktrace below was created when the solrconfig.xml has the 
> following element:
> {code}
>  <useFilterForSortedQuery>true</useFilterForSortedQuery>
> {code}
> It was determined that useFilterForSortedQuery is incompatible with the 
> /export handler.
> See the comments near the end of the ticket for a potential work around if 
> this flag needs to be set.
> Get NPE during calling export handler, here is the stack trace:
>       at org.apache.lucene.util.BitSetIterator.<init>(BitSetIterator.java:58)
>       at 
> org.apache.solr.response.SortingResponseWriter.write(SortingResponseWriter.java:138)
>       at 
> org.apache.solr.response.QueryResponseWriterUtil.writeQueryResponse(QueryResponseWriterUtil.java:53)
>       at 
> org.apache.solr.servlet.HttpSolrCall.writeResponse(HttpSolrCall.java:727)
>       at org.apache.solr.servlet.HttpSolrCall.call(HttpSolrCall.java:459)
>       at 
> org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:227)
>       at 
> org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:196)
>       at 
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274)
>       at 
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242)
>       at 
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274)
>       at 
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242)
>       at 
> org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
>       at 
> org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
>       at 
> org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:181)
>       at 
> org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285)
>       at 
> org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261)
>       at 
> org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88)
>       at 
> org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:100)
>       at 
> org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:159)
>       at 
> org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
>       at 
> org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
>       at 
> org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:567)
>       at 
> org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
>       at 
> org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53)
>       at 
> org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362)
>       at 
> org.apache.coyote.ajp.AjpAprProcessor.process(AjpAprProcessor.java:489)
>       at 
> org.apache.coyote.ajp.AjpAprProtocol$AjpConnectionHandler.process(AjpAprProtocol.java:452)
>       at 
> org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:2019)
>       at java.lang.Thread.run(Thread.java:745)
> It seems there are some FixedBitSet was set to null



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org
For additional commands, e-mail: dev-h...@lucene.apache.org

Reply via email to