Closed27

XTDB version 2 を読みながらメモ

しんせいたろうしんせいたろう

Getting started | XTDB

スタンドアロンのDockerイメージで XTDB server をスタート

docker pull ghcr.io/xtdb/xtdb-standalone-ea
  • Error response from daemon: Head "https://ghcr.io/v2/xtdb/xtdb-standalone-ea/manifests/latest": denied: denied エラーが出る場合は sudo を付けてみてください。わたしはそれでうまく行きました。
docker run -tip 3000:3000 ghcr.io/xtdb/xtdb-standalone-ea
Starting XTDB 2.x (pre-alpha) @ "dev-SNAPSHOT" @ 7ef8b33 ...
20:55:49 | INFO  xtdb.server | HTTP server started on port:  3000
20:55:49 | INFO  xtdb.cli | Node started
...
curl http://localhost:3000/status
{"latest-completed-tx":null,"latest-submitted-tx":null}
  • デフォルトでは、Dockerイメージ内のローカルディレクトリにデータが格納される
  • ボリュームを自分のローカルマシンにマウントしたい場合
docker run \
  -tip 3000:3000 \
  -v /path/to/host/dir:/var/lib/xtdb \
  ghcr.io/xtdb/xtdb-standalone-ea
  • サーバーコンテナをストップするときは
    • ctrl + c
しんせいたろうしんせいたろう

clojure で接続

deps.edn

{:mvn/repos {"ossrh-snapshots" {:url "https://s01.oss.sonatype.org/content/repositories/snapshots"}}

 :deps {org.clojure/clojure {:mvn/version "1.11.1"}
        com.xtdb/xtdb-api {:mvn/version "2.0.0-SNAPSHOT"}
        com.xtdb/xtdb-http-client-clj {:mvn/version "2.0.0-SNAPSHOT"}}}
  • REPL 起動
;; deps.edn があるディレクトリで
$ clj
  • 接続
user=> (require '[xtdb.client :as xtc]
         '[xtdb.api :as xt])

(with-open [node (xtc/start-client "http://localhost:3000")]
  (xt/status node))
user=> {:latest-completed-tx nil, :latest-submitted-tx nil}
しんせいたろうしんせいたろう

What is XTDB? | XTDB

  • XTDB は 'bitemporal' で 'dynamic' なリレーショナル・データベース
  • XTDB は system timevarid time の2つの時間を保持し、追跡する
    • system time: DB に挿入もしくは更新された時間
    • varid time: 特定のレコードが、アプリケーションのなかで「有効 / 無効」とみなされるタイミング
      • レコード: 行、レコード、ドキュメントを指す
  • この systemvarid time という2つの時間次元の組み合わせを Bitemporality (バイテンポラリティ) と呼ぶ。
    • (時間次元という言葉は、もう少し理解してから説明する)
  • XTDBにデータを挿入すると、Bitemporalなデータとして格納される
  • 「コラムの追加」「コラムの更新」などは考える必要は無い
  • すべてのデータは時間というバージョニングが自動付加されている
しんせいたろうしんせいたろう
  • time-versioning されたデータは、過去のどの地点に対するヒストリカルデータに対しても簡単にアクセスすることが出来る
  • データベースでのすべての変更を簡単に 監査(audit) することを可能にしている
  • つまり、データの完全な履歴を持っている
  • これは開発者にとって、データがimmutableであることと同じ
  • データの過去のどの修正に対してもアクセスすることが出来る
しんせいたろうしんせいたろう
  • すべてのテーブルはデフォルトで4つのカラムを持つ
    • (詳しくは後で)
  • これは自動でメンテナンスされる
  • 特記されない限り、クエリは「現在」が基準になる
  • 有効ではないヒストリカルデータは、内部でフィルターアウトされる
しんせいたろうしんせいたろう
  • アーキテクチャ
    • カラム型データアーキテクチャ
    • ストレージとコンピューティングを分離
    • ApacheArrowや、オブジェクトストレージ(S3など)で構築
      • 大量の履歴データ保持のための運用コストを下げるため
      • Apache Arrow(アパッチ・アロー)は、異なるデータ処理フレームワークや言語間での効率的なデータの交換を目的としたオープンソースのソフトウェアフレームワーク
しんせいたろうしんせいたろう
  • XTDBクラスタ内のすべてのノードは、 Write-Ahead Log を読み込むレプリカ
  • このLogは一つしか存在せず、すべてのノードに共有されている
  • このデザインの主な利点は、DBがいつ、どのように、なぜ、変更されたのかを正確に示す具体的な情報を保証出来ること
    • スループットに厳しい上限が生じる可能性はある
しんせいたろうしんせいたろう
  • Dynamic relational engine
    • XTDBの columnar エンジンは、
    • 前もってスキーマを設計する必要はない
    • XTDB はデータを documents として表現
    • documentsは、無制限の行 (wide rows) を持つ、疎なテーブルと捉えて良い
    • documentsは、任意の入れ子データを含むことが出来る
    • 入れ子構造のなかで、すべての組み込み型がサポートされている
    • 開発者は、XTDBをドキュメントストアとして扱うことができると同時に、伝統的な正規化されたデータベースとして扱うことも出来る。
しんせいたろうしんせいたろう
  • クエリ言語:2つのクエリ言語が同時に使える
    • SQL:主にデータ分析用
      • SQL:2011 。これは標準SQLとに始めてビットタイム機能を導入したもの。XTDBでもこちらが first-class citizen (どういう意味?何となくわかるけど)
    • XTQL:主に開発者の生産性を上げるため
      • JSON APIを使ってクライアントで記述出来る、コンポーザブルな形式を取れる用に拡張した新しいRDB言語
  • 2つの言語を相互利用することで、分析用途であっても、アプリケーション機能開発であっても、その都度適切なAPIを使用すればよく、犠牲や妥協を強いられない。
しんせいたろうしんせいたろう
  • XTQL
    • データ指向型クエリ言語
    • Datalogと relational algebra に理論的基盤を置く
      • Datalog
        • 論理プログラミング言語(主にDB問い合わせ用言語)
        • 論理述語論理(predicate logic)に基礎
        • Prologなどの論理プログラミング言語の基盤
      • Relational algebra
        • リレーショナルデータベースの理論的基盤
        • 集合論のアイディアに基礎
        • データベースの操作(選択、射影、結合など)を集合論的な操作として表現
        • SQLクエリ言語
    • XTQLはクエリにもトランザクションにも使える
しんせいたろうしんせいたろう
しんせいたろうしんせいたろう
  • YOUTUBE と README のご指南に従って起動を試みる
git clone git@github.com:xtdb/sakila-playground.git
cd sakila-playground/

clj -M:xtdb:nrepl
  • 例外発生
Caused by: java.lang.UnsupportedClassVersionError: xtdb/IArrowWriter has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0
  • わたしの環境
$ cat /etc/os-release 
NAME="Linux Mint"
VERSION="21.2 (Victoria)"

$ clj --version 
Clojure CLI version 1.11.1.1155

$ java -version
openjdk version "11.0.21" 2023-10-17

  • よーくさがすと deps.edn にコメントで
;; needed on JDK16+

と書いてある。(READMEとかにかいてよー。)

  • jdk を更新
# OpenJDKパッケージの利用可能なバージョンを確認する
apt search openjdk

# openjdk-xx-jdk で利用できる一番新しそうなのは、openjdk-21-jdk だったので、
sudo apt install openjdk-21-jdk
sudo apt install openjdk-21-jre # 念の為やってみたけど↑で最新にしてくれてた
  • 再度チュートリアルを起動
clj -M:xtdb:nrepl
rlwrap: warning: could not set locale
warnings can be silenced by the --no-warnings (-n) option
17:37:06 | DEBUG user | Submitting 'actor' table
17:37:07 | DEBUG user | Submitting 'address' table
17:37:07 | DEBUG user | Submitting 'category' table
17:37:07 | DEBUG user | Submitting 'city' table
17:37:07 | DEBUG user | Submitting 'country' table
17:37:07 | DEBUG user | Submitting 'customer' table
17:37:07 | DEBUG user | Submitting 'film' table
17:37:07 | DEBUG user | Submitting 'film_actor' table
17:37:07 | DEBUG user | Submitting 'film_category' table
17:37:07 | DEBUG user | Submitting 'inventory' table
17:37:07 | DEBUG user | Submitting 'language' table
17:37:07 | DEBUG user | Submitting 'payment' table
17:37:07 | DEBUG user | Submitting 'rental' table
17:37:08 | DEBUG user | Submitting 'staff' table
17:37:08 | DEBUG user | Submitting 'store' table
17:37:08 | INFO  user | Sakila playground started!
nREPL server started on port 35497 on host localhost - nrepl://localhost:35497
  • 🤩
しんせいたろうしんせいたろう
  • src/user.clj
  • nREPL に接続して、 user namespace の xt-node を評価すると resources/sakila に入っている テストデータ(どこかのレンタルビデオ屋の情報らしい)が submit される
  • submit-file! 関数で table-name にファイル名がバインドされているのクエリのテーブル名はファイル名を使うと良い
しんせいたろうしんせいたろう
(from :customer [first_name last_name])
  • (from テーブル名 [コラム名, ...])

  • :customer テーブルから、first_name last_name に対応する :first_name :last_name を取得する

  • クエリ実行の文法

(xt/q node クエリをクオートで渡す)
  • 実行
(xt/q xt-node 
        '(from :customer [first_name last_name]))

;;  [{:last-name "BROWN", :first-name "ELIZABETH"} 
;;  {:last-name "YOUNG", :first-name "CYNTHIA"} 
;;  {:last-name "BURKE", :first-name "LYDIA"} ..... 
;;  ]
  • Pipeline
  • pipeline 演算子にわたすことで結果行を変換できる
  • n件取得, コラム指定無し
(xt/q xt-node
      '(-> (from :customer [*])
           (limit 3)))
;;  [{:xt/valid-from #time/zoned-date-time "2023-12-26T10:44:13.640116Z[UTC]", :address-id 9, :email "ELIZABETH.BROWN@sakilacustomer.org", :last-name "BROWN", :store-id 1, :first-name "ELIZABETH", :active true, :xt/id 5} 
;;  {:xt/valid-from #time/zoned-date-time "2023-12-26T10:44:13.640116Z[UTC]", :address-id 32, :email "CYNTHIA.YOUNG@sakilacustomer.org", :last-name "YOUNG", :store-id 1, :first-name "CYNTHIA", :active true, :xt/id 28} 
;;  {:xt/valid-from #time/zoned-date-time "2023-12-26T10:44:13.640116Z[UTC]", :address-id 247, :email "LYDIA.BURKE@sakilacustomer.org", :last-name "BURKE", :store-id 1, :first-name "LYDIA", :active true, :xt/id 243}]
しんせいたろうしんせいたろう

https://docs.xtdb.com/static/learn-xtql-today-with-clojure.html#basic-queries

ここでの from に関する説明だと、

XTQL queries consist of composable operators, optionally combined with a pipeline, but this instance we only have from which is a "source" operator that retrieves a relation from a table stored in XTDB.

  • from は ソース演算子
  • from は xtdb に格納されたテーブルから、「a relation」を取得するために使用する

relation を取得するってなんだ?

しんせいたろうしんせいたろう
しんせいたろうしんせいたろう
  • unify
    • 複数テーブルをJoin
    • たとえば以下のSQLをXTQLで表現すると
SELECT c.customer_name, o.xt$id AS order_id, o.order_value
FROM customers c
  JOIN orders o ON (o.customer_id = c.xt$id)
(-> (unify (from :customers [{:xt/id customer-id} customer-name])
           (from :orders [{:xt/id order-id} customer-id order-value]))
    (return customer-name order-id order-value))
しんせいたろうしんせいたろう

sakilaデータの customeraddress を見ると

customer

{:email "MARY.SMITH@sakilacustomer.org", :first_name "MARY", :xt/valid-from #time/instant "2006-02-14T22:04:36Z", :active true, :last_name "SMITH", :address_id 5, :xt/id 1, :store_id 1}
{:email "PATRICIA.JOHNSON@sakilacustomer.org", :first_name "PATRICIA", :xt/valid-from #time/instant "2006-02-14T22:04:36Z", :active true, :last_name "JOHNSON", :address_id 6, :xt/id 2, :store_id 1}
{:email "LINDA.WILLIAMS@sakilacustomer.org", :first_name "LINDA", :xt/valid-from #time/instant "2006-02-14T22:04:36Z", :active true, :last_name "WILLIAMS", :address_id 7, :xt/id 3, :store_id 1}

address

{:address "47 MySakila Drive", :district "Alberta", :city_id 300, :xt/id 1}
{:address "28 MySQL Boulevard", :district "QLD", :city_id 576, :xt/id 2}
{:address "23 Workhaven Lane", :district "Alberta", :city_id 300, :phone "14033335568", :xt/id 3}

なので address テーブルの :xt/id を address-id としてバインドして、
customer テーブルの address-id で join する

(xt/q xt-node
      '(-> (unify (from :customer [address-id first-name])
                  (from :address [{:xt/id address-id} district address]))
           (return first-name district)
           (order-by district)
           (limit 3)))
[{:first-name "TOM", :district "Abu Dhabi"} 
 {:first-name "LILLIAN", :district "Abu Dhabi"} 
 {:first-name "JORGE", :district "Aceh"} ]
しんせいたろうしんせいたろう
  • Transaction
    • 文法:(xt/submit-tx <node> <tx-ops> <opts>?)
      • <tx-ops> : transaction オペレータのベクタ
      • <opts> :
        • system-time:トランザクションの時間を書き換える
        • default-tz:デフォルトタイムゾーンを上書きする
しんせいたろうしんせいたろう
  • put
    • table にドキュメントをUpsertする
    • (xt/put <table> <document>)
    • 返り値: xtdb.tx.Put
(xt/put
 :person
 {:person/name "James Cameron",
  :person/born #inst "1954-08-16T00:00:00.000-00:00",
  :xt/id 100})
#xt.tx/put {:table-name :person, :doc {:person/name "James Cameron", :person/born #inst "1954-08-16T00:00:00.000-00:00", :xt/id 100}, :valid-from nil, :valid-to nil}

これをベクタにいれてノードに submit する

(def my-node (xtn/start-node {}))

(xt/submit-tx 
 my-node
 [(xt/put :person
        {:person/name "James Cameron",
         :person/born #inst "1954-08-16T00:00:00.000-00:00",
         :xt/id 100})])

;; #xt/tx-key {:tx-id 0, :system-time #time/instant "2023-12-31T08:28:27.838133Z"}

query

(xt/q my-node '(from :person [*]))
;; [{:person/born #time/zoned-date-time "1954-08-16T00:00Z[UTC]", :person/name "James Cameron", :xt/id 100}]

options

  (def my-node (xtn/start-node {}))
  
  (xt/submit-tx
   my-node
   [(xt/put :person
            {:person/name "James Cameron",
             :person/born #inst "1954-08-16T00:00:00.000-00:00",
             :xt/id 100})]
   {:system-time #inst "2018-06-01"}) ;; :default-tz  は説明書もテストも見つからず
;; #xt/tx-key {:tx-id 0, :system-time #time/instant "2018-06-01T00:00:00Z"}
しんせいたろうしんせいたろう
  (xt/submit-tx
   my-node
   (for [doc my-persons]
     (xt/put :persons doc)))
  (xt/submit-tx
   my-node
   (for [doc my-movies]
     (xt/put :movies doc))
(xt/q my-node
      '(-> (from :movies [movie/title movie/year])
           (limit 3)))
;; [#:movie{:year 1986, :title "Aliens"} #:movie{:year 1988, :title "Die Hard"} #:movie{:year 1988, :title "Rambo III"}]
このスクラップは3ヶ月前にクローズされました