# تصميم تخزين الكائنات
## نظرة عامة
يستخدم noa نموذج تخزين معنون بالمحتوى مستوحى من Git ولكن مع بنية خلفية قابلة للتوصيل. تُعنون الكائنات بتجزئة SHA-256 وتُخزن كـ blobs غير شفافة.
## أنواع الكائنات
### Blob
محتوى ملف خام. يُعرف بـ `SHA256(content)`.
```rust
pub struct BlobId(pub String); // SHA-256 بترميز hex
```
لا يوجد ضغط دلتا. كل محتوى فريد ينتج blob واحدًا بالضبط. المحتوى المكرر يُلغى تكراره تلقائيًا بواسطة التجزئة.
### Tree
قائمة مجلد. تعين المسارات إلى مدخلات فرعية (blobs أو أشجار فرعية).
```rust
pub struct TreeEntry {
pub name: String,
pub kind: TreeEntryKind, // Blob أو Tree
pub hash: String, // SHA-256 للفرع
}
pub struct TreeId(pub String); // SHA-256(msgpack(entries))
```
تُسلسل الأشجار باستخدام MessagePack للاكتناز وسرعة إلغاء التسلسل.
## تعريف الواجهة (Trait)
```rust
#[async_trait]
pub trait ObjectStore: Send + Sync {
async fn put_blob(&self, data: &[u8]) -> Result<BlobId>;
async fn get_blob(&self, id: &BlobId) -> Result<Vec<u8>>;
async fn put_tree(&self, entries: Vec<TreeEntry>) -> Result<TreeId>;
async fn get_tree(&self, id: &TreeId) -> Result<Vec<TreeEntry>>;
}
```
## الخلفيات
### RedbObjectStore (محلي)
يستخدم مخزن المفاتيح-القيمة المدمج [redb](https://github.com/cberner/redb).
- جدولان: `blobs` (المفتاح: بايتات التجزئة، القيمة: بايتات المحتوى) و
`trees` (المفتاح: بايتات التجزئة، القيمة: مدخلات msgpack)
- قراءات بدون نسخ عبر ملفات معينة بالذاكرة
- معاملات ACID مع استرداد تلقائي من الأعطال
- كاتب واحد، قراء متعددون عبر MVCC
- لا حاجة لخادم خارجي
### MinioObjectStore (عن بُعد)
يستخدم API متوافق مع S3 عبر `aws-sdk-s3`.
- عنونة بنمط المسار: `<bucket>/blobs/<hash>`، `<bucket>/trees/<hash>`
- يدعم أي خلفية متوافقة مع S3 (MinIO, AWS S3, GCS، إلخ)
- إعادة محاولة تلقائية مع تراجع أسي (exponential backoff)
- مناسب للنشر الموزع
## قرارات التصميم
### لماذا SHA-256 بدلاً من SHA-1؟
يستخدم Git خوارزمية SHA-1، وهي مكسورة تشفيريًا (هجوم SHAttered، 2017). SHA-256 مقاومة للتصادم ومتاحة على نطاق واسع.
### لماذا لا يوجد ضغط دلتا؟
1. **البساطة**: يضيف ضغط الدلتا (ملفات pack في Git) تعقيدًا كبيرًا
(مطابقة النوافذ المنزلقة، thin packs، سلاسل الدلتا).
2. **أداء الكتابة**: كتابات blob المباشرة هي O(1). ضغط الدلتا
يتطلب قراءة كائنات موجودة.
3. **حمل عمل وكيل الذكاء الاصطناعي**: الوكلاء يعيدون توليد ملفات كاملة بشكل متكرر.
الإصدارات القديمة مؤقتة — سلاسل الدلتا ستكون قصيرة ومتعددة.
4. **تفريغ الخلفية**: S3/MinIO يتوليان إلغاء التكرار في طبقة التخزين.
### لماذا MessagePack للأشجار؟
- أصغر بنسبة 30-50% من JSON للبيانات الثقيلة بالثنائيات
- مرونة في المخطط (لا حاجة لتعريفات protobuf)
- دعم نظام Rust البيئي عبر `rmp-serde`
- إلغاء تسلسل سريع
### لماذا redb بدلاً من SQLite؟
- **أمان الأنواع**: يستخدم redb أنواع Rust العامة لتعريفات الجداول
- **الأداء**: redb مُحسَّن لأحمال عمل Rust (قراءات بدون نسخ)
- **البساطة**: اعتمادية واحدة، لا ربط بمكتبة C
- **أمان الأعطال**: سجل الكتابة المسبقة (WAL) في redb أبسط من وضع WAL في SQLite
مقايضة: redb لديه مجتمع أصغر وخيارات أدوات أقل من SQLite. بالنسبة لحالة استخدام noa (تخزين ثنائي مدمج)، المقايضة مواتية.