![見出し画像](https://assets.st-note.com/production/uploads/images/16476221/rectangle_large_type_2_81bc298c75cd8aafbe41d34eb48278a5.jpeg?width=1200)
Datomic Cloud Analytics を使ってみた
アプリケーションのデータ分析や障害対応、または会計監査の際に、過去の状態を完全に再現できたらいいなと思ったことありませんか?またはデータの関係性を変更する(一対多 → 多対多)際に、もっとシンプルに出来ないかと考えてことはありませんか?
Datomic Cloud はデータの構造、粒度、キャッシュ、それと「時間の巻き戻し」も含め、全てきめ細かく管理できるように設計されたアプリケーションアーキテクチャです。
MySQL などのリレーショナルデータベースは正規化の粒度やインデックスの持ち方を調整してパフォーマンスとデータモデルの表現力をある程度調整できますが、特別設計していない限りデータの更新は基本上書きされて過去の状態は失われていきます。
特定のテーブル群に対して過去の状態をある程度保持することはできてもテーブルの構造変更とともに複雑度が増して管理が大変だったり、毎日のバックアップはできたとしても全データベースをトランザクションが発生した瞬間の状態にを再現するのは出来なかったり、既存のリレーショナルデータベースでは冒頭の要件を満たすには極めて難しいオペレーションが必要です。
そこで Datomic の設計ですが、普段は MVCC のベールの下にに隠れている過去の情報(datum)をスケーラブルなストレージ(S3)に保存したり、リーダーとライターのリソースを分離することで書き込み性能を担保したり、ACID 性能を担保するために DynamoDB の条件付きの書き込みを利用したり、クエリーの性能極限までに高めるために EFS / ローカル SSD / メモリーの三段キャッシュを利用したり、新しい概念を積極的に取り込んでます。
一件よく出来ているように見える Datomic ですが実は分析視点で見るととても残念なことが一つありました。Datomic のスキーマとクエリーは表現力とコードインジェクションを防ぐために一般的な SQL のテキスト構文ではなく、全て EDN(Extensible Data Notation)というデータ構造で表現されていることです。
;; EDN 構文例
(d/pull db
[{:inv/color [:db/ident]}
{:inv/size [:db/ident]}
{:inv/type [:db/ident]}]
[:inv/sku "SKU-42"])
=> #:inv{:color #:db{:ident :blue},
:size #:db{:ident :large},
:type #:db{:ident :dress}}
Clojure に慣れているエンジニアはいいとしても、データサイエンティストや SQL に慣れている分析者からするととっつきにくいイメージがあると思います。
この問題を解決するために、Datomic の開発チームは 2019/09/01 より Datomic を Presto の検索エンジンに繋げることで、R、Python、Tableau、Lookerをはじめ様々な外部ツールから手軽に SQL 構文で Datomic に蓄積されたデータにアクセスすることができるようになりました。
Datomic Cloud 自体は日本語のドキュメントや実際に触れる学習サイトがありますが、Analytics に関してはあまり見かけないので今回は自分の AWS アカウントを使って遊んでみたいと思います。
(2019/12/02 時点で Datomic Cloud の Analytics Support はまだ正式にリリースされていないので Previewという位置づけでお読みください。)
基本構成は公式の Cloud Formation(Split Stack)スクリプトを使って作成した Datomic Cloud システムに公式チュートリアルの在庫データを入れた状態から始まります。
AWS Marketplace に公開されている Solo Cloud Formation テンプレートではなく Storage と Compute を個別に作った理由は、Solo の Access Gateway はコスト節約のためスペックが低すぎて Presto が起動しないようになっているのでご注意ください!
(公式ドキュメントにも書いてある通り Access Gateway インスタンスタイプはせめて t3.small に設定してください)
Datomic のデータ構造は表現力が高く、Key-Value や Graph などの構造を表現できますが、Presto などの RDBMS からアクセスする場合は四角いテーブル構造で表現しなければなりません。Datomic のプリミティブな構造から Analytics 用の構造にマッピングするために少しだけ設定ファイルが必要です。
;; data_catalog.edn
{:tables
{:inv/sku {}
:order/items {}
:item/count {}}
:joins
{:order/items "item"
:item/id "sku"}}
この設定ファイルを Datomic CLI を使って Access Gateway にアップロードしたら、サーバー側の用意は完了です。
あとは Presto の公式 CLI からクエリーを叩いてみたり:
presto:datomic_docs_tutorial> select * from inv;
count | sku | color | type | size | db__id
-------+--------+---------+--------+---------+-------------------
NULL | SKU-28 | :green | :shirt | :xlarge | 4925812092436593
NULL | SKU-53 | :yellow | :pants | :medium | 48915073296498826
NULL | SKU-40 | :blue | :shirt | :large | 43320758134374525
NULL | SKU-52 | :yellow | :shirt | :medium | 10040740184850569
NULL | SKU-47 | :blue | :hat | :xlarge | 13216129765867652
NULL | SKU-49 | :yellow | :pants | :small | 11452513114914950
NULL | SKU-9 | :red | :pants | :large | 67773896736112734
NULL | SKU-2 | :red | :dress | :small | 34247588181966935
NULL | SKU-58 | :yellow | :dress | :large | 52956878040203407
NULL | SKU-50 | :yellow | :dress | :small | 13633944184422535
NULL | SKU-61 | :yellow | :pants | :xlarge | 12354112649691282
NULL | SKU-38 | :blue | :dress | :medium | 343047627866235
NULL | SKU-20 | :green | :shirt | :medium | 5726256557457513
NULL | SKU-56 | :yellow | :shirt | :large | 49917827901030541
NULL | SKU-59 | :yellow | :hat | :large | 49948614226608272
NULL | SKU-18 | :green | :dress | :small | 10753223719649383
NULL | SKU-30 | :green | :dress | :xlarge | 14249670695977075
NULL | SKU-51 | :yellow | :hat | :small | 1895558046285960
NULL | SKU-36 | :blue | :shirt | :medium | 52165229668204665
NULL | SKU-0 | :red | :shirt | :small | 58863454504616021
NULL | SKU-11 | :red | :hat | :large | 61299972271767648
Query 20191130_150215_00020_xu4tf, FINISHED, 1 node
Splits: 17 total, 17 done (100.00%)
0:00 [64 rows, 64B] [223 rows/s, 223B/s]
BI ツール(今回は Looker)を繋げてみたり:
# inventory.view.lmkl
view: inventory {
sql_table_name: datomic_docs_tutorial.inv ;;
dimension: entity_id {
description: "The Datomic entity ID"
type: number
primary_key: yes
sql: ${TABLE}.db__id ;;
}
dimension: sku {
description: ":db.unique/identity"
type: string
sql: ${TABLE}.sku ;;
}
dimension: color {
type: string
sql: ${TABLE}.color ;;
}
dimension: type {
type: string
sql: ${TABLE}.type ;;
}
dimension: size {
type: string
sql: ${TABLE}.size ;;
}
measure: measure_item_count {
type: count
sql: ${entity_id} ;;
}
}
いろいろやってみるのも面白いかと思います。これプロダクションデータベースに直接クエリーを投げてない?と思う方はご安心ください。Analytics はあくまでも read only なのでデータの改竄は不可能です。
あと Datomic は仕様上プリケーション用のリソースと分析用のリソースを Query Group で分離できるので Analytics 側で大きめのクエリーを実行してもアプリケーション側には影響しません。むしろ Query Group を分けたことによってローカルのキャッシュがクエリーの特性に特化したデータを保持することによって検索のパフォーマンスは上昇すると思います。
現時点で繋げ方が記載されているツールは以下の通りですが、ほぼ Presto なので JDBC が使える環境は大体繋がると思います。
● R
● Python
● Jupyter Notebook
● Metabase
以上現時点での Datomic Cloud Analytics の設定から機能を少しだけかじってみました。確かに Datomic はデータの表現力、アプリケーションとしてのパフォーマンス、分析をする際の解像度などバランスよく設計されているアーキテクチャですが、データ構造を設計から分析する視点から見たとき、これから徐々に進化して欲しいポイントをまとめました。
● 日付や時間などを表現するためのデータ構造とインデックスがない
● [1, 5) や [10:00, 18:00) などの範囲を表現するためのインデックスがない
● 地理的な座標や形(ポリゴン)を表現するためのデータ構造とインデックス
● Analytics はまだ basis-t みたいな絞り込み条件に対応していない
以上の課題は全て公式の Feature Request があるのでよかったら Like をしてください。