Enterprise الإصدار
التدفق (Stream): معالجة مهام المستندات
نظرة سريعة
قسم بعنوان «نظرة سريعة»يحوّل NextPDF\Enterprise\Stream\DocumentJobStreamProcessor تدفقاً من بيانات العرض (render manifests) إلى نتائج دائمة وقابلة للمساءلة. يستهلك iterable<RenderManifest> بوصفه مولّداً (generator)، ويعرض نوافذ محدودة عبر محرك عرض Pro، ويُنهي كل مهمة وفق ترتيب المصدر. تنتهي كل مهمة في حالة نهائية واحدة تماماً: المُخرَج مُثبَّت، أو مُعترَف به على أنه مُثبَّت مسبقاً، أو مُحال إلى الرسائل الميتة. يُسجَّل التقدّم في نقاط تفتيش، بحيث يستأنف التشغيل المتعطّل دون إعادة نشر أي شيء.
تنقسم حكاية Stream عبر إصدارَين، والانقسام مقصود. يوفّر Pro محرك العرض الدائم والمتزامن ومخازن نظام الملفات المحلية أحادية المضيف — النصف داخل العملية. ويوفّر Enterprise معالج تدفق مهام المستندات هذا إضافة إلى الأجزاء التي تعبر حدود المضيفات: مُثبِّت تخزين الكائنات (ObjectStorageCommitter) وصندوق الصادر الدائم للأحداث النهائية (FilesystemOutboxEmitter). وتذكر صفحة Pro Stream الحدّ نفسه من جانبها.
التوفّر والترخيص
قسم بعنوان «التوفّر والترخيص»تُشحن هذه القدرة ضمن NextPDF Enterprise (nextpdf/enterprise) وتُفعَّل عبر مظروف ترخيص من فئة Enterprise. ولا يُحمّل أي نشر يفتقر إلى ذلك الاستحقاق أصنافَ هذه القدرة. قارن بين الإصدارات واحصل على ترخيص.
التثبيت
قسم بعنوان «التثبيت»composer require nextpdf/enterpriseتقع الأصناف الواردة في هذه الصفحة ضمن NextPDF\Enterprise\Stream وNextPDF\Enterprise\Stream\Storage. وهي تستهلك عقود Pro المجمّدة في NextPDF\Pro\Stream — واجهات المحرك، والمُثبِّت، ونقطة التفتيش، والتكافؤ (idempotency)، وإعادة المحاولة، والرسائل الميتة (dead-letter).
نظرة مفاهيمية عامة
قسم بعنوان «نظرة مفاهيمية عامة»مهمة المعالج هي دلالات التسليم، لا العرض. فهو يجمّع تدفق الـ manifest في نوافذ حسب إزاحة المصدر لا تتجاوز حجم دفعة المحرك. تُعرَض كل نافذة عبر RenderEngineInterface::renderBatch()، مع إعادة محاولة محدودة وحتمية لمُهلات كل عنصر. ثم يُنهى كل عنصر وفق ترتيب إزاحة المصدر إلى نتيجة نهائية.
حدّ المرة-الواحدة-تماماً يكون لكل عنصر، وهو مرتكز في المُثبِّت، لا في التنسيق. التقدّم الدائم هو علامة مائية عليا لإزاحة تبدأ من 1: كل إزاحة عند نقطة التفتيش أو دونها بلغت نتيجة نهائية. ترتيب الحاجز ثابت: ثبّت البايتات، ثم قدّم العلامة المائية، ثم احفظ نقطة التفتيش، ثم أفرغ علامات التكافؤ المؤقتة، ثم أطلق الأحداث النهائية. أي عطل بين التثبيت ونقطة التفتيش يعيد التثبيت بتكافؤ عند الاستئناف، لأن المُثبِّت يقارن البصمات (digests). وأي عطل بعد نقطة التفتيش يتخطّى الإزاحة سريعاً، فلا يُنشر شيء مرتين.
ينفّذ ObjectStorageCommitter واجهة OutputCommitterInterface الخاصة بـ Pro مقابل مخزن كائنات عبر واجهة ObjectStorageClientInterface الأدنى. ويكون container الهدف هو الحاوية (bucket)، وkey مفتاح الكائن. إعادة تثبيت بايتات متطابقة عملية لا-تأثير لها بعد مقارنة البصمة. والبايتات المتباينة دون overwrite تُطلق تعارض SPEC-COMMIT-409. ولا يُنشأ كائن جديد إلا عبر الكتابة الشرطية الذرّية putIfAbsent()؛ وخسارة ذلك السباق تُشغّل حلقة محدودة لإعادة القراءة والحسم. ولذلك يصمد ضمان المرة-الواحدة-تماماً عبر الكُتّاب المتعددين بقدر ما يكون putIfAbsent() في مُهايئك كتابة شرطية حقيقية — If-None-Match: * على S3، وifGenerationMatch: 0 على GCS. تشحن هذه الدورة الواجهة إضافة إلى NullObjectStorageClient العامل في الذاكرة؛ أما مُهايئ S3/GCS الحيّ فيوفّره المضيف.
تُغلق الأحداث النهائية الحلقة للأنظمة اللاحقة. فبعد حاجز نقطة التفتيش يحاول المعالج إطلاق JobTerminalEvent لكل مهمة مُنهاة — مُعرّفات، وحالة، وإيصال، وتفاصيل خطأ، وعدد المحاولات، ودون أي بايتات PDF إطلاقاً. ومع مُطلِق نداء ارتجاعي بسيط يكون الإطلاق مرة-على-الأكثر: قد تُتخطّى الأحداث الواقعة بعد نقطة التفتيش عند الاستئناف من عطل. ويجعل FilesystemOutboxEmitter كل حدث دائماً بمجرد تشغيل emit(): فكل حدث ملف JSON ذرّي واحد يُسمّى ببصمة eventId الحتمي، فتصير إعادة الإطلاق بعد الاستئناف متكافئة، ويُسلّم المُرحِّل مرة-على-الأقل، ويزيل المستهلكون التكرار وفق eventId. ويبقى حدّ واحد في كلتا الحالتين: يقع الإطلاق بعد حاجز نقطة التفتيش، فأي عطل بين checkpoint.save() وemit() يتخطّى الحدث النهائي لذلك العنصر عند الاستئناف. والأنظمة اللاحقة التي تتطلّب سجلّاً كاملاً للأحداث ينبغي أن تُطابق مقابل الكائنات المُثبَّتة (المخزن هو مصدر الحقيقة)، لا مقابل صندوق الصادر وحده.
الترخيص مربوط في مسار الإخراج. يحلّ المصنع withBrandingFromLicense() استراتيجية علامة التقييم مرة واحدة لكل تشغيل انطلاقاً من الترخيص. الترخيص المدفوع يحلّ إلى تحويل مطابق (identity transform). أما ترخيص التقييم أو غياب الترخيص فيضع علامة مائية على كل مستند مُثبَّت، ويُحال إلى الرسائل الميتة أي مستند يتعذّر وسمه — فالمعالج لا يثبّت أبداً بايتات تقييم دون علامة.
لماذا يعمل بهذه الطريقة
قسم بعنوان «لماذا يعمل بهذه الطريقة»القرار الحامل هو أن المرة-الواحدة-تماماً تستند إلى كائن المُثبِّت المقارَن بالبصمة والمُنشأ شرطياً — لا إلى أقفال موزّعة أو إجماع. الكتابة الشرطية لمخزن الكائنات هي البدائية الذرّية الوحيدة التي يتطلّبها التصميم، وكل ما عداها مسموح له بأن يفشل ويتعافى. ولهذا يجب أن يبقى محرك العرض خالياً من الآثار الجانبية، ولهذا تُعامَل الحالة المُفهرَسة ومخابئ إزالة التكرار داخل التشغيل بوصفها تسريعات قابلة لإعادة الحساب، ولهذا يُجهض التثبيت الغامض التشغيلَ بدل التخمين: فمسار الاستئناف يتقارب عبر مقارنة البصمة نفسها. وهو أيضاً سبب كون كاتب-واحد لكل runId متطلَّباً مُعلَناً لا حجزاً مفروضاً — إذ يبقى مخزن نقاط التفتيش بسيطاً عن قصد، وتبقى طبقة التثبيت شبكة الأمان.
خلفية التصميم: توليد المستندات بأحجام كبيرة.
سطح واجهة البرمجة (API)
قسم بعنوان «سطح واجهة البرمجة (API)»DocumentJobStreamProcessor
قسم بعنوان «DocumentJobStreamProcessor»ينبغي للمضيفين البناء عبر المصنع، كي لا يبقى ضبط الترخيص-إلى-العلامة غير مربوط أبداً:
public static function withBrandingFromLicense( RenderEngineInterface $engine, OutputCommitterInterface $committer, IdempotencyStoreInterface $idempotency, CheckpointStoreInterface $checkpoints, KeyedStateStoreInterface $state, DeadLetterStoreInterface $deadLetters, RetryPolicy $retryPolicy, ClockInterface $clock, EntitlementEvaluator $entitlementEvaluator, ?LicenseKey $license, ?StreamProcessorProbe $probe = null, ?JobCompletionEmitterInterface $emitter = null, ?BrandingApplicator $brandingApplicator = null,): self$clock هو Symfony\Component\Clock\ClockInterface (يمرّ تراجع إعادة المحاولة بالنوم عبره). وترخيص null يحلّ بالفشل-المُغلق إلى علامة التقييم.
نقطة الدخول الوحيدة تعالج تشغيلاً واحداً وتُعيد عدّاداته:
public function process(iterable $manifests, StreamProcessorConfig $config): ProcessingSummaryيرمي أو يفشل بـ: NextPDF\Enterprise\Stream\Exception\StreamProcessorException عند إخفاق شروط الأمان ضد الأعطال (تشغيل crashSafe مع متعاونين غير دائمين) أو عندما يكون التثبيت غامضاً؛ وInvalidArgumentException عندما يتجاوز windowSize قيمة maxBatchSize() للمحرك.
StreamProcessorConfig
قسم بعنوان «StreamProcessorConfig»public function __construct( public string $runId, int $windowSize = 32, int $checkpointIntervalJobs = 100, public bool $crashSafe = true, public bool $emitSkippedCompletions = false,)يرمي أو يفشل بـ: InvalidArgumentException عندما يكون windowSize أو checkpointIntervalJobs دون 1. و$runId هو مُعرّف التشغيل المستقر أحادي-الكاتب الذي يُفهرِس الاستئناف من نقطة التفتيش.
ObjectStorageCommitter
قسم بعنوان «ObjectStorageCommitter»public function __construct( private ObjectStorageClientInterface $client, private string $scheme, private ClockInterface $clock,) {}$scheme يُسمّي مخطّط الهدف الذي يخدمه هذا المُثبِّت (مثل s3 أو gcs)؛ و$clock هنا هو Psr\Clock\ClockInterface.
public function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceiptيرمي أو يفشل بـ: UnsupportedTargetException عند عدم تطابق المخطّط؛ وRenderManifestException عندما لا يكون مفتاح الهدف آمناً نسبةً إلى الحاوية؛ وCommitIntegrityException عندما لا تطابق بصمة sha-256 المُعلَنة البايتات؛ وOutputCommitConflictException (SPEC-COMMIT-409) عند البايتات المتباينة دون overwrite؛ وRuntimeException عندما يتعذّر تقارُب سباق الإنشاء بعد 5 محاولات تحت التعديل المتزامن.
ObjectStorageClientInterface
قسم بعنوان «ObjectStorageClientInterface»سطح المُهايئ الأدنى الذي يُنفّذه تكامل S3/GCS حيّ:
public function shaOf(string $bucket, string $key): ?string;
public function put(string $bucket, string $key, string $bytes, string $sha256): void;
public function putIfAbsent(string $bucket, string $key, string $bytes, string $sha256): bool;يجب أن يكون putIfAbsent() إنشاءً شرطياً ذرّياً حقيقياً (If-None-Match: * على S3، وifGenerationMatch: 0 على GCS) ويُعيد true فقط عندما تكون هذه الاستدعاءة هي التي كتبت الكائن. وput() هو الاستبدال غير المشروط المُستخدَم حصراً عندما يطلب الـ manifest الخيار overwrite.
JobCompletionEmitterInterface وFilesystemOutboxEmitter
قسم بعنوان «JobCompletionEmitterInterface وFilesystemOutboxEmitter»public function emit(JobTerminalEvent $event): void;المُطلِق اختياري في المعالج. ولا تُطلَق الأحداث إلا بعد إنهاء العنصر بصورة دائمة. وFilesystemOutboxEmitter هو التنفيذ الدائم المشحون:
public function __construct(string $directory, ?AtomicFileWriter $writer = null)يرمي أو يفشل بـ: InvalidArgumentException عندما لا يكون الدليل موجوداً؛ ويرمي emit() الاستثناء RuntimeException إذا تعذّر ترميز حدث بصيغة JSON. وhasEvent(string $eventId): bool يفحص صندوق الصادر؛ وcount(): int يُبلّغ عن الأحداث غير المُسلَّمة.
JobTerminalEvent وJobTerminalStatus
قسم بعنوان «JobTerminalEvent وJobTerminalStatus»public function __construct( public string $eventId, public string $runId, public int $sourceOffset, public string $jobId, public string $idempotencyKeyValue, public JobTerminalStatus $status, public ?CommitReceipt $receipt, public ?string $errorCode, public ?string $errorMessage, public int $attempts, public DateTimeImmutable $occurredAt,) {}eventId حتمي — runId:sourceOffset:idempotencyKey:status — وهو ما يجعل إزالة التكرار في صندوق الصادر ممكنة. وtoArray() يُسلسِل الحدث للنقل؛ وهو لا يحمل أي بايتات PDF. وJobTerminalStatus تعداد نصّي: Committed (committed)، وDeadLettered (dead_lettered)، وSkipped (skipped).
ProcessingSummary
قسم بعنوان «ProcessingSummary»عدّادات غير قابلة للتغيير يُعيدها process(): runId، وsourceRead، وfastForwardedByCheckpoint، وskippedByIdempotency، وwindows، وrenderBatchCalls، وrenderRetries، وcommitReceipts، وdeadLettered، وcheckpointSaves، وfinalCommittedOffset (العلامة المائية العليا النهائية).
عينة برمجية — بداية سريعة
قسم بعنوان «عينة برمجية — بداية سريعة»تثبيت مرة-واحدة-تماماً في تخزين الكائنات بمعزل. يحلّ NullObjectStorageClient العامل في الذاكرة محل مُهايئ S3/GCS لديك؛ والدلالات التي تلاحظها هي ذاتها التي يجب أن يحفظها مُهايئ حيّ.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Stream\Storage\NullObjectStorageClient;use NextPDF\Enterprise\Stream\Storage\ObjectStorageCommitter;use NextPDF\Manifest\OutputObjectKey;use NextPDF\Pro\Stream\Exception\OutputCommitConflictException;use Symfony\Component\Clock\NativeClock;
$committer = new ObjectStorageCommitter( client: new NullObjectStorageClient(), // swap in your S3/GCS adapter scheme: 's3', clock: new NativeClock(),);
$target = new OutputObjectKey(scheme: 's3', container: 'invoices', key: '2026/07/inv-1001.pdf');$bytes = '%PDF-1.7 example-rendered-bytes';$sha = hash('sha256', $bytes);
$first = $committer->commit('inv-1001', $target, $bytes, $sha);$replay = $committer->commit('inv-1001', $target, $bytes, $sha); // crash-resume replay
printf("first : reuse=%s, %d bytes\n", var_export($first->idempotentReuse, true), $first->bytesWritten);printf("replay: reuse=%s\n", var_export($replay->idempotentReuse, true));
try { $divergent = '%PDF-1.7 different-bytes'; $committer->commit('inv-1001', $target, $divergent, hash('sha256', $divergent));} catch (OutputCommitConflictException $conflict) { echo 'conflict: ' . $conflict->specCode() . "\n"; // no silent clobber}المُخرَج المتوقّع:
first : reuse=false, 31 bytesreplay: reuse=trueconflict: SPEC-COMMIT-409عينة برمجية — الإنتاج
قسم بعنوان «عينة برمجية — الإنتاج»تشغيل كامل آمن ضد الأعطال: مخازن Pro دائمة، ومُثبِّت تخزين الكائنات، وصندوق صادر دائم، وعلامة مُحلّاة من الترخيص. وإعادة تشغيل runId نفسه بعد عطل تتخطّى سريعاً وتتقارب.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Licensing\EntitlementEvaluator;use NextPDF\Enterprise\Stream\DocumentJobStreamProcessor;use NextPDF\Enterprise\Stream\Exception\StreamProcessorException;use NextPDF\Enterprise\Stream\FilesystemOutboxEmitter;use NextPDF\Enterprise\Stream\Storage\ObjectStorageCommitter;use NextPDF\Enterprise\Stream\StreamProcessorConfig;use NextPDF\Manifest\Render\SingleDocumentRenderer;use NextPDF\Manifest\RenderManifest;use NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore;use NextPDF\Pro\Stream\Dedup\FilesystemIdempotencyStore;use NextPDF\Pro\Stream\Engine\InProcessRenderEngine;use NextPDF\Pro\Stream\Retry\FilesystemDeadLetterStore;use NextPDF\Pro\Stream\Retry\RetryPolicy;use NextPDF\Pro\Stream\State\InMemoryKeyedStateStore;use Symfony\Component\Clock\NativeClock;
// Production requires a host-supplied adapter whose putIfAbsent() is a TRUE// atomic conditional create (S3 If-None-Match: *, GCS ifGenerationMatch: 0)// and whose shaOf() reads durable object state. NullObjectStorageClient is// for the quick start only - it keeps nothing across processes.$s3Client = new \Aws\S3\S3Client(['region' => 'eu-central-1', 'version' => 'latest']);$objectClient = new \Acme\Storage\S3ObjectStorageClient($s3Client); // implements ObjectStorageClientInterface
$stateDir = '/var/lib/nextpdf/stream';foreach (['checkpoints', 'idempotency', 'dead-letters', 'outbox'] as $sub) { if (!is_dir($stateDir . '/' . $sub)) { mkdir($stateDir . '/' . $sub, 0770, true); }}
// One manifest per JSONL line; the generator never materialises the batch.$manifests = (static function (string $path): Generator { $handle = fopen($path, 'rb'); if ($handle === false) { throw new RuntimeException('Cannot open job stream: ' . $path); } try { while (($line = fgets($handle)) !== false) { if (trim($line) !== '') { yield RenderManifest::fromJson(trim($line)); } } } finally { fclose($handle); }})('/var/spool/nextpdf/jobs.jsonl');
$license = null; // your licensing bootstrap yields a LicenseKey; null = evaluation branding
$processor = DocumentJobStreamProcessor::withBrandingFromLicense( engine: new InProcessRenderEngine(SingleDocumentRenderer::standalone()), // For a live bucket, implement ObjectStorageClientInterface over your S3/GCS SDK. committer: new ObjectStorageCommitter($objectClient, 's3', new NativeClock()), idempotency: new FilesystemIdempotencyStore($stateDir . '/idempotency'), checkpoints: new FilesystemCheckpointStore($stateDir . '/checkpoints'), state: new InMemoryKeyedStateStore(), // recomputable; durability not required here deadLetters: new FilesystemDeadLetterStore($stateDir . '/dead-letters'), retryPolicy: new RetryPolicy(maxAttempts: 3, baseDelayMs: 200, maxDelayMs: 5_000), clock: new NativeClock(), entitlementEvaluator: new EntitlementEvaluator(), license: $license, emitter: new FilesystemOutboxEmitter($stateDir . '/outbox'),);
$config = new StreamProcessorConfig( runId: 'nightly-invoices-2026-07-03', windowSize: 32, checkpointIntervalJobs: 100, crashSafe: true,);
try { $summary = $processor->process($manifests, $config);} catch (StreamProcessorException $e) { // Ambiguous commit or a non-durable collaborator: the finalized prefix is // checkpointed. Re-run the SAME runId; the committer converges by digest. fwrite(STDERR, 'Run aborted for safe resume: ' . $e->getMessage() . PHP_EOL); exit(1);}
printf( "run %s: read=%d committed=%d dedup-skipped=%d dead-lettered=%d checkpoints=%d final-offset=%d\n", $summary->runId, $summary->sourceRead, $summary->commitReceipts, $summary->skippedByIdempotency, $summary->deadLettered, $summary->checkpointSaves, $summary->finalCommittedOffset,);مثال على المُخرَج (تعتمد العدّادات على تدفق مهامك):
run nightly-invoices-2026-07-03: read=1200 committed=1187 dedup-skipped=13 dead-lettered=0 checkpoints=12 final-offset=1200الحالات الحدّية والمزالق
قسم بعنوان «الحالات الحدّية والمزالق»- كاتب-واحد لكل
runId مسؤوليتك. مخزن نقاط التفتيش لا يملك حجزاً ولا مقارنة-وتبديلاً. وجود كاتبَين متزامنَين على runId واحد خارج العقد؛ افرض الحصرية في مُجدولك. -
crashSafe: true يفشل سريعاً مع المتعاونين غير الدائمين. يجب أن تُنفّذ مخازن المُثبِّت ونقطة التفتيش والتكافؤ والرسائل الميتة جميعها واسِم DurableCapability، وإلا رمى process() الاستثناء StreamProcessorException مُسمّياً المُخالفين. ومخزن الحالة المُفهرَسة مُعفى عن قصد: تُعاد حَوسبة الحالة المُفهرَسة المفقودة انطلاقاً من نقطة التفتيش. -
windowSize يجب أن يلائم المحرك. نافذة أكبر من maxBatchSize() ترمي InvalidArgumentException قبل بدء أي عمل. - التثبيت الغامض يُجهض؛ والتعارض لا.
SPEC-COMMIT-409 تعارض نهائي حتمي: يُحال العنصر إلى الرسائل الميتة ويستمر التشغيل. أما أي إخفاق تثبيت آخر فغامض: تُسجَّل البادئة المُنهاة في نقطة تفتيش ويرمي التشغيل من أجل استئناف آمن. - إخفاقات العرض لا تُجهض التشغيل أبداً. نتيجة
Failed لعنصر، أو استنفاد ميزانية إعادة المحاولة، أو بايتات تقييم يتعذّر وسمها — كلها تُحيل ذلك العنصر إلى الرسائل الميتة وتستمر. - قيم
jobId المكرّرة آمنة؛ والعمل المكرّر مُفهرَس عبر idempotencyKey. تُربَط النتائج بالعناصر عبر إزاحة مصدر فريدة، لا عبر jobId أبداً. ويُتعرَّف على مفتاح التكافؤ المكرّر حتى ضمن فترة الحاجز نفسها، قبل إعادة العرض. - دوام المُطلِق يحسم دلالات الأحداث. المُطلِق ذو النداء الارتجاعي البسيط مُراقِب فقط ومرة-على-الأكثر عبر العطل. و
FilesystemOutboxEmitter يجعل صندوق الصادر دائماً ومُفهرَساً لإزالة التكرار؛ فيصير تسليم المُرحِّل مرة-على-الأقل، وتتطلّب المرة-الواحدة-تماماً لاحقاً إزالةَ تكرار من المستهلك عبر eventId. ويجب أن يكون دليله (كحال كل مخزن نظام ملفات) موجوداً مسبقاً، وإلا رمى المُنشِئ InvalidArgumentException. - الأحداث المُتخطّاة مُعطَّلة افتراضياً. اضبط
emitSkippedCompletions: true لإطلاق حدث نهائي Skipped أيضاً للعناصر التي قُصِرت دائرتها بإزالة التكرار.
ملاحظات أمنية
قسم بعنوان «ملاحظات أمنية»- مفاتيح الإخراج تفشل بالإغلاق. يعيد
commit() التأكيد أن مفتاح الهدف آمن نسبةً إلى الحاوية: لا اجتياز بـ ..، ولا هروب مطلق، ولا بايت صِفري، ولا مخطّط مُغلِّف تدفق مُضمَّن، ولا نقطتان رأسيتان (وهو ما يُغلق ناقل تدفق البيانات البديل في NTFS). والمفاتيح غير الآمنة ترمي قبل أي استدعاء تخزين. - تُعاد التحقّق من السلامة عند الحدّ. يعيد المُثبِّت حَوسبة sha-256 على البايتات الفعلية ويرفض أي عدم تطابق بـ
CommitIntegrityException، فلا يمكن أن يستقر تسليم تالف بصمت. - الأحداث لا تحمل محتوى المستند. يحمل
JobTerminalEvent وصفوف صندوق الصادر مُعرّفات وبصمات وطوابع زمنية وسلاسل أخطاء فقط. وقد تُردّد رسائل الأخطاء تشخيصات المحرك؛ نظّفها، ونظّف أي مخطّط jobId يُعرّف المستأجر، قبل شحن ملفات صندوق الصادر إلى وجهات طرف ثالث. - مُخرَج التقييم لا يُنشر أبداً دون علامة. حين تكون العلامة مطلوبة ويتعذّر تطبيقها، يُحال العنصر إلى الرسائل الميتة بدل تثبيته.
- المرة-الواحدة-تماماً عبر الكُتّاب المتعددين بقوة مُهايئك لا أكثر. إن لم يكن
putIfAbsent() كتابة شرطية ذرّية حقيقية، تراجع الضمان إلى دلالات كاتب-واحد. واعتمادات مخزن الكائنات وسياسة الحاوية شأن المضيف؛ والوحدة لا تديرها أبداً.
المطابقة
قسم بعنوان «المطابقة»لا يوجد معيار منشور يُعرّف سلوك هذه الوحدة. فضمانات المرة-الواحدة-تماماً ونقطة التفتيش وصندوق الصادر في هذه الصفحة عقود هندسية لواجهة NextPDF Enterprise البرمجية، مذكورة هنا بوصفها سلوكاً يمكن ملاحظته خارجياً — وهي ليست مطابقةً لأي معيار ولا شهادةً مقابله. والاستخدام الداخلي لـ SHA-256 بوصفه بصمة سلامة هو كذلك سباكة، لا ادّعاء امتثال. وكما هو الحال في كل مكان في NextPDF: الدعم ليس مطابقة، والمطابقة ليست شهادة. ولا يحمل NextPDF أي شهادة ولا يمنح أياً منها؛ وأما ما إذا كان نشرٌ مبنيٌّ على هذه الوحدة يفي بالتزاماتك التنظيمية أو التعاقدية فذلك تقدير يعود إلى مُقيّميك.
عقد السلوك
قسم بعنوان «عقد السلوك»الأحداث النهائية ليست جزءاً من معاملة نقطة التفتيش: يقع الإطلاق بعد checkpoint.save()، فيحفظ صندوق الصادر كل حدث مُطلَق بصورة دائمة لكنه ليس سجلّاً كاملاً عبر الأعطال. وتبقى الكائنات المُثبَّتة مصدر الحقيقة.
- كل إزاحة مصدر عند
finalCommittedOffset أو دونها بلغت نتيجة نهائية واحدة تماماً: Committed، أو Skipped، أو DeadLettered. - تُنهى العناصر وفق ترتيب إزاحة المصدر؛ وترتيب الحاجز هو التثبيت، ثم حفظ نقطة التفتيش، ثم إفراغ علامة التكافؤ، ثم إطلاق الحدث.
- إعادة تشغيل تشغيلٍ بـ
runId نفسه لا تنشر مرتين أبداً: تتخطّى الإزاحات المُسجَّلة في نقاط التفتيش سريعاً، وإعادات التثبيت المتطابقة بايتياً عمليات لا-تأثير لها بعد مقارنة البصمة مع idempotentReuse: true. - لا يُنشأ كائن جديد إلا عبر الإنشاء الشرطي الذرّي؛ والبايتات المتباينة عند مفتاح مشغول دون
overwrite هي إحالة SPEC-COMMIT-409 حتمية إلى الرسائل الميتة، لا دهساً أبداً. - التثبيت الغامض يسجّل البادئة المُنهاة في نقطة تفتيش ويُجهض بـ
StreamProcessorException؛ ولا تُقدَّم الإزاحة الفاشلة. - تشغيل
crashSafe يرفض متعاوني المُثبِّت أو نقطة التفتيش أو التكافؤ أو الرسائل الميتة غير الدائمين قبل قراءة أي مُدخَل. - مُعرّفات الأحداث دالة خالصة من التشغيل والإزاحة ومفتاح التكافؤ والحالة، فيحمل صندوق الصادر الدائم صفاً واحداً على الأكثر لكل حدث.
بديل Core
قسم بعنوان «بديل Core»يعرض NextPDF Core مستنداً واحداً في المرة عبر الكاتب وعقد بيان-العرض (render-manifest) — انظر الكاتب. وCore وحده لا يملك تدفقات مهام دائمة، ولا استئنافاً من نقطة تفتيش، ولا إزالة تكرار بالتكافؤ، ولا تثبيت تخزين كائنات، ولا صندوق صادر لأحداث نهائية. ويضيف NextPDF Pro محرك العرض الدائم والمتزامن ومخازن نظام الملفات أحادية المضيف (التدفق في Pro). أما النصف العابر للمضيفات — هذا المعالج، ومُثبِّت تخزين الكائنات، وصندوق الصادر الدائم — فيتطلّب NextPDF Enterprise.
حدّ النشر
قسم بعنوان «حدّ النشر»توثّق هذه الصفحة السلوك القابل للملاحظة خارجياً وسطح واجهة البرمجة العامة المدعوم فقط. أما مسارات فضاء الأسماء الداخلية، والأصناف المساعدة، وجداول الآليات، وأسماء ملفات كتيّبات التشغيل، وبادئات التذاكر فخارج النطاق.
انظر أيضاً
قسم بعنوان «انظر أيضاً»- التدفق (Pro) — النصف داخل العملية: محرك العرض، والمنفّذات، والمخازن الدائمة المحلية.
- التدفق — مرجع معمّق — المرجع على مستوى العقد لواجهات Stream المشتركة.
- خط أنابيب الإخراج (Enterprise) — تنسيق دفعي فوق بيانات خط الأنابيب.
- التجربة والعلامة — كيف تُحلّ علامة التقييم وتُطبَّق.
- توليد المستندات بأحجام كبيرة — السيناريو الذي وُجدت هذه الوحدة من أجله.
- تشغيل NextPDF في الإنتاج — وضعية النشر للعُمّال طويلي الأمد.