1 ポイント 投稿者 GN⁺ 2024-05-17 | 1件のコメント | WhatsAppで共有
  • Datomic Pro 1.0.7075 は、テストではトランザクション間の安全性がドキュメント上の主張より強く見えたが、トランザクション内部のセマンティクスは一般的な直列実行モデルと大きく異なっていた
  • すべてのテスト履歴は Serializable に見え、単一の peer セッションは Strong Session Serializable、書き込みと d/sync 読み取りは Strong Serializable に近かった
  • Datomic の add、retract、transaction function はトランザクション内で順番に累積実行されるのではなく、各関数が 開始時点の DB 状態のみを見る方式で動作する
  • 同じトランザクションで approvedeny のように、個別には安全な transaction function を組み合わせると、合成結果が 不変条件違反を生む可能性がある
  • 複数の transaction function を 1 つのトランザクションに入れるときは、read set と write set の関係を確認し、entity predicate、attribute predicate、entity spec のような 明示的制約を併用する必要がある

Datomic Pro のモデルとアーキテクチャ

  • Datomic は、時間の概念を明示的にモデル化する Entity-Attribute-Value OLTP データベースである
    • 特定時点の DB 状態は、[entity, attribute, value] 形式の datom 集合で表現される
    • 各 datom には、どのトランザクションが追加または撤回したかも保持される
    • datom 全体は [entity, attribute, value, transaction, asserted-or-retracted?] 形式の 5 タプルである
  • Datomic は temporal database であるため、現在だけでなく過去の論理時刻や wall-clock 時刻基準のスナップショットも要求できる
    • 履歴全体ビューを使って、過去に特定の事実が存在したかどうかも問い合わせられる
    • 問い合わせ方式として Datalog スタイル API、グラフ走査 API、ODM スタイルの Entity 型を提供する
  • Datomic Pro はユーザーが自分で運用できるバージョンであり、Datomic Cloud は AWS 上で動作し、アーキテクチャが一部異なる
  • Datomic Pro は複数のコンポーネントが協調する構成である
    • Transactor は書き込みトランザクションの実行、インデックス維持、ストレージへの記録を担当する
    • Peer は JVM ライブラリを組み込んだ厚いクライアントで、トランザクション送信、ストレージ対象の読み取りクエリ、キャッシュを行う
    • 他言語アプリケーション向けに、thin client と peer server ベースの client-server モデルも提供される
  • 内部的に Datomic は各トランザクションを時系列の log に append し、entity・attribute・value・time の組み合わせで整列された 4 つのインデックスを維持する
    • log とインデックスは、Cassandra や DynamoDB のようなストレージに永続的かつ不変なツリーとして保存される
    • ツリーノードが不変であるため、backing storage は eventual consistency の保証だけでよい
    • コミット時には transactor が新しい不変ツリーノードを保存した後、ルートポインタを compare-and-set(CaS) で前進させ、この CaS には Sequential consistency が必要である
  • Sequential CaS はトランザクションのグローバル順序を保証するが、書き込みスループットを単一 transactor の速度に縛り付ける
    • Datomic は通常、同時に 1 つの active transactor のみを置き、障害耐性のために複数の transactor を配備する
    • peer はストレージと transactor に直接接続し、それぞれ単調増加するルートポインタのコピーを保持する
    • 読み取りでは不変ツリーノードをキャッシュできるため、peer 数を増やすとほぼ線形の読み取りスケールが可能になる

Datomic のトランザクションモデル

  • Datomic は一般的な OLTP データベースのような interactive transaction を提供しない
    • トランザクションを開始し、演算結果を受け取り、次の演算を送信し、最後にコミットするモデルではない
    • stored procedure に似た transaction function はあるが、呼び出し元に任意の値を返すことはできない
  • 読み取り経路と書き込み経路は厳密に分離されている
    • db は peer が認識している最新の DB 状態を返す
    • d/sync は、すべての peer 基準での最新状態、または特定時刻以降の状態を得るために transactor と同期する
    • d/as-of は過去時点の DB 状態を得る
    • DB 状態は不変であるため、同じ状態に対する複数の問い合わせは正確に同じ論理時刻で実行される
  • 書き込みトランザクションは ordered list 形式の operation で表現される
    • 例として :db/add:db/retractdb/cas、ユーザー定義 transaction function 呼び出しを含む
    • transaction function は、トランザクション開始時点の DB 状態と引数を受け取り、新しい operation 集合を返す
    • 関数呼び出しは assertion と retraction だけが残るまで再帰的に展開される
  • transaction function は内部で読み取りを行って条件付き書き込みを決定できるが、読み取り結果や任意の情報を transact 呼び出し元へ直接返すことはない
    • transact はトランザクション直前の DB 状態、トランザクション結果の DB 状態、展開された datom 集合を返す
    • 呼び出し元は pre-state と post-state を使って、条件付き書き込みが発生したかどうかを判断できる
  • Datomic は、安価で受け渡し可能な DB スナップショットを中心に問題を解くよう設計されたモデルである
    • Nubank は Datomic の現在の開発元であり、金融サービスを約 9,400 万人のユーザーに提供し、1 日平均 23 億件のユーザートランザクションを処理している
    • Nubank のほぼすべての製品は Datomic を system of record として使用している

一貫性の主張とテスト設計

  • Datomicの文書はACIDトランザクションを主張しており、トランザクションは単一のatomic writeとしてストレージに記録され、各peerは特定時点までに完了したすべてのトランザクションを全順序で観測するとしている
    • クライアントへのacknowledgement前に、トランザクションはdurable storageにflushされる
  • 2024年1月初旬に分析を開始した時点の文書では、書き込みトランザクションがSerializableであると非公式に主張していた
    • 文書ではDatomicを「single-writer」システムとも説明していたが、Jepsenはこの説明は2つの理由で不正確だと見ている
    • 障害耐性のために複数のtransactorを運用でき、障害検知器は完全ではありえないため、複数のtransactorが同時にactiveだと判断される時間帯が生じうる
    • 単一のtransactorしかない場合でも、ネットワーク遅延によりストレージメッセージが他のtransactorメッセージとinterleaveする可能性がある
  • Datomicの安全性は「single-writer」の論理ではなく、ストレージCaS operationのSequential consistencyから生まれると評価されている
    • 複数のconcurrent transactorがあっても、CaSが安全性を提供しなければならない
  • テストはJepsen testing libraryで作成したDatomic test suiteを使用した
    • Datomic Pro 1.0.7075をDebian Bookwormノードクラスタにインストールした
    • ストレージにはAWS DynamoDB tableを使用した
    • 2つのノードはtransactorを実行し、残りのノードはpeerを実行した
  • peerはDatomic peer libraryを使う小さなClojureプログラムで、テスト操作のためのHTTP APIを公開した
    • テストではd/dbを使うstale read可能モードと、d/syncで最新性を保証するモードの両方を実行した
  • fault injectionはtransactorとpeerの両方に適用された
    • プロセスpause、crash、clock errorを注入した
    • transactor・peer間、およびノード・ストレージ間のネットワークパーティションを作成した
    • Datomic garbage collectionも要求した
  • transactorはストレージとの安定した接続を維持できないと自ら終了する
    • AWS外のノードでデフォルトの5秒timeoutを使うと、通常のネットワーク変動だけでも数分おきに終了した
    • EC2のテスト環境でも、1秒timeoutでは10~20分おきに終了した
    • Datomicはoperatorがsupervisor daemonでtransactorを再起動することを推奨しており、テストではRestart=on-failure設定のsystemd serviceを使用した

4つのワークロード

  • List Append

    • List AppendワークロードはElle transaction checkerとともに使用される
    • 論理的には整数要素のリスト群を扱い、各リストは整数primary keyで識別される
    • クライアントは、リストの読み取りまたは一意な要素のappendから成るランダムなトランザクションを実行する
    • Elleはaborted read、intermediate read、internal consistency違反、要素順序の不一致を検査し、dependency graphのcycleを見つけて一貫性モデル違反を判定する
    • Datomicではリストは1つのentityと2つの属性でエンコードされる
    • append/keyはprimary keyの役割を果たす
    • append/elementsは多値属性としてリストの整数要素を保存する
    • 多値属性は順序のないsetであるため、Jepsenは各datomのtransaction timestampで要素を並べ替え、Elleが必要とする順序を得る
    • mixed read-write transactionがないという制約は、transaction functionとpre-state計算で回避する
    • 書き込みはtransaction functionで実行する
    • transactが返したpre-stateを用いて、トランザクション内部の読み取りが何を見たはずかを計算する
    • 同じ関数をtransactで1回、peerでpre-stateベースの読み取り補完のためにもう1回実行する
  • List Append with CaS

    • List Append with CaSワークロードはdb/casパターンを使用する
    • ユーザーはd/dbで現在の状態を読み取り、たとえば値が4のときだけ5に変える[:db/cas 123 :counter/value 4 5]を送信できる
    • すべての書き込みにdb/casを使えば、logicalな「user transaction」の上にad hocなSnapshot Isolationを構築できる
    • このワークロードでは、リストを多値ではなくsingle-valuedなcomma-separated stringとして保存する
    • トランザクション開始時に読み取り、ローカルで読み取り・書き込みを適用した後、読み取り後に変更されていないことを保証するCaSトランザクションを構成する
  • Internal

    • Internalワークロードはトランザクション内部の一貫性を直接測定する
    • 同じentity attributeに1をassertした後に2をassertする場合
    • 同じトランザクションでfactをassertしてretractする場合
    • 値をassertした後にCaSで変更しようとする場合
    • 1→2、2→3のように複数のCaSを行う場合
    • entity作成後にlookup refで修正する場合
    • transaction functionで値を2回incrementしようとする場合を含む
  • Grant

    • Grantワークロードはtransaction functionが関数の不変条件を保持するかを確認する
    • grantはcreated-atapproved-atdenied-atの3つの属性を持つ単一entityとしてエンコードされる
    • grantは同時にapprovedとdeniedの状態になってはならない
    • approvedeny関数はまずgrantがすでにapprovedまたはdeniedかを確認し、必要ならabortする
    • 複数のtransaction boundaryの組み合わせで、grantが同時にapprovedかつdeniedになるかを検査する

テスト結果: トランザクション間の安全性は強固に見える

  • JepsenはDatomicの中核的な安全性の主張に反する挙動を発見できなかった
    • トランザクションは全順序で適用されたように見えた
    • その順序は各peerでのlocal operation orderと一致した
  • 書き込みトランザクションのみに制限した履歴と、読み取りで(d/sync conn)を使用した履歴は、real-time orderと一致していた
    • JepsenはこれをStrict Serializableに見えると判断している
  • 単一peerにセッションを束縛して解釈すると、DatomicはStrong Session Serializabilityを保証しているように見える
    • トランザクション履歴は、ある全順序で実行された履歴と区別がつかない
    • そのorderは各peerで観測された順序と一致する
  • d/dbは非同期に更新されるDBのコピーを返すため、stale readが起こりうる
    • Datomicの文書でも、peer readが最近commitされたトランザクションの一部を観測できない可能性があると明記している
    • d/syncはtransactorと同期してstale readを防ぐ
  • 実験的検証には限界が残る
    • バグの存在は証明できても、その不在は証明できない
    • Datomicが依存するストレージシステムのcorrectness errorは、Datomicの保証違反につながりうる
    • DynamoDB上のDatomicは、DynamoDBのcompare-and-set operationと同程度に安全である

トランザクション内部の意味論: 順序ではなく並行性

  • ほとんどのデータベースと主要なトランザクション分離の形式化は、トランザクション内部で 逐次実行意味論 を提供する
    • set x = 1; read x; であれば、read は一般に 1 を見る
    • Adya、Cerone・Bernardi・Gotsman、Crooks・Alvisi・Pu・Clement などの形式化は、トランザクション内部の operation 順序と「先行する write を後続の read が観測する」という性質を明示している
  • Datomic の transaction request は ordered list だが、実行はその順序を保持しない
    • add、retract、transaction function は互いに同時に実行されるかのように動作する
    • transaction function は常にトランザクション開始時点の DB 状態を観測する
    • 先行する assertion、retraction、transaction function の効果は見えない
  • 現在の値が 0 の entity に同じ CaS を 2 回入れると、Datomic では両方とも開始状態 0 を見て成功する
[[:db/cas 123 :internal/value 0 1]
 [:db/cas 123 :internal/value 0 1]]
  • 逐次モデルであれば、最初の CaS が値を 1 に変え、2 番目の CaS は失敗するはずである
    • Datomic では 2 つの CaS が重複 assertion を作り、最終値は 1 になる
  • 2 回の increment transaction function も逐次モデルとは異なる結果を生む
[['internal/increment "x"]
 ['internal/increment "x"]]
  • 開始値が 0 なら、逐次モデルの結果は 2 である
  • Datomic では 2 つの関数がどちらも開始状態 0 を見て、最終値は 1 になる
  • transaction function は先行する assertion も見えない
[[:db/add id-of-x :internal/value 1]
 ['internal/increment "x"]]
  • Datomic では最終値は 2 ではなく 1 になる
  • lookup ref もトランザクション開始時点の DB 状態を使う
    • 同じトランザクションで entity を追加したあと、その entity を lookup ref で参照することはできない
    • この場合は Unable to resolve entity エラーで abort される

競合検出と pseudo write skew

  • Datomic は同じトランザクション内で単一 cardinality 属性に異なる値を assert すると、:db.error/datoms-conflict で abort する
    • 開始値 0 に対して値 2 を assert し、同時に increment function が値 1 の assertion を作ると競合する
    • この競合検出は、transaction function の誤った合成から生じる多くの意外な結果を防ぐ可能性がある
  • write set が異なる [entity, attribute] ペアであれば、競合検出だけで不変条件を守るのは難しい
    • grant ワークロードがこの状況を示している
    • approvedeny はそれぞれ、grant がまだ approved でも denied でもないことを確認したうえで、異なる属性を追加する
  • 異なるトランザクションで approvedeny を呼び出す場合、Datomic の Serializable トランザクションは不変条件を保証する
    • しかし同じトランザクション内で両方を一緒に呼び出すと、両方とも開始状態を見て成功する
[['grant/approve id]
 ['grant/deny id]]
  • 結果として grant は approved-atdenied-at の両方を持つことになる
    • 「grant は同時に approved と denied になってはならない」という不変条件が破られる
    • Datomic の in-transaction conflict checker は、2 つの関数が異なる属性に assertion を作ったため、これを防げない
  • この現象は Berenson らの Write Skew に似ている
    • 2 つの関数が互いの効果を見られないため、read-write anti-dependency cycle が生じる
    • transaction function をトランザクションとして見るなら、Repeatable Read と Serializability が禁止する G2-item anomaly に類似する
  • Datomic と Nubank はこの動作をバグではなく、Datomic の想定された動作と見なしている
    • Nubank は Datomic の concurrent intra-transaction semantics を維持する計画である

entity predicate による不変条件の補強

  • Datomic は型、uniqueness、特定の attribute predicate、entity predicate のような制約メカニズムを提供する
    • entity predicate は、すべてのトランザクション効果が適用された candidate DB 状態と entity ID を受け取り、コミットを許可するかどうかを true または false で返す
    • 名前は entity predicate だが、DB 全体の状態にアクセスできるため、特定の entity を超えたグローバル制約も表現できる
  • grant の例では、valid-grant? predicate によって approved-atdenied-at が同時に存在しないようにできる
(defn valid-grant?
  [db eid]
  (let [{:grant/keys [approved-at denied-at]}
        (d/pull db '[:grant/approved-at
                     :grant/denied-at]
                   eid)]
   (not (and approved-at denied-at))))
  • schema には entity spec を追加してこの predicate を参照する
    • entity spec と結び付いた entity predicate は、すべてのトランザクションに自動適用されるわけではない
    • Datomic は entity spec の適用可否を domain decision と見ており、各トランザクションが明示的に要求すべきだという立場である
  • approvedeny 関数は、属性を追加したあと :db/ensure virtual datom で entity spec の適用を要求できる
(defn approve
  [db id]
  [[:db/add id :grant/approved-at (Date.)]
   [:db/add id :db/ensure :grant/valid?]])
  • この entity spec を使うと、同じトランザクションで approve と deny を同時に試みたときに entity predicate エラーが発生し、不変条件が保たれる
    • エラーには :db.error/entity-pred:db.error/pred-return false が含まれる

ドキュメント変更とユーザーへの勧告

  • Datomic は Jepsen との協業後、ドキュメントを大幅に改訂
    • transaction safety documentation は、Datomic が実際に提供しているとみられる、より強い安全性を反映
    • グローバルな Serializability、peer ごとの monotonicity、書き込みまたは sync を用いた読み取りに対する Strict Serializability を明記
    • 「single-writer」という議論は安全性ドキュメントから削除
  • transaction syntax and semantics ドキュメントは、transaction request の構造、map form と transaction function の展開規則、transaction 適用過程を包括的に扱う
  • transaction functions ドキュメントも改訂
    • consistency を保証する複数のメカニズム、関数の生成と呼び出し、組み込み関数の動作を説明
    • transaction function が「atomically analyze and transform database values」できる、あるいは「atomic read-modify-write processing」を保証するという表現は削除
  • Datomic は d/transact に渡されるデータ構造を、今後は「transaction」ではなく transaction request と呼ぶ方針
    • その要素は「statements」や「operations」ではなく「data」と呼ぶ方針
    • [:db/add ...][:db/retract ...] はそれぞれ assertion request と retraction request
    • これは実際の assertion datom と、transaction request 内の不完全な assertion request を区別するのに役立つ
  • ユーザーに必要な注意点は明確
    • Datomic のトランザクション間 Serializability は信頼できる
    • トランザクション内部の concurrent execution semantics は珍しい選択であるため、同一トランザクション内で複数の transaction function を呼び出す際は注意が必要
    • 特に read set が重なり、write set が分離している場合に注意すべき
    • 複数の increment が、気づかれないまま 1 つの update に collapse される可能性がある
    • attribute predicate と entity spec は利用できるが、entity spec は必要なすべてのトランザクションで明示的に要求しなければならない
  • 運用面でも transactor の再起動とネットワーク変動を考慮する必要がある
    • Datomic transactor は、数分間ストレージと通信できないと自動終了する
    • Jepsen は、ネットワーク変動への耐性を高めるため、transactor に retry loop を追加することを勧告

制約と今後の研究課題

  • 今回のテストには評価範囲外の項目が残っている
    • excision と historical query は評価していない
    • Datomic client library も調査していないが、Jepsen はその動作がテスト用 peer に近い可能性が高いとみている
    • ストレージエンジンは DynamoDB 1 つだけを使用
    • Datomic Cloud も評価しておらず、Datomic Cloud はやや異なるアーキテクチャを採用している
  • Jepsen は、トランザクション間 Serializability とトランザクション内部 concurrent semantics を同時に提供するシステムや形式化を、ほとんど知らないと述べている
  • Datomic のモデルは複数の研究課題を生み出している
    • Datomic transaction を、伝統的な transaction の双対、あるいは「co-transaction」モデルとして見なせるか
    • このモデルの長所と短所を static analysis、runtime check、API extension によって緩和できるか
    • 実際のユーザーが不変条件を破る transaction を書いてしまう可能性がどの程度あるか
  • 比較対象として、temporal Datalog の研究プロジェクトである Alvaro’s DedalusFauna が言及されている
    • Dedalus も Datomic と同様に transaction が「all at once」で発生する
    • Fauna は temporal database でありながら Strong Serializability までサポートし、Datomic と異なりトランザクション内部で serial execution と incremental side effect を提供しているように見える
  • Datomic の end-of-transaction conflict checker と Snapshot Isolation の first-committer-wins ルールの類似性も、研究機会として残されている
    • Snapshot Isolation 文献のどの部分を Datomic に適用できるか
    • Datomic transaction 内の cycle はどの anti-dependency edge として表現されるのか
    • lost update、Fractured Read、read-only transaction anomaly、Long Fork のような現象に対応する、内部意味論上の analogue があるのかという問いが残る
  • CALM theorem とのつながりも、さらに検討できる
    • 論理的に monotonic な transaction function は、同一 Datomic transaction 内で安全に組み合わせられるか
    • negation のない Datalog program が、この実行モデルでも安全かどうかを研究できる

1件のコメント

 
GN⁺ 2024-05-17
Hacker News のコメント
  • この作業が進むのを間近で見ていたが、議論の過程を見るのは本当に興味深かった。
    Jepsen が致命的なバグを見つけられなかったのも驚きだったし、ドキュメントと意図された特異な挙動を明確にしただけでも非常に有用な成果だった。
    Datomicで銀行を運営していることを考えると、信頼を築く訓練として十分に価値があった。

    • Rich Hickey が設計したデータベースだということを考えれば、結果はそれほど驚くものではない。
      本当に素晴らしい記事で、自分はかなり賢いと感じるたびに Jepsen分析 を読むと謙虚になるのにちょうどいい。
    • どの銀行なのか聞いてもいいですか?
    • 「Jepsenが致命的なバグを見つけられなかったのも驚きだった」という部分について、報告書には「バグの存在は証明できるが、バグの不在 は証明できない」と書かれている。
    • その上で銀行を運営する前に、こうした検証を自分たちでやってはいなかったのですか?
  • Jepsenレポートを深く読んだのは今回が初めてだが、Datomicのトランザクション内部動作を明確に説明している部分が気に入った。
    DatomicのトランザクションとSQLデータベースのトランザクションの違いを、自分がどれほど理解していなかったかにも気づいた。
    特に「Datomicは以前、d/transact に渡されるデータ構造を『transaction』と呼び、その要素を『statements』や『operations』と呼んでいた。今後はこの構造を『transaction request』、その要素を『data』と呼ぶことにしたい」という段落が目を引いた。
    これが datomic.api 名前空間の d/transact-async と関連機能にとって何を意味するのか気になる。
    ほぼ1年間Datomicを使っていなかったが、多くのことが変わったように見える。

    • Jepsenテストの結果として Datomicソフトウェア に変更は必要なかった。
      datomic.api のすべての機能はそのままだ。
  • 本当に良いデータベースについての、素晴らしく詳しいレポートだ。
    ドキュメントが明確になり、更新されるのもとても喜ばしい。
    付け加えると、Appleが FoundationDB に対するJepsen分析の費用を出してくれたら本当にうれしい。
    Aphyrが「彼らのテストのほうがおそらく優れている」と言ったのは知っているが、Jepsenが実際にFoundationDBで問題を見つけられなければ、それはまた一つの優れたデータベースである強い根拠になるだろう。

    • Aphyrの生計を奪いたい気持ちはまったくないが、十分に動機のあるコントリビューターが Jepsenテスト を自作することが特に不可能な理由があるのか、それとも「GoFundMeのような方式」でも賄いきれないほど高額なのか気になる。
      この分野に詳しいわけではないが、「$fooが費用を出してくれたら」という話を聞くと興味をそそられる。
      資本はあり余っているのに、Appleが何かしてくれるのを待つのは、私の経験上時間がかかる。
  • Jepsen が不変条件違反につながる明確な状況を見つけたのに、Datomic 側の対応はドキュメントの明確化だけのように見えて印象的だった
    結局、Datomic チームはこうした違反が起きることは受け入れるが、気にしないということなのか?
    記事では「Datomic の観点では、grant ワークロードの不変条件違反はユーザーエラーだ。トランザクション関数は順番にアトミックに実行されるわけではない。トランザクション内の別の操作がその事前条件を無効化し得るなら、トランザクション関数で事前条件を確認するのは安全ではない」としている

    • Jepsen が確認したように、Datomic の不変条件強制メカニズムは設計どおりに動作している
      ユーザーの観点でこれが何を意味するかを見るには、次のようなトランザクションの疑似データを考えればよい
      [ [Stu favorite-number 41] ;; maybe more stuff [Stu favorite-number 42] ]
      これを操作的に読むと、トランザクションの初めには私は 41 が好きで、後で 42 が好きになったように見える
      トランザクション終了後の観察者は、私が 42 だけを好きだと見えることを期待するだろうし、どの条件で 41 が見えるのかを心配しなければならない
      こうしたトランザクション内部の意味論の操作的解釈は多くのデータベースで一般的だが、トランザクション内に複数の時点が存在すると仮定している
      Datomic にはそのような時点はなく、また望んでもおらず、「トランザクションの途中で」何があったかを心配しなくてよい点を好んでいる
      Datomic ではトランザクションのすべてのファクトは同じ時点で発生するため、このトランザクションは私が 2 つの数字を同時に好きになり始めた、と述べている
      Datomic のトランザクションを複数操作の組み合わせとして誤って読むと、当然ながらあらゆる種類の「不変条件の異常」を見つけられる
      逆に、SQL トランザクションに Datomic のモデルを誤って当てはめても「不変条件の異常」は見つけられる
      こうした誤解の可能性があるため、よいドキュメントが必要であり、Jepsen とともにドキュメントを改善して [1] 不用意な表現を整え、誤解を減らそうとした
      この特定の誤解を直接扱う技術ノートも追加した [2]
      [1] https://docs.datomic.com/transactions/transactions.html#tran...
      [2] https://docs.datomic.com/tech-notes/comparison-with-updating...
    • 要約すると、「潜在的な落とし穴ではあるが、ドキュメントと一貫しており、設計どおりに動作している」に近い
      実際に重要かどうかは、ユーザーがどの不変条件を保持しようとしてトランザクション関数を書いているか、そしてその関数が同時ではなく逐次実行された場合にだけ不変条件を守るものかどうかに依存する
      Datomic の立場、あるいは Datomic 側から補足してもらえるとよいが、ユーザーはそのようなトランザクション関数をそれほど頻繁には使わない、というものだ
      この立場は擁護可能だ。ドキュメントには、トランザクション関数は互いを観察するのではなく、トランザクション開始時の状態を観察すると明記されていたからだ
      一方でドキュメントには、トランザクション関数を不変条件の保持に使えることを示唆する表現もあった。「[txn fns] can atomically analyze and transform database values. You can use them to ensure atomic read-modify-update processing, and integrity constraints...」
      この表現と、ほぼすべての他の直列化可能なデータベースがトランザクション内部の逐次的意味論を使っているという事実のため、報告書ではこの問題に多くの紙幅を割いた
      複雑な問いなので明確な答えはなく、一般的なデータベースコミュニティ、とりわけ Datomic ユーザーがこの意味論をどう受け止めるのか聞きたい
    • 記事の 3.1 節の末尾ですでに答えが示されていると思う。「この挙動は驚くべきものかもしれないが、おおむね Datomic のドキュメントと一貫している。Nubank はこの挙動を変更する予定はなく、われわれはこれをバグとは見なしていない」
      「不変条件違反につながる状況」と言うと Datomic のバグのように聞こえるが、これはそうではない
      Datomic がトランザクションを処理する方法を理解し、それに合わせてコードを書く必要がある
      Nubank とは関係ないが、Datomic を汎用データベースとして使っていて、これが問題になった状況には遭遇していない
    • 一部のリレーショナルデータベースで、直前に選択した値に依存して更新するには SELECT ... FOR UPDATE が必要だと知っておく必要がある状況に似ているように聞こえる
  • 知らない人のために付け加えると、Jepsen という名前は「Call Me Maybe」を歌った Carly Rae Jepsen に由来するダジャレだ
    分散システム研究プロジェクトの名前としては完璧だと思う

  • Datomic を実戦で長く使ったことはないが、かなり特異なので、この中に驚くべきことがあるのかと思う
    Datomic のトランザクションは基本的にバッチに近く、常にシングルスレッドだと思っていたので、競合状態が多くないのは当然に見える
    設計上、遅くて安全な側だ

    • 「同じトランザクションで x を 2 回増やすと、x+2 ではなく x+1 になる」という例はかなり重要に見える
      相当注意する必要がありそうだ
    • Datomic はトランザクションログをいつか圧縮するのだろうか?
  • Kyle、ありがとう
    私たちのドキュメントが不十分だったことは明らかだ
    Rich とともに、Datomic のトランザクションモデルについて、より明確で包括的なドキュメントを書こうとした
    よくある誤解を事前に防げることを願っており、あらゆるフィードバックを歓迎する
    https://docs.datomic.com/transactions/model.html

  • Datomicのデータモデルは、トリプルストアやRDFに慣れていればかなり直感的です
    しかし、ドキュメントやオンラインでの議論では、こうした類似性はあまり言及されません
    人々がそのような概念に慣れていないからなのか、それともセマンティックWeb関連の連想を邪魔なものと見ているのか、あるいは私が見落としている根本的な違いがあるのか気になります

  • この分析を本当に待っていました
    最近、Datomicに似たデータストアを自作しているので役に立ちそうで、今読んでいるところです
    MongoDBの分析も面白かったですし、Redis、RethinkDBなど他の分析もぜひ見るとよいです
    いつかrqlite/dqliteturso/libsqlについての分析もあるとうれしいです