🕸️

グラフデータベースのためのテスト基盤を構築し、開発サイクルを高速化してみた

2023/12/08に公開

本記事を閲覧いただきありがとうございます。シンプルフォーム社のエンジニアの駒井 (@rindybell) です。本記事は SimpleForm Advent Calendar 2023 の9日目の記事となっています。

本記事では Gremlin Serverpytest-docker を用いて、グラフデータベースを用いたアプリケーション向けのテスト基盤を構築した例を紹介します。

1. 背景

弊社が所有するデータの中には、オブジェクト間に関係性があり、グラフとして表現することに適するデータがあります。例えば、株式会社や商業施設に関するデータがあり、株式会社や商業施設を頂点と扱い、取引先の関係性が認められる場合に辺を定義する、といった表現が可能です。このように、何らかのデータをグラフ形式で表現したい場合、データベースとしてグラフデータベースを活用することが一般的です。Amazon NeptuneNeo4j といったグラフデータベースが候補に挙がります。

本記事ではグラフデータベースとして Amazon Neptune を利用することを考えます。プロダクトを開発するとき、本番環境の Amazon Neptune に接続することは推奨されません。従って、開発環境において動作確認をしたいとき、ローカル環境に Neptune をエミュレートする必要があります。しかしながら、AWS のモッキングフレームワークの Moto は十分に Neptune をサポートしていません。ローカル環境でグラフデータベース用のテスト基盤を構築することは、開発~テストのサイクルを効率化するためにも、極めて重要な取り組みです。

このような経緯があり、Gremlin Server や pytest-docker を組合わせ、グラフデータベース向けのテスト基盤を構築しました。本記事では、その構成例を示します。

2. テスト基盤の構成とソースコードの例

実行可能なソースコードを以下のリポジトリに配置しています。
https://github.com/ekurerice/2023_ac_example_graphdb_application/tree/main

上記のソースコードについて解説します。上記のリポジトリでは 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 の開催しておりますので、そちらも併せてご確認いただければ幸いです。

大規模言語モデル入門・輪読会 #1

シンプルフォーム株式会社 の全ての求人一覧

SimpleForm 採用お問い合わせフォーム

SimpleForm カジュアル面談お申込みフォーム

Discussion