The MQL query language

This document describes the Metrics Query Language (MQL). For more details on how to actually send a query to Snuba see Querying Snuba. The full grammar can be found in the snuba-sdk repository.

Queries are composed at their core of timeseries, which are aggregate functions on a metric:

aggregate ( metric_name / mri )

e.g.

sum(transaction.duration)
quantiles(0.99)(d:sessions/duration@second)

Timeseries can also be filtered and grouped by tags:

aggregate ( metric_name ) { tagkey:tagvalue } by tagkey

e.g.

sum(transaction.duration){environment:prod} by status_code

These timeseries can then be combined with arithmetic operations or referenced in custom functions:

timeseries arithmetic_op timeseries

function_name ( timeseries / parameter (, timeseries / parameter)* )

e.g.

sum(transaction.duration) + max(transaction.measurements.fcp)
apdex(sum(transaction.duration), 300)

Formulas, functions and timeseries all support filters and grouping and can be combined in almost any way:

count(transaction.duration){!status_code:200} / count(transaction.duration) # failure rate

apdex(sum(transaction.duration) + sum(transaction.measurements.fcp), 300){environment:prod} by status_code

However, there is information about the query that is not contained in the query string above. That’s why a MQL query to Snuba also contains a mql_context dictionary:

{
    "mql": "sum(transaction.duration)",
    "mql_context": {
        "entity": "generic_metrics_distributions",
        "start": "2023-01-02T03:04:05+00:00",
        "end": "2023-01-16T03:04:05+00:00",
        "rollup": {
            "orderby": None,
            "granularity": 3600,
            "interval": 3600,
            "with_totals": None,
        },
        "scope": {
            "org_ids": [1],
            "project_ids": [11],
            "use_case_id": "transactions",
        },
        "limit": 100,
        "offset": 5,
        "indexer_mappings": {},
    },
}

The Snuba SDK provides convenience classes around the more complex fields.

start / end

The start and end fields are ISO 8601 timestamps. They are required for all queries. In the SDK, a MetricsQuery object has a start and end field.

limit / offset

Specified in the MetricsQuery object with the limit and offset fields. These are optional and default to 1000 and 0 respectively.

Rollup

See here for the full object.

The rollup object contains information about how the timeseries should be grouped. The interval field specifies what time interval the timeseries should be grouped by. The total field serves two purposes: if the interval is set, total determines whether a totals row should be provided alongside the timeseries. Otherwise, it means that instead of a timeseries, a single value should be returned (or one per tag value, if the query is grouped by a tag). In this case, orderby can be used to specify how the results should be ordered. granularity is inferred automatically from the start/end times and the interval, and aligns with the time buckets stored in Clickhouse.

Scope

The scope object contains information about the query that is not represented in tags. Currently that is project ID(s), organization ID(s) and use case ID. The use case ID is automatically inferred based on the MRI of the metrics in the query. Queries across use case IDs are not supported.