1. Define the fact-table grain for Azure tenant API telemetry.
Model API calls so latency and volume can be analyzed by tenant, service, region, endpoint, response class, and event time. Include event identity, ingestion time, duration, success state, and schema version, and state whether the base fact is one request, one aggregate interval, or both in separate tables.
Make the base fact one row per API request. Store request identity, event and ingestion times, duration, success, schema version, and dimension keys. If interval reporting is needed, derive a separate aggregate fact by time bucket and the same dimensions.
The main decision is what one stored row represents. Here, each row should represent one API call. That keeps the original detail needed to count calls and compare how long they take across customers, services, locations, paths, outcomes, and time. Each call also keeps when it happened, when it arrived for storage, whether it succeeded, and which data format version produced it. Faster summaries can be stored separately, but they should never be mixed with the individual-call rows because the two kinds of rows represent different things.
- Should event time be treated as the primary time for analysis, and what time-zone convention should it use?
- If an aggregate table is required, what interval should each
time_bucketrepresent, such as five minutes or one hour? - Should response class represent grouped HTTP outcomes such as 2xx, 4xx, and 5xx?
I would define FactApiRequest as a transaction fact with the grain: one row per API request. request_id is the unique event identity. The request row stores event_time, ingestion_time, duration_ms, success, and schema_version.
The fact also stores the foreign keys tenant_key, service_key, region_key, endpoint_key, and response_class_key. Those keys relate each request to DimTenant, DimService, DimRegion, DimEndpoint, and DimResponseClass. Each dimension row can relate to many request rows, so each relationship is one dimension row to many facts.
Descriptive attributes stay in the dimensions. DimTenant contains values such as tenant_id and tenant_name. DimService contains service_name and service_type. DimRegion contains region_code and region_name. DimEndpoint contains endpoint_path and endpoint_category. DimResponseClass contains response_class, status_code_start, and status_code_end.
At request grain, volume can be measured by counting rows; conceptually, each row contributes request_count = 1. Latency analysis uses duration_ms. The success value records the request outcome, and the response-class dimension supports grouped outcome analysis. event_time tells when the request occurred, while ingestion_time tells when the request reached the warehouse, so arrival delay can be distinguished from event occurrence.
For faster summary reporting, I can derive a separate AggApiRequestInterval fact table. Its grain is one row per time_bucket × tenant × service × region × endpoint × response class. It uses the same dimension keys and can store request_count, avg_duration_ms, p50_duration_ms, p95_duration_ms, success_count, and failure_count.
The aggregate is derived from FactApiRequest; it is not the base fact. I would not mix request-grain and interval-grain rows in one table. Mixing different grains would make counts and latency measures ambiguous and could cause incorrect aggregation. Keeping the detailed and aggregate facts separate preserves clear analytical meaning while still allowing a pre-aggregated table to speed up common reporting.
- Identify the business process as recording Azure API calls.
- Declare
FactApiRequestat exactly one row per API request. - Use
request_idas the unique event identity. - Store
event_time,ingestion_time,duration_ms,success, andschema_versionat request grain. - Add foreign keys for tenant, service, region, endpoint, and response class.
- Measure request volume by counting request rows and latency from
duration_ms. - If recurring summaries need faster reads, derive
AggApiRequestIntervalattime_bucket × tenant × service × region × endpoint × response classgrain. - Keep request-level and interval-level facts in separate tables.
Storage for FactApiRequest grows with the number of API calls because every request produces one row. Detailed reports may need to scan many request rows. A separate interval aggregate adds storage and processing work because it must be calculated and maintained, but it can reduce the amount of data read for common summary reports. The main maintenance cost is keeping the aggregate dimensions, time buckets, and measures consistent with the request-level fact.
This question tests whether the candidate can define a precise fact-table grain before choosing measures and dimensions. It also evaluates whether they understand transaction facts, dimension relationships, latency and volume analysis, event time versus ingestion time, and why facts with different grains should be stored in separate tables.
Common mistakes are using an aggregate interval as the base grain and losing individual-request detail; mixing request-level and interval-level rows in one fact table; omitting request_id, event_time, or ingestion_time; confusing event occurrence time with ingestion time; placing descriptive tenant, service, region, endpoint, or response-class attributes directly in the fact instead of dimensions; and counting rows without first confirming the table's grain.
Start by saying, "The base grain is one row per API request." Then name the request-level fields and dimension keys, explain that row count gives volume and duration_ms gives latency, and finish by saying that interval aggregates belong in a separate fact table.









