
低メモリ向けGraphDBで、Bolt+TLSサポート、保存時暗号化、ベクトルを備え、ローカルレプリカのグラフユースケース向けに設計されています。
現在のバージョン: v0.25.2 — すべてのリリース。
一言で言うと: Slaterはメモリに収まらないグラフ — 数億ノードと数十億エッジをわずか数百MBのRAMで — 標準のBoltプロトコルで提供し、任意のneo4jドライバがそのまま動作します。ディスクネイティブなベクトル検索がグラフの隣に配置され、さらにライブで永続的な書き込みも犠牲にすることなく受け付けます。常駐メモリは、グラフのサイズではなく、あなたが選択するキャッシュ予算によって決まります。
ショートカット
| Slaterが存在する理由 | 読み取りと書き込み | 得られるもの | 機能 |
| Dockerでの実行 | 仕組み | 書き込み可能レイヤー | ストレージバックエンド |
| マウント | 設定 | ACL | ヘルスチェック |
| 実例 | 開発 | パフォーマンス | ライセンス |
| Graphitiメモリストア | 📖 完全マニュアル |
グラフデータベースは、データをもの(ノード)とそれらの間の関係(エッジ)として保存し、関係を第一級市民として扱います。これは、行ではなく接続に関する質問をするときに必要なものです — 「このアカウントから3ホップ以内にいるのは誰か?」、「このビルドの背後にある完全な依存関係チェーンは何か?」、「どのアカウントがデバイス、住所、カードを共有しているか?」 — これらのクエリはSQLでは再帰的結合の沼になりますが、グラフでは自然に導き出されます。
グラフデータベースに関する最も一般的な不満は、RAMに保持できる範囲を超えてスケールしないことです。 多くのグラフDB(例: neo4j、Memgraph、FalkorDBなど)はグラフ全体を常駐させます: 40 GBのグラフには40 GBのメモリが必要です — インスタンスごとに。リージョンごと、テナントごと、ポッドごとにレプリカが必要ですか? 請求額が倍増します。そして、あるサイズを超えると単純にロードできません: 例えば、9000万ノード / 15億エッジのWikidataグラフは約64〜128 GiBの常駐メモリが必要なので、インメモリエンジンではまったく開けません。
Slaterはその反論です。グラフをメモリにロードする代わりに、一度だけオフラインでコンパイルします: slater-buildはあなたのデータをコンテンツアドレス指定された不変のオンディスクイメージに変換し、任意の数のSlaterサーバーがそのイメージをBolt経由で提供します(既存のneo4jドライバがそのまま動作します)。ブロックをオンデマンドでページングし、固定のキャッシュ予算のみを常駐させます。これが、同じ9000万ノードのグラフが数百MBのRAMから提供できる理由です — グラフサイズとメモリ請求額が切り離されています。4 GBのグラフと400 GBのグラフは、提供するためのRAMコストが同じです。そのため、安価でステートレスな読み取りレプリカを展開し、ヒープではなくストアにグラフを保持させることができます。
これにより、RAGの背後にあるナレッジグラフ、レコメンデーションおよびアイデンティティグラフ、依存関係グラフ — 大規模で接続された、安価かつ頻繁にクエリしたいものすべてに自然に適合します。ディスクネイティブなベクトル検索がグラフのすぐ隣に配置されるため、同じエンジンが埋め込みの検索レイヤーにもなります。
ただし、一度コンパイルしたからといって凍結されるわけではありません。そのイメージはベースであり、最終状態ではありません: オプトインの書き込みレイヤーがその上に配置されるため、ライブグラフを再構築することなく修正および拡張できます。
コアは不変ですが、グラフは不変ではありません。書き込みレイヤーをオンにすると(delta.enabled)、Bolt経由で書き込みます — 1つのプロパティを修正し、ノードを追加し、エッジを撤回する — 変更は永続的に記録され、イメージの再構築は不要です。読み取り側でコストを抑えるのは、書き込みがどこに存在するかです。
書き込みは不変コア上のログ構造マージ(LSM)レイヤーに蓄積されます: 書き込み先行ログとインメモリテーブルがあり、不変のデルタセグメントにスピルし、定期的な統合によって新しいコアに折り畳まれます。これがもたらすもの:
count(*)、ラベルおよび関係タイプの周辺分布 — は、書き込みが未処理でもメタデータ読み取りのままです: デルタは独自のカウンターを保持するため、50万件の未処理書き込みがある9160万ノードのコアに対するcount(*)は、1ブロックにも触れずに数十ミリ秒で回答します。fsyncの後にのみSUCCESSを返します。書き込みをグループ化すれば安価です — 書き込みUNWINDは行ごとではなくバッチごとに1回のfsyncをコミットします。MERGE / MATCH … SET / DELETE(およびCREATE / REMOVE、デタッチ削除、関係書き込み)— または同等のISO GQLデータ変更ステートメント(INSERT / SET / / )。これらは同じパスに降りてきます。ノードとエッジに対して、データがすでにアドレス指定されている方法で、修正、挿入、アップサート、撤回を行います。レイヤーがオフの場合 — デフォルト — Slaterは純粋な不変コアを提供し、書き込みを拒否します。完全なモデルについては書き込み可能レイヤーを参照してください。
名前について。 SlaterはArcher(素晴らしい番組)のCIAエージェントにちなんで名付けられました。 彼は単一の名前で通すことを主張します — 「Just… Slater」— そして私のお気に入りの キャラクターの1人です。詳細は キャラクターwikiページを参照してください。
MERGE / SET / DELETE、グループコミットされfsyncで永続化され、統合によって新しいコアに折り畳まれます。読み取りはそのコストを負担しません。currentポインタを原子的に切り替えると、サーバーがそれを取得します。すべてのブロックがチェックサムされるため、半分コピーされたイメージは提供されず拒否されます。ワークスペースを構成する2つのバイナリ:
| バイナリ | 役割 |
|---|---|
slater | オンラインBoltサーバー(コンテナENTRYPOINT): 読み取りを提供し、delta.enabledで単一ライターの永続的な書き込みパスを提供します。 |
slater-build | オフラインコンパイラ: プリミティブCypherダンプを不変のコンテンツハッシュ化世代ディレクトリに変換します。 |
Slaterはバルクビルドと提供を分離します: slater-buildはオフラインで重い処理を行います — データを取り込み、不変の世代にコンパイルする — そのため、コールドグラフが提供ホットパスで組み立てられることはありません。サーバー内では、読み取りサーフェスが広範なCypherスライスに回答します — パターンマッチング、WITH/UNION/CALL {…}サブクエリ、70以上のスカラーおよび集計関数、時間および地理空間値、グラフアルゴリズム(algo.*)、ディスクネイティブベクトルKNN(db.idx.vector.queryNodes)— 一方、書き込みレイヤーのデルタオーバーレイはそのサーフェスの下にあり、空の場合はゼロコストなので、読み取りは書き込み側の機構を負担しません。グラフを2つの方法で更新できます: Bolt経由でライブに書き込む(書き込み可能レイヤーを参照)、または新しい世代をオフラインでビルドしてcurrentポインタを原子的に交換する。実行中のサーバーは世代ガードを介してそれを取得します(世代ガードを参照)。
完全なユーザーマニュアルは**docs/manual/**にあります — すべての機能について、それが何か、なぜ存在するか、使用方法を説明する機能別ガイドで、バンドルされたサンプルグラフに対して実行できる実例付きです。この概要を超えたものはすべてそこから始めてください。
graphiti-slaterは、Graphitiがその時間的ナレッジグラフをSlaterに保存できるようにするアダプタで、実行可能なdocker-example/を含みます — Claude CodeにMCPサーバーとして公開することも含みます。仕組みと実行方法については、そのリポジトリを参照してください。
SlaterはDockerデプロイメントとして実行されるように設計されています — それが想定された使用方法です。プリビルドのマルチアーキテクチャイメージ(linux/amd64 + linux/arm64)はDocker Hubのhikarisystems/slaterに公開されており、リリースごとに:latestおよび:vX.Y.Zタグが付けられます:```sh
docker pull hikarisystems/slater:latest
Dockerコマンドのみの使用方法、設定、および運用ガイドは
[`DOCKERHUB.md`](https://github.com/hikari-systems/slater/blob/main/DOCKERHUB.md)(Docker Hubの概要ページにもミラーリングされています)にあります —
**デプロイする場合は、まずそこから始めてください。** 簡単に言うと:```sh
# Build a graph generation with the offline writer:
docker run --rm -v slater-data:/data -v "$PWD/dumps:/dumps:ro" \
--entrypoint /app/slater-build hikarisystems/slater:latest \
--input /dumps/people.cypher --graph people --data-dir /data
# Serve it over Bolt on 7687 (read-only unless `delta.enabled`):
docker run -d --name slater -p 7687:7687 \
-v slater-data:/data:ro -v "$PWD/acl.json:/config/acl.json:ro" \
hikarisystems/slater:latest
イメージをローカルでビルドする場合(例:開発用):```sh
docker compose build
docker compose up slater
build):docker compose run --rm builder
--input /dumps/people.cypher --graph people --data-dir /data
ビルダーステージでは、rustls の `aws-lc-rs` バックエンド用に `cmake`、`clang`、`libclang-dev` をインストールします。`git`(ベースイメージに既に含まれています)は、`hs-utils` の git+tag 依存関係に必要で、`.cargo/config.toml` は git CLI 経由で取得します。
以下のセクションでは、ディスク上の形式、設定、ACL、およびローカル(Docker 以外)での実行例について説明します。
## 仕組み```
slater-build slater (Bolt server)
dump.cypher ──────────▶ /data/<graph>/<uuid>/ ──────────▶ neo4j driver
(offline, atomic) MANIFEST.json, *.blk, (bolt / bolt+s)
range/*.isam, vector/*.{vamana,pq},
current → <uuid>
MANIFEST.json(シンボルテーブル、インデックス記述子、オプションの暗号化ヘッダー)、カラムナブロックファイル(node_props.blk、node_labels.blk、edge_props.blk、topology.csr.blk、vectors.f32.blk)、レンジインデックス(range/<name>.isam)、閾値以上のANNインデックス(vector/<label>.<prop>.{vamana,pq})、およびcurrentテキストポインターで構成されます。--encryptを使用すると、各ブロックはさらにXChaCha20-Poly1305(保存時のAEAD)で封印されます。delta.enabledを使用すると、不変世代は小さなログ構造化マージツリーの完全に圧縮された最下位レベル(「コア」) となり、ライブ書き込みはその上に乗ります。```
write (Bolt) read (Bolt)
│ │
▼ ▼
┌──────────────┐ flush ┌──────────────┐ ┌──────────────────────┐
│ WAL + active │ ───────▶ │ L0 delta │ │ a query pins one │
│ memtable │ │ segments │ │ (core, delta) view │
└──────────────┘ └──────┬───────┘ │ and reads the merge │
(fsync = ack) │ └──────────────────────┘
consolidation │ (folds core + delta → fresh core)
▼
┌─────────────┐
│ new core │ (atomic current swap)
└─────────────┘
* **永続性の下限 — WAL。** 各ミューテーションはグラフごとに単一のライターの背後で直列化され、グラフごとの書き込み先行ログ(write-ahead log)に追記され、Bolt の `SUCCESS` が返される前に `fsync` されます。つまり、*確認応答 ⇒ 永続化* であり、破れた末尾はリプレイ時に破棄されます。バッチ書き込みの `UNWIND` は行を追記し、バッチ全体で **1回** の `fsync` でコミットします。WAL は **ローカルディスクのみ**(ストレージバックエンドを経由しません)であるため、*ライター* ノードはステートフルになります。`delta.walDir` に永続的なローカルボリュームが必要です。読み取りレプリカはステートレスなままです。
* **メムテーブル → L0 → 統合。** 書き込みはインメモリのメムテーブル(`delta.memtableBytes` で制限)に蓄積され、満杯になると不変の L0 デルタセグメントにフラッシュされます。**統合** は、マージされたビューを `slater-build` 経由で再シリアライズし、`current` をアトミックに交換することで、`{core + delta}` を新しいコアに折り畳みます — 公開されたどの世代と同じコンテンツハッシュガードです。`CALL slater.consolidate()` で手動でトリガーするか、コアサイズの `delta.deltaCorePercent` で自動的に(オプションでオフピークの `delta.consolidateWindow` にゲート)、または `delta.deltaHardBytes` スロットルで暴走する成長をバックストップします。
* **オーバーレイは読み取りサーフェスの下にあります。** エグゼキュータは、ベアコア(デルタが常に空)またはマージされた `(core, delta)` ビューのいずれかである `ReadView` を介して読み取ります。エンジンはその上でモノモーフィズされるため、空のデルタは単一の予測可能なブランチにコンパイルされ、読み取り専用パスはバイト単位で同一です。グラフ全体のカウンター(`count(*)`、ラベル/リレーションタイプの周辺度数)はデルタ自身のライブカウンターから提供されるため、書き込みが保留中でもメタデータ読み取りのままです。
* **クエリは安定したスナップショットを参照します。** クエリはその存続期間中、1つの `(core, delta)` タプルを固定します。マルチステートメントトランザクションやロールバックはありません — 書き込みは、OLTP トランザクションではなく、永続的でビジネスキーアドレス指定された修正です。
正確な書き込み文法とノブは、[Configuration](#environment--configuration) テーブル(`delta.*`)と以下の [Worked example](#worked-example) にあります。
### レンジインデックス(ISAM)
レンジインデックス(`range/<name>.isam`、インデックス付きの `(label, property)` ごとに1つ)により、`MATCH (n:Label {prop: v})` または `WHERE n.prop <op> v` は、**ラベルをスキャンせずに** 一致するノード ID に解決できます。これは **[ISAM](https://en.wikipedia.org/wiki/ISAM)**(Indexed Sequential Access Method)構造です — 古典的な *静的、ソート済み、ブロック構造* のインデックスであり、不変の世代にまさに適した形状です。リバランスする挿入がないため、ISAM の単純さは、B ツリーのミューテーション機構が複雑にするだけのものを実現します。
* エントリ `(value, entity_id)` は値でソートされ、他のすべてと同じ zstd 圧縮された 256 KiB ブロックにパックされます。
* 小さな **常駐トップレベル** が各ブロックの最初のキー(スパースインデックス)を保持します。ルックアップはそのインメモリのトップレベルをバイナリサーチして、キーが存在し得る *1つの* ブロックを見つけ、そのブロックを読み取り + 解凍し、スキャンします — したがって、等価ルックアップは **1ブロック読み取り** であり、レンジスキャンはそれがまたがる連続したブロックの実行を歩きます。(これが、`meshUi` インデックス付きルックアップが一桁ミリ秒である一方、インデックスなしのプロパティでの同じマッチがラベル全体をスキャンする理由です。)
* プランナーは `NodeScan::RangeEq` / `RangeRange` を介してそれを選択します。インデックスなしの述語はラベルスイープまたはフルスキャンにフォールバックし、エグゼキュータはいずれにせよすべての述語を再チェックします。
### ベクトル検索(Vamana + PQ)— コサイン、L2、ドット積、読み取り *および* 書き込み
ベクトル KNN(`db.idx.vector.queryNodes`)は **コサイン、L2、またはドット積(MIPS)** インデックス上で実行されます。ベースインデックスはオフラインで2つの実行パスで構築され、`--ann-threshold`(デフォルト 50,000 ベクトル)によってインデックスごとに選択されます:
* **しきい値未満 — ブルートフォース。** 完全な `f32` ベクトルは `vectors.f32.blk` に存在し、クエリはインデックスのグループをスキャンし、インデックスのメトリックで正確な距離を計算します。シンプルで正確。ベクトルセットが小さい場合に適しています。
* **しきい値以上 — Vamana + PQ**、ベクトル数に関係なく常駐メモリを制限し続けるディスクネイティブな ANN パス:
* **[Vamana](https://arxiv.org/pdf/2401.11324)** は DiskANN 系の研究からのグラフインデックスです:エッジが剪定された(`--vamana-r` 出力次数と `--vamana-alpha` ロングエッジ係数)単一の近傍グラフであり、*貪欲なビームサーチ* — メドイドから開始し、クエリに向かって繰り返しホップし、幅 `vectorQuery.beamWidth` の候補リストを維持 — が数ホップでノードの真の近傍に到達します。つまり、**クエリあたり少数のランダムブロック読み取り** です。グラフブロック(`vector/<label>.<prop>.vamana`)はベクトルキャッシュを介してページングされ、全体を保持しません。
* **[積量子化(PQ)](https://medium.com/aiguys/product-quantization-k-nn-for-big-datasets-12431d764c4e)**
は各ベクトルを短いコード(`--pq-subspaces` × `--pq-bits`)に圧縮します:次元はサブスペースに分割され、各サブスペースは独立に k-means クラスタリングされ、ベクトルは最も近いセントロイド ID のタプルとして格納されます。これらのコード(`vector/<label>.<prop>.pq`)は **常駐** させるのに十分小さいため、ビームサーチは RAM から候補をスコアリングし、選択された少数の完全なベクトルのみがディスクから読み取られます。その常駐 PQ セットが `cache.vectorCacheBytes` プールが固定するものです。
**書き込み可能な埋め込み — ベクトル書き込みラダー([FreshDiskANN](https://arxiv.org/abs/2105.09613) スタイル)。**
インデックス付き埋め込みは第一級の書き込み可能な値です。`SET n.embedding = vecf32([…])`(および `REMOVE`)は書き込みデルタに着地し、**正確なランクで即座に KNN から見える** ようになり、セグメントフラッシュ、マージ、統合を生き延びます。クエリは最大3つのレベル — シールされたベースインデックス、シールされたセグメントごとのインデックス、およびインメモリの **RW インデックス**(書き込みデルタ上のライブな可変 Vamana)— をマージするため、保留中の書き込み数に応じてレイテンシが増加するのではなく、書き込みが蓄積してもフラットなままです。削除は *ホール* を残します:ノードは返されなくなりますが、バックグラウンドの **削除統合** がグラフからそれをスプライスするまでナビゲーションウェイポイントのままなので、削除はクエリ IO のコストを止めます。また、オンディスクグラフはノード ID ではなくレイアウト位置で隣接ノードをアドレス指定するため、`CALL slater.consolidate()` は Vamana を **参照で** — ハードリンク、バイト同一 — 運び、小さな ID 列のみを書き換え、O(N·R·L) のグラフ再構築 **なしで** ベクトル書き込みをベースに折り畳みます。測定値(注意事項付き)は [performance report](https://github.com/hikari-systems/slater/blob/main/docs/PERF-REPORT.md) にあります。
## ストレージバックエンド(ファイルシステム / S3 / GCS)
すべての世代ファイルは `std::fs` を直接ではなく **`ObjectStore`** 抽象化を介して開かれるため、*同じ* オンディスクバイト形式 — ブロック、インデックス、マニフェスト、`current` ポインタ — がどのバックエンドからも変更なしで提供されます。異なるのは *バイトがどこから来るか* だけで、リーダー、クエリエンジン、整合性チェックは決して変わりません。ホットパスは位置読み取り(`read_exact_at`)であり、ローカルファイルの `pread` とオブジェクトストアの HTTP バイトレンジリクエストにマッピングされます — Slater は mmap しないため、明示的な境界付き読み取りモデルはどこでも同一です。
**3つの第一級バックエンド**、`dataBackend.kind` で選択されます。ファイルシステムがシンプルなデフォルトです。**Amazon S3 と Google Cloud Storage は同等で完全にサポートされたオブジェクトストアバックエンドです** — 公開イメージには両方がコンパイルされて同梱されているため、それぞれ設定のみで済み、一度構築された世代は再構築なしでそれらのいずれかから提供できます(`fs` → S3 → GCS への移行も可能)。
| `dataBackend.kind` | 位置読み取り | オープン時の整合性 | 認証情報 |
| --- | --- | --- | --- |
| `fs` *(デフォルト)* | `pread` | 各ファイルの完全な BLAKE3 再ハッシュ | — |
| `s3` | HTTP `Range` GET | `HEAD` によるサーバー **SHA-256**(存在しない場合は BLAKE3 ボディ再ハッシュ) | 設定キー、AWS チェーン、または IAM ロール |
| `gcs` | HTTP レンジ読み取り | `get_object` によるサーバー **CRC32C**(存在しない場合は BLAKE3 ボディ再ハッシュ) | ADC / Workload Identity、またはサービスアカウント JSON |
両方のオブジェクトストアは、ストアが **すでに計算して保持している** チェックサムから整合性を検証し、オブジェクトメタデータとして取得します:`slater-build` はアップロード時にチェックサムを送信し(ストアはそれに対してバイトを検証して保存)、サーバーはオープン時にそれを読み戻してマニフェストと比較します — ファイルごとに1つのメタデータリクエストで、ボディのダウンロードはありません。これはコンテンツグレードであり、S3(SHA-256)と GCS(CRC32C)で精神は同一です。オブジェクトが **サーバー保存の** チェックサムを **持たない** 場合(帯域外でコピーされた、または異なるデフォルトでアップロードされた)、サーバーはバイト長を信頼するのではなく、**マニフェスト BLAKE3 に対してオブジェクトボディを再ハッシュ** します — 要求された整合性チェックがサイズ比較に静かにダウングレードされることはありません。Slater が公開した世代は常にチェックサムを保持するため、安価なメタデータパスに留まります。
この列がすべてのバックエンドでチェックするのは、ファイルが **マニフェストと一致する** ことです。マニフェスト自体を信頼できるかは別の問題であり、それを答えるマスターキーです:キーが設定されている場合、マニフェストはキー付き MAC を保持し、サーバーは任意のフィールド(これらのハッシュを含む)を信頼する前にそれを検証するため、改ざんされたファイルを記述するように書き換えられたマニフェストは拒否されます。キーがない場合、比較は全体を通してキーなしであり、データディレクトリに書き込める人はファイルとマニフェストを一緒に書き換えることができます。[What integrity means in each configuration](https://github.com/hikari-systems/slater/blob/main/THREAT_MODEL.md#what-integrity-means-in-each-configuration) を参照してください。チェック自体は `dataBackend.verifyIntegrity: false` でオフにでき、より高速なオープンと引き換えになります。
### ファイルシステム(`fs`)
デフォルトで、`dataBackend.fs.dir` をルートとします。ほとんどのデプロイメントに適した選択肢です:ローカル SSD(または NFS/EBS マウント)上の世代が読み取り専用で提供されます。整合性はオープン時にすべてのファイルの完全な BLAKE3 再ハッシュです。
### Amazon S3(`s3`)
S3 または S3 互換バケット(AWS、MinIO、localstack)。認証情報は **最初に** 設定(`dataBackend.s3.awsAccessKey` / `awsSecretKey`、一時的な STS 認証情報用の `awsSessionToken` も)から取得され、空の場合は標準の AWS チェーン(`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` 環境変数、共有プロファイル、またはインスタンス/IRSA ロール)にフォールバックします。```sh
# serve from S3 (env-var form; see the config table for every key)
dataBackend__kind=s3
dataBackend__s3__bucket=slater
dataBackend__s3__region=eu-west-2
dataBackend__s3__awsAccessKey=… # omit to use the AWS chain / instance role
dataBackend__s3__awsSecretKey=…
# S3-compatible (e.g. MinIO): also set
dataBackend__s3__endpoint=http://minio:9000
dataBackend__s3__pathStyle=true # required by most S3-compatible servers
# publish a generation into the bucket (remote `current` pointer written last)
slater-build --input people.cypher --graph people --data-dir /data \
--publish-s3-bucket slater --publish-s3-region eu-west-2 --publish-s3-prefix prod
# MinIO: add --publish-s3-endpoint http://localhost:9000 --publish-s3-path-style
gcs)GCSバケットで、JSON API経由でアクセスします。認証はGCPネイティブで、デフォルトではアプリケーションデフォルト認証情報を解決します — GKE Workload Identity、GCEメタデータサーバー、またはgcloud / GOOGLE_APPLICATION_CREDENTIALSキーです。明示的なキーにはdataBackend.gcs.credentialsPath(サービスアカウントのJSONキーファイル)またはインラインのcredentialsJsonを設定します。dataBackend.gcs.endpointはfake-gcs-serverエミュレータを指し、dataBackend.gcs.anonymous=trueで未認証アクセスを有効にしますが、そのエミュレータのみに限定されます — 実際のGCSに対しては決して使用しないでください。```sh
dataBackend__kind=gcs dataBackend__gcs__bucket=slater dataBackend__gcs__prefix=prod dataBackend__gcs__credentialsPath=/secrets/sa.json # omit for ADC / Workload Identity
```sh
# publish a generation into the bucket (remote `current` pointer written last)
slater-build --input people.cypher --graph people --data-dir /data \
--publish-gcs-bucket slater --publish-gcs-prefix prod
# explicit key: add --publish-gcs-credentials /secrets/sa.json
すべてのケースで、slater-buildは生成結果をまず--data-dir(ローカルのステージング領域)に書き込み、さらにそれをバケットへアップロードします。リモートのcurrentポインタは最後に書き込まれるため、配信ノードが半分だけ公開された生成結果を見ることはありません。
生成結果をノードのディスクではなく、永続的な中央オブジェクトストレージに置きたい場合にs3またはgcsを利用します。典型的には、一度公開して、同じバケットを読み取る多数のステートレスでディスクレスなサーバーレプリカにファンアウトする場合、ビルドホストと配信ホストを分離する場合、またはボリューム管理の代わりにストアの耐久性/バージョニング/ライフサイクルに依存する場合です。トレードオフはレイテンシです。コールドブロックはローカル読み取り(約0.1ミリ秒)ではなく、ネットワーク往復(約10〜50ミリ秒)になります。Slaterはインメモリブロックキャッシュ、並行先読み、および下記のオプションのディスクキャッシュでそのほとんどを隠します。生成結果がすでに高速なローカルストレージにあり、中央バケットモデルが不要な場合は、fsの方がシンプルで高速です。
インメモリのBlockCacheは意図的に小さく(RSSの上限が最大の保証です)、そのためRAMより大きいワーキングセットでは、同じブロックがスピルするたびにオブジェクトストアから再取得されます。オプションのローカルSSD第2キャッシュ層がこれを解決します。RAMから退避したブロックは、新しいオブジェクトGETではなくローカルディスク(約0.1ミリ秒)から提供され、インメモリの退避を生き延び、オブジェクトストアのリクエスト数/コストを削減します。これにより、ウォーム状態になるとオブジェクトストアバックエンドのノードがローカルファイルシステムのパフォーマンスに近づきます。これはs3とgcsの両方でオプトインであり、dataBackend.<s3|gcs>.diskCacheBytes > 0と書き込み可能なdiskCacheDirを設定することで有効になります。
--encrypt生成の場合は)AEADシール済みのまま、復号/展開の下流にあります。キャッシュ層は暗号化キーを保持せず、再暗号化もしないため、保存時の状態は無料で維持されます。暗号化された生成結果はシールされたままディスクに置かれます。diskCacheDirは実際の書き込み可能なボリュームを指す必要があり、決してtmpfsにしてはいけません(tmpfsはRAMであり、RSS上限の保証を無効にします)。それを追跡するインメモリインデックスは少しのRAM(キャッシュブロックあたり数十バイト)を消費し、RSS上限にカウントされます。ディレクトリはインメモリブロックキャッシュよりはるかに大きく設定してください。blockCacheBytes / 8(diskCacheBytesで下限設定)に制限されており(デフォルトで8 MiB)、増大するのではなく切り捨てられるため、コールドスキャンで膨張することはありません。切り捨てられたブロックは次のミスで単に再取得されます。設定は不要です。blockCacheBytesに応じてスケールするため、ディスク層はインデックス以外にRSS予算に新しい数値を追加しません。読み取りレプリカは読み取り専用のルートファイルシステムと非rootユーザー(appuser:1000)で実行されます。必要なものはすべて読み取り専用でマウントされます。ライター(delta.enabled)は、WAL用に永続的で書き込み可能なボリュームが1つ追加で必要です。
設定はハウス標準の階層型ローダーで読み込まれます。組み込みのconfig.json、その上にディープマージされる/sandbox/config.json、次にKEY__sub環境変数オーバーライド(ネストには二重アンダースコア、キーはcamelCase設定と一致)の順です。
すべての設定ノブ(camelCaseキー、KEY__sub環境変数オーバーライド、デフォルト値、機能)は、**設定リファレンス**に表としてまとめられています。最も調整されるノブは、キャッシュ予算(cache.*)、クエリガード(query.*)、接続上限(server.*)、ストレージバックエンド(dataBackend.*)、書き込み可能層(delta.*)です。
常駐メモリは、blockCacheBytes + vectorCacheBytes + resultCacheBytesをエントリごとのオーバーヘッドとアロケータのオーバーヘッドの範囲内で追跡します。各プールは自身の内容(文字列とコンテナは割り当て容量で)を計量し、予算内に収まるように退避しますが、エントリごとの簿記とアロケータのサイズクラスの丸めは設定した数値の上に乗ります。さらに小さな固定オーバーヘッド(およびlazy次数列の場合はdegreeColumnBytesまで、次数合計count(endpoint)高速パスが実行されると)が加わります。これはグラフサイズに依存しません。これが最大の保証であり、rss_stays_bounded_under_sustained_knn_load統合テストで検証されています。このテストはピーク時とウォーム時のRSS増加を合計予算内に十分収めます。接続ごとのバッファはキャッシュ予算の外に存在するため、この保証は敵対的な負荷下でも、server.maxConnectionsが同時に存在できる数を制限する場合にのみ成立します。
Slaterは読み取りレプリカのハンドルです。主要な接続セキュリティ制御はバイナリではなくネットワークです。プライベートインターフェースにバインドし、ネットワーク層(セキュリティグループ / NetworkPolicy)で送信元範囲を制限し、信頼できないクライアントに直面する場合は、接続制限付きL4プロキシ(HAProxy maxconn + 送信元ごとのstick-table、またはnftables connlimit + hashlimit)を前面に配置してください。これはファイルディスクリプタがプロセスに渡される前に配置されるため、最も堅牢な制限です。
上記のバイナリ内制限(maxConnections、maxPreAuthConnections、maxConnectionsPerIp、差分バイト上限、loginTimeoutMs)は多層防御です。デフォルトで有効かつ寛大なため、正当なクライアント集団には見えませんが、プロキシを忘れた場合でもRSS上限の保証が成立します。完全な防御体制については**docs/HARDENING.md**、正規の詳細についてはTHREAT_MODEL.md / SECURITY_WORKLIST.mdを参照してください。
Slaterは各グラフのcurrentポインタをgenerationPollMsごとにポーリングします(ポーリングであり、inotifyではありません。データディレクトリはNFSなどのリモート/ネットワークストレージである可能性があり、ファイルシステム変更イベントが信頼できないためです)。変更された場合:
reloadStrategy=exit(デフォルト):サーバーは致命的エラーをログに記録して非ゼロで終了し、オーケストレーターが新しい生成結果に対してクリーンに再起動します。reloadStrategy=swap:サーバーは新しい生成結果を開いて検証し(起動時と同じコンテンツハッシュガード)、原子的にスワップインし、進行中のクエリは古い生成結果で完了させます。破損または不完全な新しいイメージは拒否され、古い生成結果が配信を続けます。acl.jsonはユーザーをargon2idパスワードハッシュとグラフごとの**read** / **write**権限にマッピングします。ハッシュを生成するには(平文を保存しないでください):```sh
slater hash-password 's3cret' # prints a $argon2id$… string for acl.json
A starter `acl.json` ships at the repo root; its shape is:```json
{
"users": {
"reporting": {
"passwordArgon2id": "$argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash>",
"grants": {
"people": ["read"],
"products": ["read", "write"]
}
}
}
}
users — ログインごとに1エントリで、ユーザー名をキーとします。
passwordArgon2id — slater hash-password からの $argon2id$… 文字列
(平文は不可。ファイル自体はプレーンなJSONで、共有ストレージ上に置かれます)。
grants — グラフごとの機能リスト。意味を持つ権限は2つです:
read — グラフをクエリします。ユーザーのgrantsに存在しないグラフは、そのユーザーには見えません。write — 書き込み可能レイヤー(delta.enabled)を通じてグラフを変更します:
MERGE / SET / DELETE ステートメントと CALL slater.consolidate()。これらは独立しています:read 権限は書き込みアクセスを付与しません。 したがって、書き込み可能
レイヤーを有効にしても、既存の読み取り専用ユーザーが書き込みユーザーに昇格することはありません。書き込みユーザーには
両方 — — が必要です。ビジネスキーを解決して書き込むことは読み取り操作だからです。
認識されない権限文字列は無視されます(何も付与しません)。
aclPath(デフォルト /config/acl.json)で指定されたパスに読み取り専用でマウントします。
サーバーは各世代のホットスワップ時にこれを再読み込みし、保存時のACLスタンプは
再読み込みのたびに再チェックされます(requireAclStamp を参照)。
slater バイナリは自身の生存確認プローブを兼ねています:slater healthcheck [host] [port] はサーバーに対して Boltハンドシェイク(HTTPリクエストではない)を実行し、
プロトコルバージョンのネゴシエーションに成功すれば 0 で終了し、それ以外は 1 で終了します — デフォルトは
localhost と設定済みのBoltポートです。これがコンテナの
HEALTHCHECK が実行する内容であり、オーケストレーターは単なる開いたソケットではなく、真にBolt対応のサーバーを確認できます:```sh
slater healthcheck localhost 7687 # exit 0 = healthy
docker exec slater /app/slater healthcheck # inside the container
## ワンショットクエリ
スクリプト、CIチェック、およびクイックルックアップのために、`slater query` はグラフの現在の世代をマウントし、プロセス内で単一の読み取り専用Cypherクエリを実行し、結果をJSONオブジェクトとして出力して終了します — サーバーもBolt接続も不要です。これはサーバーと同じ設定(ストレージバックエンド、暗号化キー、クエリ予算)を尊重します:```sh
# GRAPH defaults to `defaultGraph`. Without -q, normal datestamped logging
# (config, "opened generation", …) is written to stdout alongside the result.
slater query mygraph 'MATCH (n) RETURN count(n) AS c'
# -q/--quiet ⇒ logging suppressed, so stdout is *only* the compact result JSON
slater query mygraph -q 'MATCH (c:Company) RETURN c.ticker AS t LIMIT 3' | jq
# {"columns":["t"],"rows":[["AUPH"],["KYMR"],["MREO"]]}
ノードとリレーションシップは、そのラベル/タイプとプロパティに展開されます。機械が解析可能な出力が必要な場合は-qを使用してください(結果のJSONのみがstdoutに出力されます)。省略すると、オペレーター向けのログ付き実行になります。-qを付けない場合、各実行後にメトリクスのみのサマリーがログに記録されます。例:```text
INFO query executed cost=2389 resultCount=10 execMs=441 limitRowCount=10
`cost`(課金対象要素)、`resultCount`、`execMs`、および
`limitRowCount`(クエリが`LIMIT`を指定している場合のみ)を運びます — クエリテキストや
結果値は一切含まれません。終了ステータスは、成功時は`0`、パース・オープン・実行
エラー時は`1`(メッセージはstderrに出力)です。
## グラフのエクスポート(`slater dump`)
`slater dump`は、**実行中**のサーバーからグラフをビジネスキー`MERGE`
Cypherとしてエクスポートします — `slater-build`が取り込むのと同じ方言です — そのため、グラフは
(ダンプ → `slater-build` → 新しい世代)と往復し、移行やテキストバックアップに使用できます。
`slater query`とは異なり、**Bolt**経由で接続し、認証を行い、グラフごとの
ACLを尊重するため、サーバーへのディスクアクセスは不要です。パスワードは
`SLATER_DUMP_PASSWORD`またはstdinから読み取られます(フラグでは決して渡されず、`ps`や履歴に残りません)。```sh
# List the graphs the authenticated user may read.
SLATER_DUMP_PASSWORD=pw slater dump --list -u reporting
# Dump a graph to a file (identity keys inferred from range indexes).
SLATER_DUMP_PASSWORD=pw slater dump people -u reporting -o people.cypher
# Rebuild it into a fresh generation.
slater-build --input people.cypher --graph people --data-dir ./data
各ラベルのIDキーは、その範囲インデックスが保持するプロパティです。--key Label=prop(繰り返し可能)またはグローバルな--pk <field>で上書きします。再構築時にインデックスが再作成されるよう、CREATE INDEX DDLが最初に出力されます。マルチラベルノードはすべてのラベルを保持します — MERGE (n:Ident:Other {key: v})として出力され、IDラベル(ビジネスキーを提供するラベル)が最初に、残りはソートされて続きます。マージはIDラベルのみをキーとするため、後続のラベルは別のノードを作成せずにノードに書き込まれます。特殊文字を含むラベル、リレーションシップタイプ、プロパティキーは出力時にバッククォートで引用されるため、通常とは異なる名前も忠実に往復し、再構築にCypherを注入できません。ベクター(およびCypherリテラル表記のないその他の値)はMERGEダンプに乗せられず、stderrに警告を出して破棄されます。終了ステータスは成功時0、エラー時1です。
完全で実行可能なウォークスルー — グラフの構築、サーブ、neo4j JavaScriptおよびPythonドライバーでの接続、書き込み — は、マニュアルの**QuickstartおよびWriting data**ページにあり、docs/manual/examples/内の同梱サンプルグラフを使用しています。
export PATH="$HOME/.cargo/bin:$PATH" cargo build cargo test # unit + the bounded-RSS headline integration test cargo clippy --all-targets -- -D warnings cargo fmt --all -- --check
### オブジェクトストアバックエンドはオプトインのcargoフィーチャーです
通常の`cargo build`では**ファイルシステムのみ**のバイナリが生成されます — `s3`および`gcs`
バックエンドはcargoフィーチャーの背後にゲートされているため、デフォルトのビルドは小さく保たれます(AWS
またはGoogle SDK、非同期ランタイムは不要)。必要なものを**両方**の`slater`
(serve)と`slater-build`(publish)で有効にしてください:```sh
# S3 only / GCS only / both
cargo build -p slater -p slater-build --features s3
cargo build -p slater -p slater-build --features gcs
cargo build -p slater -p slater-build --features s3,gcs
各クレートは、graph-format/{s3,gcs} に転送される一致するs3 / gcsフィーチャーを公開しています。実行時にバックエンドを要求する場合(dataBackend.kind=s3|gcs、またはslater-build --publish-{s3,gcs}-*)、そのフィーチャーがコンパイルされていないと、「built without the … feature」という明確なエラーで即座に失敗します。公開されているDockerイメージは両方を有効にしています(DockerfileのCARGO_FEATURES)。そのため、ビルド済みイメージには追加のフラグは不要です。これはソースからビルドする場合にのみ関係します。統合テストも同様にゲートされています:--features s3 --test s3_minio、--features gcs --test gcs_emulator(fake-gcs-server)、--features gcs --test gcs_real(ADC経由の実GCS)。それぞれ、SLATER_*環境変数が設定されていない限りスキップされます。
設計、マイルストーン台帳、および決定ログについては、docs/PLAN.md、docs/PROGRESS.md、docs/DECISIONS.mdを参照してください。
最大6つのエンジン、1つのシングルクライアントスイート、62kノードのおもちゃからWikidata 91.6Mノード / 1.5Bエッジまでのグラフ。各エンジンは単独で測定されます(他のすべてのコンテナは停止 — RSSとレイテンシはそのエンジン自身のフットプリントです)。以下のレイテンシテーブルは、Slater 0.21.0(書き込み可能なビルド)で再測定されました:小/中規模グラフ(MeSH、EU-AI-Act)は新規に、91.6Mグラフは新しい同一マシン、共有アンカーでのslater対Neo4jの比較パスとして(そのテーブルを参照)。常駐メモリの数値は、以前のパスから引き継がれています(コンテナのcgroupで測定;読み取りパスは書き込み可能レイヤーがアイドル状態のものとバイト単位で同一)。他のエンジンの数値は、確立されたクロスエンジン実行のものです(それらのバージョン/パフォーマンスは変更なし)。すべての数値は中央値(ms)またはピーク常駐メモリ(MiB)です。すべてにおいて低いほど良く、太字 = 行内で最良。 slaterはローカルファイルシステム(fs)バックエンドで実行されました;S3およびGCSバックエンドは、ローカル読み取りレイテンシをオブジェクトストアのラウンドトリップと交換します(インメモリキャッシュとオプションのローカルディスクキャッシュ層で緩和)。したがって、これらの数値はエンジン自体を特徴付けるものであり、ネットワークストレージのデプロイメントではありません。
ディスクからページングする3つのエンジン — slater、Neo4j 5、LadybugDB — は、5つすべてのグラフをロードします。インメモリの3つ組(Memgraph · FalkorDB · ArcadeDB)は、1.5Bエッジのグラフをまったく保持できず(約64〜128 GiBの常駐が必要)、ArcadeDBのインポーターもそれを完了できません。
各数値はコミットされたワーキングメモリです — OSが回収できないものです。slater 以外のすべてのエンジンは、グラフをコミットされた匿名メモリ(独自のヒープ、Neo4jのオフヒープページキャッシュ、またはバッファプール)に保持するため、そのピークRSSはそのコミットされたフットプリントです。slaterだけが、オンディスクストアの回収可能なOSページキャッシュから提供するため、その数値は匿名ワーキングセットです;ストアのページキャッシュ(圧力下で退避可能 — slaterはサービスを継続)は除外され、91.6Mグラフでは括弧内に合計として表示されます。太字 = 最低。
slaterはあらゆるスケールで最低であり、グラフが約1,500倍に成長する間に約50倍に成長します — そのフットプリントはグラフではなくクエリのワーキングセットを追跡します(全体を通してアイドル時約16〜71 MiB)。インメモリの3つ組はほぼ線形に成長し、1.5Bグラフをロードできません;Neo4jはクエリに関係なく約2 GiBのヒープをコミットします。(† LadybugDBは制限されたシェイプのみ — 1.5Bエッジでのハブ / 可変長 / 最短パストラバーサルは、slaterの自動maxIntermediateキャップに対して、読み取りプールを2 GiB以上に引き上げる必要があります。)ビルド時の値→カウントヒストグラムは、無視できるほどの常駐メモリを追加します — 低カーディナリティのインデックス付きカラムでは数KB、Wikidataのようなユニークキーグラフではゼロ(wikidata_idはヒストグラムのカーディナリティ上限を超えるため、何も保存されません)— したがって、これらの数値はその機能によって変更されません。
slaterはメタデータ / インデックス / スキャンのシェイプ(count、label、idx-eq、scan — 約0.4 ms、サービスエンジンの10〜200倍)、インデックス付きポイントルックアップ(0.43 ms、現在はインメモリペアの0.48 msをわずかに上回る)、アンカーなしのマルチホップ(リレーションシップタイプスキャンによる2ホップ1.40 ms、この分野で最速)、および — インデックス付きグループ化キーに対するビルド時の値→カウントヒストグラムを介して — ラベル全体のgroup-by / count(DISTINCT)(0.45 ms、LadybugDBのカラム型5.3 msを上回る)を所有しています。インメモリサーバーは生の1ホップのみを保持します(Memgraph 1.21 ms vs slaterの1.28 ms)。(pole 62k/106kも同様に見えます:slaterはcount/scanで約0.4 ms、ホップで約1.3〜2.6 msと唯一最速です。)
slaterは、完全なブルートフォーススキャンでkNNに応答します(これらのセットは50kベクトルのANNしきい値を下回っています)。他のエンジンは近似の常駐HNSWを使用します — したがって、slaterの結果は正確です(再現率1.0)。SIMD距離カーネル + 常駐の事前正規化ベクトル行列により、Conceptは約23 → 約2.9 ms、Chunkは約10 → 約2.4 msになり、slaterは現在Neo4jとLadybugDBを上回り、Memgraphの約1.4倍以内で、FalkorDBにのみ遅れを取ります — しかも正確です。
上記のテーブルは、クロスエンジンの読み取り比較です。ベクトル書き込みパス(静的Vamanaベース上のFreshDiskANNスタイルの書き込みラダー)には、クロスエンジンの対応物がありません — ここでディスクネイティブで書き込み可能なANNを行うエンジンは他にないため — 以下の数値は、合成の埋め込み風フィクスチャ(低ランク多様体、次元768、不等ノルム)に対するシングルエンジンのコンポーネントベンチマークであり、crates/slater/benches/の下でコミットされ、docs/PERF-REPORT.mdで方法論とすべての注意事項を含めて完全に文書化されています。再現率は常に、ライブセットに対する正確なブルートフォースに対して測定され、インデックス同士の比較ではありません。ここでのスケールは代表的であり、メトリックがサイズ線形である場合にのみ外挿されます。
専用のパフォーマンスボックスを必要とする唯一の数値は、スローパスの統合書き換えスループットです — 統合が純粋な置換ではなく削除や新しいベクトルを伴う場合、シングルスレッドのzstdとローカルディスクに制限されるシーケンシャルな再圧縮であるため、絶対的なMiB/sは環境固有です(レポートは形状を示し、環境の範囲を説明しています)。
インメモリエンジン(Memgraph / FalkorDB / ArcadeDB)は、このグラフをまったくロードできません(約64〜128 GiB常駐)。slaterとNeo4j 5のみが対応します。これは新しい同一マシン、同日パスで、共有された固定アンカーセットに対して行われます — すべてのクエリが両エンジンで同一のノードにヒットするため、直接比較は公平です(中程度の次数のアンカーの共通wikidata_idプール;その重要性については以下の注記を参照)。slaterは両方のファンアウトで示されています(query.maxFanout 1 = スループットデフォルト、8 = コールドブロック読み取りと重なるレイテンシダイヤル)。太字 = 行内で最良。
正直なところ:slaterはメタデータ / インデックスシェイプを支配します — count(*)はメタデータ提供(0.41 ms vs Neo4jの3.6秒ディスクスキャン、約8800倍)、ポイントルックアップ / 次数 / 3ホップは約2〜10倍高速 — 1〜2ホップではNeo4jと互角(ファンアウト8はコールド読み取りで先行)、しかし**var-length *1..2 distinctでは決定的に負けます(約1秒 vs Neo4jの47 ms)**:slaterの可変長distinct展開はここでは著しく遅く、独自の調査に値する実際の弱点です。これらすべてが、Neo4jのコミットされた約2 GiBヒープに対して、数百MBのRSSで実現されています。
アンカーについて。 これらのトラバーサル数値は、どのノードから開始するかに大きく依存します — Wikidataのメガハブ(「human」、「country」)から1リンクのノードは、数百万規模の2ホップ近傍を持つため、可変長/ホップコストはアンカーの選択によって桁違いに変動します。このテーブルの以前の版は、各エンジン自身の「スキャンによる最初のN」をサンプリングしていましたが、これは安定しておらず比較もできません;今回のパスでは、両エンジンに対して単一の共有された次数制限付きアンカーセットを固定しています。(shortestPathは今回のパスから省略されています — 2つの任意のアンカー間ではパス存在依存であり、中央値を意味のあるものにするには分散が大きすぎます。)
count(*) — メモリが結果サイズから切り離されるキャップなしのマルチホップRETURN count(*)は、一致した行をマテリアライズする代わりに、展開中にカウントします。91.6Mグラフ上の同じハブアンカー、maxIntermediate=20M:
| 3-hop count(*) @ 91.6M | fanout=1 | fanout=8 |
|---|---|---|
| latency / peak working set | 554 ms / 0.66 GiB | 298 ms / 1.9 GiB |
カウントはO(1)行を保持します。課金は変更されていないため、メガハブのカウントは依然として計算(隣接読み取り)でmaxIntermediateに達しますが、以前と同様に制限されます。
maxFanout)query.maxFanoutを上げると、クエリのコールドでI/Oバウンドなブロック読み取りがコア間でオーバーラップします — 大きなコールドワーキングセットのディスクバウンドシェイプに役立ち、ウォームシェイプではフラットです。1.5Bグラフ:shortestPath ≤6 918 → 608 ms(1.5倍、最大検索6,269 → 2,350 ms、2.7倍);3ホップカウント547 → 298 ms。maxFanout=1がデフォルト(スループット指向);8はレイテンシダイヤルで、より多くの一時的なワーカーメモリを使用します。
完全なエンジン別テーブル(pole、MeSH、EU-AI-Act + blockCacheBytes RAM↔レイテンシダイヤル、Wikidata 1M & 91.6M)はperf/cross-engine-hs/README.mdにあります;新しいslaterのみのパス(両方のファンアウト、すべてのデータセット)はperf/PERF_CURRENT_STATUS.mdにあります。
上記のベンチマークはシングルクライアントです。補完的な軸 — 多数の同時クライアント下での動作 — には独自のハーネス、perf/loadtest/があります:Bolt上のLocustドライバーと、負荷をランプさせ、CALL slater.diagnostics()を読み取り、容量の膝を見つけ、制限要因を特定するコーディネーター(完全な方法はdocs/LOAD-TESTING.mdにあります)。Wikidata-1Mグラフ(1台の16コアマシン)での256 MiBキャッシュ実行からのヘッドライン:
負荷テストで表面化した両方のメモリ問題は現在クローズされています;すべて負荷テストドキュメントで追跡されています。
Apache License、Version 2.0の下でライセンスされています。全文はLICENSE、帰属表示はNOTICEを参照してください。明示的に別段の記載がない限り、Apache 2.0ライセンスで定義されているように、この作品に含めるために意図的に提出された貢献は、追加の条件なしに上記のとおりライセンスされます。
SPDX-License-Identifier: Apache-2.0
REMOVEDELETE| 機能 | あなたにとっての意味 |
|---|
| 制限された予測可能なメモリ | 常駐メモリは、あなたが設定する3つのキャッシュ予算を追跡し、エントリごとおよびアロケータのオーバーヘッドの範囲内で制限されます — グラフサイズに応じて成長することはありません。グラフ全体にプロビジョニングする代わりに、パフォーマンス/RAMのトレードオフを調整します。バックグラウンドパージ付きのjemallocアロケータは、重いクエリバースト後に解放されたメモリをOSに返すため、常駐サイズはバースト後の高水位マークに固定されるのではなく、アイドル時の下限に戻ります。 |
| すぐに使えるマルチテナント | 1つのサーバーが、ユーザーごとの読み取り権限を持つ多くのグラフをホストします — ほとんどのグラフDBが有料/エンタープライズ層に予約しているマルチデータベース分離です。 |
| 保存時および転送中の暗号化 | ブロックごとのXChaCha20-Poly1305シーリング(キーはディスクに書き込まれることはありません)に加えて、オプションのTLS(bolt+s://)。設計上GDPRに準拠しています。暗号化は認証された整合性ももたらします: ビルダーはキー付きMACでマニフェストをシールし、キーを保持するサーバーはそれを検証し、マニフェストが偽造、変更、またはMACが剥がされた世代の提供を拒否します。キーなし(平文)イメージは、キーなしのコンテンツハッシュのみで保護されます — 完全性と破損の検出であり、改ざんの検出ではありません。各設定における整合性の意味を参照してください。 |
| 小さなインストール | distroless glibcベース上の小さなストリップ済みバイナリ(シェル/aptなし)— マルチアーキテクチャ(amd64/arm64)イメージは約22MB、サーバーのみのslater:latest-liteタグは約12MBです。純Rust TLS、OpenSSLなし。プルして実行するだけです。 |
| 定期的な公開用に設計 | グラフをオフラインでビルドし、不変として提供し、新しいバージョンをゼロダウンタイムで原子的に交換 — データウェアハウス/スケジュール更新ワークロードに最適です。 |
| 負荷下でも堅牢 | サーバーとオフラインビルダーの両方が#![forbid(unsafe_code)]でコンパイルされます — エンジンの唯一のunsafeは監査済みのjemallocアロケータクレートにあります。コアは不変であるため、読み取りはロックを取らず、ライターを待つことはありません。単一のライターが書き込みパスの背後でミューテーションを直列化します。GCポーズなし、データレースなし。1つの悪いクエリでサーバーをダウンさせることはできません。 |
| neo4jツールと連携 | Bolt 5.4 / 4.4 / 4.1を話します — 標準のneo4jドライバ(JS、Python、Go、Javaなど)、cypher-shell、またはグラフブラウザをそのまま使用できます。 |
| 豊富なCypherクエリサーフェス | 広範な読み取りサーフェス: MATCH/WHERE/WITH/UNION、CALL {…}サブクエリ、70以上の関数と集計、時間および地理空間値、正規表現。 |
| ライブで永続的な書き込み | 不変コア上のオプトイン単一ライターLSMレイヤー(delta.enabled): ノードと関係に対するビジネスキーMERGE / SET / DELETE / CREATE / REMOVE、バッチ書き込みUNWIND(バッチごとに1回のfsync)、およびCALL slater.consolidate() — グループコミットされ、fsyncで永続化され、統合によって新しいコアに折り畳まれます。デルタが空の場合、読み取りパスはバイト単位で同一です。 |
| ISO GQL、読み取りと書き込み | 同じBolt接続上でISO GQL(ISO/IEC 39075)のサブセットを話します — 定量化パス、パス制限子、最短パスセレクタ、ラベル/タイプブール式、FOR、CAST、オプションのGQL/CYPHER方言プレフィックス — そして書き込みレイヤーがオンの場合、GQLのデータ変更ステートメント(INSERT / SET / REMOVE / [DETACH] DELETE)は同じ永続的な書き込みパスに降りてきます。CypherとGQL、読み取りと書き込み、1つのエンジンで。 |
| ベクトル+グラフを1つのエンジンで | 埋め込み/RAG用のディスクネイティブANNベクトル検索(Vamana + PQ; コサイン / L2 / ドット)に加えて、グラフアルゴリズム(PageRank、BFS、媒介中心性、WCCなど)— 数百万のベクトルでもメモリは制限されます。埋め込みは書き込み可能です(FreshDiskANNスタイルの書き込みラダー): ベクトルの挿入 / 更新 / 削除、即座にKNNで可視、再構築なしでベースに折り畳まれます。 |
| ネットワークストレージでも安全 | すべてのファイルはBLAKE3コンテンツハッシュ化され、オープン時に検証されます。破損または半分コピーされたイメージは提供されず拒否されます。NFS/リモートボリューム用に設計されています(mmapの驚きはありません)。 |
| プラグ可能なストレージバックエンド | 同じ世代フォーマットをローカルファイルシステム、S3(S3互換)バケット、またはGoogle Cloud Storageバケットから提供 — 一度公開してステートレスレプリカに展開 — オブジェクトストアの前のオプションのローカルSSDキャッシュ層付き。ストレージバックエンドを参照してください。 |
| パス | 目的 | 備考 |
|---|
/data | グラフ生成結果(<graph>/<uuid>/… + current)。 | レプリカでは読み取り専用。slater-buildによって生成されます。リモート/ネットワークストレージ(例:NFS)上にある場合があるため、読み取りは高速なローカルSSDレイテンシを前提としていません。 |
/sandbox | 環境ごとの設定オーバーレイ + シークレット。 | /sandbox/config.jsonは組み込みのconfig.jsonにディープマージされます。acl.json、TLS PEM素材、保存時暗号化キーファイルも保持します。 |
/tmp、/run | スクラッチ(tmpfs)。 | 読み取りレプリカはデフォルトでディスクに書き込みません。 |
(ライター) delta.walDir | delta.enabled時の書き込み先行ログ + L0デルタセグメント。 | 書き込み可能であり、永続的な実ボリューム — 決してtmpfsではありません(耐久性の下限です)。相対パスはデータディレクトリの下で解決されます。ライターにはここに独自の永続ボリュームを割り当ててください。 |
| (オプション) ディスクキャッシュ | dataBackend.s3.diskCacheBytes / dataBackend.gcs.diskCacheBytes > 0時のローカルディスクブロックキャッシュ。 | 書き込み可能であり、実ボリューム — tmpfsではありません。s3およびgcsバックエンドで使用されます。ストレージバックエンドを参照してください。 |
["read", "write"]| engine | class | memory bound |
|---|
| slater | disk-backed, paged | query.maxIntermediateがワーキングセットを自動的に制限 |
| Neo4j 5 | disk-backed, JVM | クエリに関係なくコミットされる約2 GiBヒープ + オフヒープ |
| Memgraph · FalkorDB | in-memory | グラフ全体がRAMに常駐 |
| ArcadeDB | in-memory, JVM | グラフ全体が常駐;最も重い |
| LadybugDB | embedded, columnar | クエリを超えなければならない手動バッファプール |
| graph (nodes / edges) | slater | Neo4j 5 | Memgraph | FalkorDB | ArcadeDB | LadybugDB |
|---|
| pole — 62k / 106k | 11 | 746 | 114 | 140 | 1,556 | 198 |
| MeSH — 341k / 469k | 63 | 1,083 | 358 | 455 | 1,631 | 121 |
| EU-AI-Act — 21k / 45k (+55 MiB vec) | 99 | 729 | 229 | 312 | 1,948 | 286 |
| Wikidata — 91.6M / 1.5B | 584 (4,595 total) | ~2,900 | cannot-load | cannot-load | cannot-load | ~652 † |
| shape | slater | Neo4j 5 | Memgraph | FalkorDB | ArcadeDB | LadybugDB |
|---|
| count(*) all nodes | 0.41 | 15.0 | 23.8 | 16.4 | 82.0 | 2.2 |
| label count | 0.42 | 4.2 | 20.7 | 1.1 | 4.4 | 4.3 |
| indexed point lookup | 0.43 | 3.9 | 0.48 | 0.48 | 0.65 | 8.8 |
| idx-eq count | 0.42 | 4.9 | 5.0 | 2.0 | 381 | 2.5 |
| 1-hop (indexed anchor) | 1.28 | 5.8 | 1.21 | 4.1 | 390 | 4.9 |
| 2-hop (unanchored) | 1.40 | 5.6 | 8.5 | 16.7 | 444 | 6.4 |
| group-by / count(DISTINCT) | 0.45 | 47–51 | 63–64 | 31–39 | 411 | 5.3 |
full-scan CONTAINS | 0.43 | 5.4 | 24.1 | 1.7 | 16.3 | 4.1 |
| shape | slater | Neo4j 5 | Memgraph | FalkorDB | LadybugDB |
|---|
| kNN top-10 Concept | 2.9 | 8.6 | 1.9 | 1.2 | 2.8 |
| kNN top-10 Chunk | 2.4 | 5.7 | 1.9 | 1.5 | 3.2 |
| property | measured | why it matters |
|---|
| KNNレイテンシ vs 保留中の書き込み | RWインデックス約1.5〜2 ms、50k保留までフラット;プリインデックスのブルートフォースオーバーレイ1.9 → 115 ms(デルタに線形)— 50kで61倍 | 統合の間に書き込みが蓄積しても、クエリレイテンシは劣化しません |
| 埋め込み挿入 | ライブインデックスへのベクトルあたり約1.5〜2 ms | 書き込みは即座にKNNで可視化されます;デルタリビルドの予算は約2 ms × デルタキャップ |
| 等再現率での削除IO | 67%削除でクエリあたりのノードフェッチが2.9倍少ない、80%で5.2倍(再現率 ≥ 0.90) | 統合されたグラフは、削除されたベクトルに対して読み取り税を支払いません |
| 統合、純粋な置換 | O(1) — .vamanaはハードリンクされバイト単位で同一、IDカラムのみが書き換えられます | ベクトル書き込みをベースに折り込むと、O(N·R·L)リビルドがスキップされます |
| ラダー全体での再現率 | cosine、L2、dotで統合後 ≥ ベース | 書き込みラダーはすべての段階で再現率を維持します |
| shape | slater (fan 1) | slater (fan 8) | Neo4j 5 |
|---|
| count(*) all nodes | 0.41 | 0.41 | 3606 |
| point lookup (indexed) | 0.72 | 0.49 | 6.3 |
| degree (1-hop count) | 0.43 | 0.44 | 6.0 |
| 1-hop neighbours | 9.8 | 4.5 | 10.1 |
| 2-hop | 37 | 23 | 34.5 |
| 3-hop | 32 | 25 | 74 |
var-length *1..2 distinct | 985 | 1056 | 47 |
| dimension | slater | best of the field | verdict |
|---|
| 常駐メモリ、任意のスケール | 11–584 MiB (62k → 91.6M) | インメモリ1.5–2.7 GiB;1.5Bをロード不可 | slater |
| count / メタデータ / スキャン | 約0.4 ms | サービスエンジン5–80 ms | slater (10–200倍) |
| インデックス付きポイントルックアップ | 0.43 ms (MeSH) | Memgraph · FalkorDB 0.48 ms | slater (インメモリペアをわずかに上回る) |
| アンカーなしマルチホップ(行) | 1.40 ms (MeSH 2ホップ) | Neo4j 5.6 ms | slater (リレーションシップタイプスキャン) |
| 集計(group-by / DISTINCT) | 0.45 ms | LadybugDB 5 ms (カラム型) | slater (ビルド時ヒストグラム) |
| kNN | 2.4–2.9 ms (正確) | FalkorDB 1.2 ms (HNSW) | Neo4j/Ladybugを上回る;Memgraphの約1.4倍;正確 |
| 91.6Mメタデータ / ポイント / 次数 / 3ホップ | 0.4–32 ms | Neo4j 6–3,600 ms | slater (2–8800倍) |
| 91.6M 1–2ホップ | 4.5–23 ms (fan 8) | Neo4j 10–35 ms | ほぼ互角 |
91.6M可変長 *1..2 distinct | 約1秒 | Neo4j 47 ms | Neo4j (slaterの実際の弱点) |
スケールでのマルチホップ count(*) | 0.3–0.6 GiB | インメモリエンジンは行セットをマテリアライズ | slater、制限付き |
| result | measurement |
|---|
| 1000の同時クライアント、ゼロ障害まで保持 | スループットは約2.5k rpsでピーク;レイテンシの膝は約750クライアントで発生(p99 51 → 750 ms)— コア競合下でのキューイングであり、ハードキャップではない(単一実行、WSL2) |
| ブロックキャッシュは制限付きで効果的 | 100%ヒット率、0エビクション、キャッシュに収まるワーキングセットで50 MB常駐 |
| 持続負荷下でのRSS | jemallocアロケータは、100→500クライアントのwiki_cache_churnランプ全体でRSSを約0.6 GBに保持 — キャッシュバウンドで安定、MALLOC_*チューニングなし(以前のMALLOC_ARENA_MAX=2 + トリムしきい値は廃止);そのバックグラウンドパージは、バースト後の高水位を固定したままにするのではなく返します |
| 集約メモリの制限 | サーバー全体の**query.maxIntermediateGlobal** + 隣接課金の展開により、wiki_budget 2ホップフラッドを1000クライアントでOOMなしに保持(RSS約0.6 GB;ガードはハブクエリの約60%を再試行可能な予算エラーとして切り捨てます) |