Browse Source

ref(ddm): Implement simpler querying structure and improve code (#68436)

Riccardo Busetti 11 months ago
parent
commit
ae63792ca4

+ 20 - 20
src/sentry/api/endpoints/organization_metrics.py

@@ -28,8 +28,8 @@ from sentry.exceptions import InvalidParams, InvalidSearchQuery
 from sentry.models.organization import Organization
 from sentry.sentry_metrics.querying.data import (
     MetricsAPIQueryResultsTransformer,
-    MetricsQueriesPlan,
-    run_metrics_queries_plan,
+    MQLQuery,
+    run_queries,
 )
 from sentry.sentry_metrics.querying.errors import (
     InvalidMetricsQueryError,
@@ -437,26 +437,26 @@ class OrganizationMetricsQueryEndpoint(OrganizationEndpoint):
         interval = parse_stats_period(request.GET.get("interval", "1h"))
         return int(3600 if interval is None else interval.total_seconds())
 
-    def _metrics_queries_plan_from_request(self, request: Request) -> MetricsQueriesPlan:
+    def _mql_queries_from_request(self, request: Request) -> Sequence[MQLQuery]:
         """
         Extracts the metrics queries plan from the request payload.
         """
-        # TODO: maybe we could use a serializer to read the body of the request.
-        metrics_queries_plan = MetricsQueriesPlan()
-
-        queries = request.data.get("queries") or []
-        for query in queries:
-            metrics_queries_plan.declare_query(name=query["name"], mql=query["mql"])
-
-        formulas = request.data.get("formulas") or []
-        for formula in formulas:
-            metrics_queries_plan.apply_formula(
-                mql=formula["mql"],
-                order=self._validate_order(formula.get("order")),
-                limit=self._validate_limit(formula.get("limit")),
+        mql_sub_queries = {}
+        for query in request.data.get("queries") or []:
+            mql_sub_queries[query["name"]] = MQLQuery(query["mql"])
+
+        mql_queries = []
+        for formula in request.data.get("formulas") or []:
+            mql_queries.append(
+                MQLQuery(
+                    mql=formula["mql"],
+                    order=self._validate_order(formula.get("order")),
+                    limit=self._validate_limit(formula.get("limit")),
+                    **mql_sub_queries,
+                )
             )
 
-        return metrics_queries_plan
+        return mql_queries
 
     def _get_query_type_from_request(self, request: Request) -> QueryType:
         include_series = (request.GET.get("includeSeries") or "true") == "true"
@@ -474,7 +474,7 @@ class OrganizationMetricsQueryEndpoint(OrganizationEndpoint):
 
             start, end = get_date_range_from_params(request.GET)
             interval = self._interval_from_request(request)
-            metrics_queries_plan = self._metrics_queries_plan_from_request(request)
+            mql_queries = self._mql_queries_from_request(request)
 
             metrics.incr(
                 key="ddm.metrics_api.query",
@@ -485,8 +485,8 @@ class OrganizationMetricsQueryEndpoint(OrganizationEndpoint):
                 },
             )
 
-            results = run_metrics_queries_plan(
-                metrics_queries_plan=metrics_queries_plan,
+            results = run_queries(
+                mql_queries=mql_queries,
                 start=start,
                 end=end,
                 interval=interval,

+ 8 - 3
src/sentry/sentry_metrics/querying/data/__init__.py

@@ -1,5 +1,10 @@
-from .api import run_metrics_queries_plan
-from .plan import MetricsQueriesPlan
+from .api import run_queries
+from .query import MQLQueriesResult, MQLQuery
 from .transformation.metrics_api import MetricsAPIQueryResultsTransformer
 
-__all__ = ["run_metrics_queries_plan", "MetricsQueriesPlan", "MetricsAPIQueryResultsTransformer"]
+__all__ = [
+    "run_queries",
+    "MQLQuery",
+    "MQLQueriesResult",
+    "MetricsAPIQueryResultsTransformer",
+]

+ 10 - 13
src/sentry/sentry_metrics/querying/data/api.py

@@ -10,7 +10,6 @@ from sentry.models.organization import Organization
 from sentry.models.project import Project
 from sentry.sentry_metrics.querying.data.execution import QueryExecutor, QueryResult
 from sentry.sentry_metrics.querying.data.parsing import QueryParser
-from sentry.sentry_metrics.querying.data.plan import MetricsQueriesPlan, MetricsQueriesPlanResult
 from sentry.sentry_metrics.querying.data.preparation.base import (
     IntermediateQuery,
     run_preparation_steps,
@@ -18,11 +17,12 @@ from sentry.sentry_metrics.querying.data.preparation.base import (
 from sentry.sentry_metrics.querying.data.preparation.units_normalization import (
     UnitsNormalizationStep,
 )
+from sentry.sentry_metrics.querying.data.query import MQLQueriesResult, MQLQuery
 from sentry.sentry_metrics.querying.types import QueryType
 
 
-def run_metrics_queries_plan(
-    metrics_queries_plan: MetricsQueriesPlan,
+def run_queries(
+    mql_queries: Sequence[MQLQuery],
     start: datetime,
     end: datetime,
     interval: int,
@@ -31,18 +31,19 @@ def run_metrics_queries_plan(
     environments: Sequence[Environment],
     referrer: str,
     query_type: QueryType = QueryType.TOTALS_AND_SERIES,
-) -> MetricsQueriesPlanResult:
+) -> MQLQueriesResult:
     """
-    Runs a MetricsQueriesPlan which is converted into a series of queries that are executed in Snuba.
+    Runs a list of MQLQuery(s) that are executed in Snuba.
 
     Returns:
-        A MetricsQueriesPlanResult object which encapsulates the results of the plan and allows a QueryTransformer
+        A MQLQueriesResult object which encapsulates the results of the plan and allows a QueryTransformer
         to be run on the data.
     """
     # We build the basic query that contains the metadata which will be shared across all queries.
     base_query = MetricsQuery(
         start=start,
         end=end,
+        rollup=Rollup(interval),
         scope=MetricsScope(
             org_ids=[organization.id],
             project_ids=[project.id for project in projects],
@@ -51,15 +52,11 @@ def run_metrics_queries_plan(
 
     intermediate_queries = []
     # We parse the query plan and obtain a series of queries.
-    parser = QueryParser(
-        projects=projects, environments=environments, metrics_queries_plan=metrics_queries_plan
-    )
+    parser = QueryParser(projects=projects, environments=environments, mql_queries=mql_queries)
     for query_expression, query_order, query_limit in parser.generate_queries():
         intermediate_queries.append(
             IntermediateQuery(
-                metrics_query=base_query.set_query(query_expression).set_rollup(
-                    Rollup(interval=interval)
-                ),
+                metrics_query=base_query.set_query(query_expression),
                 order=query_order,
                 limit=query_limit,
             )
@@ -82,4 +79,4 @@ def run_metrics_queries_plan(
     results = executor.execute()
 
     # We wrap the result in a class that exposes some utils methods to operate on results.
-    return MetricsQueriesPlanResult(cast(list[QueryResult], results))
+    return MQLQueriesResult(cast(list[QueryResult], results))

+ 6 - 0
src/sentry/sentry_metrics/querying/data/execution.py

@@ -791,6 +791,12 @@ class QueryExecutor:
             value=self._number_of_executed_queries,
         )
 
+        for query_result in self._query_results:
+            if not isinstance(query_result, QueryResult):
+                raise MetricsQueryExecutionError(
+                    "Not all queries were executed in the execution loop"
+                )
+
         return cast(Sequence[QueryResult], self._query_results)
 
     def schedule(self, intermediate_query: IntermediateQuery, query_type: QueryType):

+ 9 - 7
src/sentry/sentry_metrics/querying/data/parsing.py

@@ -5,7 +5,7 @@ from snuba_sdk.mql.mql import InvalidMQLQueryError, parse_mql
 
 from sentry.models.environment import Environment
 from sentry.models.project import Project
-from sentry.sentry_metrics.querying.data.plan import MetricsQueriesPlan
+from sentry.sentry_metrics.querying.data.query import MQLQuery
 from sentry.sentry_metrics.querying.errors import InvalidMetricsQueryError
 from sentry.sentry_metrics.querying.types import QueryExpression, QueryOrder
 from sentry.sentry_metrics.querying.visitors import (
@@ -20,18 +20,18 @@ from sentry.utils import metrics
 
 class QueryParser:
     """
-    Represents a parser which is responsible for generating queries given a MetricsQueriesPlan.
+    Represents a parser which is responsible for generating queries given a list of MQLQuery(s).
     """
 
     def __init__(
         self,
         projects: Sequence[Project],
         environments: Sequence[Environment],
-        metrics_queries_plan: MetricsQueriesPlan,
+        mql_queries: Sequence[MQLQuery],
     ):
         self._projects = projects
         self._environments = environments
-        self._metrics_queries_plan = metrics_queries_plan
+        self._mql_queries = mql_queries
 
     def _parse_mql(self, mql: str) -> VisitableQueryExpression:
         """
@@ -68,9 +68,11 @@ class QueryParser:
         Returns:
             A generator which can be used to obtain a query to execute and its details.
         """
-        for formula_definition in self._metrics_queries_plan.get_replaced_formulas():
+        for mql_query in self._mql_queries:
+            compiled_mql_query = mql_query.compile()
+
             query_expression = (
-                self._parse_mql(formula_definition.mql)
+                self._parse_mql(compiled_mql_query.mql)
                 # We validate the query.
                 .add_visitor(QueryValidationV2Visitor())
                 # We inject the environment filter in each timeseries.
@@ -82,4 +84,4 @@ class QueryParser:
                     )
                 ).get()
             )
-            yield query_expression, formula_definition.order, formula_definition.limit
+            yield query_expression, compiled_mql_query.order, compiled_mql_query.limit

+ 0 - 129
src/sentry/sentry_metrics/querying/data/plan.py

@@ -1,129 +0,0 @@
-import re
-from dataclasses import dataclass, replace
-
-from sentry.sentry_metrics.querying.data.execution import QueryResult
-from sentry.sentry_metrics.querying.data.transformation.base import (
-    QueryResultsTransformer,
-    QueryTransformerResult,
-)
-from sentry.sentry_metrics.querying.types import QueryOrder
-
-
-@dataclass(frozen=True)
-class FormulaDefinition:
-    """
-    Represents the definition of a formula which can be run in a MetricsQueriesPlan.
-
-    Attributes:
-        mql: The formula string representation using the MQL language.
-        order: The order of the formula.
-        limit: The limit of the formula, representing the maximum number of groups that will be returned.
-    """
-
-    mql: str
-    order: QueryOrder | None
-    limit: int | None
-
-    def replace_variables(self, queries: dict[str, str]) -> "FormulaDefinition":
-        """
-        Replaces all variables inside the formulas with the corresponding queries.
-
-        For example, a formula in the form "$a + $b" with queries "a: max(mri_1), b: min(mri_2)" will become
-        "max(mri_1) + min(mri_2)".
-
-        The rationale for having queries being defined as variables in formulas is to have a structure which is more
-        flexible and allows reuse of the same query across multiple formulas.
-
-        Returns:
-            A new FormulaDefinition with the MQL string containing the replaced formula.
-        """
-        replaced_mql_formula = self.mql
-        # We sort query names by length and content with the goal of trying to always match the longest queries first.
-        sorted_query_names = sorted(queries.keys(), key=lambda q: (len(q), q), reverse=True)
-        for query_name in sorted_query_names:
-            replaced_mql_formula = re.sub(
-                rf"\${query_name}", queries.get(query_name, ""), replaced_mql_formula
-            )
-
-        return replace(self, mql=replaced_mql_formula)
-
-
-# TODO: maybe we want to evaluate a form of builder pattern where we can control the
-#  chaining of methods, so that we make sure that declaration strictly happens before formula
-#  definition.
-class MetricsQueriesPlan:
-    """
-    Represents a plan containing a series of queries and formulas to execute. The queries are defined as variables and
-    the formulas will define what is actually executed.
-
-    For example, you could define a simple query "a: max(mri_1)" and use it in the formula as "$a".
-    """
-
-    def __init__(self):
-        self._queries: dict[str, str] = {}
-        self._formulas: list[FormulaDefinition] = []
-
-    def declare_query(self, name: str, mql: str) -> "MetricsQueriesPlan":
-        """
-        Declares a query with a name and the mql definition.
-
-        Returns:
-            The MetricsQueriesPlan instance in which the query was added.
-        """
-        self._queries[name] = mql
-        return self
-
-    def apply_formula(
-        self, mql: str, order: QueryOrder | None = None, limit: int | None = None
-    ) -> "MetricsQueriesPlan":
-        """
-        Applies an mql formula on the queries that were previously declared.
-
-        Returns:
-            The MetricsQueriesPlan instance in which the formula was added.
-        """
-        self._formulas.append(FormulaDefinition(mql=mql, order=order, limit=limit))
-        return self
-
-    def get_replaced_formulas(self) -> list[FormulaDefinition]:
-        """
-        Returns a list of formulas with the variables replaced with the actual mql query string.
-
-        The usage of a variable in the formulas is with the `$` + the name of the query. The rationale
-        behind choosing `$` is to keep the syntax compatible with the MQL syntax, in case we were to embed
-        variables resolution in the layer itself.
-
-        This function naively uses string substitution to replace the contents. In case we see it's too
-        fragile, we might want to switch to parsing the actual input and mutating the AST.
-
-        Returns:
-            A list of FormulaDefinition objects whose formulas have been replaced.
-        """
-        return list(map(lambda formula: formula.replace_variables(self._queries), self._formulas))
-
-    def is_empty(self) -> bool:
-        """
-        A query plan is defined to be empty is no formulas have been applied on it.
-
-        Returns:
-            A boolean which is True when the plan is empty, or False otherwise.
-        """
-        return not self._formulas
-
-
-@dataclass(frozen=True)
-class MetricsQueriesPlanResult:
-    """
-    Represents a wrapper around the results of a MetricsQueriesPlan which exposes useful methods to run on the query
-    results.
-    """
-
-    results: list[QueryResult]
-
-    def apply_transformer(
-        self, transformer: QueryResultsTransformer[QueryTransformerResult]
-    ) -> QueryTransformerResult:
-        """
-        Applies a transformer on the `results` and returns the value of the transformation.
-        """
-        return transformer.transform(self.results)

+ 86 - 0
src/sentry/sentry_metrics/querying/data/query.py

@@ -0,0 +1,86 @@
+import re
+from collections.abc import Mapping
+from dataclasses import dataclass
+from typing import Any, cast
+
+from sentry.sentry_metrics.querying.data.execution import QueryResult
+from sentry.sentry_metrics.querying.data.transformation.base import (
+    QueryResultsTransformer,
+    QueryTransformerResult,
+)
+from sentry.sentry_metrics.querying.errors import InvalidMetricsQueryError
+from sentry.sentry_metrics.querying.types import QueryOrder
+
+
+class MQLQuery:
+    """
+    Represents an MQL query that can be run against Snuba.
+
+    Example:
+    # Defining a simple query.
+    query = MQLQuery("avg(d:transactions/duration@millisecond)", order=QueryOrder.ASC, limit=10)
+
+    # Defining more complex queries that depend on each other.
+    query_1 = MQLQuery("avg(d:transactions/duration@millisecond)")
+    query_2 = MQLQuery("sum(d:transactions/duration@millisecond)")
+    query = MQLQuery("$query_1 / $query_2", order=QueryOrder.ASC, query_1=query_1, query_2=query_2)
+    """
+
+    def __init__(
+        self, mql: str, order: QueryOrder | None = None, limit: int | None = None, **sub_queries
+    ):
+        self.mql = mql
+        self.order = order
+        self.limit = limit
+        self.sub_queries = self._validate_sub_queries(sub_queries)
+
+    @staticmethod
+    def _validate_sub_queries(sub_queries: Mapping[str, Any]) -> Mapping[str, "MQLQuery"]:
+        for name, query in sub_queries.items():
+            if not isinstance(query, MQLQuery):
+                raise InvalidMetricsQueryError("A subquery must be an instance of 'MQLQuery'")
+
+        return cast(Mapping[str, MQLQuery], sub_queries)
+
+    def compile(self) -> "MQLQuery":
+        """
+        Compiles the MQL query by replacing all variables inside the formulas with the corresponding queries.
+
+        For example, a formula in the form "$a + $b" with queries "a: max(mri_1), b: min(mri_2)" will become
+        "max(mri_1) + min(mri_2)".
+
+        The rationale for having queries being defined as variables in formulas is to have a structure which is more
+        flexible and allows reuse of the same query across multiple formulas.
+
+        Returns:
+            A new MQLQuery with the MQL string containing the replaced formula.
+        """
+        sub_queries = {name: query.compile() for name, query in self.sub_queries.items()}
+        replaced_mql_formula = self.mql
+
+        # We sort query names by length and content with the goal of trying to always match the longest queries first.
+        sorted_query_names = sorted(sub_queries.keys(), key=lambda q: (len(q), q), reverse=True)
+        for query_name in sorted_query_names:
+            replaced_mql_formula = re.sub(
+                rf"\${query_name}", sub_queries[query_name].mql, replaced_mql_formula
+            )
+
+        return MQLQuery(mql=replaced_mql_formula, order=self.order, limit=self.limit)
+
+
+@dataclass(frozen=True)
+class MQLQueriesResult:
+    """
+    Represents a wrapper around the results of a list of MQLQuery(s) which exposes useful methods to run on the query
+    results.
+    """
+
+    results: list[QueryResult]
+
+    def apply_transformer(
+        self, transformer: QueryResultsTransformer[QueryTransformerResult]
+    ) -> QueryTransformerResult:
+        """
+        Applies a transformer on the `results` and returns the value of the transformation.
+        """
+        return transformer.transform(self.results)

+ 3 - 4
src/sentry/snuba/outcomes.py

@@ -17,7 +17,7 @@ from sentry.api.utils import get_date_range_from_params
 from sentry.constants import DataCategory
 from sentry.release_health.base import AllowedResolution
 from sentry.search.utils import InvalidQuery
-from sentry.sentry_metrics.querying.data import MetricsQueriesPlan, run_metrics_queries_plan
+from sentry.sentry_metrics.querying.data import MQLQuery, run_queries
 from sentry.sentry_metrics.querying.data.transformation.stats import MetricsStatsTransformer
 from sentry.snuba.referrer import Referrer
 from sentry.snuba.sessions_v2 import (
@@ -474,9 +474,8 @@ def run_metrics_outcomes_query(
     interval = parse_stats_period(query.get("interval", "1h"))
 
     # TODO(metrics): add support for reason tag
-    plan = MetricsQueriesPlan().apply_formula("sum(c:metric_stats/volume@none) by (outcome.id)")
-    rows = run_metrics_queries_plan(
-        plan,
+    rows = run_queries(
+        mql_queries=[MQLQuery("sum(c:metric_stats/volume@none) by (outcome.id)")],
         start=start,
         end=end,
         interval=int(3600 if interval is None else interval.total_seconds()),

+ 117 - 206
tests/sentry/sentry_metrics/querying/data/test_api.py

@@ -12,8 +12,8 @@ from sentry.models.project import Project
 from sentry.sentry_metrics.querying.constants import SNUBA_QUERY_LIMIT
 from sentry.sentry_metrics.querying.data import (
     MetricsAPIQueryResultsTransformer,
-    MetricsQueriesPlan,
-    run_metrics_queries_plan,
+    MQLQuery,
+    run_queries,
 )
 from sentry.sentry_metrics.querying.errors import (
     InvalidMetricsQueryError,
@@ -112,7 +112,7 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
 
     def run_query(
         self,
-        metrics_queries_plan: MetricsQueriesPlan,
+        mql_queries: Sequence[MQLQuery],
         start: datetime,
         end: datetime,
         interval: int,
@@ -122,8 +122,8 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         referrer: str,
         query_type: QueryType = QueryType.TOTALS_AND_SERIES,
     ) -> Mapping[str, Any]:
-        return run_metrics_queries_plan(
-            metrics_queries_plan,
+        return run_queries(
+            mql_queries,
             start,
             end,
             interval,
@@ -136,11 +136,8 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
 
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_no_formulas(self) -> None:
-        query_1 = self.mql("sum", TransactionMRI.DURATION.value, "transaction:/bar")
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1)
-
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -164,10 +161,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             ("min", None, 0.0),
         ):
             query_1 = self.mql(aggregate, TransactionMRI.DURATION.value, "transaction:/bar")
-            plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
             results = self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[MQLQuery(query_1)],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -189,10 +185,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_infinite_value(self) -> None:
         query_1 = self.mql("count", TransactionMRI.DURATION.value)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1 / 0")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery("$query_1 / 0", query_1=MQLQuery(query_1))],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -214,10 +209,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_one_aggregation(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -244,10 +238,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_one_aggregation_and_only_totals(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -272,10 +265,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_one_aggregation_and_unitless_aggregate(self) -> None:
         query_1 = self.mql("count", TransactionMRI.DURATION.value)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -302,10 +294,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_one_aggregation_and_environment(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -327,10 +318,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_one_aggregation_and_latest_release(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value, "release:latest")
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -352,10 +342,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_percentile(self) -> None:
         query_1 = self.mql("p90", TransactionMRI.DURATION.value)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -382,10 +371,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         # We only want to check if these percentiles return results.
         for percentile in ("p50", "p75", "p90", "p95", "p99"):
             query_1 = self.mql(percentile, TransactionMRI.DURATION.value)
-            plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
             results = self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[MQLQuery(query_1)],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -402,11 +390,10 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         # We only want to check if these percentiles result in a error.
         for percentile in ("p30", "p45"):
             query_1 = self.mql(percentile, TransactionMRI.DURATION.value)
-            plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
             with pytest.raises(MetricsQueryExecutionError):
                 self.run_query(
-                    metrics_queries_plan=plan,
+                    mql_queries=[MQLQuery(query_1)],
                     start=self.now() - timedelta(minutes=30),
                     end=self.now() + timedelta(hours=1, minutes=30),
                     interval=3600,
@@ -419,10 +406,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_group_by(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value, group_by="transaction, platform")
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -464,14 +450,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_group_by_and_order_by(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value, group_by="transaction")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .apply_formula("$query_1", order=QueryOrder.DESC)
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1, order=QueryOrder.DESC)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -501,14 +482,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_group_by_and_order_by_and_only_totals(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value, group_by="transaction")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .apply_formula("$query_1", order=QueryOrder.ASC)
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1, order=QueryOrder.ASC)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -549,10 +525,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             )
 
         query_1 = self.mql("sum", TransactionMRI.MEASUREMENTS_FCP.value, group_by="transaction")
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now(),
             end=self.now() + timedelta(hours=1),
             interval=3600,
@@ -575,10 +550,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     @with_feature("organizations:ddm-metrics-api-unit-normalization")
     def test_query_with_parenthesized_filter(self) -> None:
         query_1 = self.mql("sum", TransactionMRI.DURATION.value, "(transaction:/hello)", "platform")
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -611,10 +585,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         query_1 = self.mql(
             "sum", TransactionMRI.DURATION.value, "platform:ios AND transaction:/hello", "platform"
         )
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -640,10 +613,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         query_1 = self.mql(
             "sum", TransactionMRI.DURATION.value, "platform:ios OR platform:android", "platform"
         )
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -676,10 +648,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         query_1 = self.mql(
             "sum", TransactionMRI.DURATION.value, "!platform:ios transaction:/hello", "platform"
         )
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -705,10 +676,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         query_1 = self.mql(
             "sum", TransactionMRI.DURATION.value, "platform:[android, ios]", "platform"
         )
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -741,10 +711,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         query_1 = self.mql(
             "sum", TransactionMRI.DURATION.value, '!platform:["android", "ios"]', "platform"
         )
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -769,16 +738,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_multiple_aggregations(self) -> None:
         query_1 = self.mql("min", TransactionMRI.DURATION.value)
         query_2 = self.mql("max", TransactionMRI.DURATION.value)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1")
-            .apply_formula("$query_2")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1), MQLQuery(query_2)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -808,16 +770,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_multiple_aggregations_and_single_group_by(self) -> None:
         query_1 = self.mql("min", TransactionMRI.DURATION.value, group_by="platform")
         query_2 = self.mql("max", TransactionMRI.DURATION.value, group_by="platform")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1")
-            .apply_formula("$query_2")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1), MQLQuery(query_2)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -881,16 +836,12 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     ) -> None:
         query_1 = self.mql("min", TransactionMRI.DURATION.value, group_by="platform")
         query_2 = self.mql("max", TransactionMRI.DURATION.value, group_by="platform")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1", QueryOrder.ASC, 2)
-            .apply_formula("$query_2", QueryOrder.DESC, 2)
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(query_1, order=QueryOrder.ASC, limit=2),
+                MQLQuery(query_2, order=QueryOrder.DESC, limit=2),
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -947,14 +898,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         self,
     ) -> None:
         query_1 = self.mql("min", TransactionMRI.DURATION.value)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .apply_formula("$query_1", limit=SNUBA_QUERY_LIMIT + 10)
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1, limit=SNUBA_QUERY_LIMIT + 10)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -984,13 +930,6 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     ) -> None:
         query_1 = self.mql("min", TransactionMRI.DURATION.value, group_by="platform")
         query_2 = self.mql("max", TransactionMRI.DURATION.value, group_by="platform")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1", order=QueryOrder.DESC)
-            .apply_formula("$query_2", order=QueryOrder.DESC)
-        )
 
         # With a snuba limit of 3 and 3 intervals we expect to only have 1 group returned. Currently,
         # the test is failing because totals are correctly queried but series data is returned up to
@@ -998,7 +937,10 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         # the groups * intervals combinations. Once the bug will be fixed on the snuba side, we should
         # expect to get back 3 correct entries from the series query.
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(query_1, order=QueryOrder.DESC),
+                MQLQuery(query_2, order=QueryOrder.DESC),
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1054,10 +996,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             )
 
         query_1 = self.mql("count_unique", mri)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1094,10 +1035,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             )
 
         query_1 = self.mql("sum", mri)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1134,10 +1074,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             )
 
         query_1 = self.mql("sum", mri)
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1174,15 +1113,9 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
 
         query_1 = self.mql("sum", mri_1)
         query_2 = self.mql("sum", mri_2)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1")
-            .apply_formula("$query_2")
-        )
+
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[MQLQuery(query_1), MQLQuery(query_2)],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1207,11 +1140,10 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             TransactionMRI.DURATION.value,
             "transaction:/api/0/organizations/{organization_slug}/",
         )
-        plan = MetricsQueriesPlan().declare_query("query_1", query_1).apply_formula("$query_1")
 
         with pytest.raises(InvalidMetricsQueryError):
             self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[MQLQuery(query_1)],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -1228,16 +1160,14 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
             TransactionMRI.DURATION.value,
         )
         query_2 = self.mql("max", "d:custom/app_load@millisecond")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1 / $query_2")
-        )
 
         with pytest.raises(InvalidMetricsQueryError):
             self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[
+                    MQLQuery(
+                        "$query_1 / $query_2", query_1=MQLQuery(query_1), query_2=MQLQuery(query_2)
+                    )
+                ],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -1251,16 +1181,14 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_different_metric_types(self):
         query_1 = self.mql("count", "c:custom/page_click@none")
         query_2 = self.mql("max", "d:custom/app_load@millisecond")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1 * $query_2")
-        )
 
         with pytest.raises(InvalidMetricsQueryError):
             self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[
+                    MQLQuery(
+                        "$query_1 * $query_2", query_1=MQLQuery(query_1), query_2=MQLQuery(query_2)
+                    )
+                ],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -1274,16 +1202,16 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_different_group_bys(self):
         query_1 = self.mql("min", "d:custom/page_click@none", group_by="transaction, environment")
         query_2 = self.mql("max", "d:custom/app_load@millisecond", group_by="transaction")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1 * $query_2 / $query_1")
-        )
 
         with pytest.raises(InvalidMetricsQueryError):
             self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[
+                    MQLQuery(
+                        "$query_1 * $query_2 / $query_1",
+                        query_1=MQLQuery(query_1),
+                        query_2=MQLQuery(query_2),
+                    )
+                ],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -1297,16 +1225,16 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_complex_group_by(self):
         query_1 = self.mql("min", "d:custom/page_click@none", group_by="environment")
         query_2 = self.mql("max", "d:custom/app_load@millisecond", group_by="transaction")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("((($query_1 * $query_2) by (release)) / $query_1) by (browser)")
-        )
 
         with pytest.raises(InvalidMetricsQueryError):
             self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[
+                    MQLQuery(
+                        "((($query_1 * $query_2) by (release)) / $query_1) by (browser)",
+                        query_1=MQLQuery(query_1),
+                        query_2=MQLQuery(query_2),
+                    )
+                ],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -1320,15 +1248,13 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_basic_formula(self):
         query_1 = self.mql("count", TransactionMRI.DURATION.value)
         query_2 = self.mql("sum", TransactionMRI.DURATION.value)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_2 / $query_1")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(
+                    "$query_2 / $query_1", query_1=MQLQuery(query_1), query_2=MQLQuery(query_2)
+                )
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1347,17 +1273,17 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_complex_formula(self):
         query_1 = self.mql("count", TransactionMRI.DURATION.value)
         query_2 = self.mql("sum", TransactionMRI.DURATION.value)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_2 * $query_1 + 100")
-            .apply_formula("$query_1")
-            .apply_formula("$query_2")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(
+                    "$query_2 * $query_1 + 100",
+                    query_1=MQLQuery(query_1),
+                    query_2=MQLQuery(query_2),
+                ),
+                MQLQuery("$query_1", query_1=MQLQuery(query_1)),
+                MQLQuery("$query_2", query_2=MQLQuery(query_2)),
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1377,15 +1303,15 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_formula_and_group_by(self):
         query_1 = self.mql("count", TransactionMRI.DURATION.value)
         query_2 = self.mql("sum", TransactionMRI.DURATION.value)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("($query_2 * $query_1) by (platform, transaction)")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(
+                    "($query_2 * $query_1) by (platform, transaction)",
+                    query_1=MQLQuery(query_1),
+                    query_2=MQLQuery(query_2),
+                )
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1412,17 +1338,16 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
     def test_query_with_formula_and_filter(self):
         query_1 = self.mql("count", TransactionMRI.DURATION.value, filters="platform:android")
         query_2 = self.mql("sum", TransactionMRI.DURATION.value, filters="platform:ios")
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula(
-                "($query_2 + $query_1) by (platform, transaction)", order=QueryOrder.DESC
-            )
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(
+                    "($query_2 + $query_1) by (platform, transaction)",
+                    order=QueryOrder.DESC,
+                    query_1=MQLQuery(query_1),
+                    query_2=MQLQuery(query_2),
+                )
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1477,15 +1402,11 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         ):
             query_1 = self.mql("avg", mri_1)
             query_2 = self.mql("sum", mri_2)
-            plan = (
-                MetricsQueriesPlan()
-                .declare_query("query_1", query_1)
-                .declare_query("query_2", query_2)
-                .apply_formula(formula)
-            )
 
             results = self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[
+                    MQLQuery(formula, query_1=MQLQuery(query_1), query_2=MQLQuery(query_2))
+                ],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,
@@ -1523,15 +1444,13 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
 
         query_1 = self.mql("avg", mri_1)
         query_2 = self.mql("sum", mri_2)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1 + $query_2")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(
+                    "$query_1 + $query_2", query_1=MQLQuery(query_1), query_2=MQLQuery(query_2)
+                )
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1569,15 +1488,13 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
 
         query_1 = self.mql("avg", mri_1)
         query_2 = self.mql("count", mri_2)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1 + $query_2")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(
+                    "$query_1 + $query_2", query_1=MQLQuery(query_1), query_2=MQLQuery(query_2)
+                )
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1615,15 +1532,13 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
 
         query_1 = self.mql("avg", mri_1)
         query_2 = self.mql("sum", mri_2)
-        plan = (
-            MetricsQueriesPlan()
-            .declare_query("query_1", query_1)
-            .declare_query("query_2", query_2)
-            .apply_formula("$query_1 + $query_2")
-        )
 
         results = self.run_query(
-            metrics_queries_plan=plan,
+            mql_queries=[
+                MQLQuery(
+                    "$query_1 + $query_2", query_1=MQLQuery(query_1), query_2=MQLQuery(query_2)
+                )
+            ],
             start=self.now() - timedelta(minutes=30),
             end=self.now() + timedelta(hours=1, minutes=30),
             interval=3600,
@@ -1671,15 +1586,11 @@ class MetricsAPITestCase(TestCase, BaseMetricsTestCase):
         ):
             query_1 = self.mql("avg", mri_1)
             query_2 = self.mql("sum", mri_2)
-            plan = (
-                MetricsQueriesPlan()
-                .declare_query("query_1", query_1)
-                .declare_query("query_2", query_2)
-                .apply_formula(formula)
-            )
 
             results = self.run_query(
-                metrics_queries_plan=plan,
+                mql_queries=[
+                    MQLQuery(formula, query_1=MQLQuery(query_1), query_2=MQLQuery(query_2))
+                ],
                 start=self.now() - timedelta(minutes=30),
                 end=self.now() + timedelta(hours=1, minutes=30),
                 interval=3600,

+ 0 - 27
tests/sentry/sentry_metrics/querying/data/test_plan.py

@@ -1,27 +0,0 @@
-import pytest
-
-from sentry.sentry_metrics.querying.data import MetricsQueriesPlan
-
-
-@pytest.mark.parametrize(
-    "formula, queries, expected_formula",
-    [
-        ("$a + $b", {"a": "query_1", "b": "query_2"}, "query_1 + query_2"),
-        ("$a + $b + $c", {"a": "query_1", "b": "query_2"}, "query_1 + query_2 + $c"),
-        (
-            "$a / $aa + $ab * $b",
-            {"a": "query_1", "b": "query_2", "aa": "query_3", "ab": "query_4"},
-            "query_1 / query_3 + query_4 * query_2",
-        ),
-    ],
-)
-def test_get_replaced_formulas(formula, queries, expected_formula):
-    plan = MetricsQueriesPlan()
-    for query_name, query in queries.items():
-        plan.declare_query(query_name, query)
-
-    plan.apply_formula(formula)
-
-    replaced_formulas = plan.get_replaced_formulas()
-    assert len(replaced_formulas) == 1
-    assert replaced_formulas[0].mql == expected_formula

Some files were not shown because too many files changed in this diff