データのコピーやサーバーの立ち上げなしに、チーム横断でデータベースを統合する

2026/03/27 6:16

データのコピーやサーバーの立ち上げなしに、チーム横断でデータベースを統合する

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:


Summary

Datahikeは、データベースのすべての状態を不変スナップショットとして保存します。各スナップショットは、ノードがコンテンツアドレス化され、バージョン間で共有される永続的なB‑tree派生構造です。そのため、書き込み時には新しいノードが作成され、古いノードは変更されません。接続(

@conn
)を参照すると、ブランチヘッドキーのみがロードされ、以降のクエリノードはストレージから遅延読み込みされローカルにキャッシュされるため、読み取りは高速です。

ライターは各トランザクションを直接永続ストレージへフラッシュするため、ストア自体が権威的であり、読みアクセスには別途トランザクタやAPI層は不要です。これによりDatahikeはサーバーレスで低遅延のリードエンジンとなります。

システムはKonserveを使用しており、S3、ローカルファイルシステム、JDBC、IndexedDBなど多くのバックエンド上に抽象化されています。ブラウザクライアントは

konserve-sync
でデータベースをローカルにレプリケートでき、クエリはローカルレプリカに対して実行されるため往復通信が不要です。WebSocketによる同期では変更されたツリー・ノードのみが送信され、構造的共有を活用した効率的な更新が可能です。

Datahikeの値ベースモデルは、異なるバックエンドにある複数のデータベースを単一のDatalogクエリで結合できるようにします。また、

d/as-of
を使用して異なる時点のスナップショットを混在させることで、追加の調整なしに履歴データを監査・デバッグできます。

Clojure REPLのサンプルでは、2つの独立したデータベースを作成し、それらにデータを投入してクロスデータベース結合を実行する方法が示されています。バックエンドキーワードを変更するだけで同じコードが異なるストレージバックエンドでも動作します。従来のETLパイプライン、メッセージキュー、およびAPI層を排除することで、このモデルは遅延・保守負担と新たな障害モードを削減し、不変スナップショットデータベースを分散アプリケーションに実用的にします。

本文

協働するメモリ―2026年3月

二つのチームがデータを結合したいとき、一般的な回答はインフラストラクチャです:ETLパイプライン、API、メッセージバス。
それらはすべて遅延、保守負担、新たな障害モードを増やします。データは「同じ場所で共有できない」システム間の移動により流れます。

もっと単純なモデルがあります。もしあなたのデータベースがストレージ上の不変値(immutable value)なら、ストレージを読むことのできる者はすべてそれをクエリできます。サーバーを走らせる必要もなく、APIで交渉する必要もなく、データをコピーする手間もありません。そしてクエリ言語が複数入力をサポートしていれば、異なるチームのデータベースを一つの式で結合できます。

これが Datahike の動作原理です。機能として追加したわけではなく、アーキテクチャの二つの基本的な性質から自然に導かれます。


データベースは値

従来のデータベースでは、実行中のサーバーへの接続を通じてクエリします。
クエリ間でデータが変わる可能性がありますし、データベースは「何かを保持している」ものではなくサービスです。

Datahike はこれを逆転させます。接続(

@conn
)を参照解除すると、不変のデータベース値―特定トランザクション時点で凍結されたスナップショットが得られます。それは変わりません。関数に渡したり、変数に保持したり、別スレッドへ渡すこともできます。同じスナップショットを持つ二人の同時読み取り者はロックや調整なしで常に合意します。

これは Rich Hickey が 2012 年 Datomic とともに導入したアイデアです:書き込み(単一ライターが管理)と観測(読み取り=値のみ)のプロセスを分離すること。観測の正しい実装は調整を必要としません。

Datomic のインデックスはストレージに保存されますが、トランザクタはフラッシュされていない最近のインデックスセグメントのメモリ上オーバーレイを保持します。読者は通常、完全で現在のビューを得るためにトランザクタと調整する必要があります。ストレージだけでは不十分です。

Datahike はその依存関係を取り除きます。書き手は毎回トランザクション時にフラッシュし、ストレージが常に権威を持ちます。ストアを読むことのできるプロセスなら、オーバーレイやトランザクタ接続なしで完全かつ現在のデータベースを見られます。この仕組みを理解するには、データ構造を見る必要があります。


ストレージにおける木

Datahike はインデックスを永続的なソート済み集合―ノードが不変である B‑tree の一種―として保持します。
各ノードは konserve でキー・バリュー対として保存され、S3、ファイルシステム、JDBC、IndexedDB といったストレージバックエンドを抽象化します。

トランザクションがデータを追加すると、Datahike は既存ノードを変更せず、葉から根までの変化したパスに対して新しいノードを作成し、変更されていない部分は以前のバージョンと共有します。これが構造的共有(structural sharing)であり、Clojure の永続ベクターや Git のオブジェクトストアで使われる同じ技術です。

具体例

データ量が 100 万件のデータベースは数千ノードを持つ B‑tree を持つかもしれません。
10 件のデータを追加するトランザクションでは、影響したパスに沿っておそらく十数ノードを書き直します。新しい木の根はこれら新ノードと以前から変更されていない千数ノードを指し示します。古いスナップショットと新しいスナップショットはともに有効な完全な木であり、ほぼ全構造を共有しています。

重要な性質:各ノードは一度書き込まれ、二度と変更されません。そのキーはコンテンツアドレッシング可能です。つまりノードは積極的にキャッシュでき、独立して複製でき、ストレージへアクセスできるプロセスなら誰でも読み取れます(書き手との調整不要)。構造的共有や枝分かれ、トレードオフについては The Git Model for Databases を参照。


分散インデックス空間

ここで全てが結びつきます。

@conn
を呼ぶと、Datahike は konserve ストアから一つのキー(例:
:db
)を取得します。
それは各インデックスへのルートポインタ、スキーマメタデータ、現在のトランザクション ID を含む小さなマップです。他に何もロードされません ― データベース値は木への遅延ハンドルです。

クエリがインデックスを走査するとき、各ノードは必要時にストレージからフェッチされ、ローカル LRU にキャッシュされます。同じノードを次回以降のクエリで参照しても I/O は発生しません。

これが読み取りパス全体です。アクセスを仲介するサーバープロセスや接続プロトコル、公開ポートは不要です。インデックスはストレージにあり、ストレージを読むことのできるプロセスならブランチヘッドをロードし、木をたどり、クエリを実行できます。これが 分散インデックス空間 です。

同じデータベースを読み取る二つのプロセスは、独立して同じ不変ノードをフェッチします。互いに知覚しません。書き手は新しいツリーノードを書き込み、ブランチヘッドを原子操作で更新して新スナップショットを公開します。その後デコードすると新スナップショットが見えます。以前のスナップショットを保持した読者は影響を受けず、ノードは不変であるため到達可能な間はガベージコレクションされません。


データベース間の結合

データベースが値であり、Datalog が複数入力ソースをネイティブにサポートしているので、次のステップは自然です。異なるチーム、異なるストレージバックエンド、または異なる時点のデータベースを一つのクエリで結合します。

(def catalog   (d/connect {:store {:backend :s3 :bucket "team-a"}}))
(def inventory (d/connect {:store {:backend :s3 :bucket "team-b"}}))

(d/q '[:find ?name ?price ?stock
       :in $cat $inv
       :where [$cat ?p :product/sku   ?sku]
              [$cat ?p :product/name  ?name]
              [$cat ?p :product/price ?price]
              [$inv ?i :stock/sku     ?sku]
              [$inv ?i :stock/count   ?stock]
              [(> ?stock 0)]]
      @catalog @inventory)

@
デリファレンスはそれぞれの S3 バケットからブランチヘッドを取得し、不変データベース値を返します。クエリエンジンがローカルで結合し、サーバー間調整やデータコピーは不要です。

さらに、異なる時点のスナップショットを混ぜることもできます:

;; 前四半期のカタログと現在の在庫
(def old-catalog (d/as-of @catalog #inst "2025-11-01"))

(d/q '[:find ?name ?stock
       :in $cat $inv
       :where [$cat ?p :product/sku  ?sku]
              [$cat ?p :product/name ?name]
              [$inv ?i :stock/sku    ?sku]
              [$inv ?i :stock/count  ?stock]]
     old-catalog @inventory)

古いスナップショットと現在のものはどちらも単なる値です。クエリエンジンはそれらがいつ作成されたかを気にしません。監査、規制再現性、デバッグ(「前四半期のデータでこのレポートは何を示しただろう?」)に有用です。


ストレージからブラウザへ

ここまで「ストレージ」は S3 やファイルシステムを指していましたが、konserve には IndexedDB バックエンドもあります。つまり同じモデルはブラウザでも動作します。
Kabel WebSocket sync と konserve‑sync を使えば、ブラウザクライアントはローカルの IndexedDB にデータベースをレプリケートできます。クエリはネットワーク往復なしでローカルレプリカに対して実行されます。更新は差分同期し、同じ構造的共有がサーバー側のスナップショットコスト低減と同様に、ネットワーク上でも安価になります。


使ってみよう

以下は Clojure REPL で動作する完全なクロスデータベース結合例です:

(require '[datahike.api :as d])

;; 二つの独立したデータベース
(def catalog-cfg  {:store {:backend :memory
                           :id (java.util.UUID/randomUUID)}
                   :schema-flexibility :read})
(def inventory-cfg {:store {:backend :memory
                            :id (java.util.UUID/randomUUID)}
                    :schema-flexibility :read})

(d/create-database catalog-cfg)
(d/create-database inventory-cfg)

(def catalog  (d/connect catalog-cfg))
(def inventory (d/connect inventory-cfg))

;; チーム A:製品情報
(d/transact catalog
  [{:product/sku "W001" :product/name "Widget"      :product/price 9.99}
   {:product/sku "G002" :product/name "Gadget"      :product/price 24.50}
   {:product/sku "T003" :product/name "Thingamajig" :product/price 3.75}])

;; チーム B:在庫レベル
(d/transact inventory
  [{:stock/sku "W001" :stock/count 140}
   {:stock/sku "G002" :stock/count 0}
   {:stock/sku "T003" :stock/count 58}])

;; 結合:在庫ありの製品と価格
(d/q '[:find ?name ?price ?stock
       :in $cat $inv
       :where [$cat ?p :product/sku   ?sku]
              [$cat ?p :product/name  ?name]
              [$cat ?p :product/price ?price]
              [$inv ?i :stock/sku     ?sku]
              [$inv ?i :stock/count   ?stock]
              [(> ?stock 0)]]
     @catalog @inventory)
;; => #{["Widget" 9.99 140] ["Thingamajig" 3.75 58]}

:memory
:s3
:file
、または
:jdbc
に置き換えるだけで、同じコードがストレージバックエンドを横断して動作します。データベースは同一バックエンドにある必要はありません ― S3 データベースとローカルファイルストアを同じクエリで結合できます。

同じ日のほかのニュース

一覧に戻る →

2026/03/29 2:39

GitLab の創業者は、会社を立ち上げることでがんと闘う

## Japanese Translation: **概要** 著者は、上部脊柱のT5椎骨に位置する腫瘍性骨肉腫との個人的な闘いを語ります。標準治療オプションを試みたものの適切な臨床試験が見つからない中で、著者は自身の状態に合わせた新しい診断手法と並行治療プロトコルを開発しました。また、「癌ジャーニーデッキ」と埋め込み型OpenAIフォーラムプレゼンテーションを作成し、この経験を記録しています。著者のアプローチはevenone.venturesに掲載されている企業によって支援され、さらにエリオット・ハーシュバーグによる著者の旅路についての包括的な記事や、ルクサンドラ氏が執筆した「The bureaucracy blocking the chance(機会を阻む官僚主義)」という患者優先医療実践を批判する作品も広い文脈に含まれます。治療データと詳細なタイムラインは、https://osteosarc.com/ で公開されており、データ概要ドキュメントや25 TBの読み取り可能なGoogle Cloudバケットが含まれています。著者は読者にメールリストへの登録を促し、更新情報を受け取れるよう案内しています。また、`cancer@sytse.com` で連絡を取ることもできます。

2026/03/29 5:39

CSSは終焉を迎える運命にあります。

## Japanese Translation: この記事は、CSSのみでレンダリングを行い、ロジックには最小限のJavaScriptしか使用しない完全にプレイ可能なDOOM風ゲームをウェブブラウザ上で動かす方法を紹介しています。壁・床・天井・スプライト・弾道などを表現するために数千もの `<div>` 要素が生成され、各要素はカスタムプロパティとして生のDoom座標を保持し、CSS が `hypot()`(距離)や `atan2()`(角度)といった関数で幾何学を計算します。ワールドはプレイヤーの動きに逆行するように `translate3d` と `rotateY` で移動されますが、CSS にはカメラオブジェクトがないためです。 床は `rotateX(90deg)` で回転し、`clip-path`(または新しい `shape()` 関数)を使って任意の多角形や穴に切り取られます。テクスチャタイルはセクター全体にわたって背景位置をワールド座標に合わせて (`background-position: calc(var(--min-x)*-1px) …`) 配置されます。ドア、リフト、その他の動的要素はカスタムプロパティ上で CSS トランジションによってアニメーションし、JavaScript が状態属性を更新します。スプライトは `rotateY` でカメラに向き、`scaleX` で鏡像化したビルボードです。スプライトのアニメーションは CSS の `steps()` キーフレームで行い、攻撃・死亡フレーム用のデータ状態は JavaScript が供給します。弾道は CSS アニメーションで移動し、衝突検出はまだ JavaScript で処理されます。 照明はセクターごとに `filter: brightness(var(--light))` を使って全体的に適用され、ちらつくライトは `@property --light` を通じてアニメーションします。プロジェクトではアンカー位置決め、`@property`、および「ハッキー」な CSS‑のみのカリング手法(オフスクリーン要素を隠すために負の遅延でアニメーションを一時停止)といった実験的機能が採用されています。 数千もの 3D 転送された要素によるパフォーマンスは課題となり、著者は JavaScript で手動フラスタムカリングを実装し、条件付き `if()` のサポートが登場すれば将来的に純粋 CSS ソリューションへ移行する計画です。記事では Safari のビュー遷移による 3D フラット化、background‑image 再ラスター化の問題、コンポジタ不安定性などブラウザバグも文書化し、インラインスタイルやバグ報告といった回避策を紹介しています。 著者はより多くのロジックを純粋 CSS に移すことで JavaScript を完全に排除できる可能性があり、パフォーマンスをさらに向上させることを想定しています。成功すれば、このアプローチは軽量なブラウザベースゲームを刺激し、高度な CSS グラフィックス機能のサポートを促進し、重いエンジンを必要としない効率的なレンダリングが求められる開発者に利益をもたらすでしょう。

2026/03/27 23:39

オープンブースト・オン・モトローラ 88000プロセッサー

## Japanese Translation: (欠落している詳細を補完しつつ明瞭さを保つ)** ``` モトローラ 68000 ファミリーは、1990年代中頃のワークステーション(Apple、Amiga、Atari ST、Sun、HP、NeXT)や多くの産業用ボードで普及していました。 その RISC 後継機種である 88000(m88k)は、68k と PowerPC の間に導入されましたが、約 1994 年頃に期待された性能を提供できず廃止されました。m88k は二世代存在しました: • 88100 – 第1世代 CPU で、オプションの外部 88200 CMMU チップを搭載し、MVME180(20 MHz、2 本の CMMU)と MVME181 に使用されました。 • 88110 – 第2世代 CPU で、統合キャッシュ/MMU を備え、50 MHz を想定していましたが実際には約 40 MHz で販売されました。MVME187(25 MHz、デュアル CMMU、最大 64 MB)、MVME188(SMP、最大 4 CPU と 8 CMMU)、および MVME197 系列(セカンダリキャッシュ)に搭載されました。 VME バスは 32‑bit アドレス/データラインを備えたパッシブバックプレーンであり、複数ボードサポート、割り込みベクタ、オプションのスレーブマッピング、および終端要件があります。 OpenBSD のポートは 1995 年に MVME187 上で開始されました。Nivas Madhur、Steve Murphree、Marc Espie らの貢献は CVS マージ競合、アカウント停止(Theo de Raadt の関与)、GCC‑2.95 互換性問題、カーネルパニック(「align & align‑1」アサーション)および MVME188 上の不完全な SMP サポートに直面しました。ポートは 3.1‑beta スナップショットまで達成しましたが、ハードウェアエラー(VME バスロックアップ、DCAM2 コンフリクト、I²C フェイル)が未解決のまま残っています。 m88k アーキテクチャに関するドキュメントは、モトローラ AT&T System III/V、Data General DG/UX、Omron UniOS などのプロプライエタリ Unix バリアントと無料 CMU Mach コードから取得されました。メンテナー間の個人メール交換は協力、衝突解決、およびニッチなポートの保守課題を示しています。 MVME VME ボードおよび他の m88k システムのユーザーは、この OpenBSD ポートに安全性と安定性を依存しています。継続的なサポートがない場合、利用可能な OS を失うリスクがあり、新しいアーキテクチャへの移行が必要になるかもしれません。 ``` *改善された要約はすべての主要ポイントを反映し、不適切な推測を回避し、主旨を明確に提示し、曖昧または混乱を招く表現を排除しています。

データのコピーやサーバーの立ち上げなしに、チーム横断でデータベースを統合する | そっか~ニュース