From 9f5c3c1c0ac8aecc3fdda8c9bc39251235f93214 Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Wed, 17 Feb 2021 17:05:55 -0800
Subject: [PATCH 1/9] do not load all the segments
---
web-console/src/singletons/api.ts | 2 +-
.../src/views/segments-view/segments-view.tsx | 401 ++++++++++--------
2 files changed, 219 insertions(+), 184 deletions(-)
diff --git a/web-console/src/singletons/api.ts b/web-console/src/singletons/api.ts
index 7a05bdd398c8..e14a13b35342 100644
--- a/web-console/src/singletons/api.ts
+++ b/web-console/src/singletons/api.ts
@@ -47,7 +47,7 @@ export class Api {
static encodePath(path: string): string {
return path.replace(
- /[?#%&'\[\]]/g,
+ /[?#%&'\[\]\\]/g,
c =>
'%' +
c
diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx
index 0df4da6cadc7..8c5b71825463 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -19,7 +19,6 @@
import { Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { SqlExpression, SqlRef } from 'druid-query-toolkit';
-import * as JSONBig from 'json-bigint-native';
import React from 'react';
import ReactTable, { Filter } from 'react-table';
@@ -39,6 +38,7 @@ import { SegmentTableActionDialog } from '../../dialogs/segments-table-action-di
import { Api } from '../../singletons';
import {
addFilter,
+ booleanCustomTableFilter,
compact,
deepGet,
filterMap,
@@ -124,6 +124,7 @@ interface TableState {
}
interface SegmentsQuery extends TableState {
+ capabilities: Capabilities;
groupByInterval: boolean;
}
@@ -131,6 +132,7 @@ interface SegmentQueryResultRow {
datasource: string;
start: string;
end: string;
+ interval: string;
segment_id: string;
version: string;
time_span: string;
@@ -186,8 +188,31 @@ export class SegmentsView extends React.PureComponent;
- private segmentsNoSqlQueryManager: QueryManager;
+ static computeTimeSpan(start: string, end: string): string {
+ if (start.endsWith('-01-01T00:00:00.000Z') && end.endsWith('-01-01T00:00:00.000Z')) {
+ return 'Year';
+ }
+
+ if (start.endsWith('-01T00:00:00.000Z') && end.endsWith('-01T00:00:00.000Z')) {
+ return 'Month';
+ }
+
+ if (start.endsWith('T00:00:00.000Z') && end.endsWith('T00:00:00.000Z')) {
+ return 'Day';
+ }
+
+ if (start.endsWith(':00:00.000Z') && end.endsWith(':00:00.000Z')) {
+ return 'Hour';
+ }
+
+ if (start.endsWith(':00.000Z') && end.endsWith(':00.000Z')) {
+ return 'Minute';
+ }
+
+ return 'Sub minute';
+ }
+
+ private segmentsQueryManager: QueryManager;
private lastTableState: TableState | undefined;
@@ -208,196 +233,210 @@ export class SegmentsView extends React.PureComponent {
- const whereParts = filterMap(query.filtered, (f: Filter) => {
- if (f.id.startsWith('is_')) {
- if (f.value === 'all') return;
- return SqlRef.columnWithQuotes(f.id).equal(f.value === 'true' ? 1 : 0);
- } else {
- return sqlQueryCustomTableFilter(f);
- }
- });
+ const { page, pageSize, filtered, sorted, capabilities, groupByInterval } = query;
- let queryParts: string[];
+ if (capabilities.hasSql()) {
+ const whereParts = filterMap(filtered, (f: Filter) => {
+ if (f.id.startsWith('is_')) {
+ if (f.value === 'all') return;
+ return SqlRef.columnWithQuotes(f.id).equal(f.value === 'true' ? 1 : 0);
+ } else {
+ return sqlQueryCustomTableFilter(f);
+ }
+ });
- let whereClause = '';
- if (whereParts.length) {
- whereClause = SqlExpression.and(...whereParts).toString();
- }
+ let queryParts: string[];
- if (query.groupByInterval) {
- const innerQuery = compact([
- `SELECT "start" || '/' || "end" AS "interval"`,
- `FROM sys.segments`,
- whereClause ? `WHERE ${whereClause}` : undefined,
- `GROUP BY 1`,
- `ORDER BY 1 DESC`,
- `LIMIT ${query.pageSize}`,
- query.page ? `OFFSET ${query.page * query.pageSize}` : undefined,
- ]).join('\n');
-
- const intervals: string = (await queryDruidSql({ query: innerQuery }))
- .map(row => `'${row.interval}'`)
- .join(', ');
-
- queryParts = compact([
- SegmentsView.WITH_QUERY,
- `SELECT "start" || '/' || "end" AS "interval", *`,
- `FROM s`,
- `WHERE`,
- intervals ? ` ("start" || '/' || "end") IN (${intervals})` : 'FALSE',
- whereClause ? ` AND ${whereClause}` : '',
- ]);
-
- if (query.sorted.length) {
- queryParts.push(
- 'ORDER BY ' +
- query.sorted
- .map((sort: any) => `${JSONBig.stringify(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
- .join(', '),
- );
+ let whereClause = '';
+ if (whereParts.length) {
+ whereClause = SqlExpression.and(...whereParts).toString();
}
- queryParts.push(`LIMIT ${query.pageSize * 1000}`);
- } else {
- queryParts = [SegmentsView.WITH_QUERY, `SELECT *`, `FROM s`];
+ if (groupByInterval) {
+ const innerQuery = compact([
+ `SELECT "start" || '/' || "end" AS "interval"`,
+ `FROM sys.segments`,
+ whereClause ? `WHERE ${whereClause}` : undefined,
+ `GROUP BY 1`,
+ `ORDER BY 1 DESC`,
+ `LIMIT ${pageSize}`,
+ page ? `OFFSET ${page * pageSize}` : undefined,
+ ]).join('\n');
+
+ const intervals: string = (await queryDruidSql({ query: innerQuery }))
+ .map(row => `'${row.interval}'`)
+ .join(', ');
+
+ queryParts = compact([
+ SegmentsView.WITH_QUERY,
+ `SELECT "start" || '/' || "end" AS "interval", *`,
+ `FROM s`,
+ `WHERE`,
+ intervals ? ` ("start" || '/' || "end") IN (${intervals})` : 'FALSE',
+ whereClause ? ` AND ${whereClause}` : '',
+ ]);
+
+ if (sorted.length) {
+ queryParts.push(
+ 'ORDER BY ' +
+ sorted
+ .map((sort: any) => `${SqlRef.column(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
+ .join(', '),
+ );
+ }
- if (whereClause) {
- queryParts.push(`WHERE ${whereClause}`);
- }
+ queryParts.push(`LIMIT ${pageSize * 1000}`);
+ } else {
+ queryParts = [SegmentsView.WITH_QUERY, `SELECT *`, `FROM s`];
+
+ if (whereClause) {
+ queryParts.push(`WHERE ${whereClause}`);
+ }
+
+ if (sorted.length) {
+ queryParts.push(
+ 'ORDER BY ' +
+ sorted
+ .map((sort: any) => `${SqlRef.column(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
+ .join(', '),
+ );
+ }
+
+ queryParts.push(`LIMIT ${pageSize}`);
- if (query.sorted.length) {
- queryParts.push(
- 'ORDER BY ' +
- query.sorted
- .map((sort: any) => `${JSONBig.stringify(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
- .join(', '),
+ if (page) {
+ queryParts.push(`OFFSET ${page * pageSize}`);
+ }
+ }
+ const sqlQuery = queryParts.join('\n');
+ setIntermediateQuery(sqlQuery);
+ return await queryDruidSql({ query: sqlQuery });
+ } else if (capabilities.hasCoordinatorAccess()) {
+ let datasourceList: string[] = (await Api.instance.get(
+ '/druid/coordinator/v1/metadata/datasources',
+ )).data;
+
+ const datasourceFilter = filtered.find(({ id }) => id === 'datasource');
+ // let restFiltered: Filter[];
+ if (datasourceFilter) {
+ // restFiltered = filtered.filter(({ id }) => id !== 'datasource');
+ datasourceList = datasourceList.filter(datasource =>
+ booleanCustomTableFilter(datasourceFilter, datasource),
);
+ } else {
+ // restFiltered = filtered;
}
- queryParts.push(`LIMIT ${query.pageSize}`);
-
- if (query.page) {
- queryParts.push(`OFFSET ${query.page * query.pageSize}`);
+ if (sorted.length && sorted[0].id === 'datasource') {
+ datasourceList.sort(
+ sorted[0].desc ? (d1, d2) => d1.localeCompare(d2) : (d1, d2) => d2.localeCompare(d1),
+ );
}
- }
- const sqlQuery = queryParts.join('\n');
- setIntermediateQuery(sqlQuery);
- return await queryDruidSql({ query: sqlQuery });
- },
- onStateChange: segmentsState => {
- this.setState({
- segmentsState,
- });
- },
- });
- this.segmentsNoSqlQueryManager = new QueryManager({
- processQuery: async () => {
- const datasourceList = (await Api.instance.get(
- '/druid/coordinator/v1/metadata/datasources',
- )).data;
- const nestedResults: SegmentQueryResultRow[][] = await Promise.all(
- datasourceList.map(async (d: string) => {
+ const maxResults = (page + 1) * pageSize;
+ let results: SegmentQueryResultRow[] = [];
+
+ const n = Math.min(datasourceList.length, maxResults);
+ for (let i = 0; i < n && results.length < maxResults; i++) {
const segments = (await Api.instance.get(
- `/druid/coordinator/v1/datasources/${Api.encodePath(d)}?full`,
+ `/druid/coordinator/v1/datasources/${Api.encodePath(datasourceList[i])}?full`,
)).data.segments;
- return segments.map(
- (segment: any): SegmentQueryResultRow => {
- return {
- segment_id: segment.identifier,
- datasource: segment.dataSource,
- start: segment.interval.split('/')[0],
- end: segment.interval.split('/')[1],
- version: segment.version,
- time_span: '-',
- partitioning: '-',
- partition_num: deepGet(segment, 'shardSpec.partitionNum') || 0,
- size: segment.size,
- num_rows: -1,
- num_replicas: -1,
- is_available: -1,
- is_published: -1,
- is_realtime: -1,
- is_overshadowed: -1,
- };
- },
- );
- }),
- );
+ if (segments) {
+ results = results.concat(
+ segments
+ .map(
+ (segment: any): SegmentQueryResultRow => {
+ const [start, end] = segment.interval.split('/');
+ return {
+ segment_id: segment.identifier,
+ datasource: segment.dataSource,
+ start,
+ end,
+ interval: segment.interval,
+ version: segment.version,
+ time_span: SegmentsView.computeTimeSpan(start, end),
+ partitioning: deepGet(segment, 'shardSpec.type') || '-',
+ partition_num: deepGet(segment, 'shardSpec.partitionNum') || 0,
+ size: segment.size,
+ num_rows: -1,
+ num_replicas: -1,
+ is_available: -1,
+ is_published: -1,
+ is_realtime: -1,
+ is_overshadowed: -1,
+ };
+ },
+ )
+ .filter(Boolean),
+ );
+ }
+ }
- return nestedResults.flat().sort((d1, d2) => {
- return d2.start.localeCompare(d1.start);
- });
+ return results.slice(page * pageSize, maxResults);
+ } else {
+ throw new Error('must have SQL or coordinator access to load this view');
+ }
},
onStateChange: segmentsState => {
this.setState({
- trimmedSegments: segmentsState.data
- ? segmentsState.data.slice(0, SegmentsView.PAGE_SIZE)
- : undefined,
segmentsState,
});
},
});
}
- componentDidMount(): void {
- const { capabilities } = this.props;
- if (!capabilities.hasSql() && capabilities.hasCoordinatorAccess()) {
- this.segmentsNoSqlQueryManager.runQuery(null);
- }
- }
-
componentWillUnmount(): void {
- this.segmentsSqlQueryManager.terminate();
- this.segmentsNoSqlQueryManager.terminate();
+ this.segmentsQueryManager.terminate();
}
private fetchData = (groupByInterval: boolean, tableState?: TableState) => {
+ const { capabilities } = this.props;
if (tableState) this.lastTableState = tableState;
const { page, pageSize, filtered, sorted } = this.lastTableState!;
- this.segmentsSqlQueryManager.runQuery({
+ this.segmentsQueryManager.runQuery({
page,
pageSize,
filtered,
sorted,
- groupByInterval: groupByInterval,
+ capabilities,
+ groupByInterval,
});
};
- private fetchClientSideData = (tableState?: TableState) => {
- if (tableState) this.lastTableState = tableState;
- const { page, pageSize, filtered, sorted } = this.lastTableState!;
-
- this.setState(state => {
- const allSegments = state.segmentsState.data;
- if (!allSegments) return {};
- const sortKey = sorted[0].id as keyof SegmentQueryResultRow;
- const sortDesc = sorted[0].desc;
-
- return {
- trimmedSegments: allSegments
- .filter(d => {
- return filtered.every((f: any) => {
- return String(d[f.id as keyof SegmentQueryResultRow]).includes(f.value);
- });
- })
- .sort((d1, d2) => {
- const v1 = d1[sortKey] as any;
- const v2 = d2[sortKey] as any;
- if (typeof v1 === 'string') {
- return sortDesc ? v2.localeCompare(v1) : v1.localeCompare(v2);
- } else {
- return sortDesc ? v2 - v1 : v1 - v2;
- }
- })
- .slice(page * pageSize, (page + 1) * pageSize),
- };
- });
- };
+ // private fetchClientSideData = (tableState?: TableState) => {
+ // if (tableState) this.lastTableState = tableState;
+ // const { page, pageSize, filtered, sorted } = this.lastTableState!;
+ //
+ // this.setState(state => {
+ // const allSegments = state.segmentsState.data;
+ // if (!allSegments) return {};
+ // const sortKey = sorted[0].id as keyof SegmentQueryResultRow;
+ // const sortDesc = sorted[0].desc;
+ //
+ // return {
+ // trimmedSegments: allSegments
+ // .filter(d => {
+ // return filtered.every((f: any) => {
+ // return String(d[f.id as keyof SegmentQueryResultRow]).includes(f.value);
+ // });
+ // })
+ // .sort((d1, d2) => {
+ // const v1 = d1[sortKey] as any;
+ // const v2 = d2[sortKey] as any;
+ // if (typeof v1 === 'string') {
+ // return sortDesc ? v2.localeCompare(v1) : v1.localeCompare(v2);
+ // } else {
+ // return sortDesc ? v2 - v1 : v1 - v2;
+ // }
+ // })
+ // .slice(page * pageSize, (page + 1) * pageSize),
+ // };
+ // });
+ // };
private getSegmentActions(id: string, datasource: string): BasicAction[] {
const actions: BasicAction[] = [];
@@ -443,6 +482,7 @@ export class SegmentsView extends React.PureComponent {
this.setState({ segmentFilter: filtered });
}}
onFetchData={tableState => {
- if (capabilities.hasSql()) {
- this.fetchData(groupByInterval, tableState);
- } else if (capabilities.hasCoordinatorAccess()) {
- this.fetchClientSideData(tableState);
- }
+ this.fetchData(groupByInterval, tableState);
}}
showPageJump={false}
ofText=""
@@ -472,6 +508,7 @@ export class SegmentsView extends React.PureComponent (
String(Boolean(row.is_published)),
Filter: makeBooleanFilter(),
},
{
Header: 'Is realtime',
- show: capabilities.hasSql() && hiddenColumns.exists('Is realtime'),
+ show: hasSql && hiddenColumns.exists('Is realtime'),
id: 'is_realtime',
accessor: row => String(Boolean(row.is_realtime)),
Filter: makeBooleanFilter(),
},
{
Header: 'Is available',
- show: capabilities.hasSql() && hiddenColumns.exists('Is available'),
+ show: hasSql && hiddenColumns.exists('Is available'),
id: 'is_available',
accessor: row => String(Boolean(row.is_available)),
Filter: makeBooleanFilter(),
},
{
Header: 'Is overshadowed',
- show: capabilities.hasSql() && hiddenColumns.exists('Is overshadowed'),
+ show: hasSql && hiddenColumns.exists('Is overshadowed'),
id: 'is_overshadowed',
accessor: row => String(Boolean(row.is_overshadowed)),
Filter: makeBooleanFilter(),
@@ -654,8 +698,7 @@ export class SegmentsView extends React.PureComponent {
- this.segmentsNoSqlQueryManager.rerunLastQuery();
- this.segmentsSqlQueryManager.rerunLastQuery();
+ this.segmentsQueryManager.rerunLastQuery();
}}
>
{`Are you sure you want to drop segment '${terminateSegmentId}'?`}
@@ -666,7 +709,7 @@ export class SegmentsView extends React.PureComponent
@@ -700,11 +743,7 @@ export class SegmentsView extends React.PureComponent
- capabilities.hasSql()
- ? this.segmentsSqlQueryManager.rerunLastQuery(auto)
- : this.segmentsNoSqlQueryManager.rerunLastQuery(auto)
- }
+ onRefresh={auto => this.segmentsQueryManager.rerunLastQuery(auto)}
localStorageKey={LocalStorageKeys.SEGMENTS_REFRESH_RATE}
/>
@@ -713,11 +752,7 @@ export class SegmentsView extends React.PureComponent {
this.setState({ groupByInterval: false });
- if (capabilities.hasSql()) {
- this.fetchData(false);
- } else {
- this.fetchClientSideData();
- }
+ this.fetchData(false);
}}
>
None
From be38d1ff68594a3b9d103448d22af7b62135e2de Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Thu, 18 Feb 2021 21:17:40 -0800
Subject: [PATCH 2/9] fix filtering
---
.../src/components/header-bar/header-bar.tsx | 43 ++++++-
web-console/src/utils/general.tsx | 9 +-
web-console/src/utils/local-storage-keys.tsx | 5 +
.../src/views/segments-view/segments-view.tsx | 116 ++++++++----------
4 files changed, 103 insertions(+), 70 deletions(-)
diff --git a/web-console/src/components/header-bar/header-bar.tsx b/web-console/src/components/header-bar/header-bar.tsx
index 76bb378fe781..f28810523a71 100644
--- a/web-console/src/components/header-bar/header-bar.tsx
+++ b/web-console/src/components/header-bar/header-bar.tsx
@@ -22,6 +22,7 @@ import {
Button,
Intent,
Menu,
+ MenuDivider,
MenuItem,
Navbar,
NavbarDivider,
@@ -39,12 +40,20 @@ import {
OverlordDynamicConfigDialog,
} from '../../dialogs';
import { getLink } from '../../links';
-import { Capabilities } from '../../utils';
+import {
+ Capabilities,
+ localStorageGetJson,
+ LocalStorageKeys,
+ localStorageRemove,
+ localStorageSetJson,
+} from '../../utils';
import { ExternalLink } from '../external-link/external-link';
import { PopoverText } from '../popover-text/popover-text';
import './header-bar.scss';
+const capabilitiesOverride = localStorageGetJson(LocalStorageKeys.CAPABILITIES_OVERRIDE);
+
export type HeaderActiveTab =
| null
| 'load-data'
@@ -243,6 +252,38 @@ export const HeaderBar = React.memo(function HeaderBar(props: HeaderBarProps) {
href="#lookups"
disabled={!capabilities.hasCoordinatorAccess()}
/>
+
+
diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx
index 70b092f3b541..3835450f7339 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -125,6 +125,7 @@ interface TableState {
}
interface SegmentsQuery extends TableState {
+ hiddenColumns: LocalStorageBackedArray;
capabilities: Capabilities;
groupByInterval: boolean;
}
@@ -164,30 +165,48 @@ export interface SegmentsViewState {
export class SegmentsView extends React.PureComponent {
static PAGE_SIZE = 25;
- static WITH_QUERY = `WITH s AS (
- SELECT
- "segment_id", "datasource", "start", "end", "size", "version",
- CASE
- WHEN "start" LIKE '%-01-01T00:00:00.000Z' AND "end" LIKE '%-01-01T00:00:00.000Z' THEN 'Year'
- WHEN "start" LIKE '%-01T00:00:00.000Z' AND "end" LIKE '%-01T00:00:00.000Z' THEN 'Month'
- WHEN "start" LIKE '%T00:00:00.000Z' AND "end" LIKE '%T00:00:00.000Z' THEN 'Day'
- WHEN "start" LIKE '%:00:00.000Z' AND "end" LIKE '%:00:00.000Z' THEN 'Hour'
- WHEN "start" LIKE '%:00.000Z' AND "end" LIKE '%:00.000Z' THEN 'Minute'
- ELSE 'Sub minute'
- END AS "time_span",
- CASE
- WHEN "shard_spec" LIKE '%"type":"numbered"%' THEN 'dynamic'
- WHEN "shard_spec" LIKE '%"type":"hashed"%' THEN 'hashed'
- WHEN "shard_spec" LIKE '%"type":"single"%' THEN 'single_dim'
- WHEN "shard_spec" LIKE '%"type":"none"%' THEN 'none'
- WHEN "shard_spec" LIKE '%"type":"linear"%' THEN 'linear'
- WHEN "shard_spec" LIKE '%"type":"numbered_overwrite"%' THEN 'numbered_overwrite'
- ELSE '-'
- END AS "partitioning",
- "partition_num", "num_replicas", "num_rows",
- "is_published", "is_available", "is_realtime", "is_overshadowed"
- FROM sys.segments
-)`;
+ static baseQuery(hiddenColumns: LocalStorageBackedArray) {
+ const columns = compact([
+ hiddenColumns.exists('Segment ID') && `"segment_id"`,
+ hiddenColumns.exists('Datasource') && `"datasource"`,
+ hiddenColumns.exists('Start') && `"start"`,
+ hiddenColumns.exists('End') && `"end"`,
+ hiddenColumns.exists('Version') && `"version"`,
+ hiddenColumns.exists('Time span') &&
+ `CASE
+ WHEN "start" LIKE '%-01-01T00:00:00.000Z' AND "end" LIKE '%-01-01T00:00:00.000Z' THEN 'Year'
+ WHEN "start" LIKE '%-01T00:00:00.000Z' AND "end" LIKE '%-01T00:00:00.000Z' THEN 'Month'
+ WHEN "start" LIKE '%T00:00:00.000Z' AND "end" LIKE '%T00:00:00.000Z' THEN 'Day'
+ WHEN "start" LIKE '%:00:00.000Z' AND "end" LIKE '%:00:00.000Z' THEN 'Hour'
+ WHEN "start" LIKE '%:00.000Z' AND "end" LIKE '%:00.000Z' THEN 'Minute'
+ ELSE 'Sub minute'
+END AS "time_span"`,
+ hiddenColumns.exists('Partitioning') &&
+ `CASE
+ WHEN "shard_spec" LIKE '%"type":"numbered"%' THEN 'dynamic'
+ WHEN "shard_spec" LIKE '%"type":"hashed"%' THEN 'hashed'
+ WHEN "shard_spec" LIKE '%"type":"single"%' THEN 'single_dim'
+ WHEN "shard_spec" LIKE '%"type":"none"%' THEN 'none'
+ WHEN "shard_spec" LIKE '%"type":"linear"%' THEN 'linear'
+ WHEN "shard_spec" LIKE '%"type":"numbered_overwrite"%' THEN 'numbered_overwrite'
+ ELSE '-'
+END AS "partitioning"`,
+ hiddenColumns.exists('Partition') && `"partition_num"`,
+ hiddenColumns.exists('Size') && `"size"`,
+ hiddenColumns.exists('Num rows') && `"num_rows"`,
+ hiddenColumns.exists('Replicas') && `"num_replicas"`,
+ hiddenColumns.exists('Is published') && `"is_published"`,
+ hiddenColumns.exists('Is available') && `"is_available"`,
+ hiddenColumns.exists('Is realtime') && `"is_realtime"`,
+ hiddenColumns.exists('Is overshadowed') && `"is_overshadowed"`,
+ ]);
+
+ if (!columns.length) {
+ columns.push(`"segment_id"`);
+ }
+
+ return `WITH s AS (SELECT\n${columns.join(',\n')}\nFROM sys.segments)`;
+ }
static computeTimeSpan(start: string, end: string): string {
if (start.endsWith('-01-01T00:00:00.000Z') && end.endsWith('-01-01T00:00:00.000Z')) {
@@ -237,7 +256,15 @@ export class SegmentsView extends React.PureComponent {
- const { page, pageSize, filtered, sorted, capabilities, groupByInterval } = query;
+ const {
+ page,
+ pageSize,
+ filtered,
+ sorted,
+ hiddenColumns,
+ capabilities,
+ groupByInterval,
+ } = query;
if (capabilities.hasSql()) {
const whereParts = filterMap(filtered, (f: Filter) => {
@@ -272,7 +299,7 @@ export class SegmentsView extends React.PureComponent {
const { capabilities } = this.props;
+ const { hiddenColumns } = this.state;
if (tableState) this.lastTableState = tableState;
const { page, pageSize, filtered, sorted } = this.lastTableState!;
this.segmentsQueryManager.runQuery({
@@ -405,6 +433,7 @@ export class SegmentsView extends React.PureComponent {
+ if (!added) return;
+ this.fetchData(groupByInterval);
+ }}
tableColumnsHidden={hiddenColumns.storedArray}
/>
From a91040db934b2fb2dff2dde8641736a58586f119 Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Fri, 19 Feb 2021 11:14:09 -0800
Subject: [PATCH 4/9] updated tests
---
.../__snapshots__/header-bar.spec.tsx.snap | 28 ++++
.../datasource-view.spec.tsx.snap | 3 +-
.../views/datasource-view/datasource-view.tsx | 151 ++++++++++--------
.../__snapshots__/segments-view.spec.tsx.snap | 15 ++
4 files changed, 130 insertions(+), 67 deletions(-)
diff --git a/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap b/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
index 0aadb182e0a1..e19f7b602a6d 100644
--- a/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
+++ b/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
@@ -119,6 +119,34 @@ exports[`header bar matches snapshot 1`] = `
shouldDismissPopover={true}
text="Lookups"
/>
+
+
+
+
+
+
+
}
defaultIsOpen={false}
diff --git a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
index 947e1d2bc320..6a05886b158a 100755
--- a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
+++ b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
@@ -26,7 +26,7 @@ exports[`data source view matches snapshot 1`] = `
}
>
diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index a11700dd9c7a..0d73fd7c1c3d 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -249,41 +249,43 @@ export class DatasourcesView extends React.PureComponent<
static PARTIALLY_AVAILABLE_COLOR = '#ffbf00';
static query(hiddenColumns: LocalStorageBackedArray) {
- const columns = compact([
- hiddenColumns.exists('Datasource name') && `datasource`,
- hiddenColumns.exists('Availability') &&
- `COUNT(*) FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS num_segments`,
- hiddenColumns.exists('Availability') &&
- `COUNT(*) FILTER (WHERE is_available = 1 AND ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_available_segments`,
- hiddenColumns.exists('Segment load/drop queues') &&
- `COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND is_available = 0) AS num_segments_to_load`,
- hiddenColumns.exists('Segment load/drop queues') &&
- `COUNT(*) FILTER (WHERE is_available = 1 AND NOT ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_segments_to_drop`,
- hiddenColumns.exists('Total data size') &&
- `SUM("size") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS total_data_size`,
- hiddenColumns.exists('Segment size') &&
- `MIN("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS min_segment_rows`,
- hiddenColumns.exists('Segment size') &&
- `AVG("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS avg_segment_rows`,
- hiddenColumns.exists('Segment size') &&
- `MAX("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS max_segment_rows`,
- hiddenColumns.exists('Segment granularity') &&
- `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%:00.000Z' AND "end" LIKE '%:00.000Z') AS minute_aligned_segments`,
- hiddenColumns.exists('Segment granularity') &&
- `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%:00:00.000Z' AND "end" LIKE '%:00:00.000Z') AS hour_aligned_segments`,
- hiddenColumns.exists('Segment granularity') &&
- `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%T00:00:00.000Z' AND "end" LIKE '%T00:00:00.000Z') AS day_aligned_segments`,
- hiddenColumns.exists('Segment granularity') &&
- `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%-01T00:00:00.000Z' AND "end" LIKE '%-01T00:00:00.000Z') AS month_aligned_segments`,
- hiddenColumns.exists('Segment granularity') &&
- `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%-01-01T00:00:00.000Z' AND "end" LIKE '%-01-01T00:00:00.000Z') AS year_aligned_segments`,
- hiddenColumns.exists('Total rows') &&
- `SUM("num_rows") FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS total_rows`,
- hiddenColumns.exists('Avg. row size') &&
- `CASE WHEN SUM("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) <> 0 THEN (SUM("size") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) / SUM("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0)) ELSE 0 END AS avg_row_size`,
- hiddenColumns.exists('Replicated size') &&
- `SUM("size" * "num_replicas") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS replicated_size`,
- ]);
+ const columns = compact(
+ [
+ hiddenColumns.exists('Datasource name') && `datasource`,
+ (hiddenColumns.exists('Availability') || hiddenColumns.exists('Segment granularity')) &&
+ `COUNT(*) FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS num_segments`,
+ hiddenColumns.exists('Availability') &&
+ `COUNT(*) FILTER (WHERE is_available = 1 AND ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_available_segments`,
+ hiddenColumns.exists('Segment load/drop queues') &&
+ `COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND is_available = 0) AS num_segments_to_load`,
+ hiddenColumns.exists('Segment load/drop queues') &&
+ `COUNT(*) FILTER (WHERE is_available = 1 AND NOT ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_segments_to_drop`,
+ hiddenColumns.exists('Total data size') &&
+ `SUM("size") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS total_data_size`,
+ hiddenColumns.exists('Segment size') && [
+ `MIN("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS min_segment_rows`,
+ `AVG("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS avg_segment_rows`,
+ `MAX("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS max_segment_rows`,
+ ],
+ hiddenColumns.exists('Segment granularity') && [
+ `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%:00.000Z' AND "end" LIKE '%:00.000Z') AS minute_aligned_segments`,
+ `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%:00:00.000Z' AND "end" LIKE '%:00:00.000Z') AS hour_aligned_segments`,
+ `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%T00:00:00.000Z' AND "end" LIKE '%T00:00:00.000Z') AS day_aligned_segments`,
+ `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%-01T00:00:00.000Z' AND "end" LIKE '%-01T00:00:00.000Z') AS month_aligned_segments`,
+ `COUNT(*) FILTER (WHERE ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AND "start" LIKE '%-01-01T00:00:00.000Z' AND "end" LIKE '%-01-01T00:00:00.000Z') AS year_aligned_segments`,
+ ],
+ hiddenColumns.exists('Total rows') &&
+ `SUM("num_rows") FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS total_rows`,
+ hiddenColumns.exists('Avg. row size') &&
+ `CASE WHEN SUM("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) <> 0 THEN (SUM("size") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) / SUM("num_rows") FILTER (WHERE is_published = 1 AND is_overshadowed = 0)) ELSE 0 END AS avg_row_size`,
+ hiddenColumns.exists('Replicated size') &&
+ `SUM("size" * "num_replicas") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS replicated_size`,
+ ].flat(),
+ );
+
+ if (!columns.length) {
+ columns.push(`datasource`);
+ }
return `SELECT
${columns.join(',\n')}
@@ -1109,21 +1111,25 @@ ORDER BY 1`;
accessor: 'avg_segment_rows',
filterable: false,
width: 220,
- Cell: ({ value, original }) => (
- <>
- {' '}
- {' '}
- {' '}
- {' '}
-
- >
- ),
+ Cell: ({ value, original }) => {
+ const { min_segment_rows, max_segment_rows } = original;
+ if (isNaN(value) || isNaN(min_segment_rows) || isNaN(max_segment_rows)) return '-';
+ return (
+ <>
+ {' '}
+ {' '}
+ {' '}
+ {' '}
+
+ >
+ );
+ },
},
{
Header: twoLines('Segment', 'granularity'),
@@ -1133,24 +1139,32 @@ ORDER BY 1`;
filterable: false,
width: 100,
Cell: ({ original }) => {
+ const {
+ num_segments,
+ minute_aligned_segments,
+ hour_aligned_segments,
+ day_aligned_segments,
+ month_aligned_segments,
+ year_aligned_segments,
+ } = original;
const segmentGranularities: string[] = [];
- if (!original.num_segments) return '-';
- if (original.num_segments - original.minute_aligned_segments) {
+ if (!num_segments || isNaN(year_aligned_segments)) return '-';
+ if (num_segments - minute_aligned_segments) {
segmentGranularities.push('Sub minute');
}
- if (original.minute_aligned_segments - original.hour_aligned_segments) {
+ if (minute_aligned_segments - hour_aligned_segments) {
segmentGranularities.push('Minute');
}
- if (original.hour_aligned_segments - original.day_aligned_segments) {
+ if (hour_aligned_segments - day_aligned_segments) {
segmentGranularities.push('Hour');
}
- if (original.day_aligned_segments - original.month_aligned_segments) {
+ if (day_aligned_segments - month_aligned_segments) {
segmentGranularities.push('Day');
}
- if (original.month_aligned_segments - original.year_aligned_segments) {
+ if (month_aligned_segments - year_aligned_segments) {
segmentGranularities.push('Month');
}
- if (original.year_aligned_segments) {
+ if (year_aligned_segments) {
segmentGranularities.push('Year');
}
return segmentGranularities.join(', ');
@@ -1162,9 +1176,10 @@ ORDER BY 1`;
accessor: 'total_rows',
filterable: false,
width: 100,
- Cell: ({ value }) => (
-
- ),
+ Cell: ({ value }) => {
+ if (isNaN(value)) return '-';
+ return ;
+ },
},
{
Header: twoLines('Avg. row size', '(bytes)'),
@@ -1172,9 +1187,10 @@ ORDER BY 1`;
accessor: 'avg_row_size',
filterable: false,
width: 100,
- Cell: ({ value }) => (
-
- ),
+ Cell: ({ value }) => {
+ if (isNaN(value)) return '-';
+ return ;
+ },
},
{
Header: twoLines('Replicated', 'size'),
@@ -1182,9 +1198,12 @@ ORDER BY 1`;
accessor: 'replicated_size',
filterable: false,
width: 100,
- Cell: ({ value }) => (
-
- ),
+ Cell: ({ value }) => {
+ if (isNaN(value)) return '-';
+ return (
+
+ );
+ },
},
{
Header: 'Compaction',
diff --git a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
index 80d1467e3093..7c3a51b5915d 100755
--- a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
+++ b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
@@ -62,6 +62,7 @@ exports[`segments-view matches snapshot 1`] = `
]
}
onChange={[Function]}
+ onClose={[Function]}
tableColumnsHidden={Array []}
/>
@@ -125,7 +126,9 @@ exports[`segments-view matches snapshot 1`] = `
Object {
"Header": "Segment ID",
"accessor": "segment_id",
+ "filterable": true,
"show": true,
+ "sortable": true,
"width": 300,
},
Object {
@@ -139,7 +142,9 @@ exports[`segments-view matches snapshot 1`] = `
"Header": "Interval",
"accessor": "interval",
"defaultSortDesc": true,
+ "filterable": true,
"show": false,
+ "sortable": true,
"width": 120,
},
Object {
@@ -147,7 +152,9 @@ exports[`segments-view matches snapshot 1`] = `
"Header": "Start",
"accessor": "start",
"defaultSortDesc": true,
+ "filterable": true,
"show": true,
+ "sortable": true,
"width": 120,
},
Object {
@@ -155,14 +162,18 @@ exports[`segments-view matches snapshot 1`] = `
"Header": "End",
"accessor": "end",
"defaultSortDesc": true,
+ "filterable": true,
"show": true,
+ "sortable": true,
"width": 120,
},
Object {
"Header": "Version",
"accessor": "version",
"defaultSortDesc": true,
+ "filterable": true,
"show": true,
+ "sortable": true,
"width": 120,
},
Object {
@@ -171,6 +182,7 @@ exports[`segments-view matches snapshot 1`] = `
"accessor": "time_span",
"filterable": true,
"show": true,
+ "sortable": true,
"width": 100,
},
Object {
@@ -179,6 +191,7 @@ exports[`segments-view matches snapshot 1`] = `
"accessor": "partitioning",
"filterable": true,
"show": true,
+ "sortable": true,
"width": 100,
},
Object {
@@ -186,6 +199,7 @@ exports[`segments-view matches snapshot 1`] = `
"accessor": "partition_num",
"filterable": false,
"show": true,
+ "sortable": true,
"width": 60,
},
Object {
@@ -195,6 +209,7 @@ exports[`segments-view matches snapshot 1`] = `
"defaultSortDesc": true,
"filterable": false,
"show": true,
+ "sortable": true,
},
Object {
"Cell": [Function],
From 5d97b1b03bc75058f711818a483f67ff8486ad3f Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Fri, 19 Feb 2021 14:54:03 -0800
Subject: [PATCH 5/9] remove trimmedSegments
---
web-console/src/views/segments-view/segments-view.tsx | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx
index 3835450f7339..979d3f65e4c2 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -151,7 +151,6 @@ interface SegmentQueryResultRow {
export interface SegmentsViewState {
segmentsState: QueryState;
- trimmedSegments?: SegmentQueryResultRow[];
segmentFilter: Filter[];
segmentTableActionDialogId?: string;
datasourceTableActionDialogId?: string;
@@ -451,16 +450,10 @@ END AS "partitioning"`,
}
renderSegmentsTable() {
- const {
- segmentsState,
- trimmedSegments,
- segmentFilter,
- hiddenColumns,
- groupByInterval,
- } = this.state;
+ const { segmentsState, segmentFilter, hiddenColumns, groupByInterval } = this.state;
const { capabilities } = this.props;
- const segments = trimmedSegments || segmentsState.data || [];
+ const segments = segmentsState.data || [];
const sizeValues = segments.map(d => formatBytes(d.size)).concat('(realtime)');
From 506d03420ee291adf74c8ee1e30afe367456449e Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Fri, 19 Feb 2021 17:01:26 -0800
Subject: [PATCH 6/9] Availability detail
---
.../views/datasource-view/datasource-view.tsx | 53 ++++++-------------
1 file changed, 16 insertions(+), 37 deletions(-)
diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index 0d73fd7c1c3d..7e92dfa21036 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -74,7 +74,7 @@ const tableColumns: Record = {
full: [
'Datasource name',
'Availability',
- 'Segment load/drop queues',
+ 'Availability detail',
'Total data size',
'Segment size',
'Segment granularity',
@@ -90,7 +90,7 @@ const tableColumns: Record = {
'no-sql': [
'Datasource name',
'Availability',
- 'Segment load/drop queues',
+ 'Availability detail',
'Total data size',
'Compaction',
'% Compacted',
@@ -101,7 +101,7 @@ const tableColumns: Record = {
'no-proxy': [
'Datasource name',
'Availability',
- 'Segment load/drop queues',
+ 'Availability detail',
'Total data size',
'Segment size',
'Segment granularity',
@@ -151,7 +151,6 @@ const PERCENT_BRACES = [formatPercent(1)];
interface DatasourceQueryResultRow {
readonly datasource: string;
readonly num_segments: number;
- readonly num_available_segments: number;
readonly num_segments_to_load: number;
readonly num_segments_to_drop: number;
readonly minute_aligned_segments: number;
@@ -254,12 +253,10 @@ export class DatasourcesView extends React.PureComponent<
hiddenColumns.exists('Datasource name') && `datasource`,
(hiddenColumns.exists('Availability') || hiddenColumns.exists('Segment granularity')) &&
`COUNT(*) FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS num_segments`,
- hiddenColumns.exists('Availability') &&
- `COUNT(*) FILTER (WHERE is_available = 1 AND ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_available_segments`,
- hiddenColumns.exists('Segment load/drop queues') &&
+ (hiddenColumns.exists('Availability') || hiddenColumns.exists('Availability detail')) && [
`COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND is_available = 0) AS num_segments_to_load`,
- hiddenColumns.exists('Segment load/drop queues') &&
`COUNT(*) FILTER (WHERE is_available = 1 AND NOT ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_segments_to_drop`,
+ ],
hiddenColumns.exists('Total data size') &&
`SUM("size") FILTER (WHERE is_published = 1 AND is_overshadowed = 0) AS total_data_size`,
hiddenColumns.exists('Segment size') && [
@@ -360,7 +357,6 @@ ORDER BY 1`;
const numSegments = availableSegments + segmentsToLoad;
return {
datasource: d.name,
- num_available_segments: availableSegments,
num_segments: numSegments,
num_segments_to_load: segmentsToLoad,
num_segments_to_drop: 0,
@@ -1016,18 +1012,11 @@ ORDER BY 1`;
{
Header: 'Availability',
show: hiddenColumns.exists('Availability'),
- id: 'availability',
filterable: false,
minWidth: 200,
- accessor: row => {
- return {
- num_available: row.num_available_segments,
- num_total: row.num_segments,
- };
- },
- Cell: ({ original }) => {
- const { datasource, num_available_segments, num_segments, unused } = original;
-
+ accessor: 'num_segments',
+ Cell: ({ value: num_segments, original }) => {
+ const { datasource, unused, num_segments_to_load } = original;
if (unused) {
return (
@@ -1042,12 +1031,9 @@ ORDER BY 1`;
{pluralIfNeeded(num_segments, 'segment')}
);
- if (
- typeof num_available_segments !== 'number' ||
- typeof num_segments !== 'number'
- ) {
+ if (typeof num_segments_to_load !== 'number' || typeof num_segments !== 'number') {
return '-';
- } else if (num_available_segments === num_segments) {
+ } else if (num_segments_to_load === 0) {
return (
@@ -1057,22 +1043,16 @@ ORDER BY 1`;
);
} else {
+ const numAvailableSegments = num_segments - num_segments_to_load;
const percentAvailable = (
- Math.floor((num_available_segments / num_segments) * 1000) / 10
+ Math.floor((numAvailableSegments / num_segments) * 1000) / 10
).toFixed(1);
- const missing = num_segments - num_available_segments;
- const segmentsMissingEl = (
- goToSegments(datasource, true)}>{`${pluralIfNeeded(
- missing,
- 'segment',
- )} unavailable`}
- );
return (
- {num_available_segments ? '\u25cf' : '\u25cb'}
+ {numAvailableSegments ? '\u25cf' : '\u25cb'}
- {percentAvailable}% available ({segmentsEl}, {segmentsMissingEl})
+ {percentAvailable}% available ({segmentsEl})
);
}
@@ -1084,9 +1064,8 @@ ORDER BY 1`;
},
},
{
- Header: twoLines('Segment load/drop', 'queues'),
- show: hiddenColumns.exists('Segment load/drop queues'),
- id: 'load-drop',
+ Header: twoLines('Availability', 'detail'),
+ show: hiddenColumns.exists('Availability detail'),
accessor: 'num_segments_to_load',
filterable: false,
minWidth: 100,
From e925c13d79ca6dc44efa63de54911eb643bdd67f Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Thu, 4 Mar 2021 17:40:46 -0800
Subject: [PATCH 7/9] be smart about when showing smart modes
---
.../src/components/header-bar/header-bar.tsx | 42 +++++++++++--------
1 file changed, 25 insertions(+), 17 deletions(-)
diff --git a/web-console/src/components/header-bar/header-bar.tsx b/web-console/src/components/header-bar/header-bar.tsx
index f28810523a71..5552d2acb813 100644
--- a/web-console/src/components/header-bar/header-bar.tsx
+++ b/web-console/src/components/header-bar/header-bar.tsx
@@ -225,6 +225,7 @@ export const HeaderBar = React.memo(function HeaderBar(props: HeaderBarProps) {
);
+ const capabilitiesMode = capabilities.getModeExtended();
const configMenu = (
+ );
+ break;
+
case 'coordinator':
label = 'Coordinator mode';
message = (
@@ -225,6 +236,15 @@ export const HeaderBar = React.memo(function HeaderBar(props: HeaderBarProps) {
);
+ function setForcedMode(capabilities: Capabilities | undefined): void {
+ if (capabilities) {
+ localStorageSetJson(LocalStorageKeys.CAPABILITIES_OVERRIDE, capabilities);
+ } else {
+ localStorageRemove(LocalStorageKeys.CAPABILITIES_OVERRIDE);
+ }
+ location.reload();
+ }
+
const capabilitiesMode = capabilities.getModeExtended();
const configMenu = (