グラフデータベースのためのテスト基盤を構築し、開発サイクルを高速化してみた
本記事を閲覧いただきありがとうございます。シンプルフォーム社のエンジニアの駒井 (@rindybell) です。本記事は SimpleForm Advent Calendar 2023 の9日目の記事となっています。
本記事では Gremlin Server や pytest-docker を用いて、グラフデータベースを用いたアプリケーション向けのテスト基盤を構築した例を紹介します。
1. 背景
弊社が所有するデータの中には、オブジェクト間に関係性があり、グラフとして表現することに適するデータがあります。例えば、株式会社や商業施設に関するデータがあり、株式会社や商業施設を頂点と扱い、取引先の関係性が認められる場合に辺を定義する、といった表現が可能です。このように、何らかのデータをグラフ形式で表現したい場合、データベースとしてグラフデータベースを活用することが一般的です。Amazon Neptune や Neo4j といったグラフデータベースが候補に挙がります。
本記事ではグラフデータベースとして Amazon Neptune を利用することを考えます。プロダクトを開発するとき、本番環境の Amazon Neptune に接続することは推奨されません。従って、開発環境において動作確認をしたいとき、ローカル環境に Neptune をエミュレートする必要があります。しかしながら、AWS のモッキングフレームワークの Moto は十分に Neptune をサポートしていません。ローカル環境でグラフデータベース用のテスト基盤を構築することは、開発~テストのサイクルを効率化するためにも、極めて重要な取り組みです。
このような経緯があり、Gremlin Server や pytest-docker を組合わせ、グラフデータベース向けのテスト基盤を構築しました。本記事では、その構成例を示します。
2. テスト基盤の構成とソースコードの例
実行可能なソースコードを以下のリポジトリに配置しています。
上記のソースコードについて解説します。上記のリポジトリでは FastAPI を用いて API サーバのサンプルを実装しています。API サーバは非同期 API として実装されており、グラフデータベースに対する CRUD 操作を行います。Create 操作ならばグラフの頂点を作成したり、辺を定義します。Read 操作については、クエリと合致するサブグラフを検索します。ただしこれらは、グラフデータベースと連携する点を除いては、非同期 API としては一般的であり 、特徴的な要素は多くありません。
本ソースコードの特徴的な部分はテストコードにあります。テストデータを実行する前に、conftest.py において Amazon Neptune と互換性のある Gremlin Server を起動し、シードデータを投入しています。ここで、conftest.py とは、テストコードに対して横断的な設定を記述するためのファイルです。
ソースコードも交えて、具体的に説明します。まず、conftest.py にて pytest-docker を用いて Docker Compose コマンド を実行します。conftest.py にて次のように記すことで、docker-compose.yml を介して任意のサーバを実行することができます。また、check 関数を利用することで、疎通が確認されるまで待機することも可能です。
@pytest.fixture(scope="session")
def gremlin_service(docker_ip, docker_services):
def is_responsive():
try:
# 疎通および検索処理が実行可能かどうかを判断する
dao = CompanyKnowledgeGraphDAO("localhost", "localhost")
dao.read_vertices()
return True
except Exception:
return False
docker_services.wait_until_responsive(timeout=30.0, pause=1, check=is_responsive)
docker-compose.yml としては次のファイルを用いています。
version: "3"
services:
server:
image: tinkerpop/gremlin-server:latest
ports:
- 8182:8182
volumes:
- ./gremlin-server:/opt/gremlin-server/conf
- graph-data:/opt/gremlin-server/data
console:
image: tinkerpop/gremlin-console:latest
command: "-i conf/remote.groovy"
volumes:
- ./gremlin-console:/opt/gremlin-console/conf
volumes:
graph-data:
gremlin-server の起動が確認されたら、シードデータを挿入します。ここでは次に示すシンプルなグラフデータを挿入しています。以上がテスト基盤の概要の説明となります。
3. テストの実行例
conftest.py で起動したサーバやシードデータを元に単体テストを実行します。例えば次のテストコードはサブグラフを導出できるかどうかを確認しています。例えば name="K"のノードであればグラフ上に存在するので探索できます。しかし、name="unkown"を指定すると、これはグラフ上に存在しないノードを指定いるのでサブグラフを導出できません。理想的には戻り値の内容についての整合性を確認すべきですが、ここでは結果が空かどうかのみを検査しています。
data_for_read_graphs = [
(True, {"label": "individual", "name": "K"}),
(False, {"label": "individual", "name": "unknown"}),
]
@pytest.mark.parametrize("expected, item", data_for_read_graphs)
def test_read_graphs(
self,
gremlin_service,
expected,
item,
):
bv = BasicVertex(**item)
dao = CompanyKnowledgeGraphDAO(gremlin_service, gremlin_service)
result = dao.read_graphs(bv.label, bv.data.key(), bv.data.value())
assert expected == bool(result)
4. おわりに
本記事ではグラフデータベースを用いたアプリケーション開発のためのテスト基盤について解説しました。いかがでしたでしょうか。グラフデータベースはしばしば有用です。しかしながら、リレーショナルデータベースなどとは異なる点も多く、詰まりポイントも多いので、迅速にテストできる環境は非常に重要と考えます。本記事が少しでも読者の皆様のお力になれたら幸いです。
Appendix
本記事をお読みいただきましてありがとうございました。明日以降の弊社のアドベントカレンダーもどうぞよろしくお願いいたします。また、Open Data Labという団体にて、大規模言語モデル入門・輪読会 #1 の開催しておりますので、そちらも併せてご確認いただければ幸いです。
Discussion