تصور کنید یک گراف با ۹۱.۶ میلیون گنه و ۱.۵ میلیارد یال را روی سیستمی اجرا کنید که تنها چند صد مگابایت رم دارد. این استراتژیِ مدیریت حافظه است که Slater (نسخه ۰.۲۴.۱) در ۲۱ ژوئیه ۲۰۲۶ با معرفی آن، قواعد بازی را برای توسعهدهندگان گراف تغییر داد. Slater این دستاورد را با تغییر نگاه به حافظه محقق کرده است؛ به جای اینکه رم را پیشنیازی برای نگه داشتن کل مجموعه داده بداند، با آن به عنوان یک بودجهی ثابت برای حافظه موقت (Cache Budget) برخورد میکند.
اکثر پایگاههای داده گراف مدرن مانند Neo4j، Memgraph و FalkorDB معمولاً برای حفظ عملکرد، نیاز دارند که کل گراف در رم جای بگیرد. این بدان معنای است که برای یک گراف ۴۰ گیگابایتی، هر نمونه (Instance) به ۴۰ گیگابایت رم نیاز دارد. این موضوع یک اثر ضربکنندهی هزینهای عظیم برای توسعهدهندگانی ایجاد میکند که نیاز به نسخههای متعدد (Replica) در هر منطقه (Region)، هر مستاجر (Tenant) یا هر پاد (Pod) دارند. وقتی گرافها به مقیاس Wikidata میرسند، موتورهای درون-حافظهای (In-memory) اغلب در بارگذاری کامل دادهها شکست میخورند؛ گراف Wikidata با ۹۰ میلیون گنه و ۱.۵ میلیارد یال معمولاً به ۶۴ تا ۱۲۸ گیگابایت رم مقیم (Resident Memory) نیاز دارد و همین امر آن را برای بسیاری از موتورهای درون-حافظهای غیرقابل دسترس میکند.
طبق مستندات Hikari-Systems، Slater این مشکل را با کامپایل آفلاین گرافها به یک تصویر روی دیسک با آدرسدهی محتوایی (Content-addressed) حل کرده است. این معماری اجازه میدهد یک گراف ۴۰۰ گیگابایتی دقیقاً همان هزینه رمِ یک گراف ۴ گیگابایتی را داشته باشد. این رویکرد یادآور راهکارهای مشابهی است که پروژهی Reame برای کاهش هزینههای استنتاج از طریق ذخیرهسازی در دیسک به کار گرفته بود. این ویژگی، Slater را به انتخابی ایدهآل برای گرافهای دانش (Knowledge Graph) در پشت سیستمهای تولید بازیابیافزا (RAG)، گرافهای توصیه (Recommendation)، گرافهای شناسایی (Identity Graphs) یا گرافهای وابستگی تبدیل میکند. در واقع، این سیستم به طور موثری اندازه گراف را از صورتحساب حافظه جدا میکند.
معماری سرویسدهی کممصرف
سیستم Slater فرآیند ساخت (Build) را از فرآیند سرویسدهی (Serving) با استفاده از دو فایل باینری مجزا جدا کرده است. ابزار slater-build به عنوان یک کامپایلر آفلاین عمل کرده و دامپهای Cypher اولیه را به یک دایرکتوری نسل (Generation Directory) غیرقابلتغییر و دارای هش محتوایی تبدیل میکند. این دایرکتوری شامل موارد زیر است:
- فایل
MANIFEST.jsonبرای جداول نمادها (Symbol Tables) و توصیفکنندههای ایندکس. - فایلهای بلوک ستونی مانند
node_props.blk(ویژگیهای گره)،node_labels.blk(برچسبهای گره)،edge_props.blk(ویژگیهای یال)،topology.csr.blk(توپولوژی CSR) وvectors.f32.blk(بردارها). - ایندکسهای بازهای در مسیر
range/<name>.isam.
سرور Slater سپس این دادهها را از طریق پروتکل Bolt (با پشتیبانی از نسخههای ۵.۴، ۴.۴ و ۴.۱) ارائه میدهد. این سازگاری تضمین میکند که هر درایور استاندارد Neo4j برای پایتون، Go یا جاوااسکریپت بدون هیچ تغییری روی آن کار کند. این سرور از سطح گستردهای از دستورات خواندن Cypher پشتیبانی میکند، از جمله MATCH/WHERE/WITH/UNION و زیرپرسوجوهای CALL {…}. همچنین بیش از ۷۰ تابع و تجمیع (Aggregation) و بخشی از استاندارد ISO GQL (ISO/IEC 39075) برای مسیرهای کمّی (Quantified Paths)، محدودکنندههای مسیر، انتخابگرهای کوتاهترین مسیر و عبارتهای بولی برچسب/نوع را پشتیبانی میکند. برای تغییر دادهها، این سیستم دستورات INSERT ،SET ،REMOVE و DELETE استاندارد GQL را میپذیرد که بر روی همان مسیر نوشتن بادوام (Durable Write Path) Cypher پیاده میشوند.
مدیریت حافظه در اینجا توسط سه بودجهی کش (Cache Budget) خاص کنترل میشود:
- LRU بلوکهای فشردهنشده (Decompressed-block LRU): برای دادههای عمومی گراف.
- استخر ایندکس برداری (Vector-index pool): برای پین کردن کدهای PQ مقیم و یک LRU برای بلوکهای Vamana.
- LRU نتایج (Result LRU): برای مجموعه نتایج پرسوجو.
سیستم از یک تخصیصدهنده jemalloc با قابلیت پاکسازی پسزمینه (Background Purging) استفاده میکند که حافظه آزاد شده را پس از پیکهای کاری (Query Bursts) به سیستمعامل بازمیگرداند. این امر از تثبیت اندازه مجموعه مقیم (RSS) در سطوح حداکثری جلوگیری میکند. تنها کدهای unsafe در این موتور در کریت (Crate) حسابرسی شدهی jemalloc قرار دارند؛ زیرا هر دو بخش سرور و سازنده با دستور #![forbid(unsafe_code)] در زبان Rust کامپایل شدهاند تا از رقابت دادهها (Data Race) و توقفهای جمعکننده زباله (GC Pauses) جلوگیری شود.
ایندکسهای بازهای و متد ISAM
برای جلوگیری از اسکن کامل برچسبها، Slater از ایندکسهای بازهای (range/<name>.isam) برای جفتهای «برچسب-ویژگی» ایندکسشده استفاده میکند. این ایندکسها از ساختار روش دسترسی متوالی ایندکسشده (ISAM) بهره میبرند. به دلیل غیرقابلتغییر بودن نسلهای داده، هیچ درج جدیدی برای بازترازی (Rebalance) وجود ندارد و به همین دلیل سادگی ISAM میتواند عملکرد بهتری نسبت به پیچیدگی درختهای B-tree داشته باشد.
ورودیها بر اساس مقدار مرتب شده و در بلوکهای ۲۵۶ کیلوبایتی فشردهشده با zstd بستهبندی میشوند. یک ایندکس سطح-بالای کوچک در رم، کلید اول هر بلوک را نگه میدارد. یک جستوجو تنها به یک جستوجوی دودویی (Binary Search) در این سطح رم برای شناسایی تک بلوکی که کلید را شامل میشود، و سپس یک خواندن بلوک و اسکن نیاز دارد. این ساختار تضمین میکند که یک جستوجوی ایندکسشده روی meshUi در بازهی تک-رقمی میلیثانیه اتفاق بیفتد. برنامهریز (Planner) این موارد را از طریق NodeScan::RangeEq یا RangeRange شناسایی میکند؛ در حالی که گزارههای بدون ایندکس به اسکن کامل برچسب یا اسکن کلی بازمیگردند.
قابلیتهای ترکیبی خواندن-نوشتن
با وجود اینکه هسته سیستم غیرقابلتغییر است، Slater شامل یک لایهی نوشتنی اختیاری است که از طریق delta.enabled فعال میشود. این لایه از رویکرد ادغام لوگ-ساختارمند (LSM) برای مدیریت بهروزرسانیهای زنده بدون فشار آوردن به مسیر خواندن استفاده میکند. نوشتها در یک لایهی لوگ-ساختارمند روی هسته غیرقابلتغییر جمع میشوند، به این معنی که خواندن از یک گراف بدون تغییر، دقیقاً همان هزینه قبلی را دارد.
- WAL (لاگ پیشنویس): هر تغییر (Mutation) در پشت یک نویسنده واحد برای هر گراف سریالسازی شده و به یک WAL مخصوص هر گراف اضافه میشود. یک نوشتن تنها پس از
fsyncکه آن را پوشش میدهد، به عنوان بادوام تایید میشود. برای بهبود کارایی،write-UNWINDاجازه میدهد نوشتها را با یکfsyncبرای هر دسته (Batch) گروهبندی کنند. WAL تنها روی دیسک محلی ذخیره میشود و از طریق بکاند ذخیرهسازی مسیریابی نمیشود. - Memtables و بخشهای L0: نوشتها در یک memtable در رم (محدود شده توسط
delta.memtableBytes) جمع میشوند. پس از پر شدن، این دادهها به بخشهای دلتای L0 غیرقابلتغییر منتقل (Flush) میشوند. - تجمیع (Consolidation): وظایف دورهای، دلتا را از طریق دستور
CALL slater.consolidate()دوباره به یک هسته غیرقابلتغییر تازه تبدیل میکنند. این عملیات میتواند به صورت دستی، با رسیدن به مقدارdelta.deltaCorePercentاز اندازه هسته (که optionally توسطdelta.consolidateWindowمحدود شده)، یا از طریق یک حد حفاظتیdelta.deltaHardBytesتحریک شود.
این طراحی تضمین میکند که مسیرهای خواندن، چه لایهی نوشتنی فعال باشد و چه خالی، از نظر بایتی یکسان بمانند. خواندن متادیتا، مانند count(*)، سریع باقی میماند زیرا دلتا شمارندههای زنده خود را نگه میدارد. برای مثال، دستور count(*) روی یک هسته ۹۱.۶ میلیون گنهی با ۵۰۰ هزار نوشت در انتظار، همچنان در بازهی دهها میلیثانیه بدون لمس حتی یک بلوک پاسخ میدهد.
جستوجوی برداری بومی و RAG
Slater جستوجوی برداری نزدیکترین همسایه تقریبی (ANN) را مستقیماً در کنار گراف با استفاده از db.idx.vector.queryNodes ادغام کرده است. این سیستم از معیارهای Cosine، L2 و Dot-product (MIPS) پشتیبانی میکند. موتور بر اساس یک آستانه --ann-threshold (به طور پیشفرض ۵۰,۰۰۰ بردار) مسیر اجرا را انتخاب میکند:
- زیر آستانه: موتور یک اسکن دقیق Brute-force با استفاده از
vectors.f32.blkو یک هسته فاصله SIMD انجام میدهد که بازگشت (Recall) ۱.۰ را فراهم میکند. - بالای آستانه: از یک ایندکس گراف Vamana (برگرفته از lineage DiskANN) و کوانتش محصول (PQ) استفاده میکند. PQ بردارها را به کدهای کوتاهی تبدیل میکند (که توسط
--pq-subspacesو--pq-bitsتعریف میشوند) و این کدها در استخرcache.vectorCacheBytesمقیم میمانند. یک جستوجوی پرتوی حریصانه (Greedy Beam Search) با عرض لیست کاندیدایvectorQuery.beamWidthدر چند گام به همسایگان میرسد. بلوکهای گراف Vamana به جای نگه داشتن کلی، از طریق کش برداری صفحهبندی (Paged) میشوند.
این قابلیت دستیابی به تأخیرهای بسیار پایین در جستوجوی برداری، مشابه رویکردی است که در پیادهسازی جستوجوی معنایی محلی با تأخیر زیر ۱۰ میلیثانیه مشاهده شده است. نکته حیاتی این است که این Embeddingها از طریق یک نردبان نوشت (Write Ladder) به سبک FreshDiskANN بهصورت زنده و در جای خود قابل تغییر هستند. وقتی کاربر دستور SET n.embedding = vecf32([...]) را اجرا میکند، تغییر بلافاصله در KNN قابل رؤیت میشود. پرسوجو سه سطح را ادغام میکند — ایندکس پایه مهروموم شده، یک ایندکس مهروموم شده در هر بخش، و یک ایندکس RW در حافظه — تا تأخیر ثابت بماند. حذفها با رها کردن یک «حفره» مدیریت میشوند؛ گنه دیگر بازگردانده نمیشود اما تا زمانی که یک تجمیع حذف پسزمینه آن را بردارد، به عنوان یک نقطه راهنمای ناوبری باقی میماند. چون گراف برای همسایگان از موقعیتهای چیدمان (Layout Positions) به جای ID گنه استفاده میکند، CALL slater.consolidate() میتواند ایندکس Vamana را به عنوان یک Hard-link یکسان از نظر بایتی منتقل کند و تنها یک ستون کوچک ID را بازنویسی نماید.
بنچمارکها و رویارویی با Neo4j
در یک مقایسه مستقیم با Neo4j 5 با استفاده از مجموعه داده Wikidata (۹۱.۶ میلیون گنه / ۱.۵ میلیارد یال)، Slater دستاوردهای عظیمی در عملیاتهای متادیتا نشان داد. یک پرسوجوی count(*) که در Neo4j ۳.۶ ثانیه زمان برد تا از طریق اسکن دیسک تکمیل شود، توسط Slater در ۰.۴۱ میلیثانیه پاسخ داده شد (بهبود تقریبی ۸۸۰۰ برابری).
یافتههای کلیدی عملکرد در مقیاسهای مختلف گراف (Pole, MeSH, EU-AI-Act, Wikidata):
- حافظه مقیم: ردپای حافظه Slater در هر مقیاس کمترین بود و در حالی که گراف حدود ۱۵۰۰ برابر رشد کرد، مصرف رم Slater تنها حدود ۵۰ برابر رشد کرد. برای Wikidata، مجموعه کاری غیرنام (Anon Working Set) Slater برابر ۵۸۴ مگابایت بود (در مجموع ۴۵۹۵ مگابایت شامل کش صفحات OS)، در حالی که Neo4j حدود ۲ گیگابایت Heap اختصاص داده بود.
- تأخیر: Slater در متادیتا و اشکال ایندکس پیشتاز است (معمولاً حدود ۰.۴ میلیثانیه). در MeSH، پرسوجوهای ۲-گامی بدون لنگر (Unanchored) ۱.۴۰ میلیثانیه زمان بردند که سریعتر از ۵.۶ میلیثانیه Neo4j بود. برای مجموعه بردارهای EU-AI-Act، kNN دقیق Slater بین ۲.۴ تا ۲.۹ میلیثانیه بود و تنها پس از FalkorDB (۱.۲ میلیثانیه) قرار گرفت.
- پیمایشها (Traversals): در Wikidata، شمارشهای ۳-گامی با
maxFanout=8برابر ۲۵ میلیثانیه بود در مقابل ۷۴ میلیثانیه Neo4j. با این حال، توسعهدهندگان یک نقطه ضعف قابل توجه را پذیرفتهاند: گسترشهای متمایز با طول متغیر. در الگویvar-length *1..2 distinct، Slater تقریباً ۱ ثانیه زمان برد در حالی که Neo4j تنها ۴۷ میلیثانیه زمان نیاز داشت.
بنچمارکهای مؤلفه نردبان نوشت برداری
از آنجایی که هیچ موتور دیگری ANN نوشتنی بومی-دیسک ندارد، Slater از بنچمارکهای داخلی برای ردیابی عملکرد نردبان نوشت برداری خود استفاده میکند:
- تأخیر KNN: حتی با ۵۰ هزار نوشت در انتظار، تأخیر در حدود ۱.۵ تا ۲ میلیثانیه ثابت میماند؛ در حالی که در صورت استفاده از یک Overlay Brute-forced، این مقدار به ۱۱۵ میلیثانیه میرسید.
- عملکرد درج: بردارهای جدید در حدود ۱.۵ تا ۲ میلیثانیه در ایندکس زنده درج میشوند.
- کارایی حذف: در حالتی که ۸۰٪ بردارها حذف شده باشند، موتور در بازگشت ≥ ۰.۹۰، ۵.۲ برابر دفعات کمتری برای واکشی گنه (Node-fetch) در هر پرسوجو استفاده میکند.
- تجمیع: تجمیعهای جایگشت خالص (Permutation Consolidations) با پیچیدگی $O(1)$ هستند زیرا ایندکس Vamana به صورت hard-link منتقل میشود.
تست فشار و همزمانی
فراتر از متریکهای تک-کلاینت، تست فشار با درایور Locust روی پروتکل Bolt نشان داد که یک اجرای ۲۵۶ مگابایتی کش روی گراف Wikidata-1M میتواند ۱۰۰۰ کلاینت همزمان را بدون هیچ خطایی پشتیبانی کند. توان عملیاتی (Throughput) در پیک به حدود ۲۵۰۰ درخواست در ثانیه (rps) رسید. «زانوی تأخیر» (Latency Knee) — جایی که p99 از ۵۱ میلیثانیه به ۷۵۰ میلیثانیه جهش کرد — در حدود ۷۵۰ کلاینت به دلیل رقابت روی هسته (Core Contention) و نه محدودیتهای سختافزاری رخ داد.
برای جلوگیری از کراشهای OOM در هنگام «سیل گنههای مرکزی» (Hub Floods)، سرور از یک محافظ query.maxIntermediateGlobal استفاده میکند. در تستهای ۱۰۰۰ کلاینتی، این محافظ با موفقیت حدود ۶۰٪ از پرسوجوهای Hub را به عنوان خطاهای بودجهای قابل-تلاش مجدد (Retryable) دفع کرد و در عین حال RSS را در حدود ۰.۶ گیگابایت پایدار نگه داشت. پاکسازی پسزمینه تخصیصدهنده jemalloc با موفقیت حافظه سطح-بالای پس از پیک را به سیستمعامل بازگرداند.
استقرار و انعطافپذیری ذخیرهسازی
Slater برای استقرار در Docker طراحی شده است. ایمیجهای پیشساخته چند-معماری (amd64 و arm64) در Docker Hub با نام hikarisystems/slater منتشر شدهاند. سرور یک باینری کوچک strip شده روی پایه distroless glibc است؛ تگ latest-lite حدود ۱۲ مگابایت و ایمیج استاندارد حدود ۲۲ مگابایت است و از TLS خالص Rust به جای OpenSSL استفاده میکند.
Slater از سه بکاند ذخیرهسازی اصلی از طریق یک انتزاع ObjectStore پشتیبانی میکند و به جای mmap از خواندنهای موقعیتی (read_exact_at) استفاده مینماید:
- سیستم فایل (fs): پیشفرض برای SSDهای محلی یا نقاط اتصال NFS. این حالت برای اطمینان از یکپارچگی، یک باز-هش (Re-hash) کامل BLAKE3 از هر فایل هنگام باز کردن انجام میدهد.
- Amazon S3: پشتیبانی از AWS و ذخیرهسازهای سازگار با S3 مانند MinIO. این سیستم از HTTP Range GETها استفاده کرده و یکپارچگی را از طریق متادیتای SHA-256 تأیید میکند. اعتبارنامهها میتوانند از طریق پیکربندی (
awsAccessKey/awsSecretKey)، زنجیره استاندارد AWS یا نقشهای IAM ارائه شوند. برای MinIO، کاربرانdataBackend.s3.endpointوdataBackend.s3.pathStyle=trueرا تنظیم میکنند. - Google Cloud Storage (GCS): استفاده از APIهای JSON، اعتبارنامههای پیشفرض برنامه (ADC) و Workload Identity. این سیستم برای بررسی یکپارچگی در سطح متادیتا از CRC32C بهره میبرد. کلیدهای خاص از طریق
credentialsPathیاcredentialsJsonارسال میشوند.
برای کاهش تأخیر ذخیرهسازهای ابری (۱۰ تا ۵۰ میلیثانیه رفت و برگشت)، Slater یک لایه کش SSD محلی اختیاری را فراهم میکند. این لایه که توسط dataBackend.<s3|gcs>.diskCacheBytes فعال میشود، بایتهای مهروموم شده و فشرده را روی یک Volume واقعی (و نه tmpfs) ذخیره میکند. این کار هزینه واکشی مجدد بلوکهای اخراج شده از رم را کاهش داده و عملکرد ذخیرهسازهای ابری را به سرعتهای سیستم فایل محلی نزدیک میکند. این لایه از یک صف Write-behind استفاده میکند که محدود به blockCacheBytes / 8 است (با کف diskCacheBytes معمولاً ۸ مگابایت) تا تضمین کند مسیر پرسوجو هرگز در I/O دیسک مسدود نشود. یک چکسام مخصوص هر فایل که در هر خواندن تأیید میشود، فایلهای کش فاسد را با تحریک واکشی مجدد، خود-ترمیم (Self-heal) میکند.
امنیت، حاکمیت و ابزارها
سیستم از نظر طراحی با acl.json که کاربران را به هشهای پسورد argon2id متصل میکند، قفل شده است. دسترسیهای خواندن و نوشتن مستقل هستند؛ یک کاربر برای انجام بهروزرسانی کلید-تجاری به هر دو دسترسی ["read", "write"] نیاز دارد. سیستم از رمزنگاری دادههای ساکن با استفاده از sealing XChaCha20-Poly1305 و TLS برای اتصالات Bolt (bolt+s://) پشتیبانی میکند.
جزئیات عملیاتی هسته
- محافظ نسل (Generation Guard): Slater هر
generationPollMsاشارهگرcurrentرا چک میکند. اگرreloadStrategyرویexitتنظیم شده باشد، سرور ریاستارت میشود؛ اگر رویswapباشد، نسل جدید را به صورت اتمیک اعتبارسنجی کرده و جایگزین میکند در حالی که اجازه میدهد پرسوجوهای در جریان به پایان برسند. - Mountها: رپلیکاهای خواندن با یک Root FS فقط-خواندنی اجرا میشوند. نویسندهها (Writers) به یک Volume بادوام در
delta.walDirبرای WAL و بخشهای L0 نیاز دارند. کشهای دیسک محلی نیز به یک Volume واقعی نیاز دارند تا از تورم RSS به دلیلtmpfsجلوگیری شود. - بررسی سلامت (Health Checks): باینری
slater healthcheckیک دست-دادن (Handshake) Bolt را انجام میدهد. این کار تضمین میکند که سرور واقعاً برای ترافیک آماده است، نه اینکه صرفاً یک سوکت باز داشته باشد.
ابزارهای تخصصی تکمیلی عبارتند از:
- slater query: ابزاری تک-مرحلهای برای اسکریپتنویسی که یک پرسوجوی Cypher فقط-خواندنی را-در-فرآیند (In-process) اجرا کرده و بدون نیاز به سرور، JSON بازمیگرداند. این ابزار مگر در صورت استفاده از پرچم
-qتنها خلاصههای متریک (هزینه، تعداد نتایج، زمان اجرا) را ارائه میدهد. - slater dump: ابزاری برای استخراج که دامپهای Cypher از نوع
MERGEبا کلید-تجاری ایجاد میکند. این ابزار کلیدهای شناسایی را از ایندکسهای بازهای استنباط کرده و گنههای چند-برچسبه را به صورتMERGE (n:Ident:Other {key: v})منتشر میکند. همچنین برای کاراکترهای خاص از Backtick-quoting استفاده میکند. توجه داشته باشید که بردارها را نمیتوان از طریقdumpاستخراج کرد و با یک هشدار حذف میشوند.
این رویکرد فرض بنیادی را که مقیاس گراف نیازمند تامین رم گرانقیمت است، تغییر میدهد. با انتقال منبع حقیقت از Heap به Store، Slater امکان چند-مستاجری با تراکم بالا (High-density multi-tenancy) را فراهم میکند که در آن یک سرور میتواند چندین گراف را با ایزولاسیون شدید میزبانی کند. این پروژه تحت مجوز Apache License, Version 2.0 منتشر شده است.




گفتگو