diff --git a/static/app/views/performance/queues/charts/latencyChart.spec.tsx b/static/app/views/performance/queues/charts/latencyChart.spec.tsx
index ec92a61924e8b..c6a45ed43463e 100644
--- a/static/app/views/performance/queues/charts/latencyChart.spec.tsx
+++ b/static/app/views/performance/queues/charts/latencyChart.spec.tsx
@@ -23,7 +23,7 @@ describe('latencyChart', () => {
});
});
it('renders', async () => {
- render();
+ render();
screen.getByText('Avg Latency');
expect(eventsStatsMock).toHaveBeenCalledWith(
'/organizations/org-slug/events-stats/',
@@ -36,6 +36,8 @@ describe('latencyChart', () => {
'count_op(queue.publish)',
'count_op(queue.process)',
],
+ query:
+ 'span.op:[queue.process,queue.publish] messaging.destination.name:events',
}),
})
);
diff --git a/static/app/views/performance/queues/charts/latencyChart.tsx b/static/app/views/performance/queues/charts/latencyChart.tsx
index edc9f61692251..0e10d7b39de6f 100644
--- a/static/app/views/performance/queues/charts/latencyChart.tsx
+++ b/static/app/views/performance/queues/charts/latencyChart.tsx
@@ -1,19 +1,16 @@
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
import {t} from 'sentry/locale';
-import {decodeScalar} from 'sentry/utils/queryString';
-import {useLocation} from 'sentry/utils/useLocation';
import {CHART_HEIGHT} from 'sentry/views/performance/database/settings';
import {useQueuesTimeSeriesQuery} from 'sentry/views/performance/queues/queries/useQueuesTimeSeriesQuery';
import Chart, {ChartType} from 'sentry/views/starfish/components/chart';
import ChartPanel from 'sentry/views/starfish/components/chartPanel';
interface Props {
+ destination?: string;
error?: Error | null;
}
-export function LatencyChart({error}: Props) {
- const {query} = useLocation();
- const destination = decodeScalar(query.destination);
+export function LatencyChart({error, destination}: Props) {
const {data, isLoading} = useQueuesTimeSeriesQuery({destination});
return (
diff --git a/static/app/views/performance/queues/charts/throughputChart.spec.tsx b/static/app/views/performance/queues/charts/throughputChart.spec.tsx
index 9999329330b16..bf57530c5798c 100644
--- a/static/app/views/performance/queues/charts/throughputChart.spec.tsx
+++ b/static/app/views/performance/queues/charts/throughputChart.spec.tsx
@@ -36,6 +36,7 @@ describe('throughputChart', () => {
'count_op(queue.publish)',
'count_op(queue.process)',
],
+ query: 'span.op:[queue.process,queue.publish]',
}),
})
);
diff --git a/static/app/views/performance/queues/charts/throughputChart.tsx b/static/app/views/performance/queues/charts/throughputChart.tsx
index 06fb151da75f5..16af38434197a 100644
--- a/static/app/views/performance/queues/charts/throughputChart.tsx
+++ b/static/app/views/performance/queues/charts/throughputChart.tsx
@@ -1,19 +1,16 @@
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
import {t} from 'sentry/locale';
-import {decodeScalar} from 'sentry/utils/queryString';
-import {useLocation} from 'sentry/utils/useLocation';
import {CHART_HEIGHT} from 'sentry/views/performance/database/settings';
import {useQueuesTimeSeriesQuery} from 'sentry/views/performance/queues/queries/useQueuesTimeSeriesQuery';
import Chart, {ChartType} from 'sentry/views/starfish/components/chart';
import ChartPanel from 'sentry/views/starfish/components/chartPanel';
interface Props {
+ destination?: string;
error?: Error | null;
}
-export function ThroughputChart({error}: Props) {
- const {query} = useLocation();
- const destination = decodeScalar(query.destination);
+export function ThroughputChart({error, destination}: Props) {
const {data, isLoading} = useQueuesTimeSeriesQuery({destination});
return (
diff --git a/static/app/views/performance/queues/destinationSummary/destinationSummaryPage.tsx b/static/app/views/performance/queues/destinationSummary/destinationSummaryPage.tsx
index ff50f1c313b3c..6c26d9d5cf764 100644
--- a/static/app/views/performance/queues/destinationSummary/destinationSummaryPage.tsx
+++ b/static/app/views/performance/queues/destinationSummary/destinationSummaryPage.tsx
@@ -138,11 +138,11 @@ function DestinationSummaryPage() {
{!onboardingProject && (
-
+
-
+
diff --git a/static/app/views/performance/queues/queries/useQueuesByDestinationQuery.tsx b/static/app/views/performance/queues/queries/useQueuesByDestinationQuery.tsx
index c7933f437920f..554bdb3faf464 100644
--- a/static/app/views/performance/queues/queries/useQueuesByDestinationQuery.tsx
+++ b/static/app/views/performance/queues/queries/useQueuesByDestinationQuery.tsx
@@ -1,3 +1,4 @@
+import type {Sort} from 'sentry/utils/discover/fields';
import {decodeScalar} from 'sentry/utils/queryString';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import {useLocation} from 'sentry/utils/useLocation';
@@ -6,14 +7,19 @@ import {useSpanMetrics} from 'sentry/views/starfish/queries/useDiscover';
import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
type Props = {
+ destination?: string;
enabled?: boolean;
+ sort?: Sort;
};
-export function useQueuesByDestinationQuery({enabled}: Props) {
+export function useQueuesByDestinationQuery({enabled, destination, sort}: Props) {
const location = useLocation();
const cursor = decodeScalar(location.query?.[QueryParameterNames.DESTINATIONS_CURSOR]);
const mutableSearch = new MutableSearch(DEFAULT_QUERY_FILTER);
+ if (destination) {
+ mutableSearch.addFilterValue('messaging.destination.name', destination, false);
+ }
const response = useSpanMetrics(
{
search: mutableSearch,
@@ -27,9 +33,10 @@ export function useQueuesByDestinationQuery({enabled}: Props) {
'avg_if(span.duration,span.op,queue.publish)',
'avg_if(span.duration,span.op,queue.process)',
'avg(messaging.message.receive.latency)',
+ 'time_spent_percentage()',
],
enabled,
- sorts: [],
+ sorts: sort ? [sort] : [],
limit: 10,
cursor,
},
diff --git a/static/app/views/performance/queues/queries/useQueuesTimeSeriesQuery.tsx b/static/app/views/performance/queues/queries/useQueuesTimeSeriesQuery.tsx
index 7f014af69ae4c..33ab75ef73efc 100644
--- a/static/app/views/performance/queues/queries/useQueuesTimeSeriesQuery.tsx
+++ b/static/app/views/performance/queues/queries/useQueuesTimeSeriesQuery.tsx
@@ -1,4 +1,5 @@
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
+import {DEFAULT_QUERY_FILTER} from 'sentry/views/performance/queues/settings';
import {useSpanMetricsSeries} from 'sentry/views/starfish/queries/useDiscoverSeries';
import type {SpanMetricsProperty} from 'sentry/views/starfish/types';
@@ -16,14 +17,15 @@ const yAxis: SpanMetricsProperty[] = [
];
export function useQueuesTimeSeriesQuery({enabled, destination}: Props) {
+ const mutableSearch = new MutableSearch(DEFAULT_QUERY_FILTER);
+ if (destination) {
+ mutableSearch.addFilterValue('messaging.destination.name', destination, false);
+ }
+
return useSpanMetricsSeries(
{
yAxis,
- search: destination
- ? MutableSearch.fromQueryObject({
- 'messaging.destination.name': destination,
- })
- : undefined,
+ search: mutableSearch,
enabled,
},
'api.performance.queues.module-chart'
diff --git a/static/app/views/performance/queues/queuesLandingPage.spec.tsx b/static/app/views/performance/queues/queuesLandingPage.spec.tsx
index 00ebb21a0b0a5..69c4bba0ea27b 100644
--- a/static/app/views/performance/queues/queuesLandingPage.spec.tsx
+++ b/static/app/views/performance/queues/queuesLandingPage.spec.tsx
@@ -75,7 +75,7 @@ describe('queuesLandingPage', () => {
render();
await screen.findByRole('table', {name: 'Queues'});
await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
- screen.getByPlaceholderText('Search for events, users, tags, and more');
+ screen.getByPlaceholderText('Search for more destinations');
screen.getByText('Avg Latency');
screen.getByText('Published vs Processed');
expect(eventsStatsMock).toHaveBeenCalled();
diff --git a/static/app/views/performance/queues/queuesLandingPage.tsx b/static/app/views/performance/queues/queuesLandingPage.tsx
index 66060f80187d1..90cbf3028c563 100644
--- a/static/app/views/performance/queues/queuesLandingPage.tsx
+++ b/static/app/views/performance/queues/queuesLandingPage.tsx
@@ -10,9 +10,14 @@ import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
-import SmartSearchBar from 'sentry/components/smartSearchBar';
+import SearchBar from 'sentry/components/searchBar';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
+import {browserHistory} from 'sentry/utils/browserHistory';
+import {decodeScalar, decodeSorts} from 'sentry/utils/queryString';
+import {escapeFilterValue} from 'sentry/utils/tokenizeSearch';
+import useLocationQuery from 'sentry/utils/url/useLocationQuery';
+import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {normalizeUrl} from 'sentry/utils/withDomainRequired';
import {useOnboardingProject} from 'sentry/views/performance/browser/webVitals/utils/useOnboardingProject';
@@ -21,12 +26,47 @@ import {ModulePageProviders} from 'sentry/views/performance/modulePageProviders'
import Onboarding from 'sentry/views/performance/onboarding';
import {LatencyChart} from 'sentry/views/performance/queues/charts/latencyChart';
import {ThroughputChart} from 'sentry/views/performance/queues/charts/throughputChart';
-import {QueuesTable} from 'sentry/views/performance/queues/queuesTable';
+import {isAValidSort, QueuesTable} from 'sentry/views/performance/queues/queuesTable';
import {MODULE_TITLE, RELEASE_LEVEL} from 'sentry/views/performance/queues/settings';
+import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
+
+const DEFAULT_SORT = {
+ field: 'time_spent_percentage()' as const,
+ kind: 'desc' as const,
+};
function QueuesLandingPage() {
const organization = useOrganization();
const onboardingProject = useOnboardingProject();
+ const location = useLocation();
+
+ const query = useLocationQuery({
+ fields: {
+ destination: decodeScalar,
+ [QueryParameterNames.DOMAINS_SORT]: decodeScalar,
+ },
+ });
+
+ const sort =
+ decodeSorts(query[QueryParameterNames.DOMAINS_SORT]).filter(isAValidSort).at(0) ??
+ DEFAULT_SORT;
+
+ const handleSearch = (newDestination: string) => {
+ browserHistory.push({
+ ...location,
+ query: {
+ ...location.query,
+ destination: newDestination === '' ? undefined : newDestination,
+ [QueryParameterNames.DESTINATIONS_CURSOR]: undefined,
+ },
+ });
+ };
+
+ // The QueuesTable component queries using the destination prop.
+ // We wrap the user input in wildcards to allow for partial matches.
+ const wildCardDestinationFilter = query.destination
+ ? `*${escapeFilterValue(query.destination)}*`
+ : undefined;
return (
@@ -86,9 +126,12 @@ function QueuesLandingPage() {
- {/* TODO: Make search bar work */}
-
-
+
+
diff --git a/static/app/views/performance/queues/queuesTable.spec.tsx b/static/app/views/performance/queues/queuesTable.spec.tsx
index bc2062b5d08a6..b52815339d0c0 100644
--- a/static/app/views/performance/queues/queuesTable.spec.tsx
+++ b/static/app/views/performance/queues/queuesTable.spec.tsx
@@ -79,6 +79,7 @@ describe('queuesTable', () => {
'avg_if(span.duration,span.op,queue.publish)',
'avg_if(span.duration,span.op,queue.process)',
'avg(messaging.message.receive.latency)',
+ 'time_spent_percentage()',
],
dataset: 'spansMetrics',
}),
@@ -91,4 +92,36 @@ describe('queuesTable', () => {
expect(screen.getByRole('cell', {name: '20.00ms'})).toBeInTheDocument();
expect(screen.getByRole('button', {name: 'Next'})).toBeInTheDocument();
});
+ it('searches for a destination and sorts', async () => {
+ render(
+
+ );
+ expect(eventsMock).toHaveBeenCalledWith(
+ '/organizations/org-slug/events/',
+ expect.objectContaining({
+ query: expect.objectContaining({
+ field: [
+ 'messaging.destination.name',
+ 'count()',
+ 'count_op(queue.publish)',
+ 'count_op(queue.process)',
+ 'sum(span.duration)',
+ 'avg(span.duration)',
+ 'avg_if(span.duration,span.op,queue.publish)',
+ 'avg_if(span.duration,span.op,queue.process)',
+ 'avg(messaging.message.receive.latency)',
+ 'time_spent_percentage()',
+ ],
+ dataset: 'spansMetrics',
+ sort: '-messaging.destination.name',
+ query:
+ 'span.op:[queue.process,queue.publish] messaging.destination.name:*events*',
+ }),
+ })
+ );
+ await screen.findByText('celery.backend_cleanup');
+ });
});
diff --git a/static/app/views/performance/queues/queuesTable.tsx b/static/app/views/performance/queues/queuesTable.tsx
index 8cf64dd6f3694..d633da5a629d9 100644
--- a/static/app/views/performance/queues/queuesTable.tsx
+++ b/static/app/views/performance/queues/queuesTable.tsx
@@ -15,6 +15,7 @@ import type {Organization} from 'sentry/types';
import {browserHistory} from 'sentry/utils/browserHistory';
import type {EventsMetaType} from 'sentry/utils/discover/eventView';
import {FIELD_FORMATTERS, getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
+import type {Sort} from 'sentry/utils/discover/fields';
import {formatAbbreviatedNumber, formatPercentage} from 'sentry/utils/formatters';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
@@ -73,17 +74,34 @@ const COLUMN_ORDER: Column[] = [
},
];
+const SORTABLE_FIELDS = [
+ 'messaging.destination.name',
+ 'time_spent_percentage()',
+] as const;
+
+type ValidSort = Sort & {
+ field: (typeof SORTABLE_FIELDS)[number];
+};
+
+export function isAValidSort(sort: Sort): sort is ValidSort {
+ return (SORTABLE_FIELDS as ReadonlyArray).includes(sort.field);
+}
+
interface Props {
- domain?: string;
+ destination?: string;
error?: Error | null;
meta?: EventsMetaType;
+ sort?: ValidSort;
}
-export function QueuesTable({error}: Props) {
+export function QueuesTable({error, destination, sort}: Props) {
const location = useLocation();
const organization = useOrganization();
- const {data, isLoading, meta, pageLinks} = useQueuesByDestinationQuery({});
+ const {data, isLoading, meta, pageLinks} = useQueuesByDestinationQuery({
+ destination,
+ sort,
+ });
const handleCursor: CursorHandler = (newCursor, pathname, query) => {
browserHistory.push({
diff --git a/static/app/views/starfish/views/queryParameters.tsx b/static/app/views/starfish/views/queryParameters.tsx
index fd4784bd28a0f..e6eb27974872b 100644
--- a/static/app/views/starfish/views/queryParameters.tsx
+++ b/static/app/views/starfish/views/queryParameters.tsx
@@ -9,4 +9,5 @@ export enum QueryParameterNames {
ENDPOINTS_SORT = 'endpointsSort',
PAGES_CURSOR = 'pagesCursor',
DESTINATIONS_CURSOR = 'destinationsCursor',
+ DESTINATIONS_SORT = 'destinationsSort',
}