Files
superset2/superset/common/chart_data_timing.py
T

115 lines
3.5 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
from superset.common.query_context import QueryContext
NANOSECONDS_PER_MILLISECOND: int = 1_000_000
CHART_DATA_TIMING_VERSION: int = 1
def to_ms(value_ns: int | None) -> float | None:
"""Convert nanoseconds to rounded milliseconds for public output."""
if value_ns is None:
return None
return round(value_ns / NANOSECONDS_PER_MILLISECOND, 2)
@dataclass(frozen=True)
class QueryAcquisitionTiming:
"""Timing captured by the dataframe payload owner."""
query_planning_ns: int
cache_resolution_ns: int
data_acquisition_ns: int | None
payload_assembly_ns: int
@dataclass(frozen=True)
class QueryTiming:
"""Completed timing for one query in a chart-data execution."""
query_planning_ns: int | None
cache_resolution_ns: int | None
data_acquisition_ns: int | None
payload_assembly_ns: int | None
total_ns: int
def as_public_dict(self) -> dict[str, Any]:
"""Return the versioned chart-data API representation."""
return {
"version": CHART_DATA_TIMING_VERSION,
"query": {
"query_planning_ms": to_ms(self.query_planning_ns),
"cache_resolution_ms": to_ms(self.cache_resolution_ns),
"data_acquisition_ms": to_ms(self.data_acquisition_ns),
"payload_assembly_ms": to_ms(self.payload_assembly_ns),
"total_ms": to_ms(self.total_ns),
},
}
@dataclass(frozen=True)
class QueryAcquisitionResult:
"""A dataframe payload paired with acquisition timing."""
payload: dict[str, Any]
timing: QueryAcquisitionTiming
@dataclass(frozen=True)
class QueryDataResult:
"""A query payload paired with completed timing."""
payload: dict[str, Any]
timing: QueryTiming
@dataclass(frozen=True)
class QueryContextExecutionResult:
"""Typed query-context result with timing outside query payloads."""
queries: tuple[QueryDataResult, ...]
cache_key: str | None = None
@dataclass(frozen=True)
class ChartDataExecutionResult:
"""Typed result of executing a chart-data command."""
query_context: QueryContext
queries: tuple[QueryDataResult, ...]
cache_key: str | None = None
def materialize(self) -> dict[str, Any]:
"""Return the historical command payload shape."""
queries: list[dict[str, Any]] = []
for query_result in self.queries:
queries.append(dict(query_result.payload))
result: dict[str, Any] = {
"query_context": self.query_context,
"queries": queries,
}
if self.cache_key is not None:
result["cache_key"] = self.cache_key
return result