SolrCloudのリーダー選出の仕組み

はじめに

SolrCloudではコレクションを複数のシャードに分け、各シャードを複数のレプリカによる冗長構成にできます。レプリカの中から1台リーダーが選出されてインデックス更新更新の責任を持ちます。
旧来のマスタースレーブの構成では、マスターがダウンしたときには復旧するまでは更新処理が停止してしまいますが、SolrCloudではリーダーがダウンした場合には自動的に別のレプリカがリーダーに選出されてなるべく更新処理が停止しないように工夫されています。
この記事では、リーダー選出の仕組みを解説します。

リーダー選出の仕組み

リファレンスでは以下のように説明されています。

SolrCloud にはマスターもスレーブもありません。その代わりに各シャードは最低1台の物理的レプリカから構成され、その中の1台がリーダーとなります。
リーダーは自動的に選出されます。最初は先着順で、以後は
http://zookeeper.apache.org/doc/r3.5.5/recipes.html#sc_leaderElection
に記述されている ZooKeeper を使った手順に基づきます。

リーダー選出の手続きは org.apache.solr.cloud.LeaderElector クラスで実装されています。このクラスのコメントでもリーダー選出の仕組みについて触れられています。

これらのドキュメントとコードを参考に、リーダー選出の仕組みをまとめました

  • 冗長構成においては各レプリカが同じ状態を持つことが重要だが、リーダーを選ぶためには何らかの「違い」を作り出す必要がある
  • 違いを作り出すために ZooKeeper の Ephemeral & Sequential ノードを使う
    • ZooKeeper のクライアント(Solrのノード)は ZooKeeper 上の木構造にノードを作れる
    • ZooKeeper の Ephemeral ノードは、そのノードを作ったクライアントがダウンしたときに自動的にそのノードが無くなるので、クライアントのダウンを検知できる
    • ノードに Sequential プロパティを付与すると、兄弟ノード間で作られた順に一意のシーケンシャルな番号が振られる
    • 一番若い番号を持つノードのクライアントをリーダーとする
  • リーダーがダウンしたとき
    • ZooKeeper のクライアントは ZooKeeper 上のノードを監視することができる
    • 各レプリカは自分より1個若い番号のノードを監視しておく
    • リーダーがダウンしたらそのノードを監視していたレプリカが新しいリーダーになる
  • 全体のコーディネートは SolrCloud の Overseer が担当する
    • Overseer は SolrCloud のクラスタを構成する各 Solr ノードから選ばれるリーダー
    • Overseer 選出の手続きはシャードを構成するレプリカからリーダーを選出する手続きとほぼ同じ

Raspberry Pi 4であそんでみた

新しい生活様式もそろそろ慣れてきました。
マエダです。

 

Raspberry Pi 2を会社のみんなで共同購入して5年の月日が流れました。
気づいたらラズパイ3が出て、ラズパイ4が出て、ラズパイ4も昨年末から日本で技適も取得し販売されていたので久々Raspberry Piを購入しました。

Raspberry Pi 4
https://www.raspberrypi.org/products/raspberry-pi-4-model-b/

 

ラズパイ2のときはWifiもBluetoothもUSBモジュールを別購入して利用してましたが、今どきのラズパイはデフォで装備されているんですね。
ありがたい。

 

が、最初につまづいたのがmicro HDMI端子。
そんなの家にない。。。
すぐにAmazonで探しましたがたまたま即日で配送できそうなのがなく100均でそれっぽいのがないかググりました。

探してみるとすぐにダイソーさんから出ているmicro HDMIアダプタを発見!
チャリを立ち漕ぎして僕はダイソーさんに向かいました。

ダイソーさんにはmicro HDMIアダプタだけでなくmini HDMIアダプタもあり使う予定ないけどビデオカメラをテレビに接続とかで使うかもしれないと衝動買い。

早速自宅に帰りmicro HDMIで接続してみたところ、ラズパイ4の電源アダプタ端子とmicro HDMIアダプタがギリギリの状態で共存できました。

それではセットアップ。

Raspberry Pi 4 のセットアップ

① micro SDをPCに認識させ、RaspbianのOSイメージをダウンロードしておく
Raspbian
https://www.raspberrypi.org/downloads/raspbian/
※ A2DP認識のためRaspbian Buster with desktop をダウンロードしました。
以下の記事の通りでした。ありがとうございます。^^
https://qiita.com/dwarfJP/items/a4a90d5c0aa2833dac5b

② RaspbianのOSイメージをmicro SDに準備
※ドライブやディレクトリパスは環境にあわせて読み替えてください。

diskutil list
diskutil unMountDisk /dev/disk2
diskutil eraseDisk MS-DOS BOOT /dev/disk2
diskutil unMountDisk /dev/disk2
sudo dd if=/Users/hoge/Desktop/2020-02-13-raspbian-buster.img of=/dev/rdisk2 bs=1m

③ ssh接続許可とwifiの設定
wifiの設定はwpa_passphraseコマンドを後ほど利用してもよいです。
※ micro SDドライブ名は環境にあわせて読み替えてください。
※この時点でmicro HDMIアダプタいらなかったことに気づきました。。

touch /Volumes/boot/ssh
vi /Volumes/boot/wpa_supplicant.conf

sudo apt-get update
sudo apt-get upgrade
しとく。

以上。

ターミナルから接続してみる

micro HDMIでテレビにつなげてIPアドレスを確認。
ルーターの管理コンソールからラズパイのIPを調べるでもOK。

ssh pi@pi4 (パスワードはデフォでraspberry)

# 次回以降パスなしで接続できるようにssh公開鍵を設定しておく
mkdir .ssh
vi .ssh/authorized_keys

画面共有VNCで接続してみる

まずはTight VNC Serverをインストール

sudo apt-get install tightvncserver
sudo tightvncserver

Macだと画面共有アプリやFinderの[移動]-[サーバーへ接続]から以下のように指定。

vnc://pi4:5901

Bluetooth設定

全面的にこちらの記事を参照させていただきました。ありがとうございます。
https://qiita.com/homelan/items/e82b919638d3c458d2ec

sudo apt-get install bluez pulseaudio-module-bluetooth python-gobject python-gobject-2
reboot

# Boseのスピーカーに接続してみました。
sudo bluetoothctl
[bluetooth]# scan on
[bluetooth]# scan off
[bluetooth]# trust E8:07:BF:xx:xx:xx
[bluetooth]# pair E8:07:BF:xx:xx:xx
[bluetooth]# connect E8:07:BF:xx:xx:xx
[MR230]# paired-devices
[MR230]# quit

vi ~/.asoundrc
amixer -D bluealsa sset 'Bose Mini SoundLink - A2DP' 70%

# MPlayerインストール
sudo apt install mplayer
# 動作テスト
mplayer -ao alsa:device=bt-receiver /usr/share/sounds/alsa/Noise.wav
# サイマルラジオを聴いてみる
mplayer -ao alsa:device=bt-receiver -playlist http://www.simulradio.info/asx/radiomixkyoto.asx

Simul Radio
http://www.simulradio.info/

まとめ

ラズパイは進化して更にとても簡単に利用可能でいろいろと遊べそうです。
そしてダイソーさんをはじめ100円ショップは偉大であることを再認識しました。

弊社メンバーによるラズパイの活用記事もいろいろありますのでチェックしてみてください。
https://blog.splout.co.jp/?s=raspberry

iPadをデュアルディスプレイとして使ってみよう

こんにちわ。
リエです。

今年の梅雨は長いですね☔
とはいえ、コロナの影響でお家時間が圧倒的に増えたので季節感を中々感じにくくなっております。

今回はリモートワークでのちょっとした変化をお話したいと思います。

会社ではありがたい事にデュアルディスプレイでお仕事をさせていただいているのですが、お家にはデュアルディスプレイ用のモニターはありません。

もちろんなくてもお仕事はできますが、作業効率を考えるともう1枚モニターがほしいなぁと思ってしまいます。(贅沢病!)

でもモニターを買うのはなぁ(心の声:インテリアに合わんし、置き場所に困るし)と葛藤していたのですが、前にメンバーがSlackでiPadをデュアルディスプレイとして使う方法をシェアしていたのを思い出しました。
SidecarでiPadをMacの2台目のディスプレイとして使う

iPad持っているしこれいいやん!ということで設定してみました。
ちなみに先にネタバレするとめっちゃかんたんです。

用意するもの

・Mac
・iPad
・USB充電ケーブル
・アダプタ(PCとiPadを繋ぐのに必要です)
〈注意〉
MacとiPad がSidecar のシステム条件を満たしていることが絶対条件です。

これがあればiPadをデュアルディスプレイにすることができます。

設定してみよう

設定は本当にかんたんです。
1.MacとiPadをアダプタを使って繋ぐ
2.Macの[システム環境設定]を開き、その中の[ディスプレイ]を選択
3.[ディスプレイ]のAirPlayディスプレイからiPadを選択→保存
※MacとiPadを繋ぐとAirPlayディスプレイからiPadを選択できるようになります
4.これでiPadをデュアルディスプレイとして使えるようになります。

使ってみての感想

快適に作業できたので、めっちゃ便利でした。
つい癖でiPadを指で触ってしまうけど😂
デュアルディスプレイになっているので、当たり前ですがタッチ操作はできません。
シェアしてくれたメンバーに感謝です👏

デメリットをあげるとすると、画面が固まることでしょうか。
ネット環境や作業内容にもよるかもですが、私は割と固まっちゃいました;
そんな時は一旦接続を解除して再接続すれば直ります。

という感じでお家での作業が効率化できました。
〈最後に余談〉
コロナの影響で完全に引きこもりになったため断捨離しまくっています。
物にそんなに執着しないので溜めグセはありませんでしたが、生活していると物は増えちゃいますね。いい機会なので、この際シンプルを極めたいと思います。

Solrのパッケージ管理機能を使って自作プラグインを設定する

はじめに

Solr 8.4 で追加されたパッケージ管理機能を使って自作のプラグインを動的に設定してみます。ただし、Solr 8.4 では自作プラグインのインストールの際にキー読み込み関係のエラーが発生したので、今回の動作確認は Solr 8.5 で行っています。

作ったプラグイン

その名の通り、何もしないリクエストハンドラです。”Hello World!” のメッセージとバージョン番号を応答するだけです。これをコンパイルして plugin-nop-1.0.jar というファイルを作成しました。

リポジトリ登録

キー作成

$ openssl genrsa -out my_key.pem 512
$ openssl rsa -in my_key.pem -pubout -outform DER -out publickey.der
$ openssl dgst -sha1 -sign my_key.pem ../build/plugin-nop-1.0.jar | openssl enc -base64 | tr -d \\n
KzDSHoBGofrRbN2iRMZEnQEGArN7evEOELDsEPuFsYKRw7eQ11veFUvo5y3r0msqBPpCs8Fga4eJMxjDJyOzIA==

repository.json

Solr のソースコードに含まれる solr/core/src/test-files/solr/question-answer-repository/repository.json を参考にして作成しました。

上で作成したメッセージダイジェストの文字列を artifacts の sig に記載します。

ウェブサーバへ配置

Jarファイル、publickey.der、repository.json をウェブサーバに配置します。publickey.der と repository.json はこのファイル名である必要があります。

$ ls $DOC_ROOT/solr/repo2
plugin-nop-1.0.jar  publickey.der  repository.json

リポジトリをSolrに登録

$ bin/solr package add-repo nop http://localhost/solr/repo2/
$ bin/solr package list-available
Available packages:
-----
nop 		No Operation plugin
	Version: 1.0.0

パッケージのインストールとデプロイ

インストール

$ bin/solr package install nop:1.0.0
Posting manifest...
Posting artifacts...
Executing Package API to register this package...
Response: {"responseHeader":{
    "status":0,
    "QTime":11}}
nop installed.

デプロイ

$ bin/solr package deploy nop:1.0.0 -collections test
Executing {"add-requesthandler":{"name":"/nop","class":"nop:jp.co.splout.solr.plugins.NopRequestHandler"}} for path:/api/collections/test/config
Execute this command (y/n): 
y
Executing http://localhost:8983/api/collections/test/config/requestHandler?componentName=/nop&meta=true for collection:test
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "config":{"requestHandler":{"/nop":{
        "name":"/nop",
        "class":"nop:jp.co.splout.solr.plugins.NopRequestHandler",
        "_packageinfo_":{
          "package":"nop",
          "version":"1.0.0",
          "files":["/package/nop/1.0.0/plugin-nop-1.0.jar"],
          "manifest":"/package/nop/1.0.0/manifest.json",
          "manifestSHA512":"794a594729c8772073338aa03bbe41866d68bf76527d2a9568779c00ca4802bf362841073d323d16ce9f44cd38757371b591c5afe96876fe65a4e7c6a3f1eb89"}}}}}

Actual: 1.0.0, expected: 1.0.0
Deployed on [test] and verified package: nop, version: 1.0.0
Deployment successful

動作確認

$ curl http://localhost:8983/solr/test/nop
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "message":"Hello, World!",
  "version":"1.0"}

パッケージのアップデート

NopRequestHandler.java のバージョン文字列を “1.1” に変更して plugin-nop-1.1.jar を作成しました。これをリポジトリに追加してデプロイします。

リポジトリへの追加

まず電子署名を作成します。

$ openssl dgst -sha1 -sign my_key.pem ../build/plugin-nop-1.1.jar | openssl enc -base64 | tr -d \\n
zlEAXf6hKNbmjAW48noLyMiilESOuPXmsA4xZ5L+zYwYOX3aIuW1SnmFa+VrRMSKwk76xG+PomTX2ELmHiOvtg==

plugin-nop-1.1.jar をウェブサーバに配置して repository.json に以下を追加します。

      {
        "version": "1.1.0",
        "date": "2020-04-21",
        "artifacts": [
          {
            "url": "plugin-nop-1.1.jar",
            "sig": "zlEAXf6hKNbmjAW48noLyMiilESOuPXmsA4xZ5L+zYwYOX3aIuW1SnmFa+VrRMSKwk76xG+PomTX2ELmHiOvtg=="
          }
        ],
        "manifest": {
          "version-constraint": "8 - 9",
          "plugins": [
            {
		"name": "request-handler",
              "setup-command": {
                "path": "/api/collections/${collection}/config",
                "payload": {"add-requesthandler": {"name": "${RH-HANDLER-PATH}", "class": "nop:jp.co.splout.solr.plugins.NopRequestHandler"}},
                "method": "POST"
              },
              "uninstall-command": {
                "path": "/api/collections/${collection}/config",
                "payload": {"delete-requesthandler": "${RH-HANDLER-PATH}"},
                "method": "POST"
              },
              "verify-command": {
                "path": "/api/collections/${collection}/config/requestHandler?componentName=${RH-HANDLER-PATH}&meta=true",
                "method": "GET",
                "condition": "$['config'].['requestHandler'].['${RH-HANDLER-PATH}'].['_packageinfo_'].['version']",
                "expected": "${package-version}"
              }
            }
          ],
          "parameter-defaults": {
            "RH-HANDLER-PATH": "/nop"
          }
        }
      }
$ bin/solr package list-available
Available packages:
-----
nop 		No Operation plugin
	Version: 1.0.0
	Version: 1.1.0

インストールとデプロイ

後の流れはほぼ同じです。

$ bin/solr package install nop:1.1.0
Posting manifest...
Posting artifacts...
Executing Package API to register this package...
Response: {"responseHeader":{
    "status":0,
    "QTime":7}}
nop installed.
$ bin/solr package deploy nop:1.1.0 -collections test --update
Executing http://localhost:8983/api/collections/test/config/requestHandler?componentName=/nop&meta=true for collection:test
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "config":{"requestHandler":{"/nop":{
        "name":"/nop",
        "class":"nop:jp.co.splout.solr.plugins.NopRequestHandler",
        "_packageinfo_":{
          "package":"nop",
          "version":"1.1.0",
          "files":["/package/nop/1.1.0/plugin-nop-1.1.jar"],
          "manifest":"/package/nop/1.1.0/manifest.json",
          "manifestSHA512":"794a594729c8772073338aa03bbe41866d68bf76527d2a9568779c00ca4802bf362841073d323d16ce9f44cd38757371b591c5afe96876fe65a4e7c6a3f1eb89"}}}}}

Actual: 1.1.0, expected: 1.1.0
Deployed on [test] and verified package: nop, version: 1.1.0
Deployment successful
$ curl http://localhost:8983/solr/test/nop
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "message":"Hello, World!",
  "version":"1.1"}

無事にバージョン1.1にアップデートできました。

CSS animationを使ってみよう!

css animationについて、あまり使う機会はありませんよね?
あったとしても、ローディングなどごく一部でしか使わないので、下記の参考サイトなどからコピーや微調整して使うことは多いのではないでしょうか?
codepen
 
今回は、css animation の基本的なところを改めて確認しようと思います。
まずはシンプルに円を作成します。
<div class="circle"></div>
.circle {
  width: 30px;
  height: 30px;
  border-radius: 30px;
  background: #2196F3;
}
  
その円をゆっくり移動しましょう。
まずはキーフレームの作成から始めます。
@keyframes example {
  0% {
    transform:translateX(0);
  }

  100% {
    transform:translateX(200);
  }
}
キーフレームを作成しただけではまだ動きません。
次ように円が表示されたと思います。

 
最後に.circleにanimationのプロパティを追加します。
.circle {  
  animation-name: example;
  animation-duration: 1s;
  animation-timing-function: ease-out;
  animation-delay: 0;
  animation-iteration-count: infinite;
  animation-direction: alternate;
  animation-fill-mode: both;
  animation-play-state: running;
}

次のように円がアニメーションしたと思います。

 

各animationプロパティについて

animation-name

要素にキーフレームアニメーションを適用するアニメーション名を指定します。
(今回は「example」です。)

animation-duration

アニメーション一回分の時間の長さを指定します。

animation-duration: 1s;



animation-duration: 3s;

animation-timing-function

アニメーションの動きの加減速(イージング)を設定します。
キーワード「ease-out」やcubic-bezierなどで指定します。
animation-timing-function: ease-out;



animation-timing-function: steps(3, end);



animation-timing-function: cubic-bezier(0.86, 0, .07, 1);



easingsについては下記が参考になると思います。
https://easings.net/ja

animation-delay

アニメーションの開始を遅らせる時間を指定します。

animation-delay:0;
Start
animation-delay:1s;(1秒後開始されます)
Start

animation-iteration-count

停止するまでにアニメーション周期が再生される回数を指定します

animation-count:infinite; (無制限に繰り返されます)
Start
animation-iteration-count:3; (3回繰り返されます)
Start

animation-direction

アニメーション再生の方向をしていします。

animation-direction:normal;(周期ごとに初回に戻ります)
Start
animation-direction:reverse;(逆方向に開始され、周期ごとに初回に戻ります)
Start
animation-direction:alternate;(毎回反転させます。初回は順方向になります。)
Start
animation-direction:alternate-reverse;(毎回反転させます。初回は逆方向になります。)
Start

animation-fill-mode

実行の前後にどう対象にスタイルを適用するかを設定します。

none
適用されているcssの初期値

forwards
最後の適切なキーフレームで定義された値を対象に適用されると同時に適用

backwards
最初の適切なキーフレームで定義された値を対象に適用されると同時に適用

both
forwardsとbackwardsの両方適用

animation-play-state

アニメーションが実行中か停止中かを設定します。

running
アニメーションが現在実行中になります

paused
アニメーションが現在停止します。

以上がCSS animationの基本的な使い方です!
このように分けて考えるとわかりやすのではないでしょうか?

次回はさらに詳しい内容をお伝えできればと思います。