# APAC PQL (v1)

Direct-to-v1 Fit × Signal PQL model for APAC self-serve TiDB Cloud tenants.

## Reporting terminology lock — 2026-09-04

All APAC PQL reporting must be framed as **PLG** reporting. The data is produced by the PLG PQL
screening model. Regional sales follow-up is a downstream consumer of selected signals; it does not
define the model, the dataset, or the report. Do not rename a PQL report after the team that may act
on its output.

Every APAC result must break out **India**, **Mainland China**, and **APAC excluding India and
Mainland China**. Benchmark tables should show **Global total** and **Rest of world excluding APAC**
separately, so APAC is not benchmarked against a comparison population that contains itself.

The reporting grain is **tenant**, not person or login user. Metric labels must therefore use
"active tenants", "queried tenants", "importing tenants", "engaged tenants", and "paying tenants"
unless a source is explicitly re-aggregated to a verified person grain.

- **Original v1 planning scope:** India + APAC-Others; CN and JP were excluded from the first-round
  plan. The deployed execution later included CN. Current reporting therefore follows the geographic
  breakout above rather than silently mixing Mainland China into APAC-Others.
- **Approach:** straight to v1 (Fit × Signal) — APAC is a new project with its own engineers and data sources.
- **Status:** model + scoring SQL run today on the global warehouse; P0 is blocked on fit enrichment (see below).
- **Execution has moved ahead of this plan.** Engineering (Yujie) built and deployed a full Lambda
  pipeline in `pingcap-inc/pingcap-support/apac_lambda_tasks` that diverges from the plan below (CN
  included, different scoring model, fit enrichment solved a different way). See
  [`execution/README.md`](execution/README.md) for the comparison, open reconciliation items, and a
  direct link to the landed repo — check that first for current scoring/priority logic.
- **And execution itself is two layers, not one.** Scoring runs in the Lambda stack; the daily
  digest, routing and dedupe rules SDRs actually work from run in a separate agent, and follow-up
  state never returns to the database. Whole-mechanism picture + live evidence for the dormant-P0,
  fast-riser, self-pay and signal-outage questions:
  [`apac-operating-mechanism-2026-08-26.md`](apac-operating-mechanism-2026-08-26.md) (checked 2026-08-26).

## Measurement invariants — verified 2026-09-04 against source tables

These four are load-bearing for any APAC report. Getting them wrong silently produces a wrong number,
so they are recorded here rather than in one agent's private memory.

**1. Pool admission is operational, not geographic.** A tenant enters `apac_active_tenants` when
`country_region = 'APAC'` **OR** `sales_force_region = 'APAC'`. On 2026-09-04 that is 10,400 + 21 =
10,421. The 21 admitted by the second condition carry a non-APAC `country` (12 `US`, 6 blank, 3
`EMEA`) because `country` is the tenant's registered/billing entity country, not where the business
operates — e.g. Longbridge (US-registered LLC, Singapore/HK operation), Portcast (Singapore).
**Do not clean the pool on `country`.** The blank-country `longbridge.sg` carried QPS 114.84 on
2026-09-04, the highest workload in the whole P0 queue. Correct handling: keep them in the APAC
total, list them separately, exclude them from country charts, and fix the upstream country field.

**2. `apac_tenant_score_snapshots` is a daily incremental scoring batch, not a full-pool snapshot.**
~900–1,000 rows/day covering both `tenant_bucket='active'` and `'sales_owned'`; the all-time distinct
tenant count (10,587) is what approximates the pool. To get month-end priority stock, take each
tenant's last `active` scoring on or before the as-of date **and inner-join `apac_active_tenants`** —
without that join, tenants that have since left the pool or moved to the sales-owned book keep a
stale priority (as-of 2026-09-04 gives 64/127/10,391 instead of the correct 43/111/10,267). With the
join the method reproduces the live table exactly. Coverage: 2026-08-31 → 95.3% of the pool;
2026-07-31 → only 35%, because the table starts 2026-07-13, so **July month-end stock is not
comparable and no queue MoM should be published for it**. Note 2026-08-31 and 2026-09-01 have no rows
at all, so always express this as "last scoring ≤ date", never `snapshot_date = '<date>'`.

**3. QPS has no history anywhere, and the warehouse path is dead.** `tenant_avg_qps` in the regional
tables is a Clinic/Prometheus one-hour point sample overwritten daily; `apac_tenant_score_snapshots`
has no QPS column. In BigQuery, `dwh_cloud_platform.tenant_daily_qps_rollup`, `dwh_cloud_platform.cloud_qps`
and the raw `stg_vm_metrics.stg_vm_qps` all stop at **2026-03-25**, and the 2026-08-13 live contract
table `plg_dashboard.tenant_daily_qps` was never built. So no QPS month-over-month exists for any
month after March 2026. The last computable complete window is 2026-03-01 → 03-25. Until GAP-025
lands, engaged/QPS may only be reported as a labelled current snapshot — never as trend or
qualification.

**4. Activation and First Query have a hard gap; Import and cluster do not.** The gateway-access feed
(`model_plg.dws_daily_external_cloud_active_user_detail`) and `tenant_lifecycle_events.first_query_executed_at`
both have **zero records 2026-07-08 → 2026-08-26**, with a 2,092-row lump backfill stamped 2026-08-27.
Checking `MAX()` alone is misleading — it reads as healthy because the feed resumed. Always check the
daily distribution. Consequence: no trustworthy activation or First Query month exists for July or
August 2026; the last clean D28 cohort month is 2026-05 and the last clean D3 cohort month is 2026-06.
By contrast `first_import_completed_at` and `first_cluster_created_at` are continuous through both
months, so **Import can be reported as a full month while First Query cannot**.

Also: the source `tenant_region` puts **Japan in its own region, outside APAC** (201 signups in
2026-08), so it falls inside "Global excluding APAC". State this whenever a global benchmark is shown.

## Read order for engineers
1. [`apac-pql-v1-model.md`](apac-pql-v1-model.md) — the model: axes, weights, matrix, current output.
2. [`apac-pql-v1.sql`](apac-pql-v1.sql) — runnable BigQuery scoring (read-only).
3. [`apac-data-inventory.md`](apac-data-inventory.md) — what's readable today vs. what's blocked.
4. [`apac-stakeholder-asks.md`](apac-stakeholder-asks.md) — decisions we need from APAC GTM.
5. [`reference/apac-journey-reference.md`](reference/apac-journey-reference.md) — live APAC journey numbers + the exact queries.
6. Shared contract: [`../universal/`](../universal).

## Aha moments for APAC
Strongest → weakest, with where the data lives:

| Aha | Signal | Available in APAC today? |
|-----|--------|--------------------------|
| Data import completed | migration intent (strongest) | ✅ `first_import_completed_at` |
| Sustained QPS | live workload | ❌ **Clinic API** (Prometheus `tidb_executor_statement_total`) — how EMEA's `cluster_metrics.py` computes `tenant_avg_qps`; not in APAC BQ |
| First DDL | schema/build intent | ❌ **O11Y** (slowlog/statements) — not extracted anywhere yet: https://pingcap.feishu.cn/wiki/AoO4wmSKviDfG5kKvhdcA3t8nLf |
| Spending-limit raised | capacity/commercial | ❌ **v1-planned** (EMEA upgrade, not started); raw events exist in DevOps/billing, need further check in Grafana/metadb |
| Card on file | commercial intent | ✅ `has_payment_method` (EMEA: DevOps billing proxy + Stripe) |
| First query | first real use | ✅ `first_query_executed_at` |
| Cluster created | provisioned (near-noise, ~92%) | ✅ |

## The headline finding
With **zero fit enrichment** today, **P0 = 0** — nothing can reach Fit High. The actionable queue
right now is **P1 = 459** (import/card-driven). Wiring fit enrichment unlocks a **latent P0 pool of
180+** immediately. The biggest, cheapest win is the enrichment ask, not more scoring logic.

## Two things that are NOT SQL (upstream services to build)
- **Fit enrichment** (domain / AI-category) → the entire Fit axis.
- **O11Y ingestion** (QPS, first DDL) → the strongest workload signals.

See [`../universal/delivery-architecture.md`](../universal/delivery-architecture.md): own the SQL contract,
deliver the **bot + write-back** loop around it.
