Lewati ke konten
getnextpdf.com

Enterprise edisi

Metering — Referensi Mendalam

Namespace NextPDF\Enterprise\Metering menyediakan metering penggunaan tingkat orkestrasi untuk visibilitas penagihan dan audit. Permukaan publiknya terdiri dari enam simbol: MeterCollector, MeterEntry, MeteringReporter, MeteringBackendInterface, PrometheusMeteringBackend, dan PrometheusPushgatewayException. Collector menyangga entri imutabel di memori dan mem-flush-nya dalam batch. Reporter menyebarkan setiap batch ke satu atau lebih backend dengan retry per-backend dan isolasi kegagalan. Metering bersifat best-effort dan non-fatal: pemadaman metering-backend menurunkan observability, tidak pernah pemrosesan dokumen. Aliran ini bukan sumber otoritatif untuk penegakan kuota. Untuk panduan tingkat workflow, lihat Metering.

Kapabilitas ini dikirimkan dalam NextPDF Enterprise (nextpdf/enterprise) dan aktif dengan envelope lisensi tier Enterprise. Deployment tanpa entitlement tersebut tidak memuat kelas-kelas kapabilitas ini. Bandingkan edisi dan dapatkan lisensi.

Metering adalah kapabilitas dasar Enterprise, tersedia begitu paket Enterprise terpasang; tidak ada flag per-fitur terpisah. NextPDF Core (Apache-2.0) dan NextPDF Pro tidak memiliki permukaan collector, reporter, atau backend; kontraknya hanya dikirimkan dalam nextpdf/enterprise.

SimbolParameterPerilaku defaultMengembalikanMelempar atau gagal denganCatatan
MeterCollector::__constructMeteringReporter $reporter, int $bufferSize = 100Membuat collector dengan buffer in-memory kosongMeterCollector baruTidak melempar$bufferSize didokumentasikan positive-int
MeterCollector::recordstring $operation, int $count, string $tenantId, string $licenseId, int $pagesProcessed = 0, float $durationMs = 0.0, array $metadata = []Menambahkan satu MeterEntry imutabel yang distempel dengan waktu saat ini; auto-flush ketika buffer mencapai $bufferSizevoidTidak melempar; auto-flush mendelegasikan ke reporter, yang tidak pernah melemparTimestamp diambil pada waktu record
MeterCollector::flushMenyerahkan seluruh entri tersangga ke reporter; buffer kosong adalah no-opvoidTidak melempar; kegagalan backend diserap oleh reporterBuffer ditukar keluar sebelum diserahkan; aman re-entrant
MeterCollector::bufferCountMengembalikan jumlah entri tersanggaint<0, max>Tidak melemparUntuk keputusan diagnostik dan back-pressure
MeterCollector::registerShutdownFlushMendaftarkan flush() melalui register_shutdown_functionvoidTidak melemparPanggil sekali saat bootstrap pada deployment PHP-FPM
MeterEntry::__constructstring $operation, int $count, DateTimeImmutable $timestamp, string $tenantId, string $licenseId, int $pagesProcessed = 0, float $durationMs = 0.0, array $metadata = []Menyimpan nilai yang diberikan apa adanyaMeterEntry baruTidak ada @throws yang dideklarasikan; PHP memunculkan TypeError pada tipe argumen yang tidak cocok di bawah strict_typesfinal readonly; kedelapan properti terpromosi bersifat public
MeteringReporter::__constructlist<MeteringBackendInterface> $backends, int $maxRetries = 2, LoggerInterface $logger = new NullLogger()Memvalidasi dan menyimpan daftar backendMeteringReporter baruInvalidArgumentException ketika $backends kosong$maxRetries menghitung total percobaan pengiriman per backend
MeteringReporter::reportlist<MeterEntry> $entriesMengirim batch ke setiap backend secara independen, dengan retry per-backendvoidTidak melempar; percobaan yang habis dicatat di level error dan batch backend tersebut di-dropDaftar kosong adalah no-op
MeteringBackendInterface::reportlist<MeterEntry> $entriesMengirim batch ke backendvoidRuntimeException ketika backend tidak dapat dijangkauImplementasi HARUS idempoten (dedupe berdasarkan timestamp + operation + tenantId)
MeteringBackendInterface::isHealthyProbe keterjangkauanboolTidak ada @throws yang dideklarasikanHanya diagnostik; reporter tidak bergantung padanya
MeteringBackendInterface::backendNameNama backend diagnostiknon-empty-stringTidak ada @throws yang dideklarasikanMisalnya "prometheus", "billing-api", "null"
PrometheusMeteringBackend::__constructClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, string $pushgatewayUrl, string $jobName = 'nextpdf_metering'Mengonfigurasi target push PushgatewayPrometheusMeteringBackend baruTidak melemparKlien PSR-18 dan factory PSR-17 di-inject
PrometheusMeteringBackend::reportlist<MeterEntry> $entriesMengagregasi batch berdasarkan seri operation-dan-tenant dan mem-POST teks eksposisi ke <pushgatewayUrl>/metrics/job/<jobName>voidPrometheusPushgatewayException pada status non-2xx atau kegagalan transport PSR-18Daftar kosong adalah no-op
PrometheusMeteringBackend::isHealthyMenguji endpoint health Pushgateway; true hanya pada HTTP 200boolTidak melempar; setiap kegagalan mengembalikan falseProbe GET read-only
PrometheusMeteringBackend::backendNameMengembalikan "prometheus"non-empty-stringTidak melemparKonstanta
PrometheusPushgatewayExceptionMenandakan pengiriman Pushgateway yang gagalAdalah throwable-nyafinal; memperluas RuntimeException
public function __construct(
private readonly MeteringReporter $reporter,
private readonly int $bufferSize = 100,
) {}
public function record(
string $operation,
int $count,
string $tenantId,
string $licenseId,
int $pagesProcessed = 0,
float $durationMs = 0.0,
array $metadata = [],
): void
public function flush(): void
public function bufferCount(): int
public function registerShutdownFlush(): void
public function __construct(
public string $operation,
public int $count,
public DateTimeImmutable $timestamp,
public string $tenantId,
public string $licenseId,
public int $pagesProcessed = 0,
public float $durationMs = 0.0,
public array $metadata = [],
) {}
public function report(array $entries): void;
public function isHealthy(): bool;
public function backendName(): string;
public function __construct(
array $backends,
private readonly int $maxRetries = 2,
private readonly LoggerInterface $logger = new NullLogger(),
)
public function report(array $entries): void
public function __construct(
private readonly ClientInterface $httpClient,
private readonly RequestFactoryInterface $requestFactory,
private readonly StreamFactoryInterface $streamFactory,
private readonly string $pushgatewayUrl,
private readonly string $jobName = self::DEFAULT_JOB_NAME,
) {}
final class PrometheusPushgatewayException extends RuntimeException {}

Properti public readonly MeterEntry

PropertiTipeMakna
$operationnon-empty-stringJenis operasi, misalnya "parse", "compress", "embed", "rag_query"
$countpositive-intJumlah unit yang dikonsumsi
$timestampDateTimeImmutableKapan operasi terjadi; collector menstempelnya pada waktu record
$tenantIdnon-empty-stringPengenal tenant
$licenseIdnon-empty-stringPengenal lisensi
$pagesProcessedint<0, max>Halaman PDF yang diproses; 0 untuk operasi non-PDF
$durationMsfloatDurasi operasi dalam milidetik
$metadataarray<string, mixed>Metadata spesifik-operasi berbentuk bebas
  • MeterCollector::record() membangun satu MeterEntry imutabel, menstempelnya dengan waktu saat ini, dan menambahkannya ke buffer in-memory. Ketika buffer mencapai $bufferSize entri, collector melakukan auto-flush.
  • flush() bersifat idempoten dan aman re-entrant. Buffer kosong adalah no-op. Buffer ditukar keluar sebelum batch diserahkan ke reporter, sehingga flush re-entrant tidak dapat mengirim ganda.
  • MeteringReporter menolak konstruksi dengan daftar backend kosong. InvalidArgumentException tersebut adalah satu-satunya eksepsi pada jalur collector/reporter.
  • MeteringReporter::report() mengirim setiap batch ke setiap backend secara independen. Backend yang gagal tidak pernah mencegah backend lain menerima batch yang sama.
  • $maxRetries menghitung total percobaan pengiriman per backend; default 2 berarti satu percobaan awal ditambah satu retry. Setiap percobaan yang gagal mencatat peringatan dengan nama backend, nomor percobaan, dan jumlah entri.
  • Ketika percobaan terakhir untuk suatu backend gagal, reporter tambahan mencatat di level error dengan jumlah entri yang di-drop, lalu melanjutkan. Ia tidak pernah melempar dari report(), jadi pemanggil tidak boleh menyimpulkan pengiriman dari return normal.
  • Backend HARUS idempoten. Kontrak interface mengharuskan deduplikasi berkunci pada timestamp, operation, dan pengenal tenant. Reporter sendiri tidak melakukan deduplikasi.
  • PrometheusMeteringBackend::report() mengagregasi batch ke dalam seri per-operation, per-tenant dan mem-POST teks eksposisi Prometheus ke <pushgatewayUrl>/metrics/job/<jobName> dengan Content-Type text/plain; version=0.0.4. Nama job default adalah nextpdf_metering.
  • Payload yang di-push membawa tiga counter — nextpdf_operations_total, nextpdf_pages_processed_total, dan nextpdf_operation_duration_ms_total — masing-masing diberi label berdasarkan operation dan tenant.
  • Aliran metering ini non-otoritatif. Penegakan kuota dan metering komputasi otoritatif mengonsumsi angka penggunaan otoritatif deployment yang terpisah, tidak pernah buffer ini. Celah dalam metering orkestrasi adalah celah observability, bukan celah kebenaran penagihan.
  • Batch terduplikasi atau ter-replay. Diserap oleh idempotensi backend; reporter tidak melakukan deduplikasi. Jangan mengandalkan pengiriman exactly-once.
  • Retry yang habis. Batch untuk backend tersebut di-drop dan dicatat di level error. Return normal dari report() atau flush() tidak pernah menyiratkan pengiriman.
  • Proses keluar sebelum flush. Buffer hanya di memori. Sebuah crash, atau keluar tanpa handler shutdown terdaftar, kehilangan entri tersangga.
  • Ketidakcocokan model worker. Deployment PHP-FPM memanggil registerShutdownFlush() sekali saat bootstrap sehingga sisanya di-flush di akhir request. Worker berjalan-lama (Octane, Symfony worker, queue worker) harus mem-flush pada timer periodik; jika tidak, entri menumpuk sampai proses worker keluar.
  • $bufferSize di bawah 1. Melanggar kontrak positive-int yang didokumentasikan; hasil yang teramati adalah flush pada setiap panggilan record().
  • Metadata sensitif. $metadata berbentuk bebas dan dapat membawa konteks operasi sensitif. Penyimpanan, retensi, dan kontrol akses adalah tanggung jawab operator backend.
  • Kegagalan pengiriman Pushgateway. Respons non-2xx memunculkan PrometheusPushgatewayException yang membawa status HTTP dan body respons; kegagalan transport PSR-18 dibungkus dalam tipe eksepsi yang sama. Loop retry-dan-isolasi reporter menyerap keduanya.
  • Probe health. PrometheusMeteringBackend::isHealthy() mengeluarkan GET terhadap <pushgatewayUrl>/-/healthy dan mengembalikan true hanya pada HTTP 200. Setiap error transport mengembalikan false; probe tidak pernah melempar.
  • Nilai label yang berbahaya. Karakter backslash, tanda kutip ganda, dan line-feed dalam nilai operation atau tenant di-escape saat emisi, sehingga sebuah nilai label tidak dapat menyisipkan baris eksposisi tambahan atau merusak blok label.
  • Mode FIPS. Collector dan reporter tidak melakukan operasi kriptografis dan tidak memiliki perilaku spesifik-FIPS. Backend yang menandatangani atau mengenkripsi saat transit mewarisi postur FIPS penyedia crypto host-nya.

Tidak ada standar eksternal yang mengatur kontrak collector, reporter, atau backend in-process; tidak ada spesifikasi normatif untuk dikutip, sehingga halaman ini tidak membawa kutipan RAG secara desain. Backend Prometheus memancarkan format teks eksposisi Prometheus dan mem-push dengan Content-Type text/plain; version=0.0.4; format tersebut adalah konvensi ekosistem, bukan standar ISO atau IETF, dan klaimnya didasarkan pada sumber produk. NextPDF tidak membuat klaim konformansi atau sertifikasi untuk permukaan ini.

  • Semua kelas mendeklarasikan strict_types=1 dan bersifat final; MeterEntry adalah final readonly dengan properti public terpromosi. Tipe argumen yang tidak cocok memunculkan TypeError PHP di pemanggil.
  • Kelas-kelas modul membawa anotasi paket @since 2.1.0; PrometheusPushgatewayException membawa @since 3.2.0.
  • Logger reporter default ke NullLogger PSR-3. Inject logger nyata di produksi, atau batch yang di-drop tidak meninggalkan jejak.
  • Unit testing: implementasikan MeteringBackendInterface palsu dan konstruksi nilai MeterEntry secara langsung. Backend Prometheus mengambil abstraksi PSR-18/PSR-17, sehingga klien HTTP mock menjalankan seluruh jalur push secara offline.
  • Uji batas yang direkomendasikan: buffer tepat pada $bufferSize, flush re-entrant, flush buffer kosong, satu backend gagal sementara backend kedua berhasil, dan pencatatan retry-exhaustion.
  • Pengimplementasi backend melempar RuntimeException (atau subkelasnya) pada kegagalan pengiriman; reporter menyerapnya. Hormati persyaratan idempotensi sebelum menambahkan retry lebih lanjut di hulu.

Halaman ini hanya mendokumentasikan perilaku yang teramati secara eksternal dan permukaan API publik yang didukung. Jalur namespace internal, kelas helper, tabel mekanisme, nama file runbook, dan prefiks tiket berada di luar cakupan.