タグ: Solr

SolrのReadOnlyモード

Solr 8.1から追加された機能にRead-Onlyモードがあります。SolrCloudでの運用時に利用できるモードです。リファレンスマニュアルの該当部分を翻訳してみました。

readOnly属性をtrueにセットするとコレクションはRead-Onlyモードに移行します。このモードではインデックスの更新リクエストは拒否されます。他のコレクションレベルのアクション(コレクションの追加/削除、レプリカの移動)はこのモードにおいても有効です。


(デフォルトの)read-writeモードからread-onlyモードへは以下の手順で移行されます。
・コレクション内のreadOnlyフラグが変更される
・すべての更新リクエストが403 FORBIDDENエラーで拒否される(継続中の長期実行リクエストは中止される)
・実行中の更新が強制的にコミットされる
(注意)大きなセグメントがバックグラウンドでマージされている最中であれば、この強制コミットは長時間に及ぶかもしれません。
・コレクションのRELOADアクションが実行される
readOnly属性が削除されるかfalseに設定されるかすると、更新リクエストが有効になりコレクションがリロードされます。

https://lucene.apache.org/solr/guide/8_2/collection-management.html#readonlymode

簡単な動作確認をしてみました。

$ curl 'http://localhost:8983/solr/admin/collections?action=CREATE&name=test_readonly&collection.configName=_default&numShards=1&wt=xml'

$ curl 'http://localhost:8983/solr/admin/collections?action=MODIFYCOLLECTION&collection=test_readonly&readOnly=true'
{
  "responseHeader":{
    "status":0,
    "QTime":476},
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":350}}}}
$ bin/post -c test_readonly data.json 
SimplePostTool version 5.0.0
Posting files to [base] url http://localhost:8983/solr/test_readonly/update...
Entering auto mode. File endings considered are xml,json,jsonl,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,log
POSTing file data.json (application/json) to [base]/json/docs
SimplePostTool: WARNING: Solr returned an error #403 (Forbidden) for url: http://localhost:8983/solr/test_readonly/update/json/docs
SimplePostTool: WARNING: Response: {
  "responseHeader":{
    "status":403,
    "QTime":207},
  "error":{
    "metadata":[
      "error-class","org.apache.solr.common.SolrException",
      "root-error-class","org.apache.solr.common.SolrException"],
    "msg":"Collection test_readonly is read-only.",
    "code":403}}
SimplePostTool: FATAL: Looks like Solr is secured and would not let us in. Try with another user in '-u' parameter

テスト用のコレクション(test_readonly)を作成し、MODIFYCOLLECTIONアクションでreadOnlyをtrueに設定してからドキュメントをポストするとリファレンスの説明通り403エラーになりました。

$ curl 'http://localhost:8983/solr/test_readonly/select?q=*%3A*&rows=1&start=0'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":1,
    "params":{
      "q":"*:*",
      "start":"0",
      "rows":"1"}},
  "response":{"numFound":0,"start":0,"docs":[]
  }}

当然インデックスは空っぽのままです。

$ curl 'http://localhost:8983/solr/admin/collections?action=MODIFYCOLLECTION&collection=test_readonly&readOnly=false'
{
  "responseHeader":{
    "status":0,
    "QTime":466},
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":344}}}}
	
$ bin/post -c test_readonly data.json 
SimplePostTool version 5.0.0
Posting files to [base] url http://localhost:8983/solr/test_readonly/update...
Entering auto mode. File endings considered are xml,json,jsonl,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,log
POSTing file data.json (application/json) to [base]/json/docs
1 files indexed.
COMMITting Solr index changes to http://localhost:8983/solr/test_readonly/update...
Time spent: 0:00:01.420

次にMODIFYCOLLECTIONアクションでreadOnlyをfalseに戻してからドキュメントをポストしたところ、今度はエラー無しでアップデートできました。

$ curl 'http://localhost:8983/solr/test_readonly/select?q=*%3A*&rows=1&start=0'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":0,
    "params":{
      "q":"*:*",
      "start":"0",
      "rows":"1"}},
  "response":{"numFound":9236,"start":0,"docs":[
      {
        "id":"158",
        "type":["官公庁"],
        "area":["住之江区"],
        "name":["軽自動車検査協会大阪主管事務所"],
        "address":["住之江区南港東3-4-62"],
        "_version_":1648645250184380416}]
  }}

クエリの結果からも、正しく更新できたことを確認できます。

ReadOnlyモードを使えば内容固定のインデックスを運用中に誤って更新してしまうことを防ぐことができて便利ですが、このモードはSolrCloudでのみ利用可能なので、その点は注意が必要です。

WSL上でSolrが動くかどうか試してみた

SolrはJavaで書かれているのでもちろんWindowsでも動くわけですが、ふとWSL上ではどうなのかが気になって試してみました。

まずは WSL 上でいつもの手順通りSolrを動かしてみます。ディストリビューションはUbuntu 18.04.2 LTSです。

Solrをダウンロードして展開

$ wget https://www-eu.apache.org/dist/lucene/solr/8.2.0/solr-8.2.0.tgz
tar zxf solr-8.2.0.tgz

起動

$ cd solr-8.2.0
$ bin/solr -e cloud

Welcome to the SolrCloud example!

This interactive session will help you launch a SolrCloud cluster on your local workstation.
To begin, how many Solr nodes would you like to run in your local cluster? (specify 1-4 nodes) [2]:
1
Ok, let's start up 1 Solr nodes for your example SolrCloud cluster.
Please enter the port for node1 [8983]:
8983
Creating Solr home directory solr-8.2.0/example/cloud/node1/solr

Starting up Solr on port 8983 using command:
"bin/solr" start -cloud -p 8983 -s "example/cloud/node1/solr"

*** [WARN] ***  Your Max Processes Limit is currently 7823.
 It should be set to 65000 to avoid operational disruption.
  If you no longer wish to see this warning, set SOLR_ULIMIT_CHECKS to false in your profile or solr.in.sh
  Waiting up to 180 seconds to see Solr running on port 8983 [|]  bin/solr: line 660:   177 Aborted                 (core dumped) nohup "$JAVA" "${SOLR_START_OPTS[@]}" $SOLR_ADDL_ARGS -Dsolr.log.muteconsole "-XX:OnOutOfMemoryError=$SOLR_TIP/bin/oom_solr.sh $SOLR_PORT $SOLR_LOGS_DIR" -jar start.jar\
 "${SOLR_JETTY_CONFIG[@]}" $SOLR_JETTY_ADDL_CONFIG > "$SOLR_LOGS_DIR/solr-$SOLR_PORT-console.log" 2>&1
   [\]  ^C
   ERROR: Failed to start Solr using command: "bin/solr" start -cloud -p 8983 -s "example/cloud/node1/solr" Exception : org.apache.commons.exec.ExecuteException: Process exited with an error: 130 (Exit value: 130)

エラー終了してしまいました。
実行ログを調べると、以下のようなJava VMレベルでのエラーが発生していました。

$ cat example/cloud/node1/logs/solr-8983-console.log
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (g1PageBasedVirtualSpace.cpp:49), pid=177, tid=185
#  guarantee(is_aligned(rs.base(), page_size)) failed: Reserved space base 0x00007f77bc420000 is not aligned to requested page size 2097152
#
# JRE version:  (11.0.4+11) (build )
# Java VM: OpenJDK 64-Bit Server VM (11.0.4+11-post-Ubuntu-1ubuntu218.04.3, mixed mode, aot, sharing, tiered, compressed oops, g1 gc, linux-amd64)
# Core dump will be written. Default location: core.177 (may not exist)
#
# An error report file with more information is saved as:
# solr-8.2.0/server/hs_err_pid177.log
#
# If you would like to submit a bug report, please visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-lts
#

WSLのバグレポートによるとJavaのプロセスで似たようなエラーが出ることはあるようで順次修正はされているものの、今回のエラーと同じものは見つかりませんでした。

Java VMの起動オプションを変更すれば起動するのではないかと考えて、まずはヒープサイズをいろいろ変えてみましたが駄目でした。
次にJMX関連のオプションを外してみたところ問題なく起動するようになりました。具体的には起動用のSolrスクリプトを以下のように変更しました。

--- solr.bak    2019-10-22 23:25:45.830541600 +0900
+++ solr        2019-10-27 19:05:03.758148800 +0900
@@ -1964,6 +1964,7 @@
 fi

 # These are useful for attaching remote profilers like VisualVM/JConsole
+ENABLE_REMOTE_JMX_OPTS=false
 if [ "$ENABLE_REMOTE_JMX_OPTS" == "true" ]; then

   if [ -z "$RMI_PORT" ]; then

開発中の動作確認ではWSL側でSolrが動かせた方が都合が良い場合もあるので、謎のエラーを回避する方法を見つけることができて良かったです。

SolrCloudにおけるインデックスのリストア

はじめに

前回の記事で Collection API を使ってインデックスのバックアップを取る方法を説明しました。この記事ではバックアップからのリストアの方法を説明します。

リストアの実行

リストアは以下のように実行します。

$ curl 'http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=backup_test2&location=/tmp/solr_backup'

基本パラメータ

パラメータ説明
actionリストアコマンドとして”RESTORE”を指定
nameバックアップの名前
collectionリストア先のコレクション名
locationバックアップ出力先ディレクトリ

name と location はバックアップのときと同じものを指定します。

設定上書き用のパラメータ

パラメータ説明
collection.configNameconfigsetを変更
replicationFactorシャードあたりのレプリカ数を変更
ntrReplicas,tlogReplicasそれぞれの種類のレプリカ数を変更
maxShardsPerNodeノードあたりの最大シャード数を変更
autoAddReplicasレプリカの自動追加を許可するかどうかを変更

バックアップ/リストアに備えた運用

リストアが実行されると、バックアップ元とは異なるコレクション名で新しいコレクションが作成されます。コレクション名が変更されると外部で参照している箇所にも影響が出てしまうので、Solr のリファレンスではエイリアスの利用を推奨しています。

外部から参照するコレクション名を mycollection とすると、実際のコレクション名を mycollection20190915 という風に付けてエイリアスを mycollection とします。バックアップ→リストアによって新しく mycollection20190920 が作られたら mycollection の実体をそちらに切り替える、という形で運用します。

SolrCloudにおけるインデックスのバックアップ

はじめに

SolrCloudにおいては、Collection API の BACKUP コマンドを使ってインデックスのバックアップを取ることができます。複数のノード、シャード、レプリカが存在する場合に具体的にどのような形でバックアップが残るのかを調べてみました。

対象のコレクション

実験用に backup_test というコレクションを作成しました。シャード2個がそれぞれレプリカを2個持つ構成です。

コレクションシャードレプリカ
backup_testshard1localhost:8983
backup_testshard1localhost:7574
backup_testshard2localhost:8983
backup_testshard2localhost:7574

ここに、以前実験に利用した大阪の施設情報を投入しました。

バックアップの実行

バックアップは以下のように実行します。

$ curl 'http://localhost:8983/solr/admin/collections?action=BACKUP&name=backup1&collection=backup_test&location=/tmp/solr_backup'
パラメータ説明
actionバックアップコマンドとして”BACKUP”を指定
nameバックアップの名前。以前指定したものとは衝突しないようにする
collectionバックアップ対象のコレクション
locationバックアップ出力先ディレクトリ

一つ注意が必要なのは、クラスタが複数のサーバから構成される場合には、バックアップの出力先は共有ドライブでなければならないということです。

バックアップの内容

出力先を調べると、以下の内容がバックアップに含まれていることが分かります。

  • backup.properties
  • コレクションの設定情報
  • シャード毎のインデックスファイル
$ find /tmp/solr_backup/backup1
/tmp/solr_backup/backup1
/tmp/solr_backup/backup1/backup.properties
/tmp/solr_backup/backup1/zk_backup
/tmp/solr_backup/backup1/zk_backup/configs
/tmp/solr_backup/backup1/zk_backup/configs/backup_test
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/solrconfig.xml
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/protwords.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/stopwords.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/params.json
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/synonyms.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/lang
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/lang/stopwords_tr.txt
(略)
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/lang/contractions_fr.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/configoverlay.json
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/managed-schema
/tmp/solr_backup/backup1/zk_backup/collection_state.json
/tmp/solr_backup/backup1/snapshot.shard2
/tmp/solr_backup/backup1/snapshot.shard2/_3.fdt
/tmp/solr_backup/backup1/snapshot.shard2/_1_Lucene50_0.tim
/tmp/solr_backup/backup1/snapshot.shard2/segments_2
(略)
/tmp/solr_backup/backup1/snapshot.shard2/_0_Lucene50_0.pos
/tmp/solr_backup/backup1/snapshot.shard2/_2_Lucene80_0.dvm
/tmp/solr_backup/backup1/snapshot.shard2/_1_Lucene50_0.doc
/tmp/solr_backup/backup1/snapshot.shard1
/tmp/solr_backup/backup1/snapshot.shard1/_3.fdt
/tmp/solr_backup/backup1/snapshot.shard1/_1_Lucene50_0.tim
/tmp/solr_backup/backup1/snapshot.shard1/segments_2
(略)
/tmp/solr_backup/backup1/snapshot.shard1/_0_Lucene50_0.pos
/tmp/solr_backup/backup1/snapshot.shard1/_2_Lucene80_0.dvm
/tmp/solr_backup/backup1/snapshot.shard1/_1_Lucene50_0.doc

backup.properties は以下のような内容のファイルです。

#Backup properties file
#Sun Sep 19 14:21:47 UTC 2019
collection.configName=backup_test
collectionAlias=backup_test
startTime=2019-09-29T14\:21\:47.122280Z
collection=backup_test
backupName=backup1
index.version=8.1.0

おわりに

コレクションのバックアップを作成したときに具体的にどういうファイルが生成されるのかを調べました。次回はリストアの動作を確認してみます。

[Solr]ドキュメントルーティングのcompositeIdのハッシュを実際に計算してみる

前回の記事に書いたように、compositeIdによるドキュメントルーティングを採用した場合、compositeIdのハッシュ値のどの範囲をどのシャードが担当するのかはSolrの管理画面で確認することができます。ただし、実際のハッシュ値を知ることは実はできません。

簡単なテストプログラムを書いて、compositeIdのハッシュ値がどのように計算されるのかを確認してみました。

まずは単独のIDでハッシュを計算してみます。

$ java -cp .:solr-solrj-8.1.0.jar CompositeIdTest '1234'
id = 1234
hash = 721c5dc3
$ java -cp .:solr-solrj-8.1.0.jar CompositeIdTest '中央区'
id = 中央区
hash = a812f493
$ java -cp .:solr-solrj-8.1.0.jar CompositeIdTest '公園'
id = 公園
hash = eb2b06c4

IDを組み合わせてみます。

$ java -cp .:solr-solrj-8.1.0.jar CompositeIdTest '中央区!1234'
id = 中央区!1234
hash = a8125dc3
$ java -cp .:solr-solrj-8.1.0.jar CompositeIdTest '中央区!公園!1234'
id = 中央区!公園!1234
hash = a82b5dc3

8ビットで16進数2桁、16ビットで16進数4桁です。
「中央区!1234」の場合は16ビット:16ビットになるので
「中央区(a812f493)」の上位4桁
「1234(721c5dc3)」の下位4桁
でa8125dc3になっていることが分かります。
「中央区!公園!1234」の場合は8ビット:8ビット:16ビットなので
「中央区(a812f493)」の上位2桁
「公園(eb2b06c4)」の3桁目と4桁目
「1234(721c5dc3)」の下位4桁
でa82b5dc3となります。

さらにビット数を指定してみます。

$ java -cp .:solr-solrj-8.1.0.jar CompositeIdTest '中央区/4!公園/4!1234'
id = 中央区/4!公園/4!1234
hash = ab1c5dc3
$ java -cp .:solr-solrj-8.1.0.jar CompositeIdTest '中央区/4!公園!1234'
id = 中央区/4!公園!1234
hash = ab2c5dc3

「中央区/4!公園/4!1234」の場合は4ビット:4ビット:24ビットなので
「中央区(a812f493)」の上位1桁
「公園(eb2b06c4)」の2桁目
「1234(721c5dc3)」の3桁目以降
でab1c5dc3となります。
「中央区/4!公園!1234」の場合に無指定の「公園」の扱いがどうなるか興味があったのですが、2階層の場合はデフォルト8ビットなので「中央区/4!公園/8!1234」と同じになるようです。
「中央区(a812f493)」の上位1桁
「公園(eb2b06c4)」の2,3桁目
「1234(721c5dc3)」の4桁目以降
でab2c5dc3となります。

簡単なプログラムではありますが、特定のレコードがどのシャードに配置されているのかを確認したい場合に便利に使えそうです。