uniqueKey生成在SolrCloud中不起作用(但如果是独立的则可以工作)

时间:2016-09-08 23:52:34

标签: java apache solr

当相同的应用程序代码在SolrCloud中没有唯一密钥(应该由SOLR自动生成)索引文档时,我遇到了一个奇怪的问题,并成功在独立的SOLR实例中索引它(甚至在云模式下,但是从其中一个副本的Web界面)。差异显然仅在客户端(CloudSolrClient与HttpSolrClient)和SOLR URL(Zokeeper主机名+端口与独立SOLR实例主机名和端口)之间。

我正在使用SOLR 5.1。在云模式下,我有1个分片和3个副本。 Documentation states

  

架构默认值和copyFields不能用于填充uniqueKey字段。您可以使用UUIDUpdateProcessorFactory自动生成uniqueKey值。

因此我将uniqueKey字段添加到架构中:

<fieldType name="uuid" class="solr.UUIDField" indexed="true" />
...
<field name="id" type="uuid" indexed="true" stored="true" required="true" />
...
<uniqueKey>id</uniqueKey>

然后我将updateRequestProcessorChain添加到我的solrconfig:

<updateRequestProcessorChain name="uuid">
    <processor class="solr.UUIDUpdateProcessorFactory">
        <str name="fieldName">id</str>
    </processor>
    <processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>

并使其成为UpdateRequestHandler的默认设置:

<initParams path="/update/**">
 <lst name="defaults">
  <str name="update.chain">uuid</str>
 </lst>
</initParams>

使用null / absent id添加新文档可以从其中一个副本的Web界面中正常工作,就像在我的应用程序中以独立模式(非云)使用SOLR一样。虽然我只使用SolrCloud并从我的应用程序添加文档(使用SolrJ中的CloudSolrClient),但它失败了“org.apache.solr.client.solrj.SolrServerException:org.apache.solr.client.solrj.impl.HttpSolrClient $ RemoteSolrException:Document缺少必需的uniqueKey字段:id“

ping或搜索文档等所有其他操作在任一模式(独立模式或云端模式)下都能正常工作。

有没有人遇到过同样的行为?这可能是什么解决方案?

调查(即更多细节):

  1. 在独立模式下,显然更新请求是:

    POST standalone_host:port/solr/collection_name/update?wt=json
    
  2. 在SOLR云模式下,从一个副本的Web界面添加文档时,更新请求(通过检查Web界面的调用找到):

    POST replica_host:port/solr/collection_name_shard1_replica_1/update?wt=json
    
  3. 在这两种情况下,有效负载类似于:

    {
        "add": {
            "doc": {
                     .....
            },
            "boost": 1.0,
            "overwrite": true,
            "commitWithin": 1000
        }
    }
    

    如果使用CloudSolrClient,会发生以下情况(通过调试找到):

    1. 使用ZK和一些逻辑,构建副本的URL列表,如下所示:

      [http://replica_1_host:port/solr/collection_name/,
       http://replica_2_host:port/solr/collection_name/,
       http://replica_3_host:port/solr/collection_name/]
      
    2. 此代码称为:

      LBHttpSolrClient.Req req = new LBHttpSolrClient.Req(request, theUrlList);
      LBHttpSolrClient.Rsp rsp = lbClient.request(req);
      return rsp.getResponse();
      

      第二行因例外而失败。

    3. 如果要进一步调试第二行,它最终会调用HttpClient.execute(来自HttpSolrClient.executeMethod):

      POST http://replica_1_host:port/solr/collection_name/update?wt=javabin&version=2 HTTP/1.1
      POST http://replica_2_host:port/solr/collection_name/update?wt=javabin&version=2 HTTP/1.1
      POST http://replica_3_host:port/solr/collection_name/update?wt=javabin&version=2 HTTP/1.1
      

      第一个请求返回400 Bad Request,副本1记录“Document缺少必需的uniqueKey字段:id”在日志中。

      有趣的是,当我使用POSTMAN执行相同的请求(但使用JSON而不是二进制有效负载)时,它可以工作!我在这里做错了吗?我认为这肯定是请求如何制作的方式......

      更新

      我使用了本地代理,以便查看我的应用程序发送的这两个请求之间的差异,以便了解其中的不同之处。看起来唯一的区别是内容类型。在云模式的情况下,POSTing文档的有效负载作为“application / javabin”发送,而在独立模式下,它作为“application / xml; charset = UTF-8”发送。其他一切都是一样的。第一个请求结果为400而第二个请求结果为200.我认为这可能是SolrJ / SOLR错误,因此提交了ticket for that。将保持此线程更新。

1 个答案:

答案 0 :(得分:1)

首先,它在独立模式下工作,因为版本5.1中的HttpSolrClient将有效负载发送为XML(不包括空值 - 这部分很重要),而CloudSolrClient以二进制序列化格式将其作为“application / javabin”发送(包含空值) )。在版本6.2中,这两个版本都以二进制序列化格式将有效负载发送为“application / javabin”,因此在6.2版本的云和独立模式下都会出现此问题。

问题的真正原因是,为了生成UUID,必须完全不存在该字段。如果它存在并且它为null,则跳过UUID生成。这就是错误的原因。因此,如果我们使用SolrInputDocument来索引我们的文档,那么它很容易 - 我们根本不应该为“id”字段添加值。但是如果我们将POJO与“org.apache.solr.client.solrj.beans.Field”注释一起使用呢?我们不能从那里排除一个字段。这就是IgnoreFieldUpdateProcessorFactory发挥作用的地方:

<updateRequestProcessorChain name="uuid">
    <!-- Using IgnoreFieldUpdateProcessorFactory because of https://issues.apache.org/jira/browse/SOLR-9493:
         can't generate UUID for a field coming as NULL, field must be absent. -->
    <processor class="solr.IgnoreFieldUpdateProcessorFactory">
        <str name="fieldName">id</str>
    </processor>
    <processor class="solr.UUIDUpdateProcessorFactory">
        <str name="fieldName">id</str>
    </processor>
    <processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>

在solrconfig.xml中使用“uuid”updateRequestProcessorChain,如上面的问题所示,一切都像魅力(在生成UUID之前从文档中删除了“id”字段)。虽然在这种情况下,不可能添加带有自定义ID的文档,但这仍然是另一个线程的另一个问题。