Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
slater — قاعدة بيانات رسومية منخفضة استهلاك الذاكرة مع دعم Bolt+TLS، وتشفير عند التخزين، ومتجهات مصممة لحالات استخدام النسخ المتماثل المحلي للرسوم البيانية. | Kitploit
أدوات/GitHubGitHub/hikari-systems/slater
المصادقة والترخيصأدوات التشفير/فك التشفيرأمن الشبكاتأمن السحابةالأدوات والمكوناتأمن قواعد البيانات
GitHubhikari-systems/slater

slater

قاعدة بيانات رسومية منخفضة استهلاك الذاكرة مع دعم Bolt+TLS، وتشفير عند التخزين، ومتجهات مصممة لحالات استخدام النسخ المتماثل المحلي للرسوم البيانية.

عرض المستودع
10023منذ 24 أيامتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

Slater

CI Release

الإصدار الحالي: v0.25.2 — جميع الإصدارات.

في سطر واحد: يقدّم Slater رسومًا بيانية لا تتسع في الذاكرة — مئات الملايين من العقد ومليارات الحواف في بضع مئات من ميغابايتات RAM — عبر بروتوكول Bolt القياسي، بحيث يعمل أي برنامج تشغيل neo4j دون تعديل، مع بحث متجهي أصلي على القرص بجوار الرسم البياني، ويقبل كتابات حية ودائمة دون التخلي عن ذلك. يتم تحديد الذاكرة المقيمة بميزانية ذاكرة تخزين مؤقت تختارها أنت، وليس بحجم الرسم البياني.


اختصارات

لماذا يوجد Slaterالقراءات والكتاباتما الذي تحصل عليهالميزات
التشغيل مع Dockerكيف يعملالطبقة القابلة للكتابةخلفيات التخزين
التركيباتالإعدادACLفحص الصحة
مثال عمليالتطويرالأداءالترخيص
مخزن ذاكرة Graphiti📖 الدليل الكامل

لماذا يوجد Slater

قاعدة بيانات الرسوم البيانية تخزّن البيانات كـأشياء (عقد) والعلاقات بينها (حواف)، مع اعتبار العلاقات مواطنين من الدرجة الأولى. هذا ما تريده عندما تكون أسئلتك حول الاتصالات بدلاً من الصفوف — "من يقع ضمن ثلاث قفزات من هذا الحساب؟"، "ما هي سلسلة التبعيات الكاملة وراء هذا البناء؟"، "أي الحسابات تشترك في جهاز وعنوان وبطاقة؟" — الاستعلامات التي تتحول إلى مستنقع من الانضمامات العودية في SQL لكنها تخرج بشكل طبيعي في رسم بياني.

الشكوى الأكثر شيوعًا حول قواعد بيانات الرسوم البيانية هي أنها لا تتوسع إلى ما يتجاوز ما يمكنك الاحتفاظ به في RAM. العديد منها (مثل neo4j وMemgraph وFalkorDB وغيرها) يُبقي الرسم البياني كاملاً مقيمًا: رسم بياني بحجم 40 GB يتطلب 40 GB من الذاكرة — لكل مثيل. تريد نسخة متماثلة لكل منطقة أو مستأجر أو pod؟ اضرب الفاتورة. وبعد حجم معين لن يتم تحميلها ببساطة: على سبيل المثال، رسم Wikidata البياني الذي يضم 90 مليون عقدة / 1.5 مليار حافة يحتاج إلى ~64–128 GiB مقيمًا، لذا لا يمكن لمحركات الذاكرة فتحه إطلاقًا.

Slater هو الرد. بدلاً من تحميل الرسم البياني في الذاكرة، يقوم بتجميعه مرة واحدة، دون اتصال: يحوّل slater-build بياناتك إلى صورة على القرص غير قابلة للتغيير وموجهة بالمحتوى، ثم يخدم أي عدد من خوادم Slater تلك الصورة عبر Bolt (بحيث تعمل برامج تشغيل neo4j الحالية لديك دون تعديل)، مع ترحيل الكتل عند الطلب والاحتفاظ فقط بميزانية ذاكرة تخزين مؤقت ثابتة مقيمة. هكذا يخدم نفس الرسم البياني ذو 90 مليون عقدة من بضع مئات من ميغابايتات RAM — يتم فصل حجم الرسم البياني عن فاتورة الذاكرة. رسم بياني بحجم 4 GB ورسم بياني بحجم 400 GB يكلفان نفس RAM للخدمة، لذا يمكنك نشر نسخ متماثلة للقراءة رخيصة وعديمة الحالة والسماح للمخزن، وليس الكومة، بحمل الرسم البياني.

هذا يجعله مناسبًا بشكل طبيعي للرسوم البيانية المعرفية خلف RAG، ورسوم التوصيات والهوية، ورسوم التبعيات — أي شيء كبير ومترابط تريد الاستعلام عنه بتكلفة منخفضة وبشكل متكرر. البحث المتجهي الأصلي على القرص يعيش بجوار الرسم البياني مباشرة، لذا فإن نفس المحرك هو طبقة الاسترجاع للتضمينات أيضًا.

لكن التجميع مرة واحدة لا يعني التجميد. تلك الصورة هي أساس، وليست حالة نهائية: طبقة كتابة اختيارية تقع فوقها، بحيث يمكن تصحيح رسم بياني حي وتوسيعه دون إعادة بناء أي شيء.

القراءات والكتابات

النواة غير قابلة للتغيير؛ الرسم البياني ليس كذلك. فعّل الطبقة القابلة للكتابة (delta.enabled) وستكتب عبر Bolt — صحّح خاصية واحدة، أضف عقدة، اسحب حافة — وستُسجَّل التغييرات بشكل دائم، دون إعادة بناء الصورة. ما يُبقيها رخيصة على جانب القراءة هو مكان عيش الكتابات.

تتراكم الكتابات في طبقة دمج سجل-منظم (LSM) فوق النواة غير القابلة للتغيير: سجل كتابة مسبق وجدول في الذاكرة، مع انسكاب إلى مقاطع دلتا غير قابلة للتغيير، تُطوى مرة أخرى في نواة جديدة بواسطة توحيد دوري. ما الذي يشتريه لك ذلك:

  • القراءات عبر رسم بياني غير مكتوب تكلف بالضبط ما كانت تكلفه من قبل. دلتا فارغة هي فرع واحد يمكن التنبؤ به، وليست دمجًا — مسار القراءة متطابق بايتًا سواء كانت الطبقة القابلة للكتابة مفعّلة أم لا.
  • تكلفة قراءة الكتابة تتدرج مع حجم الدلتا، وليس مع حجم الرسم البياني. إجابات الرسم البياني الكامل — count(*)، والهوامش الخاصة بالتسميات وأنواع العلاقات — تبقى قراءات بيانات وصفية حتى مع وجود كتابات معلقة: تحتفظ الدلتا بعداداتها الخاصة، لذا فإن count(*) عبر نواة بحجم 91.6 مليون عقدة مع نصف مليون كتابة معلقة لا يزال يجيب في عشرات المللي ثانية دون لمس كتلة واحدة.
  • الإقرار يعني الديمومة. كاتب واحد يفرغ قائمة الانتظار ويعيد SUCCESS فقط بعد fsync الذي يغطي الكتابة. جمّع كتاباتك وستكون رخيصة — كتابة-UNWIND تلتزم fsync واحدًا لكل دفعة بدلاً من كل صف.
  • كتابات بمفاتيح الأعمال، في أي من اللهجتين. MERGE / MATCH … SET / DELETE (وCREATE / REMOVE، وحذف الفصل، وكتابات العلاقات) مفتاحية على خاصية هوية العقدة — أو عبارات ISO GQL المكافئة لتعديل البيانات ( / / / )، التي تنزل على نفس المسار. صحّح وأدرج وحدّث واسحب، عبر العقد والحواف، موجهة بالطريقة التي تُخاطب بها بياناتك بالفعل.

مع إيقاف الطبقة — الافتراضي — يخدم Slater النواة غير القابلة للتغيير النقية ويرفض الكتابات. راجع الطبقة القابلة للكتابة للنموذج الكامل.

حول الاسم. سُمي Slater على اسم عميل CIA في Archer (مسلسل رائع) الذي يصر على استخدام اسم واحد فقط — "فقط… Slater" — وهو أحد شخصياتي المفضلة فيه. راجع صفحة الويكي للشخصية.

ما الذي تحصل عليه

  • RAM تحدده ميزانية ذاكرة التخزين المؤقت، وليس حجم الرسم البياني — انشر أكبر عدد تريده من نسخ القراءة المتماثلة؛ لا يجب أن يتسع الرسم البياني في الذاكرة أبدًا.
  • بديل جاهز للرسم البياني — يتحدث Bolt، لذا يعمل أي برنامج تشغيل neo4j قياسي (JS وPython وGo…) دون تغيير. إنها Cypher (بالإضافة إلى شريحة من ISO GQL، قراءات وكتابات)؛ لا شيء جديد لتتعلمه.
  • كتابات حية ودائمة — طبقة LSM اختيارية فوق النواة غير القابلة للتغيير: MERGE / SET / DELETE بمفاتيح الأعمال عبر العقد والحواف، ملتزمة جماعيًا ودائمة عبر fsync، مطوية مرة أخرى في نواة جديدة بواسطة التوحيد. القراءات لا تدفع ثمنها.
  • النشر عبر تبديل الملفات — ابنِ جيلًا جديدًا موجهًا بالمحتوى دون اتصال، اقلب مؤشر current بشكل ذري، وسيلتقطه الخوادم. كل كتلة مُدققة المجموع الاختباري، لذا تُرفض الصورة نصف المنسوخة بدلاً من خدمتها.
  • بحث متجهي مدمج — بحث الجار الأقرب التقريبي الأصلي على القرص (cosine أو L2 أو dot KNN) يجلس بجوار رسمك البياني مباشرة، لحين استخدام هذا كطبقة استرجاع خلف خط أنابيب RAG، والتضمينات قابلة للكتابة في مكانها — لا إعادة بناء دون اتصال لإضافة أو تغيير متجه.
  • مؤمَّن بالتصميم — منح القراءة والكتابة مستقلة، بالإضافة إلى تشفير اختياري أثناء الراحة، وTLS Bolt، وACLs مجزأة بـ argon2id، ونظام ملفات جذر للقراءة فقط لنسخ القراءة المتماثلة. هيّئ مفتاحًا رئيسيًا وستكون الصورة على القرص موثَّقة بالإضافة إلى كونها مشفرة — يحمل بيانها MAC مفتاحيًا، لذا لا يمكن لمهاجم لديه حق الوصول للكتابة إلى دليل البيانات ولكن بدون مفتاح تزوير بيان سيقبله الخادم. بدون مفتاح لا تزال تحصل على تجزئة المحتوى، التي تلتقط صورة نصف منسوخة أو تالفة — ولكن ليس صورة متعمدة. أي إعداد يشتري ماذا.

الميزات

ثنائيان يشكلان مساحة العمل:

الثنائيالدور
slaterخادم Bolt عبر الإنترنت (نقطة دخول الحاوية): يخدم القراءات ومع delta.enabled، مسار الكتابة الدائم بكاتب واحد.
slater-buildالمترجم دون اتصال: يحوّل تفريغ Cypher بدائي إلى دليل جيل غير قابل للتغيير وموجه بالمحتوى.

يقسم Slater البناء بالجملة عن الخدمة: يقوم slater-build بالعمل الشاق دون اتصال — استيعاب بياناتك وتجميعها في جيل غير قابل للتغيير — بحيث لا يتم تجميع رسم بياني بارد أبدًا على المسار الساخن للخدمة. داخل الخادم، يجيب سطح القراءة على شريحة Cypher واسعة — مطابقة الأنماط، واستعلامات فرعية WITH/UNION/CALL {…}، وأكثر من 70 دالة عددية وتجميعية، وقيم زمنية وجغرافية، وخوارزميات رسوم بيانية (algo.*)، وKNN متجهي أصلي على القرص (db.idx.vector.queryNodes) — بينما تقع طبقة الدلتا العلوية للطبقة القابلة للكتابة تحت ذلك السطح وهي بتكلفة صفرية عندما تكون فارغة، بحيث لا تحمل القراءات أبدًا آليات جانب الكتابة. يمكنك تحديث رسم بياني بطريقتين: اكتب إليه حيًا عبر Bolt (راجع الطبقة القابلة للكتابة)، أو ابنِ جيلًا جديدًا دون اتصال واستبدل مؤشر current بشكل ذري، والذي يلتقطه الخادم الجاري عبر حارس الجيل الخاص به (راجع حارس الجيل).

التوثيق

يعيش دليل المستخدم الكامل في docs/manual/ — دليل ميزة-بميزة يشرح، لكل قدرة، ما هي، ولماذا توجد، وكيفية استخدامها، مع أمثلة عملية يمكنك تشغيلها مقابل رسم بياني عينة مرفق. ابدأ هناك لأي شيء يتجاوز هذه النظرة العامة.

  • جديد هنا؟ بدء سريع يبني ويخدم رسمًا بيانيًا في خمس خطوات.
  • تكتب استعلامات؟ الاستعلام، الدوال والتعبيرات، الإجراءات والخوارزميات، البحث المتجهي، كتابة البيانات.
  • تبني رسومًا بيانية؟ بناء الرسوم البيانية و مرجع CLI البناء.
  • تشغّل Slater؟ النشر، التخزين، مرجع الإعداد، الأمان، ضبط الأداء.

استخدام Slater كمخزن ذاكرة Graphiti

graphiti-slater هو محوّل يسمح لـ Graphiti بتخزين رسمه البياني المعرفي الزمني في Slater، مع docker-example/ قابل للتشغيل — بما في ذلك تعريضه لـ Claude Code كخادم MCP. راجع ذلك المستودع لكيفية عمله وكيفية تشغيله.

التشغيل مع Docker

صُمم Slater ليعمل كـنشر Docker — هذه هي الطريقة المتوقعة لاستخدامه. تُنشر صور متعددة البنى مبنية مسبقًا (linux/amd64 + linux/arm64) إلى Docker Hub على hikarisystems/slater، موسومة بـ :latest و:vX.Y.Z عند كل إصدار:```sh docker pull hikarisystems/slater:latest

root@kitploit:~
دليل استخدام 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

Build the image (both binaries).

docker compose build

Serve (expects generations under the slater-data volume / your /data mount).

docker compose up slater

Build a generation with the offline writer (profile build):

docker compose run --rm builder
--input /dumps/people.cypher --graph people --data-dir /data

root@kitploit:~
مرحلة البناء تثبّت `cmake` و`clang` و`libclang-dev` لخلفية rustls
`aws-lc-rs`؛ و`git` (الموجود بالفعل في الصورة الأساسية) مطلوب لاعتماد
`hs-utils` git+tag، الذي يجلبه `.cargo/config.toml` عبر واجهة git CLI.

تغطي الأقسام أدناه تنسيق التخزين على القرص، والإعداد، وقوائم التحكم في الوصول (ACLs)،
ومثالًا عمليًا محليًا (بدون 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.
  • كل كتلة مضغوطة بـ zstd ومُتحقق منها بـ BLAKE3؛ مع --encrypt تكون كل كتلة مختومة إضافيًا بـ XChaCha20-Poly1305 (AEAD عند التخزين).
  • يفتح الخادم الجيل عن طريق إعادة تجزئة كل ملف مقابل المانيفست، بحيث يتم رفض صورة منسوخة جزئيًا / مبتورة — نسخة ممزقة إلى دليل البيانات، والتي قد تكون تخزينًا شبكيًا/بعيدًا — بدلاً من تقديمها.
  • تمر القراءات عبر ثلاث مجموعات تخزين مؤقت محدودة — LRU للكتل المفكوكة الضغط، و مجموعة فهرس المتجهات (رموز PQ المقيمة + LRU لكتل Vamana)، وLRU للنتائج — ولكل منها ميزانية بايت خاصة بها. تزن كل مجموعة ما تحمله وتُخرج العناصر لتبقى تحت ميزانيتها، بحيث يتتبع RSS الميزانيات ضمن حدود النفقات العامة لكل إدخال والمُخصص بدلاً من النمو مع الرسم البياني.

الطبقة القابلة للكتابة

مع تفعيل 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) └─────────────┘

root@kitploit:~
* **حد المتانة — سجل الكتابة المسبقة (WAL).** كل تعديل يُسلسل خلف كاتب
  واحد لكل رسم بياني، ويُلحق بسجل كتابة مسبقة خاص بالرسم البياني، ويُستدعى له `fsync`
  قبل إرجاع `SUCCESS` الخاص بـ Bolt — لذا *الإقرار يعني المتانة*، ويُتجاهل الذيل الممزق
  عند إعادة التشغيل. عملية كتابة مجمّعة عبر `UNWIND` تُلحق صفوفها وتُلتزم **بـ`fsync` واحد**
  للدفعة بأكملها. سجل WAL **على القرص المحلي فقط** (لا يُمرَّر عبر الواجهة الخلفية للتخزين)،
  مما يجعل عقدة *الكاتب* ذات حالة: فهي تحتاج وحدة تخزين محلية متينة عند `delta.walDir`.
  بينما تبقى نسخ القراءة عديمة الحالة.
* **جدول الذاكرة (Memtable) → L0 → الدمج.** تتراكم الكتابات في جدول ذاكرة داخل
  الرام (محدود بـ`delta.memtableBytes`)؛ وعند امتلائه يُفرَّغ إلى مقطع دلتا L0 غير قابل
  للتعديل. **الدمج** يطوي `{core + delta}` في نواة جديدة عبر تسلسل العرض المدمج
  مرة أخرى من خلال `slater-build` وتبديل `current` ذريًا — نفس حارس تجزئة المحتوى
  كأي جيل منشور. يمكنك تشغيله يدويًا عبر `CALL slater.consolidate()`، أو تلقائيًا عند
  `delta.deltaCorePercent` من حجم النواة (اختياريًا مقيدًا بنافذة خارج الذروة
  `delta.consolidateWindow`)، أو اترك `delta.deltaHardBytes` كحد أقصى يمنع النمو الجامح.
* **الطبقة الفوقية تقع أسفل سطح القراءة.** يقرأ المنفّذ عبر
  `ReadView` إما أن تكون النواة المجردة (دلتا فارغة دائمًا) أو عرضًا مدمجًا
  `(core, delta)`؛ المحرك مُتجانس الشكل فوقها، لذا الدلتا الفارغة
  تُترجم إلى فرع واحد قابل للتنبؤ والمسار للقراءة فقط متطابق بايتًا ببايت.
  العدادات الكاملة للرسم البياني (`count(*)`, هوامش التسميات/أنواع العلاقات) تُقدَّم
  من عدادات الدلتا الحية نفسها، لذا تبقى قراءات بيانات وصفية حتى مع وجود كتابات معلقة.
* **الاستعلام يرى لقطة مستقرة.** يثبّت زوجًا واحدًا `(core, delta)` طوال
  حياته بالكامل. لا توجد معاملات متعددة العبارات ولا تراجع — الكتابة هي
  تصحيح متين موجه بمفتاح العمل، وليست معاملة OLTP.

القواعد الدقيقة للكتابة والمقابض موجودة في جدول [الإعدادات](#environment--configuration)
(`delta.*`) وفي [المثال العملي](#worked-example) أدناه.

### فهارس النطاق (ISAM)

فهرس النطاق (`range/<name>.isam`, واحد لكل `(label, property)` مفهرس) يتيح
لـ`MATCH (n:Label {prop: v})` أو `WHERE n.prop <op> v` أن يصل إلى معرفات العقد
المطابقة **دون مسح التسمية**. وهو بنية
**[ISAM](https://en.wikipedia.org/wiki/ISAM)** (طريقة الوصول المتسلسل المفهرس) —
الفهرس الكلاسيكي *الثابت، المرتب، ذو الكتل المنظمة*، وهو الشكل المناسب تمامًا
لجيل غير قابل للتعديل: لا توجد إدراجات تحتاج إعادة توازن، لذا
بساطة ISAM تحقق ما كانت آليات تعديل شجرة B ستُعقّده فقط.

* الإدخالات `(value, entity_id)` مرتبة حسب القيمة ومُعبأة في نفس
  كتل 256 KiB المضغوطة بـzstd مثل كل شيء آخر.
* **مستوى علوي مقيم صغير** يحمل المفتاح الأول لكل كتلة (فهرس متناثر).
  البحث الثنائي في ذلك المستوى العلوي في الذاكرة يجد *الكتلة الواحدة*
  التي قد يوجد بها المفتاح، ثم يقرأ تلك الكتلة ويفك ضغطها ويمسحها — لذا البحث
  بالتساوي هو **قراءة كتلة واحدة**، ومسح النطاق يمشي عبر سلسلة الكتل المتجاورة
  التي يغطيها. (لهذا يكون البحث المفهرس عبر `meshUi` في نطاق أجزاء من الثانية
  بينما نفس المطابقة على خاصية غير مفهرسة تمسح التسمية بالكامل.)
* المخطط يختاره عبر `NodeScan::RangeEq` / `RangeRange`؛ المسند غير المفهرس
  يتراجع إلى مسح تسمية أو مسح كامل، مع إعادة فحص المنفّذ لكل مسند في كلتا الحالتين.

### البحث المتجهي (Vamana + PQ) — جيب التمام، L2 والضرب النقطي، قراءة *وكتابة*

بحث KNN المتجهي (`db.idx.vector.queryNodes`) يعمل عبر فهارس
**جيب التمام، أو L2، أو الضرب النقطي (MIPS)**. الفهرس الأساسي يُبنى دون اتصال
بمسارين تنفيذ، يُختار لكل فهرس عبر `--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 بشكل مستقل،
    والمتجه يُخزَّن كمجموعة معرفات أقرب مراكز. هذه الرموز
    (`vector/<label>.<prop>.pq`) صغيرة بما يكفي لتبقى **مقيمة**، لذا
    بحث الشعاع يقيّم المرشحين من الرام وفقط المتجهات الكاملة القليلة المختارة
    تُقرأ من القرص. مجموعة PQ المقيمة هذه هي ما يثبّته تجمع `cache.vectorCacheBytes`.

**تضمينات قابلة للكتابة — سلم الكتابة المتجهية (بأسلوب [FreshDiskANN](https://arxiv.org/abs/2105.09613)).**
التضمين المفهرس قيمة قابلة للكتابة من الدرجة الأولى. `SET n.embedding = vecf32([…])` (و`REMOVE`) يهبط في
دلتا الكتابة ويكون **مرئيًا فورًا لـKNN بترتيب دقيق**، ثم ينجو من تفريغ
مقطع، ودمج، ودمج نهائي. الاستعلام يدمج حتى ثلاثة مستويات — الفهرس الأساسي المغلق،
فهرس مغلق لكل مقطع، وفهرس **RW في الذاكرة** (Vamana قابل للتعديل حي
فوق دلتا الكتابة) — لذا تبقى زمنية الاستجابة ثابتة مع تراكم الكتابات بدلًا من النمو
مع عدد الكتابات المعلقة. الحذف يترك *ثقبًا*: العقدة تتوقف عن الظهور في النتائج
لكنها تبقى نقطة تنقل حتى **دمج حذف** في الخلفية يزيلها
من الرسم البياني، لذا تتوقف عمليات الحذف عن تكلفة إدخال/إخراج الاستعلام. ولأن الرسم البياني
على القرص يخاطب جيرانه بموضع التخطيط وليس بمعرف العقدة، فإن `CALL slater.consolidate()`
ينقل Vamana **بالإشارة** — روابط صلبة، متطابقة بايتًا ببايت — ويعيد كتابة
عمود معرف صغير فقط، مطويًا كتابات المتجهات في الأساس **دون** إعادة بناء الرسم البياني
O(N·R·L). الأرقام المقاسة، مع التحفظات، موجودة في [تقرير الأداء](https://github.com/hikari-systems/slater/blob/main/docs/PERF-REPORT.md).

## الواجهات الخلفية للتخزين (نظام الملفات / S3 / GCS)

كل ملف جيل يُفتح عبر تجريد **`ObjectStore`** بدلًا من
`std::fs` مباشرة، لذا *نفس* تنسيق البايت على القرص — الكتل، الفهارس،
البيان، مؤشر `current` — يُقدَّم دون تغيير من أي واجهة خلفية؛ فقط *من أين
تأتي البايتات* يختلف، وليس القراء أو محرك الاستعلام أو
فحوصات السلامة. المسار الساخن هو قراءات موضعية (`read_exact_at`)، والتي تُرسم
على `pread` لملف محلي وطلب نطاق بايت HTTP على مخزن كائنات
— Slater لا يستخدم mmap أبدًا، لذا نموذج القراءة الصريح المحدود متطابق
في كل مكان.

**ثلاث واجهات خلفية من الدرجة الأولى**، تُختار عبر `dataBackend.kind`. نظام الملفات هو
الافتراضي البسيط؛ **Amazon S3 وGoogle Cloud Storage واجهتان خلفيتان متساويتان
ومدعومتان بالكامل لمخازن الكائنات** — الصورة المنشورة تأتي مع كلتيهما
مُجمَّعتين، لذا كل منهما مجرد إعداد، والجيل المبني مرة واحدة يمكن تقديمه من
أي منهما (حتى الترحيل `fs` → S3 → GCS) دون إعادة بناء.

| `dataBackend.kind` | القراءة الموضعية | السلامة عند الفتح | بيانات الاعتماد |
| --- | --- | --- | --- |
| `fs` *(الافتراضي)* | `pread` | إعادة تجزئة BLAKE3 كاملة لكل ملف | — |
| `s3` | طلب `Range` عبر HTTP | **SHA-256** من الخادم عبر `HEAD` (→ إعادة تجزئة BLAKE3 للجسم إذا غائب) | مفاتيح الإعداد، سلسلة AWS، أو دور IAM |
| `gcs` | قراءة نطاق HTTP | **CRC32C** من الخادم عبر `get_object` (→ إعادة تجزئة BLAKE3 للجسم إذا غائب) | ADC / Workload Identity، أو JSON لحساب الخدمة |

كلا مخزني الكائنات يتحققان من السلامة من **المجموع الاختباري الذي يحسبه المخزن
ويحتفظ به بالفعل**، المسترجع كبيانات وصفية للكائن: `slater-build` يرسل
المجموع الاختباري عند الرفع (يتحقق المخزن من البايتات ضده ويخزنه)، و
الخادم يقرأه عند الفتح ويقارنه بالبيان — طلب بيانات وصفية واحد
لكل ملف، دون تنزيل الجسم. إنه بجودة المحتوى ومتطابق في الروح
عبر S3 (SHA-256) وGCS (CRC32C). عندما يحمل كائن **لا** مجموعًا اختباريًا مخزنًا
على الخادم (مُنسخ خارج النطاق، أو مرفوع بإعداد افتراضي مختلف)، يقوم الخادم
**بإعادة تجزئة جسم الكائن مقابل BLAKE3 في البيان** بدلًا من الثقة بطوله
البايتي — فحص السلامة المطلوب لا يُخفَّض أبدًا بصمت إلى مقارنة حجم.
الأجيال المنشورة عبر Slater تحمل دائمًا المجموع الاختباري، لذا تبقى على
مسار البيانات الوصفية الرخيص.

ما يفحصه هذا العمود، على كل واجهة خلفية، هو أن الملفات **تطابق البيان**.
سواء كان البيان نفسه جديرًا بالثقة سؤال منفصل، وهو
المفتاح الرئيسي الذي يجيبه: مع تكوين مفتاح يحمل البيان MAC بمفتاح
يتحقق منه الخادم قبل الثقة بأي حقل (بما في ذلك هذه التجزئات)، لذا
بيان أعيدت كتابته لوصف ملفات معدلة يُرفض؛ بدون مفتاح تكون المقارنة غير موقعة في كل مكان، و
من يستطيع الكتابة إلى دليل البيانات يمكنه إعادة كتابة ملف والبيان
معًا. انظر
[ماذا تعني السلامة في كل إعداد](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`، بالإضافة إلى
`awsSessionToken` لبيانات اعتماد STS المؤقتة) وتتراجع إلى سلسلة AWS القياسية
(`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` كمتغيرات بيئة، ملف تعريف مشترك، أو
دور instance/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
root@kitploit:~
# 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

Google Cloud Storage (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

serve from GCS (env-var form; see the config table for every key)

dataBackend__kind=gcs dataBackend__gcs__bucket=slater dataBackend__gcs__prefix=prod dataBackend__gcs__credentialsPath=/secrets/sa.json # omit for ADC / Workload Identity

root@kitploit:~
```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)

اللجوء إلى s3 أو gcs يكون عندما تريد توليدات في مخزن كائنات مركزي دائم بدلاً من قرص العقدة — عادةً: النشر مرة واحدة والتوزيع على العديد من نسخ الخوادم عديمة الحالة وعديمة الأقراص التي تقرأ جميعها نفس الحاوية؛ أو فصل مضيف البناء عن مضيفي الخدمة؛ أو الاعتماد على متانة/إصدار/دورة حياة المخزن بدلاً من إدارة وحدات التخزين. المقابل هو زمن الاستجابة: الكتلة الباردة هي رحلة شبكة ذهاباً وإياباً (~10–50 مللي ثانية) بدلاً من قراءة محلية (~0.1 مللي ثانية). يخفي Slater معظم ذلك عبر ذاكرة التخزين المؤقت للكتل في الذاكرة، والقراءة المسبقة المتزامنة، و ذاكرة التخزين المؤقت الاختيارية على القرص أدناه. إذا كانت توليداتك موجودة بالفعل على تخزين محلي سريع ولا تحتاج إلى نموذج الحاوية المركزية، فإن fs أبسط وأسرع.

ذاكرة التخزين المؤقت للكتل على القرص المحلي (الطبقة الثانية لمخزن الكائنات)

ذاكرة BlockCache في الذاكرة صغيرة عمداً (حجم RSS المحدود هو الضمان الرئيسي)، لذا على مجموعة عمل أكبر من ذاكرة الوصول العشوائي، ستُعاد جلب نفس الكتل من مخزن الكائنات عند كل تجاوز. طبقة ثانية اختيارية على SSD محلي تحل ذلك: الكتلة المطرودة من الذاكرة تُخدم من القرص المحلي (~0.1 مللي ثانية) بدلاً من GET كائن جديد، مما ينجو من الإخراج من الذاكرة ويقلل عدد/تكلفة طلبات مخزن الكائنات — مما يقرب العقدة المدعومة بمخزن الكائنات من أداء نظام الملفات المحلي بمجرد أن تصبح دافئة. وهي اختيارية لكل من s3 و gcs، تُفعَّل بتعيين dataBackend.<s3|gcs>.diskCacheBytes > 0 ودليل diskCacheDir قابل للكتابة.

  • تخزّن البايتات المختومة تماماً كما جُلبت — مضغوطة بالفعل، و (لتوليدات --encrypt) لا تزال مختومة بـ AEAD — أسفل فك التشفير/فك الضغط. لا تحتفظ طبقة التخزين المؤقت بمفتاح التشفير أبداً ولا تعيد التشفير، لذا تُحفظ حالة التخزين مجاناً: التوليد المشفر يصل إلى القرص لا يزال مختوماً.
  • الكتابات مؤجلة: عند عدم التطابق، تُعاد البايتات المجلوبة إلى الاستعلام فوراً، ثم يقوم خيط خلفي بكتابة القرص وتقليم LRU، لذا لا يحجب مسار الاستعلام أبداً على إدخال/إخراج القرص. يُبقي الإخراج ذاكرة التخزين المؤقت ضمن ميزانية البايتات الخاصة بها؛ ومجموع اختباري لكل ملف يُتحقق منه عند كل قراءة يعالج ذاتياً ملف تخزين مؤقت تالف إلى عدم تطابق (→ إعادة جلب من مخزن الكائنات).
  • diskCacheDir يجب أن يشير إلى وحدة تخزين حقيقية قابلة للكتابة — أبداً tmpfs (tmpfs هي ذاكرة وصول عشوائي وستُبطل ضمان RSS المحدود). الفهرس في الذاكرة الذي يتتبعه يكلف القليل من الذاكرة (~عشرات البايتات لكل كتلة مخزنة)، والتي تُحسب ضد سقف RSS الخاص بك — اجعل الدليل ≫ ذاكرة التخزين المؤقت للكتل في الذاكرة.
  • تكلفة الذاكرة الأخرى للطبقة هي قائمة الانتظار المؤجلة للكتابة، التي ترتب الكتل في طريقها إلى القرص. وهي محدودة بـ blockCacheBytes / 8 (مقيدة أرضياً بـ diskCacheBytes) — 8 ميجابايت عند الافتراضي — وتتخلص بدلاً من أن تنمو، لذا لا يمكن لفحص بارد تضخيمها؛ الكتلة المتخلص منها تُعاد جلبها ببساطة عند عدم تطابقها التالي. لا تحتاج إلى إعداد: فهي تتدرج مع blockCacheBytes، لذا تضيف طبقة القرص رقماً جديداً إلى ميزانية RSS إلى جانب فهرسها.

الوحدات المثبتة

تعمل نسخة القراءة بنظام ملفات جذر للقراءة فقط ومستخدم غير جذر (appuser:1000) — كل ما تحتاجه مثبت للقراءة فقط. الكاتب (delta.enabled) يحتاج بالإضافة إلى ذلك وحدة تخزين دائمة واحدة قابلة للكتابة لسجل WAL الخاص به.

البيئة / التكوين

يُحمَّل التكوين بواسطة المُحمِّل المتدرج القياسي للمنزل: config.json المدمج، ثم /sandbox/config.json المدمج بعمق فوقه، ثم تجاوزات بيئة KEY__sub (شرطة سفلية مزدوجة للتداخل؛ المفاتيح تطابق تكوين camelCase).

كل مقبض تكوين — مفتاحه camelCase، وتجاوز البيئة KEY__sub، وافتراضيه، وما يفعله — مُجدول في مرجع التكوين. المقابض الأكثر ضبطاً هي ميزانيات التخزين المؤقت (cache.*)، وحُرّاس الاستعلام (query.*)، وسقوف الاتصال (server.*)، وخلفية التخزين (dataBackend.*)، والطبقة القابلة للكتابة (delta.*).

الذاكرة المقيمة تتتبع blockCacheBytes + vectorCacheBytes + resultCacheBytes ضمن حمل ثابت لكل إدخال وحمل المُخصِّص — كل تجمع يزن محتوياته الخاصة (السلاسل والحاويات حسب السعة المخصصة) ويُخرج ليظل تحت الميزانية، لكن الدفاتر لكل إدخال وتقريب فئات الحجم للمُخصِّص تقع فوق الرقم الذي تضبطه — بالإضافة إلى حمل ثابت صغير (وحتى degreeColumnBytes لعمود الدرجة lazy، بمجرد تمرين المسار السريع لمجموع الدرجات 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

acl.json يربط المستخدمين بهاشات كلمات مرور argon2id ومنح read / write لكل رسم بياني. اصنع هاشاً (لا تخزن نصاً واضحاً أبداً) باستخدام:```sh slater hash-password 's3cret' # prints a $argon2id$… string for acl.json

root@kitploit:~
ملف `acl.json` مبدئي يُرفق في جذر المستودع؛ شكله هو:```json
{
  "users": {
    "reporting": {
      "passwordArgon2id": "$argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash>",
      "grants": {
        "people": ["read"],
        "products": ["read", "write"]
      }
    }
  }
}
  • users — إدخال واحد لكل تسجيل دخول، مُفترحًا باسم المستخدم.

  • passwordArgon2id — سلسلة $argon2id$… الناتجة من slater hash-password (وليس نصًا صريحًا أبدًا؛ فالملف نفسه بصيغة JSON عادية ويقع على تخزين مشترك).

  • grants — قوائم الصلاحيات لكل رسم بياني. هناك صلاحيتان ذات معنى:

    • read — الاستعلام عن الرسم البياني. الرسم البياني غير الموجود في صلاحيات المستخدم يكون غير مرئي له.
    • write — تعديل الرسم البياني عبر الطبقة القابلة للكتابة (delta.enabled): عبارات MERGE / SET / DELETE وCALL slater.consolidate().

    وهما لذا فإن تفعيل الطبقة القابلة للكتابة لا يمكن أن يرقّي القرّاء الحاليين إلى كتّاب. الكاتب يحتاج كلتيهما — — لأن حل مفتاح العمل لكتابته يُعد عملية قراءة. سلاسل الصلاحيات غير المعروفة يتم تجاهلها (لا تمنح شيئًا).

قم بتركيبه للقراءة فقط في المسار المحدد بواسطة 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

root@kitploit:~
## استعلام لمرة واحدة

للسكربتات، فحوصات 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

root@kitploit:~
حمل الاستعلام `cost` (العناصر المُحصَّلة)، `resultCount`، `execMs`، و
`limitRowCount` (فقط عندما يحدد الاستعلام `LIMIT`) — ولا يُرسل نص الاستعلام أبدًا
أو أي قيمة نتيجة. حالة الخروج هي `0` عند النجاح، و`1` عند خطأ في التحليل/الفتح/التنفيذ
(رسالة على stderr).

## تصدير رسم بياني (`slater dump`)

يقوم `slater dump` بتصدير رسم بياني من خادم **قيد التشغيل** كـ Cypher بصيغة `MERGE`
بمفتاح العمل — نفس اللهجة التي يستوعبها `slater-build` — بحيث يعود الرسم البياني
(تفريغ ← `slater-build` ← جيل جديد) للترحيل أو النسخ الاحتياطي النصي. بخلاف
`slater query`، فإنه يتصل عبر **Bolt**، ويُصادق، ويحترم قوائم التحكم بالوصول (ACLs) الخاصة بكل رسم بياني،
لذا لا يحتاج إلى وصول للقرص على الخادم. تُقرأ كلمة المرور من
`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

معرّف الهوية لكل تسمية هو الخاصية التي يحملها فهرس النطاق الخاص بها؛ يمكنك تجاوز ذلك باستخدام --key Label=prop (قابل للتكرار) أو --pk <field> عام. يتم إصدار CREATE INDEX DDL أولاً حتى يعيد إعادة البناء إنشاء الفهارس. العقدة متعددة التسميات تحتفظ بكل تسمية — يتم إصدارها كـ MERGE (n:Ident:Other {key: v})، مع تسمية الهوية (التسمية التي توفر المفتاح التجاري) أولاً والباقي مرتبًا؛ يتم ربط الدمج على تسمية الهوية وحدها، لذا تُكتب التسميات اللاحقة على العقدة دون إنشاء عقدة أخرى. التسميات وأنواع العلاقات ومفاتيح الخصائص التي تحتوي على أحرف خاصة تُحاط بعلامات backtick عند الإصدار، لذا تعود الأسماء غير المعتادة بأمان ولا يمكنها حقن Cypher في إعادة البناء. المتجهات (والقيم الأخرى التي لا تحتوي على تهجئة حرفية في Cypher) لا يمكنها ركوب تفريغ MERGE ويتم إسقاطها مع تحذير على stderr. حالة الخروج هي 0 عند النجاح، 1 عند الخطأ.

مثال عملي

دليل كامل وقابل للتشغيل — بناء رسم بياني، تقديمه، الاتصال به عبر برامج تشغيل neo4j JavaScript و Python، والكتابة إليه — موجود في صفحتي Quickstart و Writing data من الدليل، باستخدام الرسم البياني النموذجي المرفق في docs/manual/examples/.

التطوير```sh

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

root@kitploit:~
### خلفيات تخزين الكائنات هي ميزات اختيارية في Cargo

إن `cargo build` عادي ينتج ملفًا ثنائيًا **يعتمد على نظام الملفات فقط** — حيث أن خلفيتي `s3` و `gcs`
مقفلتان خلف ميزات Cargo بحيث يظل البناء الافتراضي صغيرًا (بدون AWS
أو Google SDK، وبدون وقت تشغيل غير متزامن). قم بتمكين ما تحتاجه على **كلٍّ من** `slater`
(الخدمة) و `slater-build` (النشر):```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

كل crate يكشف ميزات s3 / gcs المطابقة التي تُمرَّر إلى graph-format/{s3,gcs}. طلب backend في وقت التشغيل (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 (GCS حقيقي عبر ADC)؛ كل منها يُتخطى ما لم تُضبط متغيرات بيئة SLATER_* الخاصة به.

انظر docs/PLAN.md وdocs/PROGRESS.md وdocs/DECISIONS.md للتصميم، وسجل المعالم، وسجل القرارات.

الأداء

حتى ستة محركات، مجموعة واحدة ذات عميل واحد، رسوم بيانية من عقدة تجريبية بحجم 62 ألف عقدة إلى ويكي بيانات 91.6 مليون عقدة / 1.5 مليار حافة. كل محرك يُقاس بمعزل عن الآخرين (كل حاوية أخرى متوقفة — RSS وزمن الاستجابة هما بصمته الخاصة). جداول زمن الاستجابة أدناه أُعيد قياسها على Slater 0.21.0 (البناء القابل للكتابة): الرسوم البيانية الصغيرة/المتوسطة (MeSH، EU-AI-Act) حديثًا، والرسم البياني 91.6 مليون كتمرير نفس الجهاز، مرساة مشتركة slater-vs-Neo4j (انظر ذلك الجدول). أرقام الذاكرة المقيمة منقولة من التمرير السابق (تُقاس عبر cgroup الحاوية؛ مسار القراءة مطابق بايتًا بايتًا مع طبقة الكتابة خاملة). أرقام المحركات الأخرى هي نتائج التمرير عبر المحركات الراسخة (إصداراتها/أداؤها لم يتغير). كل الأرقام هي متوسطات (مللي ثانية) أو ذاكرة مقيمة ذروية (MiB). الأقل أفضل في كل مكان؛ الغامق = الأفضل في الصف. شُغّل slater على backend نظام الملفات المحلي (fs) الخاص به؛ بينما تستبدل backends S3 وGCS زمن استجابة القراءة المحلية بجولات تخزين الكائنات (يُخففها التخزين المؤقت في الذاكرة وطبقة التخزين المؤقت الاختيارية على القرص المحلي)، لذا تميز هذه الأرقام المحرك، وليس نشر تخزين الشبكة.

المحركات الثلاثة التي تُقسّم من القرص — slater وNeo4j 5 وLadybugDB — تُحمّل كل الرسوم البيانية الخمسة. الثلاثي في الذاكرة (Memgraph · FalkorDB · ArcadeDB) لا يمكنه استيعاب الرسم البياني ذي 1.5 مليار حافة على الإطلاق (يحتاج ~64–128 غيغابايت مقيم)، ومستورد ArcadeDB لا يمكنه إنهاؤه أيضًا.

الذاكرة المقيمة (MiB) — محدودة مع نمو الرسم البياني ~1,500×

كل رقم هو ذاكرة عمل ملتزمة — ما لا يمكن لنظام التشغيل استعادته. كل محرك باستثناء slater يحتفظ برسمه البياني في ذاكرة مجهولة ملتزمة (كومة خاصة، أو ذاكرة تخزين صفحات خارج الكومة في Neo4j، أو تجمع مخازن)، لذا فإن ذروة RSS الخاصة به هي بصمته الملتزمة. slater وحده يخدم من ذاكرة تخزين صفحات OS القابلة للاستعادة لمخزنه على القرص، لذا فإن رقمه هو مجموعة العمل المجهولة؛ ذاكرة تخزين صفحات المخزن (القابلة للإخلاء تحت الضغط — يستمر slater في الخدمة) مستبعدة، وتُعرض كـإجمالي بين قوسين للرسم البياني 91.6 مليون. الغامق = الأدنى.

slater هو الأدنى في كل مقياس وينمو ~50× بينما ينمو الرسم البياني ~1,500× — بصمته تتبع مجموعة عمل الاستعلام، وليس الرسم البياني (خامل ~16–71 MiB طوال الوقت). ينمو الثلاثي في الذاكرة ~خطيًا ولا يمكنه تحميل الرسم البياني 1.5 مليار؛ يلتزم Neo4j بكومة ~2 غيغابايت بغض النظر عن الاستعلام. († LadybugDB على الأشكال المحدودة فقط — اجتيازات المحور / الطول المتغير / أقصر مسار عند 1.5 مليار حافة تحتاج رفع تجمع القراءة إلى ≥2 غيغابايت، مقابل حد maxIntermediate التلقائي في slater.) الرسوم البيانية للقيمة→العدد في وقت البناء تضيف ذاكرة مقيمة لا تذكر — بضعة كيلوبايت لعمود مفهرس منخفض التنوع، وصفر للرسوم البيانية ذات المفاتيح الفريدة مثل Wikidata (wikidata_id يتجاوز حد تنوع الرسم البياني، لذا لا يُخزن أي شيء) — لذا فإن هذه الأرقام لم تتغير بفعل تلك الميزة.

زمن الاستجابة (متوسط مللي ثانية) — الرسم البياني يناسب RAM (MeSH، 341 ألف / 469 ألف)

يمتلك slater أشكال البيانات الوصفية / الفهرس / الفحص (count، label، idx-eq، scan — ~0.4 مللي ثانية، 10–200× محركات الخدمة)، والبحث النقطي المفهرس (0.43 مللي ثانية، الآن يتفوق على الزوج في الذاكرة 0.48 مللي ثانية)، والقفزات المتعددة بدون مرساة (قفزتان 1.40 مللي ثانية عبر فحص نوع العلاقة، الأسرع في المجال)، و— عبر رسم بياني للقيمة→العدد في وقت البناء على مفتاح التجميع المفهرس — group-by / count(DISTINCT) للتسمية الكاملة (0.45 مللي ثانية، متقدمًا على LadybugDB العمودي 5.3 مللي ثانية). تحتفظ خوادم الذاكرة فقط بـقفزة واحدة خام (Memgraph 1.21 مللي ثانية مقابل 1.28 مللي ثانية في slater). (pole 62 ألف/106 ألف يبدو مشابهًا: slater الأسرع الوحيد في count/scan ~0.4 مللي ثانية، ~1.3–2.6 مللي ثانية في القفزات.)

زمن الاستجابة (متوسط مللي ثانية) — المتجهات (EU-AI-Act kNN، 15 ألف × 1024 بُعد)

يجيب slater على kNN بفحص قوة غاشمة دقيق (هذه المجموعات أقل من عتبة ANN البالغة 50 ألف متجه) حيث يستخدم الآخرون HNSW مقيمًا تقريبيًا — لذا فإن نتائج slater دقيقة (استدعاء 1.0). نواة مسافة SIMD + مصفوفة متجهات مقيمة وطبيعية مسبقًا أخذت Concept من ~23 → ~2.9 مللي ثانية وChunk من ~10 → ~2.4 مللي ثانية، لذا يتفوق slater الآن على Neo4j وLadybugDB ويقع ضمن ~1.4× من Memgraph، متخلفًا فقط عن FalkorDB — بينما هو دقيق.

سلم كتابة المتجهات — إدراج / تحديث / حذف دون إعادة بناء

الجداول أعلاه مقارنات قراءة عبر المحركات. مسار كتابة المتجه (سلم الكتابة بنمط FreshDiskANN فوق قاعدة Vamana الثابتة) ليس له نظير عبر المحركات — لا يوجد محرك آخر هنا يقوم بـANN أصلي على القرص قابل للكتابة — لذا فإن الأرقام أدناه هي معايير مكون لمحرك واحد عبر تركيبة اصطناعية شبيهة بالتضمين (متشعب منخفض الرتبة، بُعد 768، معايير غير متساوية)، ملتزمة تحت crates/slater/benches/ ومكتوبة بالكامل — مع المنهجية وكل تحفظ — في docs/PERF-REPORT.md. الاستدعاء يُقاس دائمًا مقابل قوة غاشمة دقيقة على المجموعة الحية، وليس فهرسًا مقابل آخر. المقياس هنا تمثيلي ومُستقرأ فقط حيث يكون المقياس خطيًا في الحجم.

الرقم الوحيد الذي يريد صندوق الأداء المخصص هو إنتاجية إعادة كتابة الدمج بالمسار البطيء — عندما يحمل الدمج عمليات حذف أو متجهات جديدة بدلًا من تبديل نقي، فهو إعادة ضغط تسلسلية محدودة بـzstd أحادي الخيط والقرص المحلي، لذا فإن MiB/s المطلقة خاصة بالبيئة (يُظهر التقرير الشكل ويشرح النطاق البيئي).

زمن الاستجابة (متوسط مللي ثانية) — الرسم البياني ≫ RAM (Wikidata 91.6 مليون / 1.5 مليار)

محركات الذاكرة (Memgraph / FalkorDB / ArcadeDB) لا يمكنها تحميل هذا الرسم البياني على الإطلاق (~64–128 غيغابايت مقيم). فقط slater وNeo4j 5 يفعلان ذلك. هذا تمرير جديد على نفس الجهاز ونفس اليوم مقابل مجموعة مراسي ثابتة مشتركة — كل استعلام يصل إلى نفس العقد على كلا المحركين، لذا فإن المواجهة المباشرة قابلة للمقارنة تمامًا (تجمع wikidata_id مشترك من المراسي متوسطة الدرجة؛ انظر الملاحظة أدناه حول سبب أهمية ذلك). يُعرض slater عند كلا التفرعين (query.maxFanout 1 = افتراضي الإنتاجية، 8 = قرص زمن الاستجابة الذي يتداخل مع قراءات الكتل الباردة). الغامق = الأفضل في الصف.

الصورة الصادقة: slater يهيمن على أشكال البيانات الوصفية / الفهرس — count(*) هو مُقدَّم من البيانات الوصفية (0.41 مللي ثانية مقابل فحص قرص Neo4j 3.6 ثانية، ~8800×)، وبحث النقطة / الدرجة / ثلاث قفزات أسرع ~2–10× — متساوٍ مع Neo4j في 1–2 قفزة (التفرع 8 يتقدم في القراءات الباردة)، لكنه يخسر var-length *1..2 distinct بشكل حاسم (≈1 ثانية مقابل 47 مللي ثانية في Neo4j): توسيع slater المميز للطول المتغير أبطأ ماديًا هنا، نقطة ضعف حقيقية تستحق تحقيقًا خاصًا بها. كل ذلك عند بضع مئات من ميغابايت من RSS مقابل كومة Neo4j الملتزمة ~2 غيغابايت.

حول المراسي. تعتمد أرقام الاجتياز هذه بشدة على أي العقد تبدأ منها — العقدة على بُعد رابط واحد من محور Wikidata الضخم ("human"، "country") لها حي قفزتين بقوة الملايين، لذا تتأرجح تكلفة الطول المتغير/القفزة بمراتب حجم مع اختيار المرساة. النسخة السابقة من هذا الجدول أخذت عينات من "أول N بالفحص" الخاص بكل محرك، وهو ليس مستقرًا ولا قابلاً للمقارنة؛ هذا التمرير يثبت مجموعة مراسي مشتركة واحدة محدودة الدرجة لكلا المحركين. (shortestPath محذوف من هذا التمرير — بين مرساتين عشوائيتين يعتمد على وجود المسار ومتغير جدًا بحيث لا يمكن حساب متوسطه بشكل ذي معنى.)

count(*) متعدد القفزات — الذاكرة مفصولة عن حجم النتيجة

RETURN count(*) متعدد القفزات غير المحدود يعد أثناء التوسيع بدلًا من تجسيد الصفوف المطابقة. نفس مراسي المحور على الرسم البياني 91.6 مليون، maxIntermediate=20M:

3-hop count(*) @ 91.6Mfanout=1fanout=8
زمن الاستجابة / مجموعة عمل ذروية554 مللي ثانية / 0.66 غيغابايت298 مللي ثانية / 1.9 غيغابايت

يحمل العدد صفوف O(1). الشحن لم يتغير، لذا فإن عدد المحور الضخم لا يزال يصل إلى maxIntermediate على الحساب (قراءات الجوار)، محدودًا كما كان من قبل.

التوازي لكل استعلام (maxFanout)

رفع query.maxFanout يتداخل مع قراءات الكتل الباردة، المرتبطة بـI/O للاستعلام عبر النوى — يساعد الأشكال المرتبطة بالقرص ذات مجموعة العمل الباردة الكبيرة وهو ثابت على الأشكال الدافئة. على الرسم البياني 1.5 مليار: shortestPath ≤6 918 → 608 مللي ثانية (1.5×، أكبر بحث 6,269 → 2,350 مللي ثانية، 2.7×)؛ 3-hop count 547 → 298 مللي ثانية. maxFanout=1 هو الافتراضي (موجه للإنتاجية)؛ 8 هو قرص زمن الاستجابة، بذاكرة عامل عابرة أكثر.

أين يفوز / يتخلف slater

الجداول الكاملة لكل محرك (pole، MeSH، EU-AI-Act + قرص RAM↔زمن الاستجابة blockCacheBytes، Wikidata 1 مليون و91.6 مليون) موجودة في perf/cross-engine-hs/README.md؛ التمرير الجديد الخاص بـslater فقط (كلا التفرعين، كل مجموعة بيانات) موجود في perf/PERF_CURRENT_STATUS.md.

التزامن والانقطاع (اختبار الحمل)

المعايير أعلاه ذات عميل واحد. المحور التكميلي — السلوك تحت العديد من العملاء المتزامنين — له أداته الخاصة، perf/loadtest/: سائق Locust عبر Bolt بالإضافة إلى منسق يزيد الحمل تدريجيًا، ويقرأ CALL slater.diagnostics()، ويجد نقطة انعطاف السعة، ويسمي المُحدد (الطريقة الكاملة في docs/LOAD-TESTING.md). أبرز النتائج من تشغيل ذاكرة تخزين مؤقت 256 MiB على رسم Wikidata-1M البياني (صندوق واحد 16 نواة):

كلتا مشكلتي الذاكرة اللتين كشفهما اختبار الحمل مغلقتان الآن؛ كل ذلك مُتتبع في وثيقة اختبار الحمل.

الترخيص

مرخص بموجب رخصة Apache، الإصدار 2.0. انظر LICENSE للنص الكامل وNOTICE للإسناد. ما لم تذكر صراحةً خلاف ذلك، فإن أي مساهمة مقدمة عمدًا للإدراج في هذا العمل، كما هو معرّف في رخصة Apache 2.0، ستُرخص كما هو مذكور أعلاه، دون أي شروط أو قيود إضافية.

SPDX-License-Identifier: Apache-2.0

تنزيل الأداة
INSERT
SET
REMOVE
DELETE
الميزةماذا تعني لك
ذاكرة محدودة وقابلة للتنبؤالذاكرة المقيمة تتبع ثلاث ميزانيات ذاكرة تخزين مؤقت تحددها أنت، ضمن حمل زائد محدود لكل إدخال وللمخصص — لا تنمو مع حجم الرسم البياني؛ تضبط مقايضة الأداء/RAM بدلاً من توفير الموارد للرسم البياني بأكمله. مخصص jemalloc مع تطهير خلفي يعيد الذاكرة المحررة إلى نظام التشغيل بعد دفعات الاستعلام الثقيلة، لذا ينخفض الحجم المقيم نحو أرضيته الخاملة بدلاً من البقاء مثبتًا عند علامة ما بعد الدفعة العالية.
متعدد المستأجرين جاهزًاخادم واحد يستضيف العديد من الرسوم البيانية مع منح قراءة لكل مستخدم — عزل متعدد قواعد البيانات تحتفظ به معظم قواعد بيانات الرسوم البيانية لطبقة مدفوعة/مؤسسية.
تشفير أثناء الراحة وأثناء النقلختم XChaCha20-Poly1305 لكل كتلة (المفتاح لا يُكتب أبدًا على القرص) بالإضافة إلى TLS اختياري (bolt+s://). صديق للـ GDPR بالتصميم. التشفير هو أيضًا ما يشتري سلامة موثَّقة: يختم الباني البيان بـ MAC مفتاحي، ويتحقق خادم يحمل المفتاح منه ويرفض خدمة جيل تم تزوير بيانه أو تغييره أو إزالة MAC منه. صورة بدون مفتاح (نص عادي) محمية بتجزئة المحتوى بدون مفتاح فقط — الاكتمال والفساد، وليس العبث. راجع ماذا تعني السلامة في كل إعداد.
تثبيت صغيرثنائي صغير مجرد على أساس glibc distroless (بدون shell/apt) — صورة متعددة البنى (amd64/arm64) تُسحب بحوالي ~22 MB، أو ~12 MB لعلامة slater:latest-lite الخاصة بالخادم فقط؛ TLS نقي بـ Rust، بدون OpenSSL. اسحب وشغّل.
مصمم للنشر الدوريابنِ رسمًا بيانيًا دون اتصال، اخدمه غير قابل للتغيير، ثم استبدل إصدارًا جديدًا بشكل ذري مع صفر توقف — مثالي لأحمال عمل مستودع البيانات / التحديث المجدول.
متين تحت الحملالخادم والباني دون اتصال يترجمان معًا مع #![forbid(unsafe_code)] — unsafe الوحيد في المحرك يعيش في كريت مخصص jemalloc المُدقق. النواة غير قابلة للتغيير، لذا لا تأخذ القراءات أقفالًا ولا تنتظر كاتبًا أبدًا؛ كاتب واحد يسلسل الطفرات خلف مسار الكتابة وحده. لا توقف GC، ولا سباقات بيانات. استعلام سيئ واحد لا يمكنه إسقاط الخادم.
يعمل مع أدوات 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 مجمعة (fsync واحد لكل دفعة)، وCALL slater.consolidate() — ملتزمة جماعيًا، ودائمة عبر fsync، ومطوية مرة أخرى في نواة جديدة بواسطة التوحيد. مسار القراءة متطابق بايتًا عندما تكون الدلتا فارغة.
ISO GQL، قراءة وكتابةيتحدث مجموعة فرعية من ISO GQL (ISO/IEC 39075) عبر نفس اتصال Bolt — مسارات كمية، ومقيدات مسار، ومحددات أقصر مسار، وتعبيرات منطقية للتسميات/الأنواع، وFOR، وCAST، وبادئة لهجة اختيارية GQL/CYPHER — ومع تفعيل الطبقة القابلة للكتابة، تنزل عبارات GQL لتعديل البيانات (INSERT / SET / REMOVE / [DETACH] DELETE) على نفس مسار الكتابة الدائم. Cypher وGQL، قراءات وكتابات، في محرك واحد.
متجهات + رسم بياني في محرك واحدبحث ANN متجهي أصلي على القرص (Vamana + PQ؛ cosine / L2 / dot) للتضمينات/RAG، بالإضافة إلى خوارزميات الرسوم البيانية (PageRank وBFS وbetweenness و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، ومواد PEM الخاصة بـ TLS، وملف المفتاح أثناء التخزين.
/tmp, /runمساحة مؤقتة (tmpfs).نسخة القراءة لا تكتب على القرص أبداً افتراضياً.
(الكاتب) delta.walDirسجل الكتابة المسبقة + مقاطع دلتا L0، عند تفعيل delta.enabled.قابل للكتابة، ووحدة تخزين حقيقية دائمة — أبداً tmpfs (هي أرضية المتانة). المسار النسبي يُحل تحت دليل البيانات؛ أعطِ الكاتب وحدة تخزين دائمة خاصة به هنا.
(اختياري) ذاكرة التخزين المؤقت على القرصذاكرة التخزين المؤقت للكتل على القرص المحلي، عند dataBackend.s3.diskCacheBytes / dataBackend.gcs.diskCacheBytes > 0.قابلة للكتابة، ووحدة تخزين حقيقية — ليست tmpfs. تُستخدم بواسطة خلفيتي s3 و gcs؛ انظر خلفيات التخزين.
مستقلتان: صلاحية read لا تمنح أي وصول للكتابة.
["read", "write"]
المحركالفئةحد الذاكرة
slaterمدعوم بالقرص، مُقسَّم إلى صفحاتquery.maxIntermediate يحد مجموعة العمل تلقائيًا
Neo4j 5مدعوم بالقرص، JVM~2 غيغابايت كومة + خارج الكومة، ملتزم به بغض النظر عن الاستعلام
Memgraph · FalkorDBفي الذاكرةالرسم البياني كاملًا مقيم في RAM
ArcadeDBفي الذاكرة، JVMالرسم البياني كاملًا مقيم؛ الأثقل
LadybugDBمضمّن، عموديتجمع مخازن يدوي يجب أن يتجاوز الاستعلام
الرسم البياني (العقد / الحواف)slaterNeo4j 5MemgraphFalkorDBArcadeDBLadybugDB
pole — 62 ألف / 106 ألف117461141401,556198
MeSH — 341 ألف / 469 ألف631,0833584551,631121
EU-AI-Act — 21 ألف / 45 ألف (+55 MiB متجه)997292293121,948286
Wikidata — 91.6 مليون / 1.5 مليار584 (4,595 إجمالي)~2,900لا يمكن التحميللا يمكن التحميللا يمكن التحميل~652 †
الشكلslaterNeo4j 5MemgraphFalkorDBArcadeDBLadybugDB
count(*) كل العقد0.4115.023.816.482.02.2
عدد التسميات0.424.220.71.14.44.3
بحث نقطة مفهرس0.433.90.480.480.658.8
عدد idx-eq0.424.95.02.03812.5
قفزة واحدة (مرساة مفهرسة)1.285.81.214.13904.9
قفزتان (بدون مرساة)1.405.68.516.74446.4
group-by / count(DISTINCT)0.4547–5163–6431–394115.3
فحص كامل CONTAINS0.435.424.11.716.34.1
الشكلslaterNeo4j 5MemgraphFalkorDBLadybugDB
kNN أفضل 10 Concept2.98.61.91.22.8
kNN أفضل 10 Chunk2.45.71.91.53.2
الخاصيةالمُقاسلماذا يهم
زمن استجابة KNN مقابل الكتابات المعلقةفهرس RW ~1.5–2 مللي ثانية، ثابت حتى 50 ألف معلق؛ الطبقة المتراكبة بالقوة الغاشمة قبل الفهرس 1.9 → 115 مللي ثانية (خطي في الدلتا) — 61× عند 50 ألفزمن استجابة الاستعلام لا يتدهور مع تراكم الكتابات بين عمليات الدمج
إدراج التضمين~1.5–2 مللي ثانية لكل متجه في الفهرس الحيالكتابة مرئية لـKNN فورًا؛ ميزانية إعادة بناء الدلتا ≈ 2 مللي ثانية × حد الدلتا
حذف IO عند استدعاء متساوٍ2.9× أقل جلب عقد لكل استعلام عند 67% محذوف، 5.2× عند 80% (استدعاء ≥ 0.90)الرسم البياني المدمج لا يدفع ضريبة قراءة للمتجهات المحذوفة
الدمج، تبديل نقيO(1) — .vamana مرتبط صلبًا بايتًا بايتًا، فقط عمود المعرّف يُعاد كتابتهطي كتابات المتجهات في القاعدة يتخطى إعادة بناء O(N·R·L)
الاستدعاء عبر السلمالمدمج ≥ القاعدة لجيب التمام وL2 والضرب النقطيسلم الكتابة يحافظ على الاستدعاء في كل درجة
الشكلslater (fan 1)slater (fan 8)Neo4j 5
count(*) كل العقد0.410.413606
بحث نقطة (مفهرس)0.720.496.3
الدرجة (عدد قفزة واحدة)0.430.446.0
جيران قفزة واحدة9.84.510.1
قفزتان372334.5
ثلاث قفزات322574
طول متغير *1..2 مميز985105647
البُعدslaterأفضل المجالالحكم
الذاكرة المقيمة، أي مقياس11–584 MiB (62 ألف → 91.6 مليون)في الذاكرة 1.5–2.7 غيغابايت؛ لا يمكن تحميل 1.5 مليارslater
count / البيانات الوصفية / الفحص~0.4 مللي ثانيةمحركات الخدمة 5–80 مللي ثانيةslater (10–200×)
بحث نقطة مفهرس0.43 مللي ثانية (MeSH)Memgraph · FalkorDB 0.48 مللي ثانيةslater (يتفوق على الزوج في الذاكرة)
قفزات متعددة بدون مرساة (صفوف)1.40 مللي ثانية (MeSH قفزتان)Neo4j 5.6 مللي ثانيةslater (فحص نوع العلاقة)
التجميع (group-by / DISTINCT)0.45 مللي ثانيةLadybugDB 5 مللي ثانية (عمودي)slater (رسم بياني في وقت البناء)
kNN2.4–2.9 مللي ثانية (دقيق)FalkorDB 1.2 مللي ثانية (HNSW)يتفوق على Neo4j/Ladybug؛ ~1.4× خلف Memgraph؛ دقيق
91.6 مليون بيانات وصفية / نقطة / درجة / 3 قفزات0.4–32 مللي ثانيةNeo4j 6–3,600 مللي ثانيةslater (2–8800×)
91.6 مليون 1–2 قفزة4.5–23 مللي ثانية (fan 8)Neo4j 10–35 مللي ثانية~متساوٍ
91.6 مليون طول متغير *1..2 مميز~1 ثانيةNeo4j 47 مللي ثانيةNeo4j (نقطة ضعف حقيقية في slater)
count(*) متعدد القفزات على نطاق واسع0.3–0.6 غيغابايتمحركات الذاكرة تجسد مجموعة الصفوفslater، محدود
النتيجةالقياس
يصمد حتى 1000 عميل متزامن، صفر فشلالإنتاجية تبلغ ذروتها ~2.5 ألف طلب/ثانية؛ نقطة انعطاف زمن الاستجابة تبدأ حوالي 750 عميلًا (p99 51 → 750 مللي ثانية) — طابور تحت تنافس النوى، وليس حدًا صلبًا (تشغيل واحد، WSL2)
ذاكرة تخزين الكتل محدودة وفعالةمعدل إصابة 100%، صفر إخلاء، 50 ميغابايت مقيم لمجموعة عمل تناسب التخزين المؤقت
RSS محتجز تحت حمل مستمرمُخصص jemalloc يحتفظ بـRSS عند ~0.6 غيغابايت عبر منحدر wiki_cache_churn من 100→500 عميل — مرتبط بالتخزين المؤقت ومستقر، مع عدم ضبط MALLOC_* (الحد السابق MALLOC_ARENA_MAX=2 + عتبة التقليم متقاعد)؛ تنظيفه الخلفي يعيد أيضًا الحد الأقصى بعد الاندفاع بدلًا من تركه مثبتًا
الذاكرة الإجمالية محدودةquery.maxIntermediateGlobal على مستوى الخادم + توسيع محسوب بالجوار يمسك فيضان wiki_budget ذي القفزتين عند 1000 عميل دون OOM (RSS ~0.6 غيغابايت؛ الحارس يتخلص من ~60% من استعلامات المحور كأخطاء ميزانية قابلة لإعادة المحاولة)