Vespa Search Engine
validation-overrides.xml (設定ファイルの反映が失敗する場合の対処方法)
概要
- validationに失敗したデプロイメントを実行できるようにするためのファイル
- validationは、運用中のインスタンスを不注意で壊してしまわないようにするためのもの
- アプリケーションがまだ実稼働していない場合や、不整合や特定のフィールドのデータの損失があっても構わないと考えている場合は、オーバーライドすることで設定を反映できる
- アプリケーションパッケージのrootに置く(services.xmlと同じ階層)
- この方法で、上書きできるvalidationは、validationメッセージの前にダッシュで区切られたvalidation-idをつけて返される
Error: invalid application package (400 Bad Request)
Invalid application package:
Error loading default.default:
field-type-change:
Document type 'ドキュメント':
Field 'フィールド名' changed:
data type:
'double' -> 'int'
To allow this add <allow until='yyyy-mm-dd'>field-type-change</allow> to validation-overrides.xml, see https://docs.vespa.ai/en/reference/validation-overrides.html
ファイルの書き方
<validation-overrides>
<allow
until="2016-01-31"
comment="Reduce to needed cluster size after benchmarking">cluster-size-reduction</allow>
<allow until="2016-02-03">field-type-change</allow>
</validation-overrides>
-
allow
タグで特定のvalidationを無効にする期間を指定する- 最大30日後まで指定できるが、安全のためできるだけ現在に近い日付にする
- 過去の日時を指定したタグは無視される
<allow
until="[必須] いつまで許可するかをISO-8601フォーマットのUTCで指定する"
comment="[任意] 変更を加えた理由を説明するテキスト">
validation-id
</allow>
Ranking Expressions and Features
Ranking expressions
- vespaはクエリにマッチする文書をランク付けするためにranking expressionを使用し、文書ごとにランクスコアを計算する
- ranking expressionはrank profileに格納される
- ranking expressionは数学的な関数
- ranking expressionはif関数もサポートしている
- ディシジョンツリーや条件付きビジネスロジックを実現することができる
- ranking expressionはテンソル関数を包括的にサポートしている
- ディープニューラルネットのような機械学習の関数を表現することができる
- ランキングはtwo-phase rankingにより最適化される
- ランキング下位の候補を排除するために、リソースをあまり使用しない軽量なfirst -phaseのランキングを実行後、強力なsecond phaseのランキングにリソースを集中させる
Rank features
- ranking expressionで使用されるプリミティブな値をrank featuresと呼ぶ
- rank featureには、テンソル、マルチバリューフィールド、スカラーなどがあり、以下のいずれか
- アプリケーションパッケージで設定された定数
- クエリで送信された値またはドキュメントに設定された値
- Vespaによって計算される、クエリとドキュメントがどの程度マッチしたかを示す情報
- Vespaのrank feature setは多くの低レベルなfeatureに加えて、いくつかの高レベルのfeatureを含んでいる
Normalization
- rank featureには、0〜1の範囲で正規化されたものと、カウントや位置のように正規化されていないものがある
- 可能な限り、正規化された機能を使用する
- 同じ情報(あるいはそれ以上)を取得できる上に、他の特徴量と容易に組み合わせることができるため、使い勝手がよくなる
- さらに、合計ではなく平均を取るなどして、組み合わせたrank scoreも正規化されるようなranking expressionを書くようにする
- 正規化されたrank scoreにより、関連性に基づく混合や、良いヒット商品がない場合の検索支援などが可能になる
Configuration
- 一部の機能、特にfieldMatch機能には設定パラメータがある
- 性能または関連性のためにフィールドごとにfeatureの計算を微調整することができる
rank-properties {
featureName.configurationProperty: "value"
}
Feature contribution functions
- vespaのranking featureは線形
- 例えば、earliness featureは、マッチがフィールドの最後にある場合は0、フィールドの最初にある場合は1、ちょうど真ん中にある場合は0.5となる
- 多くの場合、featureの貢献度は「良さ」と線形であってはならない
- 例えば、earlinessはフィールドの最初の方で早く減衰し、最後の方ではゆっくり減衰する可能性がある
- これは、フィールドの最初の方の単語と一致するかは、最後の方の単語と一致するかどうかより、より重要という直感から
- 例えば、earlinessはフィールドの最初の方で早く減衰し、最後の方ではゆっくり減衰する可能性がある
- そのためには、featureを関数に渡して、線を意図に沿った曲線に変換する
- 例えば、
pow(0-x,2)
のような関数を使用する
- 例えば、
Dumping rank features for specific documents
Accessing feature/function values in results
- どのようなfeatureでも、rank profileのsummary-featuresのリストに追加することで、返すことができる
rank-profile test {
summary-features: tensor_join join_sum
function tensor_join() {
expression: attribute(my_tensor_field) * query(my_query_tensor)
}
function join_sum() {
expression: sum(tensor_join())
}
}
- また、multiphaseの場合match-featuresを使用することで、first phaseのrank featuresを取得することができる
- match-featuresとsummary-faeturesは同じ機能のように見えるが、パフォーマンスのトレードオフがある
- match-featuresは、最初のフェーズで返されたすべてのヒットに対して、度のヒットをfillするか選択する前に計算する必要がある。これは、fill()を呼び出す前に、matchfeaturesフィールドを使用して、どのヒットを残し、どれを削除するか選択することが可能であることも意味する。
- summary-featuresの式は、fill phase以前には利用できないが、実際にfillされたヒットだけ計算すれば良い
The "if" function and string equality tests
- "if"はMLR学習済み決定木のエンコード以外の目的にも使用できる
- ひとつは、同じ検索で異なるタイプのドキュメントに対して異なるランキング関数を選択することである
- ランキング式は文字列の等値判定ができるので、文字列属性(例えば「カテゴリー」)の値に基づいて異なるランキングサブ関数を選択するには、次のような式を使用する
if (attribute(category)=="restaurant",…restaurant function, if (attribute(category)=="hotel",…hotel function, …))
Using constants
- ranking expressionでは
constants
で定義された定数を参照できる
first-phase {
expression: myConst1 + myConst2
}
constants {
myConst1: 1.5
myConst2: 2.5
...
}
Documents
- vespaはデータをドキュメントとしてモデル化する
- ドキュメントには、アプリケーションによって設定された、すべてのドキュメントで一意の文字列識別式がある
- ドキュメントは、キーと値のペアの集合
- ドキュメントはスキーマを持つ
- クラスタを構成するとき、services.xmlのdocuments要素はクラスタが保持するドキュメントの種類を設定する
- ガベージコレクタを設定する
- アプリケーションに送信されるドキュメントのデフォルトルートを定義する
- デフォルトでは、ドキュメントは定義されたドキュメントタイプを持つ全てのクラスタに送信される
- vespaはドキュメントのIDを使用して、ドキュメントをノードに分配する
- content layerはドキュメントのIDから、ロケーションを計算する
- バケットにはロケーションの最下位ビットがすべて等しいドキュメントがすべて含まれる
- この性質により文章の共同保管が可能になる
Document IDs
- ドキュメントIDは文字列で表現されるURIであり、定義されたURIスキームに準拠する必要がある
- ドキュメントIDの文字列は、com.yahoo.text.Textで定義された文字のみ含むことができる
id schema
idのschemaは以下のようになっている
id:<namespace>:<document-type>:<key/value-pairs>:<user-specified>
required | 説明 | |
---|---|---|
namespace | Yes | 下記を参照 |
document-type | Yes | services.xml およびschemaで定義された文書タイプ |
key/value-pairs | Optional | bucketへのドキュメント分配を設定するために使用する修飾子。この修飾子がない場合、すべてのドキュメントを一律に配布する。ドキュメント種別が mode=streaming か mode=store-only の場合にのみ有用。通常のインデックスには使用しないこと |
user-specified | Yes | ユニークID文字列 |
Namespace
以下のようにdocument Aとdocument Bをfeedする場合
- document A
curl -X POST https:.../document/v1/first_namespace/my_doc_type/docid/shakespeare
- document B
curl -X POST https:.../document/v1/second_namespace/my_doc_type/docid/shakespeare
これらは別々のドキュメントになり、どちらも検索可能だが、IDが異なる。ドキュメントIDはuser-specified部(これは上記の例では両方shakespeare)ではなく、namespace部(first_namespaceとsecond_namespace)で異なっている。
ドキュメントAの完全なIDはid:first_namespace:my_doc_type::shakespeare
このnamespaceは、services.xmlやスキーマのような他の場所での設定とは何の関係も持たない。その意味では、各IDのユーザ指定部分と同じ。namespaceは、完全なドキュメントIDの一部として以外では、クエリで使用することはできない。しかし、ドキュメントの選択には使えます。たとえば id.namespace にアクセスして、指定した文字列と比較することができる。使用例としては、ドキュメントのサブセットを訪問する場合など。
Fields
- ドキュメントはフィールドを持つことができる
- フィールドにデフォルト値を定義することはできない
- ドキュメントプロセッサを使用して、ドキュメントの put/update 操作にデフォルト値を割り当てる
Fieldsets
- フィールドセットを使用して、get や visit のような読み込み操作から返されるフィールドを制限する
- フィールドセットはVespaのヒントと見なされるべきで、最適化のために使用される
- Vespaが指定された以上のフィールドを返しても、エラーとはみなされない
注:ドキュメントフィールドセットは、検索可能なフィールドセットとは別物
Document expiry
- ドキュメントを自動で削除するには
now
とともにselection
を使用する
<documents garbage-collection="true">
<document type="music" selection="music.timestamp > now() - 86400" />
</documents>
timestamp-fieldは、EPOCHからの秒数で値を持たなければならない
field timestamp type long {
indexing: attribute
attribute {
fast-access
}
}
- garbage-collection="true "の場合、Vespaは期限切れのドキュメントをパージするためにドキュメント空間を繰り返し処理する
- Vespaは、各保存されたドキュメントに対して、garbage-collection-interval秒ごとに最大1回、設定されたGCセレクションを呼び出すことになる
Ranking Introduction
Ranking
- ランキングで実行される計算はranking expressionsと呼ばれる関数で表現され、スキーマに定義されたrank profilesにまとめられる
- いくつかのランク特徴を組み合わせた簡単な数式から、テンソル式や大規模な機械学習されたOnnxモデルまで、様々なものがある
- これらのランキング式はコンテンツノード上でローカルに評価されるため、データに対する推論を行うために計算ノードにデータを転送する必要がない
Two-phase ranking
Node and network setup
- Vespaは、互いに通信し、相互作用するサービスから構成されている
- これらのサービスは、スケーリングのために実際のハードウェア上にいくらでも分割することができるし、開発のために単一の環境上にすべてを共存させることもできる
- このような柔軟性を実現するためには、サービスが動作する環境について、いくつかの要件を満たす必要がある
Node
- ノードは、いくつかのVespaサービスが実行されている環境
- ノード上で実行されるさまざまなVespaサービスは、ほとんどがネットワークを介して互いに通信する
- これは、すべてのノードがIPアドレスを持ち、他のすべてのノードにネットワーク接続できる必要があることを意味する
- IPv4とIPv6の両方のプロトコルがサポートされている
- 単一のノードでVespaスタック全体を実行する場合でも、同じフレームワークが使用されることに注意
Memory settings
- 最適なノード構成を見つける方法は、パフォーマンスガイドを参照
- Vespaには多くの設定、フィード、および使用する方法があるため、一般的な推奨設定はない
- メモリ不足は診断が難しい様々な問題を引き起こす可能性があるため、テストを推奨
Hostname
- Vespaサービスがノード上で開始されると、ノードは設定システムに対して自分自身を識別して、設定(実行するサービスを含む)を取得する必要がある
- このため、configサーバーにノードの一意な識別子が必要
- ノードがconfigサーバーが知っているホスト名を持っていることは既に要件となっているので、Vespaはノードが設定を取得するために自分自身を識別するときに同じホスト名を使用
- ノードのIPアドレスを見つけ、それに接続するために、ノードはそれを識別し、そのIPアドレスにマップするホスト名を持っている必要がある
- ネットワーク上の実際のマシンは、通常DNSでFQDN(Fully Qualified Domain Name)を持っており、これをこの目的のためのホスト名として使用する
- hosts.xmlで設定されたホスト名は、ノードのIPアドレスを検索するために使用できることが要件であることに注意
- 設定サーバーはこのホスト名を使用して、そのノード上で動作しているVespaサービスへのネットワーク接続を開くために使用するURLを作成する
- ノードがDNS名を持たないIPアドレスを使用している場合、Vespaをインストールしたすべてのノードの/etc/hostsに、ホスト名に対応するすべてのIPアドレスを持つ必要がある
- etc/hostsを公開する代わりにDNSサーバーを使用する場合にもFQDNとして使用可能な名前を使用すること
- これは、ノードが自分自身のホスト名(FQDN)を知っていて、そのホスト名が正確に何であるかについて設定サーバーと一致している必要があることを意味する
- 通常、これは単にhostnameコマンドを実行することで達成される
- hostnameによって返される値とは異なる値を使用するには、VESPA_HOSTNAMEを使用する
Simple single-node development environment
- シングルノードのセットアップでVespaの設定をテストするとき、通常、ホスト名を "localhost "という値で上書きすることでセットアップの手間を省くことができる
$ echo "override VESPA_HOSTNAME localhost" >> $VESPA_HOME/conf/vespa/default-env.txt
- Javaユニットテストを実行すると、default-env.txtの設定が拾われず、VESPA_HOSTNAMEが環境に設定されていない場合、デフォルトで「localhost」が使用される
Reindexing
- Vespaアプリケーションのインデクシングパイプラインが変更されると、 Vespaは自動的に保存されたデータを再読み込みし、 新しい仕様に従ってインデックスが更新されることがある
- インデックスパイプラインの変更は、linguisticsなどの外部ラブラリの変更、ユーザーによるdocumentスキーマのindexing script設定の変更、コンテンツクラスタ内のドキュメントタイプのindexing modeの変更などによって起こる
- 再インデックスは、アプリケーションの全コーパス、特定のコンテンツクラスターのみ、または特定のクラスター内の特定の文書タイプのみに対して、POST reindex endpointを使用してトリガーすることができる
- また、GET reindex endpointで検査できる
When to reindex
- デプロイによってアプリケーションのインデクシングパイプラインが変更されると、コンフィグサーバーによってそれが検知される
- 変更をデプロイする場合は、validation-overrideを追加する必要がある
- Deploymentは新しいインデクシングパイプラインをインデックスに反映するために必要な再インデックスのためのアクションをリストアップする
-
POST reindex endpointを使用して、影響を受けるドキュメントの再インデックスをトリガーする。ただし、新しいインデクシングパイプラインが正常にデプロイされた後
- すなわち、アプリケーションが変更を加えた世代に収束した時
- その後、アプリケーションの次のデプロイメントで再インデックス化が開始される
Reindexing progress
- 再インデックスは各コンテンツクラスタのコンポーネントによって行われる
- 指定されたタイプのすべてのドキュメントをvistisし、クラスタのindexing chainに再フィードする
- 再インデックスプロセスは、コーパスの小さなサブセット(バケット)をロックして再インデックス処理を行うことで、同時並行的なフィードとの書き込み競合を回避する
- このため、一部の同時書き込み操作で書き込み待ち時間が長くなることがあるが、一般的なスループットには影響はない
- さらに、再インデックスはコーパスによっては時間もリソースも消費するため、この処理は外部フィードやサービングなど他のタスクにリソースをゆだねるようにチューニングされており、一般にバックグラウンドで実行しても安全になっている
- 再インデックスは、コンテンツクラスタ間で並行して、一度に1つの文書タイプに対して行われる
- 詳細な進捗はGET reindex endpointで見ることができる
- 失敗した場合、再度トリガーされるまで、そのドキュメントタイプでのインデックス作成は再試行されない
- pending状態は、クラスタの準備ができ次第、インデックス作成が開始または再開されることを示し、runningは現在進行中であることを示す
- successful は、そのタイプのドキュメントがすべて正常にインデックス付けされたことを意味する
Schemas
- スキーマはドキュメントタイプとその上で何を計算したいのかを定義するもの
- スキーマはアプリケーションパッケージの
schemas/
ディレクトリに、スキーマと同じ名前のファイル、末尾に.sd
をつけて保存される - ドキュメントタイプ、rank profiles、documment summariesは継承可能
Schema concepts
document
- ドキュメントはランクプロファイルが評価する単位であり、クエリ結果で返される
- ドキュメントはフィールドを持つ
- ドキュメントIDはドキュメントのフィールドではないので、必要に応じて明示的に追加する必要がある
field
- フィールドは、文字列のような単一値、または文字列の配列のような多値にすることができる
- ほとんどの多値フィールドはgroupingで使用可能
- マップや構造体配列の属性にランキングでアクセスすることはできない
- ランキング機能
attribute(name).count
は、多値属性の要素数によるランキングに使用できる
- ランキング機能
- 要素数でフィルタをするには、rank-score-drop-limitを組み合わせたstrict tiering rank functionを作成し、query variableで要素数を指定する
indexing
- indexingは、インデックス作成時にフィールドのデータをどのように処理するかを設定する
- 重要なのは以下の3つ
-
index
- 非構造化テキスト用
- テキストのインデックスを作成する
- テキストマッチングとすべてのテキストランキング機能が利用可能
- インデックスはディスクにバックアップされ、メモリに収まる必要はない
-
attribute
- 構造化データ用
- grouping、ソート、ランキングに利用できる
- 完全一致、または範囲(数値の場合)によって検索できる
- fast-searchオプションを指定すると、メモリ上にB-treeを作成できる
- これはattributeが強い基準(すなわち、多くのドキュメントをフィルタリングする)場合、パフォーマンスを向上させることができる
-
summary
- 検索結果のドキュメントサマリーにこのフィールドを含めるようにする
- indexing命令はUnixのシェルコマンドに似たパイプラインセマンティックスを持ち、データは左から右へと流れていく
- フィールド値に対して複雑な変換処理ができる
- フィールド値を変更せずに次のセクションに送るだけということも可能
indexing: summary | attribute | index
- フィールドに属性とインデックスの両方が設定されている場合、このフィールドへのクエリはインデックスモードを使用する
- 両方を設定する通常のケースは、(attributeを必要とする)groupingを伴う(indexを使用する)クエリを実行すること
match
- マッチモードは、クエリ項目とフィールドのマッチング方法 (完全一致またはプレフィックス一致など) を設定する
fieldset
- フィールドセットは、クエリ用にフィールドをグループ化したもの
- フィールド/フィールドセット名がクエリに与えられていない場合、defaultと名付けられたフィールドセットがクエリされる
rankprofile
- ランクプロファイルは、このドキュメントタイプに対して行われる計算を定義する
Schema modifications
- Vespa は、フィールドの追加やインデックス作成、マッチモードの変更など、スキーマを安全に変更するために構築されている
- スキーマの新しいバージョンは、アプリケーションパッケージの中にデプロイされる
- いくつかの変更は潜在的に破壊的であるため (たとえば、フィールドのインデックス設定を変更する)、 deploy コマンドはデフォルトでそのような変更を受け付けないようになっている
- このような変更を許可するには、validation-overrideを追加する
- 破壊的な変更をブロックすることで、進化するスキーマを安全かつ簡単に自動化することができる
- スキーマの変更の多くは非破壊的であり、フィールドの追加など、バリデーションのオーバーライドを必要としない
Multiple schemas
- アプリケーションは複数のドキュメントタイプを定義でき、それぞれを独自のスキーマで定義することができる
- 複数のスキーマをひとつのコンテンツクラスタにマッピングすることもできる
- スキーマごとに個別のコンテンツクラスタを定義して、ドキュメントタイプごとに異なるスケーリングを行うこともできる
- どちらの構成でも、ひとつのコンテナクラスタを用いてすべてのドキュメントタイプにクエリを発行することができる
- 複数のドキュメントタイプを持つアプリケーションでは、クエリによって使用するドキュメントタイプを制限できる
- Vespa はデフォルトで、すべてのドキュメントタイプとすべてのクラスタに並行してクエリを実行し、スコアに基づいて結果を統合する
- クエリを特定のドキュメントタイプに限定するには、restrictパラメータにスキーマ名のカンマ区切りリストを設定する
$ENDPOINT/search/?
yql=select * from sources * where title contains "bob"&
restrict=music,books
Content cluster mapping
- スキーマはservices.xmlの中でコンテンツクラスタにマッピングされる
- アプリケーションは、多くのスキーマを一つのコンテンツクラスタにマップすることができる
- 異なるスキーマのドキュメントが異なるパフォーマンス特性を持つ場合、複数のコンテンツクラスタを使用すべき
- クエリを特定のコンテンツクラスタに制限するには、sourceパラメータにコンテンツクラスタIDのカンマ区切りリストを設定する
$ENDPOINT/search/?
yql=select * from sources * where title contains "bob"&
sources=items,news
Schema inheritance
Query API
- クエリーは以下のような構成要素を持つ
- 入力データ
- ランキングとグループ化の指定
- 結果
- その他の実行パラメータ
Input
- 入力データは構造化・非構造化データの両方ある
$ vespa query "select * from music where artist contains \"coldplay\" and userInput(@q)" \
"q=head"
- 最初の行はYQLクエリ文字列
- 構造化された入力(artist=coldplay)と非構造化のユーザ入力への参照を持つ
- ユーザ入力は、2行目のqパラメータで指定する
- 構造化データと非構造化データを分離することで、アプリケーションコードでは入力データの解釈とサニタイズをせずにすむ
- ユーザの入力は、userQuery演算子を用いてsimple query languageで表現することもできる
Query Profiles
- query profileを使用して、クエリパラメータを設定に保存できる
- これにより、クエリ文字列が短くなり、設定のみを変更することでクエリを簡単に変更できるようになる
- クエリプロファイルはネスト化、バージョン管理、継承が可能
Geo Filter and Ranking
- 緯度・経度を用いた位置によるフィルタリングを行い、地理検索を実現する
- DistanceToPathは、近さによるランク関数
- ランキングを使用することで、ジオフィルタリングの代わりに結果を改善できる場合がある
Ranking
- Rankingは、クエリとデータの計算を指定する
- ドキュメントにスコアを付け、スコアの高い順にドキュメントを返す
- ランクプロファイルとは、ドキュメントのスコアをどのように計算するかの仕様
- アプリケーションは複数のランクプロファイルを持ち、異なる計算を実行することができる
- ランキングではなくsortingで結果を並べることもできる
Grouping
- グループ化とは、ランキング後の結果セットに含まれるドキュメントをグループ化すること
- グループ化で使用されるフィールドはattributeでなければならない
- グループ化式はYQLクエリ文字列の一部であり、最後に追加される
- アプリケーションはすべてのドキュメントをグループ化することができる
- limit 0を指定すると、グループ化の結果のみが返される
Results
- デフォルトでは、すべてのフィールドが結果に返される
- フィールドのサブセットを指定するには、ドキュメントサマリーを使用する
- テキストを検索する場合、フィールドにドキュメントの静的な要約を持たせるか、動的な要約を使用すると、検索の視覚的な関連性を向上させ、使用する帯域幅を削減することができる
Query execution
出典元: Vespa | Query API | Query execution
-
フェーズ
-
Query processing
- 正規化、リライト、リッチ化。サーチチェインにおけるカスタムロジック
-
Matching, ranking and grouping/aggregation
- このフェーズでは、クエリをコンテンツノードにディスパッチする
-
Result processing, rendering
- クエリフェーズで発見されたトップヒットのコンテンツ取得とスニペット化
-
-
上記は簡略化されており、クエリが結果のグループ化を指定する場合、クエリフェーズはコンテナノードとコンテンツノード間で複数のフェーズまたはラウンドトリップを含む可能性がある
Query processing and dispatch
- クエリは、アプリケーションからコンテナノードに、Query APIを使用して、またはカスタムリクエストハンドラによって処理される任意のカスタムリクエスト形式で送信される
-
言語処理やクエリ書き換えなどのクエリの前処理は、ビルトインやカスタムのsearch chainで行われる
- デフォルトの検索チェーンは
vespa
-
ApplicationStatus
を調べて、このチェーンにインストールされているコンポーネントを見つける - クエリに
&trace.level=4 (またはそれ以上)
を追加すると、クエリで呼び出されたコンポーネントが出力され、順序を分析できる
- デフォルトの検索チェーンは
- クエリはコンテナからコンテンツクラスタに送信される
- アプリケーションは複数のコンテンツクラスタを持つことができる
- Vespa はデフォルトですべてのコンテンツクラスタを検索す
- Federationは、クラスタへのクエリ方法、クラスタのソース名を制御する
- 上の図では、コンテンツクラスタは1つですが、複数が完全にサポートされており、ドキュメントタイプを異なる形でスケーリングすることができる
Matching, ranking, grouping
- この時点でクエリは1つ以上のコンテンツクラスタに配布される
- Groupd distributionコンテンツクラスタでは、クエリはdispatch-policyに基づいて1つのグループにディスパッチされる
- フラットな1つのコンテンツクラスタでは、クエリはすべてのコンテンツノードにディスパッチされる
- コンテンツノードに到着したクエリは、Ready sub databaseのドキュメントセットに対して、matching, ranking そして aggregation/groupingを実行する
- vespa-protonは readyのドキュメントに対してmatchingを行い、リクエスト/スキーマに指定されたようにランキングを行う
- 各コンテンツ ノードは、ドキュメント コーパス全体のサブセットを照合してランク付けし、要求に応じて、ヒット総数やデータの並べ替えやグループ化などのメタ情報と共にヒットを返す
- グループ内のコンテンツノードがタイムアウト時間内に返信すると、max-hits / top-k 結果がコンテナに返され、クエリフェーズの結果処理が行われる
- このフェーズではヒットごとのグローバルid(gid)とランキングスコアのみ利用できる
- カバレッジや総ヒット数などの結果のメタ情報もある
- フィールドの内容など、ヒット固有の追加のデータは、Result processing フェーズでコンテンツの取得が完了するまで利用できない
Result processing (fill) phase
- クエリフェーズの結果が利用可能になると、カスタムチェーンサーチャーコンポーネントは、コンテンツノードからヒットのコンテンツを取得する前に、最初の検索フェーズで利用可能な限られたデータを処理することができる
- リクエストされたdocument summaryフィールドのみがコンテンツノードからフェッチされる。summaryのリクエストは、クエリフェーズで結果を生成したコンテンツノードに直接送られる
- コンテンツノードへのリクエストが完了したあと、術tネオ結果セットがカスタムコンポーネント(例えば、結果の重複排除、top-kリランキング)で処理され、その後でレスポンスがレンダリングされる
Timeout
- よくある質問
- タイムアウトはクエリ全体に適用されるのですか、それともコンテンツ・クラスターに送信された時点から適用されるのですか?Searcherがコンテナ内で2*timeoutの間スリープした場合、呼び出し元はタイムアウトを示す応答を取得しますか?
- タイムアウトは、コンテナとコンテンツノードの両方の処理であるクエリ全体に適用される。ただし、タイムアウトの処理は協調的である。時間がかかるサーチャーや外部リソースにアクセスするサーチャーがある場合、サーチャーのコードはQuery.getTimeLeft()を確認する必要がある。そのため、この場合はタイムアウトになるが、2*タイムアウト+αの後にタイムアウトになる
- マルチフェーズ検索において、クエリーのタイムアウトは個々のサーチャーに対して設定されるのか、それともサーチチェーン全体に対して設定されるのか?
- タイムアウトはクエリ全体に対するものです(ほとんどのSearcherはタイムアウトをチェックしません - Query.getTimeLeft()を使用してください)
- 例えば、サーチチェーンにサーチャーが3人いる場合、サーチャー1人が497ミリ秒、サーチャー2人がそれぞれ1ミリ秒かかり、クエリーのタイムアウトが500ミリ秒でも問題ない
- 複数のサーチチェーンを非同期で実行する場合、それぞれのチェーンに異なるクエリタイムアウトを設定し、さらに非同期実行を行うサーチャーに別の全体タイムアウトを設定することは可能でしょうか?
- これらのチェーンのいずれかに送信するクローン化されたクエリごとに異なるタイムアウトを設定し、チェーンからの応答を待つ際にタイムアウトを指定することができる
- タイムアウトはクエリ全体に適用されるのですか、それともコンテンツ・クラスターに送信された時点から適用されるのですか?Searcherがコンテナ内で2*timeoutの間スリープした場合、呼び出し元はタイムアウトを示す応答を取得しますか?
Troubleshooting
- Vespaがクエリ文字列から有効な検索式を生成できない場合、「Null query」というエラーメッセージが発行される
- トラブルシューティングを行うには、リクエストに &trace.level=2 を追加する
- yql パラメータが見つからない場合も、このエラーメッセージが表示される
Query tracing
- クエリトレースを使用して、クエリの実行をデバッグする
-
trace.level=1
(またはそれ以上)を使用することで有効になる -
trace.timestamps=true
を追加して、起動されたサーチャーのタイミング情報を取得する
Linguistics in Vespa
Configuration Servers
- Vespa Configuration Serversは、アプリケーションパッケージがデプロイされるエンドポイントをホストし、生成されたconfigurationをすべてのサービスに提供する
- 一つ以上のconfigサーバーによって設定される
- configサーバは、設定システムの分散データストレージとしてZooKeeperを使用する
- また、各ノードは設定データをキャッシュするために config proxy を実行する
Redundancy
-
configuration serverはVESPA_CONFIGSERVERS, services.xml, hosts.xmlに定義される
-
VESPA_CONFIGSERVERS
は全てのノードにセットされなければならない -
コンマまたは空白で区切られたリストで、すべてのconfigサーバのホスト名
-
複数のconfigsサーバが存在する場合、config proxyはランダムにconfigサーバを選択する
- configサーバ間の負荷分散のため
-
config proxyは耐障害性があり、使用しているconfigサーバが利用できなくなった場合、または受信したコンフィグにエラーがあった場合、別のconfigサーバに切り替わる
-
ZooKeeperはn個の障害に耐えられるようにするために、設計上
(2*n)+1
個のノードを用意する必要がある- 奇数台のみのノードが有効
- 障害耐性のあるシステムを構築するためには最低3代のノードが必要
-
configサーバ一台のみの場合であっても、サーバがダウンしてもアプリケーションは動作する
- ただし、アプリケーションの変更はデプロイできない
-
config proxyは全てのノードで動作し、configをキャッシュしているので、各ノードのサービスに対してconfigを提供し続けることが可能
-
ただし、configサーバが利用できない状態でノードを再起動すると、config proxyを再起動する際にキャッシュが破壊されるため、そのノードのサービスが起動できなくなる
Start sequence
- Vespaアプリケーションインスタンスをブートストラップするための手順
- config serverを起動
- configをデプロイ
- Vespaノードを起動
- configサーバを起動する手順
-
- 完全修飾ホスト名を使用し、設定サーバーを含むすべてのノードで同じ値を使用して、VESPA_CONFIGSERVERSを設定する
-
- services/hosts.xmlで設定したノードでconfigサーバを起動
-
/state/v1/health
(デフォルトのポートは19071)にリクエストし、起動に成功したかを確認する - health APIから応答がない場合、以下の2つのことが考えられる
- configサーバプロセスが起動していない
-
vespa-logfmt
を使うか、$VESPA_HOME/logs/vespa/vespa.log
のログを調べる
-
- configサーバープロセスが開始され、Zookeeprのquorumを待っている
- quoramに達し、
Application config generation: 0
というログが出力されるまでハングする - quoramが不足する原因は以下の通り
- configサーバ間の接続ができない
- Zookeeperは以下のようにメンバーをログに記録する(members: [node0.vespanet, node1.vespanet, node2.vespanet], attempt 1)
- configサーバが動作しているノードが、ポート2181で互いに到達できることを確認する
- ネットワークの設定が間違っている可能性
- configサーバ間の接続ができない
- quoramに達し、
- configサーバプロセスが起動していない
-
- すべてのconfigサーバが
state/v1/health
からup
を返すと、アプリケーションパッケージのデプロイが可能になる
- つまり、デプロイに失敗した場合、まずconfigサーバの状態を確認するのが良い
- configサーバが起動しているにもかかわらず、デプロイに失敗する場合は、アプリケーションパッケージの問題である可能性が高い
- すべてのconfigサーバが
-
- デプロイが完了すると、prepareとactivateのステップでログが出力される
-
- Vespaノードが起動される
- 技術的には、いつでも起動可能
- トラブルシューティングの際には、他のノードを起動する前に、configサーバが正常に起動し、デプロイが正常に行われたことを確認すること
- また、デバッグの際には全てのconfigサーバのログを確認すること
-
Scaling up
- スケールはノードを一つずつ追加していくことで行う
- 耐障害性を高めるため、または、configサーバのノードを交換する際に、configサーバを追加する
- 手順
-
- 新しいconfigサーバノードにVespaをインストールする
-
- 全ノードのVESPA_CONFIGSERVERSにconfigサーバーノードを追加する(configサーバーノードとその他のノードの両方)
-
- 一度に一台ずつ、元のconfigサーバノードでconfigサーバを再起動し、新しいconfigサーバノードでconfigサーバを起動する
- 新しいコンフィグが使用されていることを確認したい場合、再起動後に、
/opt/vespa/libexec/vespa/vespa-curl-wrapper https://localhost:19071/status| jq .configserverConfig.zookeeperserver
を実行する
-
- services.xmlとhosts.xmlを新しいサーバ設定で更新し、vespa-deploy prepareとvespa-deploy activateを実行する
-
- configサーバ以外の他のノードを順次再起動し、新しいconfigサーバを利用するようにする
-
Scaling up by Majority
- 1ノードから3ノード、3ノードから7ノードまで増加した場合、空のノードがクラスタの過半数を構成してしまう
- この場合、Zookeeperのマスター選定で空のノードが勝つ可能性があるため、configサーバを再起動すると、既存のアプリケーションデータが破棄される可能性がある
- この問題を避けるために、ノードのマイナーセットでスケールアップすること
- たとえば、1ノードから3ノードにする場合は、1から2、2から3に増やす
Scaling down
- configサーバをクラスタから削除するには以下の手順を踏む
-
- 全てのVespaノードのVESPA_CONFIGSERVERSから当該のconfigサーバを削除する
-
- 新しいconfigサーバ集合の利用を開始するために、configサーバ以外のノードを順次再起動する
-
- 残っているconfigサーバを順次再起動する
-
-
vespa-get-config
もしくはvespa-zkcli ls
でconfigサーバノードにデータがあることを確認する
- もし空なら、
vespa-deploy prepare
およびvespa-deploy activate
を再実行する
-
-
- 外れたconfigサーバを削除する
-
Replacing nodes
- 一度に1つのノードだけを交換すること
- configサーバーが3台未満の場合は、まず新しいノードでスケールアップし、次に古いノードを削除してスケールダウンする
- 交換する各ノードについて、この手順を繰り返す
- 3台以上ある場合は、VESPA_CONFIGSERVERS内の古いノードを1台追加する代わりに新しいノードと置き換えることができ、それ以外は「スケールアップ」と同じ手順で行う
- 交換する各ノードについて、この手順を繰り返す
Tools
ZooKeeper
- ZooKeeperは複数のconfigサーバにまたがるデータの整合性を担保する
- configサーバのJavaアプリケーションはZooKeeperサーバを起動する
- 他のノードが使用するRPCフロントエンドが組み込まれている
- ZooKeeperはフィルシステムのようにサブノードを持つことができるノードにデータを内部的に保存する
- configサーバを起動すると、Zookeeper用の設定ファイル(
$VESPA_HOME/var/zookeeper/conf/zookeeper.cfg
)がVESPA_CONFIGSERVERS
の内容に従って生成される- そのため、configサーバノードに変更があった場合、configサーバを全て再起動する必要がある
-
vespa-deploy prepare
実行時、アプリケーションファイルはグローバル設定とともに、ZooKeeperに保存される - アプリケーションデータは
/config/v2/tenants/default/sessions/[sessionid]/userapp
に保存される -
vespa-deploy activate
実行時、/config/v2/tenants/default/applications/default:default:default
にセッションIDを書き込むことで、最新のアプリケーションをliveにアクティベートする- 他のノードが設定されるのはその時点
- 状態を確認するには、
vespa-zkcli
を使用する
$ vespa-zkcli ls /config/v2/tenants/default/sessions/{sessionid}/userapp
$ vespa-zkcli get /config/v2/tenants/default/sessions/{sessionid}/userapp/services.xml
- ZooKeeperのサーバーログは
$VESPA_HOME/logs/vespa/zookeeper.configserver.0.log
にある
ZooKeeper Recovery
- ハードウェアの故障などでコンフィグサーバのデータが破損した場合は、以下の手順で復旧させる
- 一つの例は、
$VESPA_HOME/logs/vespa/zookeeper.configserver.0.log
のログにjava.io.IOException: Negative seek offset at java.io.RandomAccessFile.seek(Native Method)
が出力されている場合で、Zookeeprがdiskfullで復旧できないことを示す
- 一つの例は、
- 以下の手順の最中に、Vespaの他のノードの再起動は不要
-
- vespa-stop-configserver
-
- vespa-configserver-remove-state
-
- vespa-start-configserver
-
- vespa-deploy prepare <application path>
-
- vespa-deploy active
-
- この手順は、Zookeeper内部のデータスナップショットを完全に削除し、〇からデプロイする
- デフォルトでは、コンテンツクラスタの状態を維持するクラスタコントローラは、共有の同じZooKeeperインスタンスを使用するため、状態を削除する際にはコンテンツクラスタの状態もリセットされる
- 手動で設定した状態は失われる
- クラスタコントローラをスタンドアローンzookeeprモードで動作させることも可能
Zookeeper barrrier timeout
- コンフィグサーバの負荷が高い場合や、デプロイするアプリケーションが大きい場合、デプロイ時に他のサーバと同期する際に、サーバーの内部がタイムアウトすることがある
- 回避策は、VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUTを600秒以上に設定し、コンフィグサーバを再起動させる
Configuration
- configシステムを実行していないノードからconfigにアクセスするには(例えば、Document API経由でfeedを行うノード)、環境変数VESPA_CONFIG_SOURCESを使用する
System requirements
- 実行するJVMの最小ヒープサイズは128Mb、最大ヒープサイズは2GB(設定で変更可能)
- トランザクションログを書き込みますが、定期的に古い項目が削除されるため、ディスクスペースはほとんど不要
- ディスクI/Oが多いサーバーで実行すると、パフォーマンスに悪影響が出るので注意
Ports
- コンフィグサーバーRPCポートは、システム内の全ノードでVESPA_CONFIGSERVER_RPC_PORTを設定することで変更可能
- HTTP ポートを変更するには、
$VESPA_HOME/conf/configserver-app/services.xml
のポートを変更する - デプロイ時に、ポートがデフォルトから変更される場合は、-pオプションを使用する
<http>
<server port="19079" id="configserver" />
</http>
Troubleshooting
Service location broker - slobrok
- SlobrokはService Location Brokerの頭文字をとったもので、Vespaで使用されるネームサービス
- このサービスは特定のポートでリッスンする
- Slobrokサービスのポート番号を見つけるにはvespa-model-inspectを使用する
- Slobrokはデフォルトでadminノードと、冗長性のために他のランダムな1~2ノードで実行される
- 複数ノードの高可用性アプリケーションでは、slobrokインスタンスはコンフィグサーバーを実行しているノードでホストされる
- コンフィグサーバーと同様に、Vespaがサービスを機能させるためにslobrokを立ち上げておく必要があるため
- slobrokの運用もconfigサーバーと同じで、ほとんどのアプリケーションでは3つで十分
- slobrokは最小限のシステムリソースを必要とするので、同じノードで動作する他のサービスに影響を与えない
- Document APIなどのクライアントは、サービスロケーションブローカーノードのいずれかでルックアップを行うことになる
- Slobrokはクエリパイプラインでは使用されない
- クラスタコントローラは、サービスの可用性を評価するためにslobrokを使用する
Content Cluster Elasticity
- Vespaクラスターはクエリや書き込みに対応しながら、スケールイン・アウトが可能
- コンテンツクラスタ内のドキュメントは、変更時に自動的に再分配され、最小限のデータ移動で均一な分散を維持する
- クラスタのサイズを変更するには、ノード設定を変更してアプリケーションを再デプロイするだけでよく、再起動に必要はない
- ドキュメントは、Vespaによってバケットと呼ばれる塊で管理される
- バケットのサイズや数はVespaによって完全に管理されており、手動でシャーディングを制御する必要はない
- また、ノードの損失から回復するために、elasticityメカニズムが使用される
- ドキュメントの新しいレプリカは、設定された冗長性を維持するために、他のノード上に自動的に作成される
- そのため、ノードが故障してもすぐに対処する必要はない
- クラスタは十分なリソースがある限り、ノードの故障から自己回復する
Adding nodes
- コンテントクラスタのノードの追加や削除は、searvice.xmlのコンテンツクラスタのnodesタグを修正し、再デプロイするだけで良い
- 新しいノードを追加すると、すべてのバケットについて新しい理想的な状態が計算される
- 新しいノードにマッピングされたバケットは移動され、余分なものは取り除かれる
-
冗長性が2の場合に、ノードを追加する場合の例
- 分配アルゴリズムは、各バケットに対してランダムなノードシーケンスを生成する
- この例では、n=2で、レプリカは最初にソートされた2つのノードにマッピングされる
- 図では、3番目のノードが追加されると、2つのノードへの配置がどのように変化するかを示している
-
新しいノードは、最初にソートされたバケットではプライマリとして、2番目にソートされたバケットではセカンダリとして引き継ぐ
-
これにより、ノードの出入りに伴うデータの移動が最小限に抑えられ、容量の変更も容易に行える
-
新しいノードが追加されても、既存のノード間でバケットが移動することはない
-
擬似ランダムシーケンスに基づき、一部のバケットがプライマリからセカンダリに変更されたり、削除されたりする
-
同じデプロイメントで複数のノードを追加することができる
Removing nodes
-
ノードの故障や退役でも、同じく再分配が行われる
-
ノードが退役した場合、他のノードでレプリカが生成され、ノードは行動したままだがアクティブなレプリカはなくなる
-
ノードがダウンした場合の再分配の例
- ここでは、ノード2がダウンしている
- このノードはバケット2と6のアクティブレプリカを保持していた
- ノードがダウンすると、セカンダリレプリカがアクティブに設定される
- すでにready状態であった場合は、クエリの提供を開始する
- そうでない場合は、レプリカをインデックスする
- セカンダリのレプリカがなくなったバケットは、理想的な状態になるように、残りのノードにマージされる
Grouped distribution
- コンテンツ・クラスター内のノードは、グループ単位で配置できる
- コンテンツ・クラスター内のノードのグループは、文書コーパス全体の1つ以上の完全な複製を持つ。
- これは、以下のような場合に役に立つ
- Cluter upgrade
- 複数のグループがあれば、一度に1つのノードだけをアップグレードするのではなく、一つのグループをまとめてアップグレードできる
- Live Upgrade
- Query throughput
- Vespaは自動的に単一のグループだけにクエリーを送信するので、高いクエリレートや高い静的クエリコストを持つアプリケーションは、グループを使用してより高いクエリレートに拡張することができる
- Topology
- グループを使用することで、ネットワークスイッチやラック上でレプリカの配置を制御し、スイッチやラックレベルでの冗長性を確保することができる
- グループ・サイズとノード・リソースを調整することで、アプリケーションはレイテンシーとコストのスイート・スポットを簡単に見つけることができ、エラスティック・オペレーションは自動的に実行され、クエリーと書き込みはダウンタイムなしに通常通り実行される
- Cluter upgrade
Changing topology
Vespaのエラスティック機能とは、サービスを中断することなくトポロジー(グループ化された配信)を変更できる機能です。これはライブ変更であり、新しいトポロジーにドキュメントを自動再配布します。
Replicas
- トポロジーを変更する場合は、min-redundancy設定に注意
- この設定は、クラスタ内のレプリカの最小数を構成するもので、実際の数はトポロジーに依存
- 例
- 最小冗長度n=2、15ノードのフラットクラスタを、5ノードずつ3つのグループを持つグループ化クラスタに変更する(総ノード数とnは変更しない)
- この場合、3つのグループにはそれぞれ少なくとも1つのレプリカが存在し、クエリを完全にカバーするため、変更後の実際の冗長性は3となる。
- 現実的な結果として、トポロジーの変更によりノードあたりのディスクとメモリーの必要量が増加する。したがって、トポロジーを再構成する前に、実際のレプリカ数を計算することが重要である。
Query coverage
-
トポロジーを変更すると、正しい順序で手順を踏まない限り、移行時にクエリーのカバレッジが失われる可能性がある。完全なカバレッジが重要でない場合は、変更だけを行い、ドキュメントの再配布が完了するのを待つ。
-
完全なクエリ・カバレッジを維持するために、グループ・サイズとグループ数を同時に変更しないようにする
-
- ノードを追加してデータ量を増やしたり、ノードあたりのデータ量を少なくするには、グループサイズを大きくする。 例えば、1グループ8ノードの2グループクラスタでは、4ノードを追加して1グループ10ノードとし、容量を25%増加させる。
-
- クエリのキャパシティを増やすことが目的であれば、既存のグループと同じノード数で、1つ以上のグループを追加する。フラット・クラスターは1グループと同じ。フラット・クラスターに8ノードがある場合は、グループごとに8ノードを2列持つグループ化クラスターに変更する。 これは空の行を追加し、クエリサービングに入力されます。
-
-
要するに、最終状態が行数と行ごとのノード数の両方を変更することを意味する場合は、上記の組み合わせとして、別々のステップとしてこれを実行します。各ステップの間に、merge_bucket.pendingメトリックを使用してドキュメントの再配布が完了するのを待ちます
Buckets
- ドキュメントを管理するために、VespaはドキュメントIDにハッシュやヒントを用いてバケットにグループ化する
- ドキュメントのPutまたはUpdateは、そのドキュメントを持つBucketの全レプリカに送信される
- バケットレプリカが同期していない場合、バケットマージ操作を実行してバケットを再同期させる
- バケットには、最近削除されたドキュメントの墓石が格納されている
- バケットは大きくなりすぎると分割され、小さくなると結合される
- これは小さなインスタンスから大きなインスタンスまで高いパフォーマンスを発揮するための重要な機能であり、スケーリング時のダウンタイムや手動操作の必要性を排除する
- バケットは純粋にコンテンツ管理の概念であり、データは別々のバケットに保存されたりインデックス化されたりすることはなく、クエリがバケットに関連することも一切ない
Ideal state distribution algorithm
- 理想的な状態分布アルゴリズムは、バケットの配置を決定するためにCRUSHアルゴリズムの変種を使用
- ノードが追加されたり削除されたりするときに、最小数のドキュメントを移動させる
- アルゴリズムの中心は、各バケットへのノード配列の割り当て
- バケットをノードに割り当てる手順
-
- 擬似乱数リストを生成するために、バケットID を乱数生成器の種とする
- バケットIDをシードとして使えば、そのバケツに対して常に同じシーケンスが生成される
-
- ノードはディストリビューションキー順に並び、その順番で上記のリストの乱数を割り当てる
- 例えば、ディストリビューション・キー0を持つノードは最初の乱数を取得し、ノード1は2番目の乱数を取得する
-
- ノードリストを乱数でソートする
-
- 乱数の降順でノードを選択する
- すべてのバケットに対して繰り返す
-
Consistency
- 一貫性はバケットレベルで維持される
- コンテンツノードはバケットの内容に基づいてチェックサムを計算し、バケットのレプリカ間でチェックサムを比較する
- 矛盾が検出されると、それを解決するためにバケットマージが発行される
- 矛盾したバケットレプリカが存在する間は、操作は "最良 "のレプリカにルーティングされる
- バケットが分割・結合されると、バケットのレプリカが異なるレベルで分割される可能性がある
- バケットが分割・結合されている間にノードがダウンしている可能性もある
- これは一貫性のないバケット分割と呼ばれる
- バケットのチェックサムは、異なる分割レベルのバケット間で比較することはできない
- そのため、コンテンツノードは、この状態ですべてのドキュメントが十分なレプリカに存在するかどうかを知ることができない
- このため、一貫性のない分割は最も優先度の高いメンテナンスのひとつ
- すべてのバケットが同じレベルに分割または結合された後、コンテンツノードはすべてのレプリカの整合性を確認し、検出された問題をマージで修正することができる
Linguistics in Vespa
- Vespaはlinguisticsモジュールを使って、インデックス作成と検索時にクエリやドキュメントのテキストを処理する
- linguisticsのゴールはprecisionを損なわずに、recallを増加させること
- 以下のような処理で構成されている
- テキストを単語や句読点など、基地のタイプのかたまりにトークン化する
- アクセントを正規化する
- 単語の基本形を見つける(stemming or lemmatization)
- linguisticsの処理は
index
モードのstringフィールドに対して実行される - 処理概要
-
- ドキュメントの書き込み時、
indexing: index
を持つ文字列フィールドは、デフォルトで処理される
- フィールドの言語によって、この処理が設定される
- 文書やフィールドに明示的に言語が設定されている場合もあるが、そうでない場合は検出される。
- ドキュメントの書き込み時、
-
- フィールドの中身がトークン化、正規化、stemmingされ、得られたtermがインデックスに追加される
フィールドの言語はコンテンツ ノードには保持されず、処理された用語自体のみが保持される
-
- クエリも同じように処理される
- model.localeやlanguageなどのアノテーションで指定されない限り、文字列の言語が検出される
これは一般的なクエリの問題で、短い文字列から言語を正確に検出することは困難
-
-
Proton
- ProtonはVespaの検索のコア
- Protonはドキュメントのディスクとメモリー構造を維持する
- データは動的であるため、これらの構造はメンテナンス・ジョブによって定期的に最適化される
- これらのバックグラウンド・ジョブのリソース・フットプリントは、主に同時実行設定によって制御される
- コンテンツノードはバケット管理システムを持つ
- バケット管理システムは
ready
,not ready
,removed
の3つのサブデータベースを持つドキュメントデータベースにリクエストを送る
Bucket management
- ノードが起動すると、まずそのノードがどのようなドキュメントとバケットを持っているかの概要を知る必要がある
- すべてのバケットのメタデータがわかると、コンテンツノードはdownからup状態に遷移する
- distributorはバケットのメタデータに素早くアクセスしたいので、これらのリクエストに効率的に対応するために、インメモリのバケットデータベースを保持している
- オペレーションは優先順位に従って並べられ、1つのバケットにつき、一度に1つのオペレーションしか実行されない
- バケット管理の下にあるのは永続化エンジンであり、SPIをVespa検索の観点から実装している
- 永続化エンジンは、ドキュメントIDからドキュメントタイプを読み取り、リクエストを適切なドキュメントデータベースにディスパッチする
Document database
- 各ドキュメントデータベースは単一のドキュメントタイプを担当
- FeedHandlerと呼ばれるコンポーネントが、ドキュメントの受信、更新、削除リクエストの処理を行う
- すべてのリクエストはまずトランザクションログに書き込まれ、次にリクエストタイプに基づいて適切なサブデータベースに渡される
Sub-database
- サブデータベースには3つのタイプがあり、それぞれが独自のドキュメントメタストアとドキュメントストアを持つ
- ドキュメントメタストアは、ドキュメントIDからローカルIDへのマップを保持する
- このローカルIDはドキュメントストア内でドキュメントをアドレス指定するために使用される
- ドキュメントメタストアはサブデータベースに存在するバケットの状態に関する情報も保持する