Enterprise édition
Metering — référence détaillée
L’espace de noms NextPDF\Enterprise\Metering fournit un metering d’utilisation au niveau de l’orchestration, pour la visibilité de facturation et l’audit. La surface publique compte six symboles : MeterCollector, MeterEntry, MeteringReporter, MeteringBackendInterface, PrometheusMeteringBackend et PrometheusPushgatewayException. Le collecteur met en tampon des entrées immuables en mémoire et les vide (flush) par lots. Le reporter diffuse chaque lot vers un ou plusieurs backends, avec réessai par backend et isolation des pannes. Le metering fonctionne au mieux (best-effort) et n’est jamais fatal : une panne d’un backend de metering dégrade l’observabilité, jamais le traitement des documents. Ce flux n’est pas la source de référence pour l’application des quotas. Pour le guide au niveau du flux de travail, voir Metering.
Disponibilité et licence
Section intitulée « Disponibilité et licence »Cette capacité est fournie dans NextPDF Enterprise (nextpdf/enterprise) et s’active avec une enveloppe de licence de niveau Enterprise. Un déploiement sans ce droit ne charge pas les classes de la capacité. Compare les éditions et procure-toi une licence.
Le metering est une capacité Enterprise de base, disponible dès que le paquet Enterprise est installé ; il n’existe pas de drapeau distinct par fonctionnalité. NextPDF Core (Apache-2.0) et NextPDF Pro n’ont ni collecteur, ni reporter, ni surface de backend ; le contrat n’est fourni que dans nextpdf/enterprise.
Surface de l’API publique
Section intitulée « Surface de l’API publique »| Symbole | Paramètres | Comportement par défaut | Renvoie | Lève ou échoue avec | Notes |
|---|---|---|---|---|---|
MeterCollector::__construct | MeteringReporter $reporter, int $bufferSize = 100 | Crée un collecteur avec un tampon vide en mémoire | Nouveau MeterCollector | Ne lève pas | $bufferSize est documenté positive-int |
MeterCollector::record | string $operation, int $count, string $tenantId, string $licenseId, int $pagesProcessed = 0, float $durationMs = 0.0, array $metadata = [] | Ajoute un MeterEntry immuable horodaté à l’instant courant ; se vide automatiquement quand le tampon atteint $bufferSize | void | Ne lève pas ; un flush automatique délègue au reporter, qui ne lève jamais | L’horodatage est pris au moment de l’enregistrement |
MeterCollector::flush | — | Remet toutes les entrées en tampon au reporter ; un tampon vide est sans effet | void | Ne lève pas ; les pannes de backend sont absorbées par le reporter | Le tampon est échangé avant la remise ; sûr en réentrance |
MeterCollector::bufferCount | — | Renvoie le nombre d’entrées en tampon | int<0, max> | Ne lève pas | Diagnostics et décisions de contre-pression |
MeterCollector::registerShutdownFlush | — | Enregistre flush() via register_shutdown_function | void | Ne lève pas | À appeler une fois au bootstrap dans les déploiements PHP-FPM |
MeterEntry::__construct | string $operation, int $count, DateTimeImmutable $timestamp, string $tenantId, string $licenseId, int $pagesProcessed = 0, float $durationMs = 0.0, array $metadata = [] | Stocke les valeurs fournies telles quelles | Nouveau MeterEntry | Aucun @throws déclaré ; PHP lève TypeError sur des types d’arguments incohérents sous strict_types | final readonly ; les huit propriétés promues sont toutes publiques |
MeteringReporter::__construct | list<MeteringBackendInterface> $backends, int $maxRetries = 2, LoggerInterface $logger = new NullLogger() | Valide et stocke la liste des backends | Nouveau MeteringReporter | InvalidArgumentException quand $backends est vide | $maxRetries compte le nombre total de tentatives de livraison par backend |
MeteringReporter::report | list<MeterEntry> $entries | Livre le lot à chaque backend indépendamment, avec réessai par backend | void | Ne lève pas ; les tentatives épuisées sont journalisées au niveau error et le lot de ce backend est abandonné | Une liste vide est sans effet |
MeteringBackendInterface::report | list<MeterEntry> $entries | Livre un lot au backend | void | RuntimeException quand le backend est injoignable | Les implémentations DOIVENT être idempotentes (déduplication par timestamp + operation + tenantId) |
MeteringBackendInterface::isHealthy | — | Sonde d’accessibilité | bool | Aucun @throws déclaré | Diagnostics uniquement ; le reporter ne s’y conditionne pas |
MeteringBackendInterface::backendName | — | Nom de backend pour diagnostic | non-empty-string | Aucun @throws déclaré | Par exemple "prometheus", "billing-api", "null" |
PrometheusMeteringBackend::__construct | ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, string $pushgatewayUrl, string $jobName = 'nextpdf_metering' | Configure une cible de push Pushgateway | Nouveau PrometheusMeteringBackend | Ne lève pas | Le client PSR-18 et les fabriques PSR-17 sont injectés |
PrometheusMeteringBackend::report | list<MeterEntry> $entries | Agrège le lot par série opération-et-tenant et envoie (POST) le texte d’exposition vers <pushgatewayUrl>/metrics/job/<jobName> | void | PrometheusPushgatewayException sur un statut non-2xx ou un échec de transport PSR-18 | Une liste vide est sans effet |
PrometheusMeteringBackend::isHealthy | — | Sonde le point de terminaison de santé de Pushgateway ; true uniquement sur HTTP 200 | bool | Ne lève pas ; tout échec renvoie false | Sonde GET en lecture seule |
PrometheusMeteringBackend::backendName | — | Renvoie "prometheus" | non-empty-string | Ne lève pas | Constante |
PrometheusPushgatewayException | — | Signale un échec de livraison Pushgateway | — | Est le throwable | final ; étend 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(): voidpublic 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): voidpublic 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 {}Propriétés publiques readonly de MeterEntry
| Propriété | Type | Signification |
|---|---|---|
$operation | non-empty-string | Type d’opération, par exemple "parse", "compress", "embed", "rag_query" |
$count | positive-int | Nombre d’unités consommées |
$timestamp | DateTimeImmutable | Moment où l’opération a eu lieu ; le collecteur l’horodate au moment de l’enregistrement |
$tenantId | non-empty-string | Identifiant de tenant |
$licenseId | non-empty-string | Identifiant de licence |
$pagesProcessed | int<0, max> | Pages PDF traitées ; 0 pour les opérations non-PDF |
$durationMs | float | Durée de l’opération en millisecondes |
$metadata | array<string, mixed> | Métadonnées libres spécifiques à l’opération |
Contrat de comportement
Section intitulée « Contrat de comportement »MeterCollector::record()construit unMeterEntryimmuable, l’horodate à l’instant courant et l’ajoute au tampon en mémoire. Quand le tampon atteint$bufferSizeentrées, le collecteur se vide automatiquement.flush()est idempotent et sûr en réentrance. Un tampon vide est sans effet. Le tampon est échangé avant que le lot ne soit remis au reporter, de sorte qu’un flush réentrant ne peut pas envoyer deux fois.MeteringReporterrefuse la construction avec une liste de backends vide. CetteInvalidArgumentExceptionest la seule exception sur le chemin collecteur/reporter.MeteringReporter::report()livre chaque lot à chaque backend indépendamment. Un backend en échec n’empêche jamais un autre backend de recevoir le même lot.$maxRetriescompte le nombre total de tentatives de livraison par backend ; la valeur par défaut2signifie une tentative initiale plus un réessai. Chaque tentative échouée journalise un avertissement avec le nom du backend, le numéro de tentative et le nombre d’entrées.- Quand la dernière tentative pour un backend échoue, le reporter journalise en plus au niveau error avec le nombre d’entrées abandonnées, puis passe à la suite. Il ne lève jamais depuis
report(), donc les appelants ne doivent pas déduire la livraison d’un retour normal. - Les backends DOIVENT être idempotents. Le contrat de l’interface exige une déduplication indexée sur le timestamp, l’opération et l’identifiant de tenant. Le reporter lui-même ne déduplique pas.
PrometheusMeteringBackend::report()agrège le lot en séries par opération et par tenant et envoie (POST) l’exposition texte Prometheus vers<pushgatewayUrl>/metrics/job/<jobName>avec le Content-Typetext/plain; version=0.0.4. Le nom de job par défaut estnextpdf_metering.- La charge utile poussée porte trois compteurs —
nextpdf_operations_total,nextpdf_pages_processed_totaletnextpdf_operation_duration_ms_total— chacun étiqueté par opération et par tenant. - Ce flux de metering ne fait pas référence. L’application des quotas et le metering de calcul de référence consomment le chiffre d’utilisation de référence distinct du déploiement, jamais ce tampon. Une lacune dans le metering d’orchestration est une lacune d’observabilité, pas une lacune d’exactitude de facturation.
Cas limites et modes de défaillance
Section intitulée « Cas limites et modes de défaillance »- Lot dupliqué ou rejoué. Absorbé par l’idempotence du backend ; le reporter ne déduplique pas. Ne compte pas sur une livraison exactement-une-fois.
- Réessais épuisés. Le lot destiné à ce backend est abandonné et journalisé au niveau error. Un retour normal de
report()ouflush()n’implique jamais la livraison. - Sortie du processus avant le flush. Le tampon n’existe qu’en mémoire. Un crash, ou une sortie sans gestionnaire d’arrêt enregistré, perd les entrées en tampon.
- Incompatibilité de modèle de worker. Les déploiements PHP-FPM appellent
registerShutdownFlush()une fois au bootstrap pour que le reliquat se vide à la fin de la requête. Les workers de longue durée (Octane, worker Symfony, worker de file d’attente) doivent au contraire se vider sur un minuteur périodique ; sinon les entrées s’accumulent jusqu’à la sortie du processus worker. $bufferSizeinférieur à1. Viole le contrat documentépositive-int; le résultat observable est un flush à chaque appel derecord().- Métadonnées sensibles.
$metadataest libre et peut porter un contexte d’opération sensible. Le stockage, la rétention et le contrôle d’accès relèvent de la responsabilité de l’opérateur du backend. - Échec de livraison Pushgateway. Une réponse non-2xx lève
PrometheusPushgatewayExceptionportant le statut HTTP et le corps de la réponse ; un échec de transport PSR-18 est enveloppé dans le même type d’exception. La boucle de réessai et d’isolation du reporter absorbe les deux. - Sonde de santé.
PrometheusMeteringBackend::isHealthy()émet un GET vers<pushgatewayUrl>/-/healthyet renvoietrueuniquement sur HTTP 200. Toute erreur de transport renvoiefalse; la sonde ne lève jamais. - Valeurs d’étiquette hostiles. Les caractères barre oblique inverse, guillemet double et saut de ligne dans les valeurs d’opération ou de tenant sont échappés à l’émission, de sorte qu’une valeur d’étiquette ne peut ni injecter des lignes d’exposition supplémentaires ni corrompre le bloc d’étiquettes.
- Mode FIPS. Le collecteur et le reporter n’effectuent aucune opération cryptographique et n’ont aucun comportement propre à FIPS. Un backend qui signe ou chiffre en transit hérite de la posture FIPS de son fournisseur cryptographique hôte.
Conformité
Section intitulée « Conformité »Aucun standard externe ne régit le contrat en-processus du collecteur, du reporter ou du backend ; il n’existe aucune spécification normative à citer, donc cette page ne porte volontairement aucune citation RAG. Le backend Prometheus émet le format d’exposition texte de Prometheus et pousse avec le Content-Type text/plain; version=0.0.4 ; ce format est une convention d’écosystème plutôt qu’un standard ISO ou IETF, et l’affirmation s’appuie sur la source du produit. NextPDF ne formule aucune revendication de conformité ou de certification pour cette surface.
Notes de développement
Section intitulée « Notes de développement »- Toutes les classes déclarent
strict_types=1et sontfinal;MeterEntryestfinal readonlyavec des propriétés publiques promues. Des types d’arguments incohérents lèvent unTypeErrorPHP chez l’appelant. - Les classes du module portent une annotation de paquet
@since2.1.0;PrometheusPushgatewayExceptionporte@since3.2.0. - Le logger du reporter est par défaut un
NullLoggerPSR-3. Injecte un vrai logger en production, sinon les lots abandonnés ne laissent aucune trace. - Tests unitaires : implémente un faux
MeteringBackendInterfaceet construis directement des valeursMeterEntry. Le backend Prometheus prend des abstractions PSR-18/PSR-17, de sorte qu’un client HTTP simulé exerce hors ligne l’intégralité du chemin de push. - Tests aux limites recommandés : tampon exactement à
$bufferSize, flush réentrant, flush de tampon vide, un backend en échec pendant qu’un second réussit, et journalisation d’épuisement des réessais. - Les implémenteurs de backend lèvent
RuntimeException(ou une sous-classe) en cas d’échec de livraison ; le reporter l’absorbe. Respecte l’exigence d’idempotence avant d’ajouter d’autres réessais en amont.
Frontière de publication
Section intitulée « Frontière de publication »Cette page ne documente que le comportement observable de l’extérieur et la surface publique de l’API prise en charge. Les chemins d’espaces de noms internes, les classes utilitaires, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de ticket sont hors périmètre.
Voir aussi
Section intitulée « Voir aussi »- Metering — NextPDF Enterprise — la page de capacité : flux de travail, configuration et exemples de déploiement détaillés.
- Facturation — référence détaillée — les niveaux de forfait, la sémantique de dépassement et l’échelle d’alertes.
- SaaS — référence détaillée — la surface d’orchestration multi-tenant.
- Licences — référence détaillée — l’enveloppe de licence qui active les capacités Enterprise.