What you will be able to do
- Create an InferenceLog, TimeSeries or Snapshot monitor with the Databricks SDK and refresh it
- Set time granularities and slicing expressions, and predict the slices they produce
- Define aggregate, derived and drift custom metrics with Jinja SQL templates
- Set up a Databricks SQL alert that fires when a drift metric crosses a threshold
- Monitor serving endpoint health with latency, request rate, error rate, CPU and memory metrics
1.Creating and refreshing a monitor
A monitor is created with w.data_quality.create_monitor. The call takes a DataProfilingConfig naming the output schema and exactly one profile type: time_series=TimeSeriesConfig(...), inference_log=InferenceLogConfig(...) or snapshot=SnapshotConfig(). A table in a Unity Catalog metastore can have only one profile. To create one you need USE CATALOG, USE SCHEMA, SELECT on the table and MANAGE on the catalog, schema or table. An inference monitor looks like this:
config = DataProfilingConfig(
output_schema_id=schema.schema_id,
assets_dir=f"/Workspace/Users/{username}/databricks_quality_monitoring/{TABLE_NAME}",
inference_log=InferenceLogConfig(
problem_type=InferenceProblemType.INFERENCE_PROBLEM_TYPE_CLASSIFICATION,
prediction_column="preds",
model_id_column="model_ver",
label_column="label", # optional
timestamp_column="ts",
granularities=[AggregationGranularity.AGGREGATION_GRANULARITY_1_DAY])
)
info = w.data_quality.create_monitor(
monitor=Monitor(
object_type="table",
object_id=table.table_id,
data_profiling_config=config,
),
)The output goes to {output_schema}.{table_name}_profile_metrics and {output_schema}.{table_name}_drift_metrics, along with a dashboard you can customise. create_refresh recomputes the tables on serverless compute, not on your notebook's cluster. For scheduled refreshes, pass a schedule with a Quartz cron expression. For time series and inference tables, turn on change data feed so that each refresh processes only newly appended rows instead of the whole table.
Checkpoint 1 of 7· Check yourself
A newly created inference monitor's first dashboard shows only about a month of history, although the table holds two years of requests. Why?
Time series and inference profiles start from a 30-day lookback and then process all new data. CDF affects how efficient refreshes are, not the lookback, and the 4TB limit applies to snapshot profiles.
“When you first create a time series or inference profile, Databricks analyzes only data from the 30 days prior to its creation.”Source: docs.databricks.com
Sources1
2.Granularities and slices
Granularity sets the window size for time series and inference profiles. The options run from AGGREGATION_GRANULARITY_5_MINUTES through 30 minutes, 1 hour, 1 day, 1 to 4 weeks and 1 month, up to AGGREGATION_GRANULARITY_1_YEAR. You can choose several at once, and each row in the metric tables records its granularity. Small windows catch sudden changes but contain fewer rows each. Large windows smooth out noise. A snapshot profile has no granularity; its window is the moment of the refresh.
Slices appear in the metric tables as slice_key and slice_value. The whole table is the row where both are NULL. Every slice key is a single expression, so slices are not crossed with one another. Inference profiles also slice automatically by every distinct model_id_col value. Slicing is what turns a vague whole-table signal into a specific one. If drift appears in one slice but not in the others, the cause lies in that segment.
Checkpoint 2 of 7· Check yourself
You want the aggregate, unsliced drift for a column. Which filter selects it in the drift metrics table?
The whole table is stored with NULL slice key and value. ':table' is a column_name value, used for multi-column metrics.
“The entire table is equivalent to slice_key = NULL and slice_value = NULL.”Source: docs.databricks.com
Checkpoint 3 of 7· Exam question
An engineer monitors a `payment_method` categorical column and needs the drift metrics table to flag when category proportions diverge from baseline. Which test runs for this column type?
Correct answer: B — Chi-squared test, comparing observed category counts against the baseline category distribution.
- A. Kolmogorov-Smirnov applies to continuous numerical columns, not category counts like payment method, so it is not the test that runs here.
- B. Chi-squared is the built-in test Lakehouse Monitoring applies to categorical columns, comparing category counts against the baseline to detect drift.
- C. Jensen-Shannon distance is not one of the automatically computed drift tests for categorical columns in the drift metrics table.
- D. Wasserstein distance is not a built-in statistical test Lakehouse Monitoring computes for categorical drift.
Sources2
3.Custom metrics
When the built-in statistics don't capture what the business cares about, you add MonitorMetric definitions. Each one is a Jinja template for a single SQL column expression, with no joins or subqueries. It has a type, a name that becomes a column in the metric tables, input_columns (:table when the metric uses several columns), a definition and an output_data_type.
| Type | Computed from | Stored in |
|---|---|---|
| CUSTOM_METRIC_TYPE_AGGREGATE | Columns in the primary table | Profile metrics table |
| CUSTOM_METRIC_TYPE_DERIVED | Previously computed aggregate metrics | Profile metrics table |
| CUSTOM_METRIC_TYPE_DRIFT | Aggregate or derived metrics from two windows, or primary vs baseline | Drift metrics table |
MonitorMetric(
type=MonitorMetricType.CUSTOM_METRIC_TYPE_AGGREGATE,
name="weighted_error",
input_columns=[":table"],
definition="""avg(CASE
WHEN {{prediction_col}} = {{label_col}} THEN 0
WHEN {{prediction_col}} != {{label_col}} AND critical=TRUE THEN 2
ELSE 1 END)""",
output_data_type=T.StructField("output", T.DoubleType()).json(),
)Only aggregate metrics read the primary table. Derived and drift metrics are computed from values already aggregated, so where you can, build on an aggregate instead of scanning the table again. To track how weighted_error changes, reference it in the current window as {{current_df}} and in the comparison window, either the baseline or the previous window, as {{base_df}}.
Checkpoint 4 of 7· Fill the gap
Which metric type completes this definition so it is stored in the drift metrics table?
MonitorMetric(
type=MonitorMetricType. ? ,
name="error_rate_delta",
input_columns=[":table"],
definition="{{current_df}}.weighted_error - {{base_df}}.weighted_error",
output_data_type=T.StructField("output", T.DoubleType()).json(),
)It compares one metric across two windows using {{current_df}} and {{base_df}}, which makes it a drift metric. Drift metrics are stored in the drift metrics table.
Source: docs.databricks.comSources3
4.Alerting when drift crosses a threshold
Metric tables are ordinary Unity Catalog Delta tables, so alerts are ordinary Databricks SQL alerts. You write a query on the profile or drift metrics table that returns, for example, the latest population_stability_index or the ks_test.pvalue for a feature. You then create an alert on that query with a threshold, a schedule for evaluating it, and destinations. Email is the default, and webhooks, Slack and PagerDuty are also supported. Typical uses: a statistic leaves its range, the distribution changes, or inputs drift from the baseline, which for an inference profile can signal that the model needs retraining.
Two details catch people out. If the alert query has parameters, the alert uses their default values, so check those defaults. And the notification_settings you pass to create_monitor are a different thing: its on_failure destination, with up to 5 email addresses, tells you when a refresh fails or times out. It does not tell you that the data has drifted.
Checkpoint 5 of 7· Check yourself
Stakeholders want an email whenever PSI on income reaches 0.2 or more against the baseline. What do you configure?
Drift alerts are SQL alerts on queries over the metric tables. on_failure only covers refresh failures and timeouts, and custom metrics don't send notifications.
“You then create a Databricks SQL alert for this query.”Source: docs.databricks.com
Checkpoint 6 of 7· Exam question
A company logs nightly batch demand-forecast scores into a Delta table with a run timestamp, but the table has no request/response schema and is not tied to a serving endpoint. Which profile type fits, and why?
Correct answer: B — Time series, because the timestamp column lets metrics be computed over time-based windows.
- A. Inference profiling is reserved for tables matching the request-log schema tied to a serving endpoint; having model outputs alone does not qualify a table for that type.
- B. Time series profiling fits exactly this case: a timestamp column without a request-log schema, letting Lakehouse Monitoring window and compare metrics across periods.
- C. The table does have a timestamp column, so it is not forced into snapshot profiling, which would discard the time-windowing that column enables.
- D. Both time series and inference profile types can produce drift metrics, so drift availability does not force this table into the inference category.
Sources4
5.Endpoint health metrics
Data drift and model quality come from the inference table. The health of the serving infrastructure comes from the endpoint itself, on the Metrics tab of the Serving UI or through the metrics export API, which can feed Prometheus or Datadog.
| Metric | What it measures |
|---|---|
| Latency (ms) | P50 and P99 round-trip latency within Databricks |
| Request queue time (ms) | P50 and P99 time a request waits before a replica processes it |
| Request rate (per second) | Requests per minute divided by 60 |
| Request error rate (per second) | Rate of 4xx and 5xx HTTP error responses |
| CPU usage (%) | Average CPU utilization across all server replicas |
| Memory usage (%) | Average memory utilization across all server replicas |
| Provisioned concurrency | Maximum parallel requests, adjusting within the scale-out range |
Checkpoint 7 of 7· Check yourself
P99 latency rises sharply while model inference latency stays flat and request queue time climbs. What does this point to?
Inference time is unchanged, so the delay is spent waiting in the queue, which is the documented sign of too little capacity.
“Elevated queue time indicates the endpoint is under-provisioned for its incoming traffic”Source: docs.databricks.com
Sources5
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Setting notification_settings.on_failure in create_monitor emails stakeholders when drift exceeds a threshold.Why is that wrong?
on_failure notifies you when a monitoring refresh fails or times out. Drift thresholds need a Databricks SQL alert on a query over the metric tables.
Covered in Alerting when drift crosses a threshold
2.A custom metric definition can join the inference table to a reference table.Why is that wrong?
The definition must be a single SQL expression in a Jinja template, with no joins or subqueries.
Covered in Custom metrics
Practise it for real
Create an inference monitor on a model's request-log table, refresh it, and alert when a feature's drift crosses a threshold.
1.Install the SDK with %pip install "databricks-sdk>=0.68.0", then get the output schema and the inference table with w.schemas.get and w.tables.get.
Why: create_monitor needs the schema_id and table_id, not the names.
You should see: Objects whose schema_id and table_id you can pass to the config.
2.Build a DataProfilingConfig with inference_log=InferenceLogConfig(...) setting problem_type, prediction_column, model_id_column, label_column, timestamp_column and a 1-day granularity, and call w.data_quality.create_monitor.
Why: With a label column you get model quality metrics. With a model id column you get per-version slices.
You should see: The monitor info is returned, and a dashboard is created in assets_dir.
3.Call w.data_quality.create_refresh, then poll get_refresh until the state is no longer PENDING or RUNNING.
Why: The refresh writes the metric tables on serverless compute.
You should see: The tables <table>_profile_metrics and <table>_drift_metrics appear in the output schema.
4.Query the drift metrics table for one numeric feature with slice_key IS NULL, ordered by window, and read ks_test and population_stability_index.
Why: These are the drift signals you will set a threshold on.
You should see: CONSECUTIVE rows, plus BASELINE rows if you provided a baseline table.
5.Save that query and create a Databricks SQL alert that triggers when population_stability_index >= 0.2, with an email or Slack destination.
Why: 0.2 is the documented threshold for significant population change.
You should see: The alert is evaluated on its schedule and notifies you when the threshold is crossed.
Stuck? Get a nudge
If the drift table has no CONSECUTIVE rows yet, wait until a second daily window exists.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“When CDF is enabled, only newly appended data is processed, rather than re-processing the entire table every refresh.”
↩︎ Creating and refreshing a monitor“This calculation runs on serverless compute, not on the cluster that the notebook is attached to.”
↩︎ Creating and refreshing a monitor“Notify the given email when a monitoring refresh fails or times out.”
↩︎ Exam trap 1“When you first create a time series or inference profile, Databricks analyzes only data from the 30 days prior to its creation.”
↩︎ Checkpoint - 2.
“Slices are automatically created based on the distinct values of model_id_col.”
↩︎ Granularities and slices“For Snapshot analysis, the time window is a single point in time corresponding to the time the metric was refreshed.”
↩︎ Granularities and slices“one for col_2 > 10, one for col_2 <= 10, and one for each unique value in col1.”
↩︎ Prediction“The entire table is equivalent to slice_key = NULL and slice_value = NULL.”
↩︎ Checkpoint - 3.
“Only aggregate metrics access data from the primary table.”
↩︎ Custom metrics“The comparison window can be either the baseline data or the data from the previous time window.”
↩︎ Custom metrics“It cannot contain joins or subqueries.”
↩︎ Exam trap 2 - 4.
“By default, email notification is sent. You can also set up a webhook or send notifications to other applications such as Slack or Pagerduty.”
↩︎ Alerting when drift crosses a threshold“If the query uses parameters, the alert is based on the default values for those parameters.”
↩︎ Alerting when drift crosses a threshold“You then create a Databricks SQL alert for this query.”
↩︎ Checkpoint - 5.https://docs.databricks.com/aws/en/machine-learning/model-serving/metrics-export-serving-endpointOfficial docs
“Shows the average CPU utilization percentage across all server replicas.”
↩︎ Endpoint health metrics“Tracks the rate of 4xx and 5xx HTTP error responses per second.”
↩︎ Endpoint health metrics“Elevated queue time indicates the endpoint is under-provisioned for its incoming traffic”
↩︎ Checkpoint