
# المحركات

## اختيار المحرك

تخزن المحركات الثلاثة الشيء نفسه: معرّف UUIDv7 ومصفوفة بيانات، لكن طريقة تنظيمها على القرص تختلف. ويحدد هذا التنظيم ما يناسب كل محرك:

|            | DocStore | SegmentedLog | Queue |
| ---------- | -------- | ------------ | ----- |
| على القرص  | ملف JSONC واحد لكل سجل، مع تقسيم لاحقة UUID | مقاطع إلحاقية فقط من أسطر JSON مسبوقة بالطول وبصمة التحقق، مع بيان | سجل أحداث واحد إلحاقي فقط |
| الكتابة    | استبدال ذري للملف لكل سجل | عمليات إلحاق ببصمات تحقق؛ تُلحق التعديلات والحذوفات كمدخلات جديدة، وتُستعاد المساحة عبر `compact()` | إلحاق ببصمة تحقق لكل حدث في قائمة الانتظار |
| القراءة    | `get()` حسب المعرّف، واستعلامات مفهرسة | مسح بالمؤشر والنطاق الزمني، و`get()` عبر فهرس الحالة | `claim()` بترتيب الإدراج في قائمة الانتظار |
| الاستخدام الأمثل | القراءات المباشرة وعمليات البحث المفهرسة مهما كان حجم المجموعة | تدفقات كثيفة الإلحاق تصل إلى نحو 1M سجل | توزيع مهام دائم بين العمليات |
| نقطة الضعف | مسح الحقول غير المفهرسة بعد نحو 10k سجل | تكرار إعادة كتابة سجل واحد (تشغل الإصدارات القديمة مساحة حتى الدمج) | ليس مخزنًا عامًا للسجلات؛ تُحذف الحمولات بعد اكتمال المهمة |

يشترك `DocStore` و`SegmentedLog` في واجهة [الاستعلامAPI](https://storh.inline0.dev/ar/docs/query-builder) السلسة نفسها: تعمل سلسلة `where()` نفسها على أي من المحركين، ولا يختلف إلا التنفيذ. ويمكن لـ`DocStore` الإجابة باستخدام فهارسه الثانوية، بينما يرشح `SegmentedLog` النتائج أثناء مسح المقاطع. أما `Queue` فيعتمد على المطالبة ولا يدعم الاستعلام: يطالب العمال `claim()` بالمهمة التالية بدلاً من ترشيح المهام.

إرشادات عامة:

- السجلات التي تجلبها أو تحدّثها أو تحذفها منفردة وتستعلم عنها حسب الحقل: `DocStore`. أضف [فهارس](https://storh.inline0.dev/ar/docs/indexes) للحقول التي ترشح النتائج وفقها.
- الأحداث أو الإصدارات التي تضيفها غالباً وتقرأها بالترتيب أو ضمن نطاق زمني: `SegmentedLog`.
- المهام التي ينقلها المنتجون إلى عمليات العاملين: `Queue`. عمليات المطالبة والإكمال آمنة عند تعدد العمليات، كما توزع `claimMany()`/`completeMany()` تكلفة القفل وfsync على دفعات.
- عمليات الربط أو البحث أو إعداد التقارير عن أي مجموعة: أضف [مرآة SQL](https://storh.inline0.dev/ar/docs/sql-mirror). تظل الملفات مرجعية؛ والمرآة إسقاط قابل لإعادة البناء في SQLite أو MySQL.

يمكن تركيب المحركات معاً: فهي لا تشترك إلا في المجلد الجذر، لذا يمكن لتطبيق واحد حفظ المحتوى في `DocStore` وسجل التدقيق في `SegmentedLog` والعمل في الخلفية في `Queue` ضمن جذر التخزين نفسه.

## DocStore

`DocStore` مخصص للقراءات المفردة والمسح البسيط للسجلات الدائمة. ويكتب ملف JSONC واحداً لكل سجل:

```php
$store = new Storh\DocStore($root, 'articles');
$record = $store->put(['slug' => 'hello', 'published' => true]);
$same = $store->get($record->id());
```

تُقسم ملفات السجلات حسب أحرف UUID الأخيرة لتوزيع دفعات الكتابة ذات UUIDv7 على المجلدات:

```text
articles/data/2d/018bcfe5-6800-7000-8000-2d5dba6274da.jsonc
```

## SegmentedLog

`SegmentedLog` مخصص للسجلات كثيرة الإضافة وقراءات المؤشر وقراءات النطاق الزمني. تُكتب السجلات في المقطع النشط، سطر واحد لكل سجل بالصيغة `<byte-length>\t<xxh32>\t<json>` (راجع [الذرية](https://storh.inline0.dev/ar/docs/atomicity)). تستخدم ملفات المقاطع امتداد `.ndjson`، لكن بادئة الطول والمجموع الاختباري تعني أن أدوات NDJSON العادية لا تستطيع قراءتها؛ اقرأ السجلات باستخدام `stream()` أو أداة إنشاء الاستعلامات. وعند امتلاء المقاطع، تتحول إلى مقاطع مختومة ذات فهارس متناثرة.

```php
$events = new Storh\SegmentedLog($root, 'events');
$events->put(['type' => 'user.created']);
$events->compact();
```

## قائمة الانتظار

`Queue` مخصص لقوائم انتظار العمل الدائمة عالية الإنتاجية. تكتب عمليات enqueue وclaim وcomplete وrequeue وpurge أحداثاً إلحاقية فقط في `queue.log`؛ وتُعاد قراءة حالة قائمة الانتظار إلى الذاكرة عند فتحها. وتوزع الأساليب الدفعية تكلفة القفل والتفريغ على مهام كثيرة. يزداد حجم السجل مع كل حدث؛ ويعرض `stats()` حجمه الحالي بالبايت.

```php
$queue = new Storh\Queue($root, 'mail');
$queue->enqueue(['to' => 'person@example.test']);
$job = $queue->claim();
```

## المتانة والتزامن

ينشر storh كتابات الملفات باستخدام إعادة تسمية ذرية داخل المجلد الهدف. ويزيل كل من إعادة الفتح و`repair()` الملفات المؤقتة المتروكة التي انتهت عملية مالكها، مع ترك ملفات العمليات الكاتبة النشطة كما هي. وتجعل علامات الكاتب هذا الفحص ثابت الزمن، لذلك لا يُمسح المخزن إلا بعد تعطل.

تكتب `SegmentedLog` و`Queue` أسطر سجل محمية بالطول وبقيمة التحقق. تؤدي إعادة الفتح أو الإصلاح إلى اقتطاع النهايات المبتورة عند آخر حدث معتمد، وإعادة بناء الحالة في الذاكرة من محتويات السجل الدائمة. تُسلسَل عمليات الكتابة إلى السجل والصف باستخدام أقفال نظام الملفات.

تُسلسَل عمليات التعديل في `DocStore` باستخدام قفل كتابة على مستوى المجموعة، بحيث تُحدَّث ملفات السجلات والفهارس الثانوية ضمن حد اتساق واحد عبر العمليات. تكون الكتابات المتزامنة إلى معرّفات سجلات مختلفة آمنة، وفق افتراضات إعادة التسمية الذرية نفسها في نظام الملفات. أما الكتابات المتزامنة إلى المعرّف نفسه، فتسود فيها آخر عملية إعادة تسمية.

يفرغ storh مقابض الملفات ويزامنها باستخدام fsync قبل اكتمال إعادة التسمية أو الإلحاق، ويحاول مزامنة الأدلة الأصلية بعد عمليات إعادة التسمية الذرية. تظل استمرارية البيانات عند انقطاع الطاقة معتمدة على احترام نظام الملفات الأساسي وخيارات التركيب لعمليات المزامنة هذه.
