この 1 か月間、私は CouchDB に関連する概念実証プロジェクトに積極的に取り組み、その機能を調査し、将来のタスクに備えてきました。この期間中、私は CouchDB のドキュメントを何度も読み、すべてがどのように機能するかを確実に理解しました。ドキュメントを読んでいると、CouchDB には JavaScript で書かれたデフォルトの Query Server が同梱されているにもかかわらず、カスタム実装の作成は比較的簡単で、カスタム ソリューションはすでに世の中に存在しているという記述に遭遇しました。
簡単に調査したところ、Python、Ruby、または Clojure で記述された実装が見つかりました。実装全体はそれほど長くないようだったので、独自のカスタム Query Server を作成して CouchDB を実験することにしました。これを行うために、言語として Go を選択しました。 Helm のチャートで Go テンプレートを使用することを除いて、私はこれまでこの言語をあまり使った経験がありませんでしたが、何か新しいことを試してみたいと思い、このプロジェクトはその素晴らしい機会になると思いました。
作業を開始する前に、Query Server が実際にどのように動作するかを理解するために、CouchDB のドキュメントをもう一度見直しました。ドキュメントによると、Query Server の高レベルの概要は非常に単純です。
クエリ サーバーは、stdio インターフェイス上の JSON プロトコルを介して CouchDB と通信し、すべてのデザイン関数呼び出しを処理する外部プロセスです […].
CouchDB によって Query Server に送信されるコマンドの構造は、[
基本的に、私がしなければならなかったのは、STDIO からこの種の JSON を解析し、期待される操作を実行し、ドキュメントで指定されている応答を返すことができるアプリケーションを作成することでした。 Go コードでさまざまなコマンドを処理するには、多くの型キャストが必要でした。各コマンドの具体的な詳細については、ドキュメントの「クエリ サーバー プロトコル」セクションを参照してください。
ここで私が直面した問題の 1 つは、クエリ サーバーが設計ドキュメントで提供される任意のコードを解釈して実行できる必要があるということでした。 Go がコンパイル言語であることを知っていたので、この時点で行き詰まるだろうと予想していました。ありがたいことに、Go コードを簡単に解釈できる Yeagi パッケージをすぐに見つけました。これにより、サンドボックスを作成し、解釈されたコードでインポートできるパッケージへのアクセスを制御できます。私の場合、couchgo というパッケージのみを公開することにしましたが、他の標準パッケージも簡単に追加できます。
仕事の結果、CouchGO!というアプリケーションができました。が現れた。これはクエリ サーバー プロトコルに従っていますが、設計ドキュメントの関数を処理するための独自のアプローチがあるため、JavaScript バージョンの 1 対 1 の再実装ではありません。
たとえば、CouchGO! には、emit のようなヘルパー関数はありません。値を出力するには、map 関数から値を返すだけです。さらに、設計ドキュメント内の各関数は同じパターンに従います。関数固有のプロパティを含むオブジェクトである引数が 1 つだけあり、結果として 1 つの値のみを返すことになっています。この値はプリミティブである必要はありません。関数によっては、オブジェクト、マップ、またはエラーの場合もあります。
CouchGO! の使用を開始するには、GitHub リポジトリから実行可能バイナリをダウンロードし、CouchDB インスタンスのどこかに配置し、CouchDB が CouchGO! を開始できるようにする環境変数を追加するだけです。プロセス。
たとえば、couchgo 実行可能ファイルを /opt/couchdb/bin ディレクトリに配置する場合、それが機能するように次の環境変数を追加します。
export COUCHDB_QUERY_SERVER_GO="/opt/couchdb/bin/couchgo"
CouchGO! で関数を記述する方法を簡単に理解するために、次の関数インターフェイスを見てみましょう:
func Func(args couchgo.FuncInput) couchgo.FuncOutput { ... }
CouchGOの各機能!はこのパターンに従い、Func が適切な関数名に置き換えられます。現在、CouchGO!次の関数タイプをサポートしています:
map 関数とreduce 関数、および validate_doc_update 関数を使用してビューを指定するサンプル デザイン ドキュメントを調べてみましょう。さらに、言語として Go を使用していることを指定する必要があります。
{ "_id": "_design/ddoc-go", "views": { "view": { "map": "func Map(args couchgo.MapInput) couchgo.MapOutput {\n\tout := couchgo.MapOutput{}\n\tout = append(out, [2]interface{}{args.Doc[\"_id\"], 1})\n\tout = append(out, [2]interface{}{args.Doc[\"_id\"], 2})\n\tout = append(out, [2]interface{}{args.Doc[\"_id\"], 3})\n\t\n\treturn out\n}", "reduce": "func Reduce(args couchgo.ReduceInput) couchgo.ReduceOutput {\n\tout := 0.0\n\n\tfor _, value := range args.Values {\n\t\tout = value.(float64)\n\t}\n\n\treturn out\n}" } }, "validate_doc_update": "func Validate(args couchgo.ValidateInput) couchgo.ValidateOutput {\n\tif args.NewDoc[\"type\"] == \"post\" {\n\t\tif args.NewDoc[\"title\"] == nil || args.NewDoc[\"content\"] == nil {\n\t\t\treturn couchgo.ForbiddenError{Message: \"Title and content are required\"}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tif args.NewDoc[\"type\"] == \"comment\" {\n\t\tif args.NewDoc[\"post\"] == nil || args.NewDoc[\"author\"] == nil || args.NewDoc[\"content\"] == nil {\n\t\t\treturn couchgo.ForbiddenError{Message: \"Post, author, and content are required\"}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tif args.NewDoc[\"type\"] == \"user\" {\n\t\tif args.NewDoc[\"username\"] == nil || args.NewDoc[\"email\"] == nil {\n\t\t\treturn couchgo.ForbiddenError{Message: \"Username and email are required\"}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn couchgo.ForbiddenError{Message: \"Invalid document type\"}\n}", "language": "go" }
さて、マップ関数から始めて各関数を詳しく見てみましょう:
func Map(args couchgo.MapInput) couchgo.MapOutput { out := couchgo.MapOutput{} out = append(out, [2]interface{}{args.Doc["_id"], 1}) out = append(out, [2]interface{}{args.Doc["_id"], 2}) out = append(out, [2]interface{}{args.Doc["_id"], 3}) return out }
CouchGO! には出力機能がありません。代わりに、キーと値の両方が任意の型であるキーと値のタプルのスライスを返します。 JavaScript のように、ドキュメント オブジェクトは関数に直接渡されません。むしろ、オブジェクトに包まれています。ドキュメント自体は、さまざまな値の単純なハッシュマップです。
次に、reduce 関数を調べてみましょう:
func Reduce(args couchgo.ReduceInput) couchgo.ReduceOutput { out := 0.0 for _, value := range args.Values { out = value.(float64) } return out }
JavaScriptと同様に、CouchGO!のreduce関数も機能します。キー、値、rereduce パラメーターを受け取り、すべて 1 つのオブジェクトにラップされます。この関数は、リダクション操作の結果を表す任意の型の単一の値を返す必要があります。
最後に、validate_doc_update プロパティに対応する Validate 関数を見てみましょう。
func Validate(args couchgo.ValidateInput) couchgo.ValidateOutput { if args.NewDoc["type"] == "post" { if args.NewDoc["title"] == nil || args.NewDoc["content"] == nil { return couchgo.ForbiddenError{Message: "Title and content are required"} } return nil } if args.NewDoc["type"] == "comment" { if args.NewDoc["post"] == nil || args.NewDoc["author"] == nil || args.NewDoc["content"] == nil { return couchgo.ForbiddenError{Message: "Post, author, and content are required"} } return nil } return nil }
この関数では、新しいドキュメント、古いドキュメント、ユーザー コンテキスト、セキュリティ オブジェクトなどのパラメータを受け取り、すべて関数の引数として渡される 1 つのオブジェクトにラップされます。ここでは、ドキュメントが更新できるかどうかを検証し、更新できない場合はエラーを返すことが期待されています。 JavaScript バージョンと同様に、ForbiddenError または UnauthorizedError の 2 種類のエラーを返すことができます。ドキュメントを更新できる場合は、nil を返す必要があります。
より詳細な例については、私の GitHub リポジトリにあります。注意すべき重要な点の 1 つは、関数名は任意ではないということです。これらは常に、Map、Reduce、Filter など、それが表す関数のタイプと一致する必要があります。
独自の Query Server を作成するのはとても楽しい経験でしたが、既存のソリューションと比較しなければ意味がありません。そこで、Docker コンテナーでいくつかの簡単なテストを用意して、CouchGO がどのくらい高速になるかを確認しました。できる:
データベースに予想される数のドキュメントをシードし、専用のシェル スクリプトを使用して応答時間を測定するか、Docker コンテナからのタイムスタンプ ログを区別しました。実装の詳細は、私の GitHub リポジトリにあります。結果を以下の表に示します。
テスト | カウチGO! | CouchJS | ブースト |
---|---|---|---|
インデックス作成 | 141.713秒 | 421.529秒 | 2.97x |
削減中 | 7672ミリ秒 | 15642ミリ秒 | 2.04x |
フィルタリング | 28.928秒 | 80.594秒 | 2.79x |
更新中 | 7.742秒 | 9.661秒 | 1.25倍 |
ご覧のとおり、JavaScript 実装の向上は著しく、インデックス作成の場合はほぼ 3 倍、reduce 関数と filter 関数の場合は 2 倍以上高速になっています。更新関数の増加は比較的小さいですが、それでも JavaScript より高速です。
ドキュメントの作成者が約束したように、Query Server プロトコルに従っている場合、カスタム Query Server を作成するのはそれほど難しくありません。 CouchGO!なのに一般に、非推奨の関数がいくつか欠けていますが、開発のこの初期段階でも JavaScript バージョンよりも大幅に強化されています。まだまだ改善の余地はたくさんあると思います。
この記事のすべてのコードを 1 か所にまとめたい場合は、私の GitHub リポジトリで見つけることができます。
この記事をお読みいただきありがとうございます。この解決策についてのご意見をぜひお聞かせください。 CouchDB インスタンスで使用しますか? それとも、すでにカスタムメイドの Query Server を使用しているのでしょうか?コメントでそれについて聞いていただければ幸いです。
このシリーズの作成中のヒント、洞察、その他の部分については、私の他の記事も忘れずにチェックしてください。ハッピーハッキング!
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3