Rust+OxigraphでサーバレスSPARQLエンドポイント構築

4 min read読了の目安(約3900字

これまで、AWS LambdaとAPI Gateway上でSPARQLエンドポイントを動かす試みをいくつか行ってきました。

https://qiita.com/uedayou/items/bdf7a802e27fe330044e

https://qiita.com/uedayou/items/18f968e3d07b5aa2db50

今回は、Rust製のOxigraphを使って、AWSサーバレス上で動くプログラムを作ってみました。

https://github.com/uedayou/oxigraph-sparql-api-serverless

ここでは、oxigraph-sparql-api-serverlessを紹介します。

SPARQLとは?

SPARQLはRDF用のクエリ言語です。SPARQLクエリで検索できるエンドポイントは、DBpediaWikidataなど、すでにさまざまなものが公開されています。
詳しくは以下を参照してください。

https://qiita.com/uedayou/items/9e4c6029a2cb6b76de9f

使い方

oxigraph-sparql-api-serverlessは、AWS Lambda関数部分をRustで実装しています。Dockerを使って Amazon Linux 2上でビルドし、AWS SAM CLIでAWSにデプロイします。

ビルド

SPARQLエンドポイントで検索したいRDFファイルをTurtle形式で準備してください。
dump.ttlにリネームして rdf/ディレクトリに上書きします。

以下のDockerコマンドでRustプログラムをビルドします。

$ cd oxigraph-sparql-api-serverless/
$ docker image build -t oxigraph-build -f Dockerfile.build .
$ docker container run --rm -v $PWD:/code -v $HOME/.cargo/registry:/root/.cargo/registry -v $HOME/.cargo/git:/root/.cargo/git oxigraph-build

ビルドが完了すると lambda.zip が生成されます。

デプロイ

AWS SDKやAWS SAM CLIのインストール、設定後以下を実行するとデプロイできます。

$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket {デプロイ用S3バケット}
$ sam deploy --template-file packaged.yaml --stack-name {スタック名} --capabilities CAPABILITY_IAM

デプロイ後に表示されるURL(例えば:https://${ID}.execute-api.${Region}.amazonaws.com/Prod/)をコピーして以下のようにアクセスすれば、SPARQLエンドポイントとして動作します。

https://${ID}.execute-api.${Region}.amazonaws.com/Prod/sparql?query={URLエンコードされたSPARQLクエリ}&format={json(デフォルト) か xml}

パフォーマンス

検索速度

過去作成した、Node.js/Quadstore版Java/Apache Jena版と同様に検索速度を計測しました。

上記2記事では、「図書館及び関連組織のための国際標準識別子(ISIL)」試行版LODで公開されていた2020年版のRDFファイル(計160491トリプル)と以下の3種類のSPARQLクエリを使いました。
今回も同じ条件で行いました。

(1) トリプルを100件取得

select * where {?s ?p ?o} limit 100

(2) 全トリプル数を取得

select (count(*) as ?count) where {?s ?p ?o}

(3) filterを使って文字列の絞り込み

prefix schema: <http://schema.org/>
prefix org:   <http://www.w3.org/ns/org#>
prefix dbpedia: <http://dbpedia.org/ontology/>

select * where {
  ?uri dbpedia:originalName ?name;
  org:hasSite/org:siteAddress/schema:addressRegion ?pref.
  filter( regex(?pref, "東京") )
}
limit 10

結果は以下の通りとなりました。

実装別 (1) (2) (3)
Rust/Oxigraph 88ms 279ms 80ms
Node.js/Quadstore 220ms 12910ms 12910ms
Java/Apache Jena 141ms 579ms 104ms

Node.js/Quadstore と Java/Apache Jena の結果も比較のためにのせました。すべてにおいてRust/Oxigraph が最も早い結果となりました。

初回起動時間

AWS Lambda には所謂コールドスタートと呼ばれる、最初にプログラムを実行するときに初期化に時間がかかることがあるという問題があります。

Rust/Oxigraph と Node.js/Quadstore、Java/Apache Jena の3つでAWS Lambda で実行したときの初期所要時間(Init Duration)を調べました。

実装別 初期所要時間 メモリサイズ
Rust/Oxigraph 81.96 ms 1024MB
Node.js/Quadstore 383.82 ms 1024MB
Java/Apache Jena 3690.75 ms 2048MB

※ 初期所要時間は一定ではなく、場合によってはこれよりももっと時間がかかるときがあります。

ここでも Rust/Oxigraph が最も早いという結果になりました。

まとめ

これまでは、Java/Apache Jena版がコールドスタート問題に目をつぶれば一番安定したパフォーマンスが期待できましたが、Rust/Oxigraph版はコールドスタート問題もほぼ無視できるレベルで早く、15万トリプルレベルであれば検索速度も安定しているようなので十分使えると思いました。

ただ、100万トリプル以上のRDFファイルでも試したところ、フルスキャンがかかりそうなクエリ(例えば、filterとかorder byとか)の検索速度はRust/Oxigraph版(10秒以上)よりもJava/Apache Jena版(1秒未満)が早かったので、場合によっては Java/Apache Jena版 のほうが有利な場合がありそうです。

Oxigraph は2021年3月時点ではまだバージョンが若く、最適化はまだこれからとのことなので、今後に大きく期待が持てそうです。

Rust/Oxigraph版の検索は以下で試すことができます。

https://uedayou.net/isillod/