Performance budgets, measurements, SLOs and alerts

Every number here is either a limit in the code or a measurement quoted from a commit message on feat/magento-10-10. All measurements are from the local Magento 2.4.7-p4 test stack (Mage-OS 1.0.6, PHP 8.3, Docker, developer mode, no sample data, against a mock Conviro or the backend code run locally) unless a line says production. Nothing on this branch is deployed, so there are no production measurements of the integration yet. The 1.5.0 section (Customer identity and My orders) quotes the commits of the 1.5.0 branches instead.

Storefront and admin impact

WhatBudget / limitMeasured (local)Source
Queueing one event (outbox INSERT) in a storefront/admin request20 ms (p95)p50 0.8 ms / p95 2.0 ms52fc1635
same, later runsp95 3.26 ms90c79bc8
samep50 1.39 ms / p95 3.48 msfff8da0a
Order placement with Conviro downno wait on Conviroorder placed in 264 ms; events queued and delivered later, once52fc1635, outbox-e2e.sh
Config page (Integration Health card), Conviro down0.61 s, verdict "Action required: Connection"fff8da0a, admin-health.sh
Config page, Conviro black-holed (no answer)3 s connect timeout per call; Conviro's view cached 60 s; version check cached 12 h (15 min after a failure)7.46 s once (health call + cold version-check call), then 0.56 s from cachefff8da0a

With Conviro unreachable, a config page render can therefore wait about 3.5 s once a minute, up to about 7 s when the version cache is cold too. Storefront pages never call Conviro from PHP; the widget loads asynchronously in the browser — since 1.4.1 by default only on the first interaction (or after 4 s), and without a version.json request on most page views (Widget loading).

Delivery and sync limits (code)

LimitValueWhere
Delivery cronevery minute, one process (lock)crontab.xml, Cron/DeliverEvents.php
Batch / time budget per run50 events / 50 sEventDelivery::run
HTTP per event15 s total, 5 s connectEventTransport
Claim lock300 sEventOutbox::LOCK_SECONDS
Retry schedule+30 s, +2 min, +10 min, +30 min, +2 h, +8 h; 7 attemptsRetryPolicy
Sync job batch200 items per job type per cron run (every minute)SyncJobRunner::BATCH
Sync back-pressureno new batch while ≥ 1,000 events waitSyncJobRunner::MAX_PENDING
Configurable children per product event50ProductPayloadBuilder::MAX_CHILDREN
Attributes per product event30ProductPayloadBuilder
Cart items per cart.updated10Observer/CartSave.php
Connect-minted API key600 requests / minutemagento-connect.service.ts

A minute cron alone spreads the outbox wait over 0–60 s, so delivery lag p95 is about 57 s before any network or processing time (SLO.md).

Conviro-side read limits (code)

WhatValue
Store REST request timeout10 s default (magento-http.ts)
Live product + price cache5 min per store view; stock re-read after 1 min
Store topology cache1 h
Order lookup cache5 min; customer profile 10 min; cart 2 min
Nightly KB pull salability batches50 SKUs per call, quantity calls 8 at a time (measured: 2 salability calls for 60 SKUs)

A product event drops cached reads for its SKU in every store view with one Redis prefix scan; a re-sync of an unchanged product skips the scan (fff8da0a).

SLIs, SLOs and alerts

The authority is apps/api/src/modules/magento/SLO.md with the numbers in magento-slo.ts (853499e4); this is a summary. Readable by Conviro staff at GET /api/super-admin/magento/slo?window=1h|6h|24h|7d|30d (super-admin JWT).

SLOObjectiveTargetWindow
ingest_success2xx / (2xx + 5xx) on POST /v1/api/magento/events≥ 99.5 %30 d
ingest_lag_p95p95 occurredAt → processed≤ 120 s30 d
ingest_lag_p99p99 occurredAt → processed≤ 300 s30 d
live_read_successlive reads answered 2xx or 404≥ 99 %30 d
live_read_p95p95 live-read time to response headers≤ 1,500 ms30 d

The 1.5.0 Customer Hub adds four identity SLOs and nine alert rules: Customer identity and My orders.

Why not plan §15's 99.9 % / 60 s: a 5xx is retried seven times over about 10 h 43 min (RetryPolicy::describe()), so a per-attempt 99.5 % means well above 99.9 % of events accepted eventually; and a minute cron puts p95 lag near 57 s by design.

Alert rules (evaluated every 5 minutes over 1 h and 6 h, sent through Conviro's ops channel when the firing set changes, daily while it lasts, once on recovery; volume floors of 20 events / 50 live reads): ingest fast / slow burn (14.4× / 6× budget), lag p95 / p99, signature failures (≥ 10 rejections and ≥ 20 % of attempts in 1 h), any dead-lettered (invalid) answer, 409 share > 5 %, live-read fast / slow burn, live-read p95 > 1,500 ms, ≥ 10 live reads answered 401/403, ≥ 10 answered 429, ≥ 3 verifications in 1 h ending other than oauth1 / bearer.

Not observable from Conviro (visible in the module's Integration Health card instead): the outbox backlog and retries, a stuck re-sync, a module below the security minimum. Requests refused before the interceptor (unknown key, missing scope, rate limiter, body size) are not counted.

Production baseline (read-only, 2026-09-24 07:19 UTC, from SLO.md): 33 Magento integration rows, 2 active (one workspace), none with an authMode yet; the events endpoint is not deployed, so no ingest traffic exists to measure. The first 30-day window starts with the deploy.

Customer identity and My orders (1.5.0, unreleased)

Status 2026-09-27. Implemented on the Conviro backend branch feat/magento-1.5.0-customer-hub — ADR-IDENTITY-001 Phase 5 P5-01 c6995ce0 (SLIs), P5-02 d433f2c3 (store vs Conviro attribution), P5-03 53dd77d0 (SLOs and alert rules) — unit-tested, not merged, not deployed: nothing in this section is measured in production. Targets, thresholds and volume floors were set on 2026-09-28 and are reviewed after 30 days of production data (MAGENTO_SLO_TARGETS, MIN_VOLUME in magento-slo.ts). The authority is apps/api/src/modules/magento/SLO.md on that branch; this is a summary. What a merchant does about a failure: customer identity runbook.

SLOs

SLOWhat is measuredTargetWindow
identity_exchange_p95Conviro's clock from the challenge it issued to its answer to the exchange: the widget's same-origin call to your store's /conviro/identity/bootstrap and both network legs are inside it; the browser's rendering is not. Successful exchanges only.p95 ≤ 1 s (1,000 ms)30 d
my_orders_first_card_p95Conviro's server time of a verified shopper's first My orders read, request received → answered (your store's order route included). Each API replica counts its own "first" — an approximation. The browser's rendering is not in it.p95 ≤ 2 s (2,000 ms)30 d
my_orders_successverified reads answered (loaded, empty, not found) or refused for security (signed out, expired, switched off) / (those + Conviro-caused + contract failures)≥ 99.9 %30 d
my_orders_store_failure_shareverified reads that failed at your store / all verified readsreported separately, no target, never in the budget above30 d

Alerts (Conviro operations)

Evaluated every 5 minutes with the other Magento rules and sent to Conviro's ops channel (Telegram / Slack) when the firing set changes; the message header takes the worst severity (critical > warning > info). Volume floors: 20 exchanges, 50 verified reads — below them nothing fires.

AlertSeverityFires when
my_orders_fast_burncriticalConviro-caused failure share over 1 h > 1.44 % (14.4 × the 0.1 % budget), ≥ 50 reads
my_orders_slow_burnwarningthe same over 6 h > 0.6 % (6 ×), ≥ 50 reads — only when the fast burn does not fire
my_orders_first_card_latencywarningfirst-read p95 over 1 h > 2,000 ms, ≥ 50 first reads
identity_exchange_latencywarningexchange p95 over 1 h > 1,000 ms, ≥ 20 exchanges
identity_exchange_failureswarningfailed share over 1 h > 20 %, too_many left out, ≥ 20 exchanges
identity_invalid_spikewarning≥ 20 invalid exchanges in 1 h (a key mismatch after a restore, an unmapped store view, or an attack)
identity_replayed_sharewarningreplayed > 5 % of ≥ 20 exchanges in 1 h (a cache in front of a store serving the bootstrap)
my_orders_contract_errorswarningany contract_error read in 1 h (module / backend mismatch)
my_orders_permission_missinginfoany read refused for a missing order-listing grant in 1 h — a fact, never a page

Store-caused failures and sign-outs page nobody at Conviro. The labels are closed sets — no workspace, store URL, customer, session, principal, order or binding id — so an alert says what happened, not to whom; the API logs carry the workspace of a failing request.

What you see as a merchant

Measured so far

WhatWhereResult
The verification legs, local stack against the mock Conviro (P1-18, 555e7ce1, branch feat/magento-1.5.0-e2e, 8 rounds)Magento 2.4.7-p4 (Mage-OS 1.0.6, PHP 8.3, developer mode), dev/tests/identity-bridge.sh with mock-conviro.mjs IDENTITY=1challenge p95 6 ms, bootstrap p95 90 ms, exchange p95 3 ms; all three p95 99 ms — measured locally, with a mock instead of the real backend, so it is not identity_exchange_p95
identity_exchange_p95 itself (the real backend's clock)—not measured anywhere yet (the local latency run is Phase 5 P5-16, not done)
my_orders_first_card_p95—not measured anywhere yet; the local order-route E2E (P2-18, 3f586ba6) checked the answers, not the time
my_orders_success, my_orders_store_failure_share—not measured: no verified traffic exists
Production—nothing: the backend is not deployed — ölçüm, kodla doğrulanamaz

How to measure locally

Not yet run end to end on this branch; the steps the Phase 5 rows P5-16 (local latency run) and P5-17 (Docker) are meant to script:

  1. Run the Conviro API of feat/magento-1.5.0-customer-hub with Redis (without Redis nothing is recorded) and the 1.5.0 module on the local Magento stack; switch magento_identity_bridge and magento_my_orders on for the test workspace and Enable signed customer identity / Show verified shoppers their own orders on for the website.
  2. Sign in as a test customer, open the chat (the bridge runs on open, not on page load) and ask for My orders; repeat past the floors (20 exchanges, 50 reads) — below them a p95 is noise.
  3. Read GET /api/super-admin/magento/slo?window=1h with a super-admin JWT (| jq '.slos, .alerts'): identity_exchange_p95, my_orders_first_card_p95, my_orders_success, my_orders_store_failure_share and any identity alert that would fire.
  4. A store-caused failure (stop the store's PHP-FPM, or withdraw the order-listing grant) must raise my_orders_store_failure_share (and my_orders_permission_missing as info) and leave my_orders_success unchanged.
  5. The browser's own time (chat opened → order card painted) is in neither SLI: time it in the browser's developer tools on the local stack.

Report every number from these steps as local, with the commit and the stack, never as production.