AWSサーバレスでSPARQLエンドポイントを構築(Node.js+quadstore編)

※ 2020/08/22 追記
本記事の第二弾[AWSサーバレスでSPARQLエンドポイントを構築(Java+Apache Jena編)](https://qiita.com/uedayou/items/18f968e3d07b5aa2db50)の公開によりタイトルを変更しました。AWS環境でサーバレスSPARQLエンドポイントを構築したい方は、第二弾のほうがお勧めです。

---

個人的に[SPARQLエンドポイント](https://ja.wikipedia.org/wiki/SPARQL)を作るときに、運用やメンテナンスが楽になればということで、AWSのマネージドサービスを活用できないかと検討してみました。

AWSというと[Amazon Neptune](https://aws.amazon.com/jp/neptune/)がありますが、最小構成でも年間10万近くかかるようなので、AWS Lambda を使ったサーバレス構成で低コストでメンテナンスフリーなSPARQLエンドポイントの構築に挑戦してみました。

結論から言うと、ちゃんと動きましたが、期待してたよりもうまくいきませんでした。

一応今回使ったコードは以下で公開していますが、利用される場合は以下を最後まで読まれることをお勧めします。

<https://github.com/uedayou/quadstore-server-on-aws-serverless>

## 環境

- [AWS Lambda](https://aws.amazon.com/jp/lambda/)
- [Amazon API Gateway](https://aws.amazon.com/jp/api-gateway/)
- [Amazon Cloudfront](https://aws.amazon.com/jp/cloudfront/)

Lambda と API Gateway は[AWS SAM](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/what-is-sam.html)でデプロイするようにしました。
サーバレス環境の設定をあらかじめ指定しておけるのでデプロイが楽ですし、ある程度の設定もコード上で細かく変更できていいですね。

SPARQLエンドポイントサーバ自体のコードは、[node-quadstore](https://github.com/beautifulinteractions/node-quadstore) と [levelDB](https://github.com/google/leveldb) を使いました。

あらかじめRDFファイルを使ってlevelDBファイルを生成しておき、それを利用してSPARQLクエリで検索結果を得るような形です。

```
cd quadstore-server-on-aws-serverless/quadstore-server-lambda
npm install
npm run build:db -- ../sample/isillod.ttl
```

## パフォーマンス

この環境でどれくらい使えるか、トリプル数と以下の3つのクエリ毎の検索時間を計ってみました。
Lambdaのメモリは1024MB、タイムアウトは30秒(API Gatewayが30秒でタイムアウトするため)で設定しています。
データセットは[「図書館及び関連組織のための国際標準識別子（ISIL）」試行版LOD](https://www.ndl.go.jp/jp/dlib/standards/opendataset/index.html)を使いました。
上記サイトでは Turtleファイルを分割して公開しています。これらを1つずつ追加して作成したDBファイル毎に計測しています。


(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) 
--- | --- | --- | ---
21,788 | 0.192 秒 | 2.85 秒 | 5.20 秒
42,585 | 0.186 秒 | 4.35 秒 | 7.53 秒
63,448 | 0.193 秒 | 5.58 秒 | 11.08 秒
84,587 | 0.181 秒 | 9.74 秒 | 14.02 秒
104,826 | 0.163 秒 | 11.47 秒 | 16.19 秒
124,718 | 0.225 秒 | 14.19 秒 | 21.83 秒
144,669 | 0.244 秒 | 12.64 秒 | 12.11 秒
160,491 | 0.220 秒 | 12.91 秒 | 12.91 秒

若干ゆれがありますが、概ねトリプル数の増加とともに検索時間が増えてますが、(2)、(3) はその増え方がかなり大きく、トリプル数取得するだけで10秒もかかるのはどうかと思いました。API Gatewayの仕様で30秒でタイムアウトするので、これ以上のトリプル数だと結果が得られずタイムアウトする可能性が高いです。

どうやら全スキャンがかかるようなクエリの実行のより多くの時間がかかっているっぽい感じがします。例えば、(1) に `order by` 追加するだけで極端に時間がかかるようになりました。ちなみに、Lambdaはメモリサイズを増やすとCPUの性能もアップするらしいですが、最大の3008MBにしても大きな差はなかったです。

個人的な用途でいろいろ目をつぶるにしても、10万トリプルぐらいが限界かなと思います。

## まとめ

- (個人的には)SPARQLエンドポイント構築は簡単になった
- ちゃんとSPARQLで検索ができる
- トリプル数とクエリによっては実行が遅い、最悪タイムアウトする
- トリプル数が少ないデータセットなら一応運用は可能

今回使っているRDFストア[node-quadstore](https://github.com/beautifulinteractions/node-quadstore)の開発中のブランチを見てみるとSPARQLクエリ実行の高速化も現在対応しているっぽい？ので、将来的に結構使えるものになるかもしれません。ちょっとこの方法を試すには時期が早かったのかも...

今後この方法とは違う方法でも、AWSサーバレスでSPARQLエンドポイント構築挑戦してみたいと思います。