123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- import {Fragment, ReactNode} from 'react';
- import {Location} from 'history';
- import {Organization} from 'sentry/types';
- import {parsePeriodToHours} from 'sentry/utils/dates';
- import EventView from 'sentry/utils/discover/eventView';
- import {canUseMetricsData} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
- import MetricsCompatibilityQuery, {
- MetricsCompatibilityData,
- } from 'sentry/utils/performance/metricsEnhanced/metricsCompatibilityQuery';
- import MetricsCompatibilitySumsQuery, {
- MetricsCompatibilitySumData,
- } from 'sentry/utils/performance/metricsEnhanced/metricsCompatibilityQuerySums';
- import {createDefinedContext} from './utils';
- export interface MetricDataSwitcherOutcome {
- forceTransactionsOnly: boolean;
- compatibleProjects?: number[];
- shouldNotifyUnnamedTransactions?: boolean;
- shouldWarnIncompatibleSDK?: boolean;
- }
- export interface MetricsCardinalityContext {
- isLoading: boolean;
- outcome?: MetricDataSwitcherOutcome;
- }
- type MergedMetricsData = MetricsCompatibilityData & MetricsCompatibilitySumData;
- const [_Provider, _useContext, _Context] =
- createDefinedContext<MetricsCardinalityContext>({
- name: 'MetricsCardinalityContext',
- strict: false,
- });
- /**
- * This provider determines whether the metrics data is storing performance information correctly before we
- * make dozens of requests on pages such as performance landing and dashboards.
- */
- export const MetricsCardinalityProvider = (props: {
- children: ReactNode;
- location: Location;
- organization: Organization;
- }) => {
- const isUsingMetrics = canUseMetricsData(props.organization);
- if (!isUsingMetrics) {
- return (
- <_Provider
- value={{
- isLoading: false,
- outcome: {
- forceTransactionsOnly: true,
- },
- }}
- >
- {props.children}
- </_Provider>
- );
- }
- const baseDiscoverProps = {
- location: props.location,
- orgSlug: props.organization.slug,
- cursor: '0:0:0',
- };
- const eventView = EventView.fromLocation(props.location);
- eventView.fields = [{field: 'tpm()'}];
- const _eventView = adjustEventViewTime(eventView);
- return (
- <Fragment>
- <MetricsCompatibilityQuery eventView={_eventView} {...baseDiscoverProps}>
- {compatabilityResult => (
- <MetricsCompatibilitySumsQuery eventView={_eventView} {...baseDiscoverProps}>
- {sumsResult => (
- <_Provider
- value={{
- isLoading: compatabilityResult.isLoading || sumsResult.isLoading,
- outcome:
- compatabilityResult.isLoading || sumsResult.isLoading
- ? undefined
- : getMetricsOutcome(
- compatabilityResult.tableData && sumsResult.tableData
- ? {
- ...compatabilityResult.tableData,
- ...sumsResult.tableData,
- }
- : null,
- !!compatabilityResult.error && !!sumsResult.error
- ),
- }}
- >
- {props.children}
- </_Provider>
- )}
- </MetricsCompatibilitySumsQuery>
- )}
- </MetricsCompatibilityQuery>
- </Fragment>
- );
- };
- export const MetricsCardinalityConsumer = _Context.Consumer;
- export const useMetricsCardinalityContext = _useContext;
- /**
- * Logic for picking sides of metrics vs. transactions along with the associated warnings.
- */
- function getMetricsOutcome(
- dataCounts: MergedMetricsData | null,
- hasOtherFallbackCondition: boolean
- ) {
- const fallbackOutcome: MetricDataSwitcherOutcome = {
- forceTransactionsOnly: true,
- };
- const successOutcome: MetricDataSwitcherOutcome = {
- forceTransactionsOnly: false,
- };
- if (!dataCounts) {
- return fallbackOutcome;
- }
- const compatibleProjects = dataCounts.compatible_projects;
- if (hasOtherFallbackCondition) {
- return fallbackOutcome;
- }
- if (!dataCounts) {
- return fallbackOutcome;
- }
- if (checkForSamplingRules(dataCounts)) {
- return fallbackOutcome;
- }
- if (checkNoDataFallback(dataCounts)) {
- return fallbackOutcome;
- }
- if (checkIncompatibleData(dataCounts)) {
- return {
- shouldWarnIncompatibleSDK: true,
- forceTransactionsOnly: true,
- compatibleProjects,
- };
- }
- if (checkIfAllOtherData(dataCounts)) {
- return {
- shouldNotifyUnnamedTransactions: true,
- forceTransactionsOnly: true,
- compatibleProjects,
- };
- }
- if (checkIfPartialOtherData(dataCounts)) {
- return {
- shouldNotifyUnnamedTransactions: true,
- compatibleProjects,
- forceTransactionsOnly: false,
- };
- }
- return successOutcome;
- }
- /**
- * Fallback if very similar amounts of metrics and transactions are found.
- * No projects with dynamic sampling means no rules have been enabled yet.
- */
- function checkForSamplingRules(dataCounts: MergedMetricsData) {
- const counts = normalizeCounts(dataCounts);
- if (!dataCounts.dynamic_sampling_projects?.length) {
- return true;
- }
- if (counts.metricsCount === 0) {
- return true;
- }
- return false;
- }
- /**
- * Fallback if no metrics found.
- */
- function checkNoDataFallback(dataCounts: MergedMetricsData) {
- const counts = normalizeCounts(dataCounts);
- return !counts.metricsCount;
- }
- /**
- * Fallback and warn if incompatible data found (old specific SDKs).
- */
- function checkIncompatibleData(dataCounts: MergedMetricsData) {
- const counts = normalizeCounts(dataCounts);
- return counts.nullCount > 0;
- }
- /**
- * Fallback and warn about unnamed transactions (specific SDKs).
- */
- function checkIfAllOtherData(dataCounts: MergedMetricsData) {
- const counts = normalizeCounts(dataCounts);
- return counts.unparamCount >= counts.metricsCount;
- }
- /**
- * Show metrics but warn about unnamed transactions.
- */
- function checkIfPartialOtherData(dataCounts: MergedMetricsData) {
- const counts = normalizeCounts(dataCounts);
- return counts.unparamCount > 0;
- }
- /**
- * Temporary function, can be removed after API changes.
- */
- function normalizeCounts({sum}: MergedMetricsData) {
- try {
- const metricsCount = Number(sum.metrics);
- const unparamCount = Number(sum.metrics_unparam);
- const nullCount = Number(sum.metrics_null);
- return {
- metricsCount,
- unparamCount,
- nullCount,
- };
- } catch (_) {
- return {
- metricsCount: 0,
- unparamCount: 0,
- nullCount: 0,
- };
- }
- }
- /**
- * Performance optimization to limit the amount of rows scanned before showing the landing page.
- */
- function adjustEventViewTime(eventView: EventView) {
- const _eventView = eventView.clone();
- if (!_eventView.start && !_eventView.end) {
- if (!_eventView.statsPeriod) {
- _eventView.statsPeriod = '1h';
- _eventView.start = undefined;
- _eventView.end = undefined;
- } else {
- const periodHours = parsePeriodToHours(_eventView.statsPeriod);
- if (periodHours > 1) {
- _eventView.statsPeriod = '1h';
- _eventView.start = undefined;
- _eventView.end = undefined;
- }
- }
- }
- return _eventView;
- }
|