private renderDatasourcesTable()

in web-console/src/views/datasources-view/datasources-view.tsx [1166:1709]


  private renderDatasourcesTable() {
    const { goToTasks, capabilities, filters, onFiltersChange } = this.props;
    const { datasourcesAndDefaultRulesState, showUnused, visibleColumns, showSegmentTimeline } =
      this.state;

    let { datasources, defaultRules } = datasourcesAndDefaultRulesState.data || { datasources: [] };

    if (!showUnused) {
      datasources = datasources.filter(d => !d.unused);
    }

    // Calculate column values for bracing

    const totalDataSizeValues = datasources.map(d => formatTotalDataSize(d.total_data_size));

    const minSegmentRowsValues = datasources.map(d => formatSegmentRows(d.min_segment_rows));
    const avgSegmentRowsValues = datasources.map(d => formatSegmentRows(d.avg_segment_rows));
    const maxSegmentRowsValues = datasources.map(d => formatSegmentRows(d.max_segment_rows));

    const minSegmentSizeValues = datasources.map(d => formatSegmentSize(d.min_segment_size));
    const avgSegmentSizeValues = datasources.map(d => formatSegmentSize(d.avg_segment_size));
    const maxSegmentSizeValues = datasources.map(d => formatSegmentSize(d.max_segment_size));

    const totalRowsValues = datasources.map(d => formatTotalRows(d.total_rows));

    const avgRowSizeValues = datasources.map(d => formatAvgRowSize(d.avg_row_size));

    const replicatedSizeValues = datasources.map(d => formatReplicatedSize(d.replicated_size));

    const leftToBeCompactedValues = datasources.map(d =>
      d.compaction?.status
        ? formatLeftToBeCompacted(d.compaction?.status.bytesAwaitingCompaction)
        : '-',
    );

    return (
      <ReactTable
        data={datasources}
        loading={datasourcesAndDefaultRulesState.loading}
        noDataText={
          datasourcesAndDefaultRulesState.getErrorMessage() ||
          (!datasourcesAndDefaultRulesState.loading && datasources && !datasources.length
            ? 'No datasources'
            : '')
        }
        filterable
        filtered={filters}
        onFilteredChange={onFiltersChange}
        defaultPageSize={STANDARD_TABLE_PAGE_SIZE}
        pageSizeOptions={STANDARD_TABLE_PAGE_SIZE_OPTIONS}
        showPagination={datasources.length > STANDARD_TABLE_PAGE_SIZE}
        columns={[
          {
            Header: twoLines('Datasource', 'name'),
            show: visibleColumns.shown('Datasource name'),
            accessor: 'datasource',
            width: 150,
            Cell: ({ value, original }) => (
              <TableClickableCell
                onClick={() => this.onDetail(original)}
                hoverIcon={IconNames.SEARCH_TEMPLATE}
                tooltip="Show detail"
              >
                {showSegmentTimeline ? (
                  <>
                    <span style={{ color: getDatasourceColor(value) }}>&#9632;</span> {value}
                  </>
                ) : (
                  value
                )}
              </TableClickableCell>
            ),
          },
          {
            Header: 'Availability',
            show: visibleColumns.shown('Availability'),
            filterable: false,
            width: 220,
            accessor: 'num_segments',
            className: 'padded',
            Cell: ({ value: num_segments, original }) => {
              const { datasource, unused, num_segments_to_load, num_zero_replica_segments, rules } =
                original as Datasource;
              if (unused) {
                return (
                  <span>
                    <span style={{ color: DatasourcesView.UNUSED_COLOR }}>&#x25cf;&nbsp;</span>
                    Unused
                  </span>
                );
              }

              const hasZeroReplicationRule = RuleUtil.hasZeroReplicaRule(rules, defaultRules);
              const descriptor = hasZeroReplicationRule ? 'pre-cached' : 'available';
              const segmentsEl = (
                <a
                  onClick={() =>
                    this.setState({ showSegmentTimeline: { capabilities, datasource } })
                  }
                  data-tooltip="Show in segment timeline"
                >
                  {pluralIfNeeded(num_segments, 'segment')}
                </a>
              );
              const percentZeroReplica = (
                Math.floor((num_zero_replica_segments / num_segments) * 1000) / 10
              ).toFixed(1);

              if (typeof num_segments_to_load !== 'number' || typeof num_segments !== 'number') {
                return '-';
              } else if (num_segments === 0) {
                return (
                  <span>
                    <span style={{ color: DatasourcesView.EMPTY_COLOR }}>&#x25cf;&nbsp;</span>
                    Empty
                  </span>
                );
              } else if (num_segments_to_load === 0) {
                return (
                  <span>
                    <span style={{ color: DatasourcesView.FULLY_AVAILABLE_COLOR }}>
                      &#x25cf;&nbsp;
                    </span>
                    {assemble(
                      num_segments !== num_zero_replica_segments
                        ? `Fully ${descriptor}`
                        : undefined,
                      hasZeroReplicationRule ? `${percentZeroReplica}% deep storage only` : '',
                    ).join(', ')}{' '}
                    ({segmentsEl})
                  </span>
                );
              } else {
                const numAvailableSegments = num_segments - num_segments_to_load;
                const percentAvailable = (
                  Math.floor((numAvailableSegments / num_segments) * 1000) / 10
                ).toFixed(1);
                return (
                  <span>
                    <span style={{ color: DatasourcesView.PARTIALLY_AVAILABLE_COLOR }}>
                      {numAvailableSegments ? '\u25cf' : '\u25cb'}&nbsp;
                    </span>
                    {`${percentAvailable}% ${descriptor}${
                      hasZeroReplicationRule ? `, ${percentZeroReplica}% deep storage only` : ''
                    }`}{' '}
                    ({segmentsEl})
                  </span>
                );
              }
            },
            sortMethod: (d1, d2) => {
              const percentAvailable1 = d1.num_available / d1.num_total;
              const percentAvailable2 = d2.num_available / d2.num_total;
              return percentAvailable1 - percentAvailable2 || d1.num_total - d2.num_total;
            },
          },
          {
            Header: twoLines('Historical', 'load/drop queues'),
            show: visibleColumns.shown('Historical load/drop queues'),
            accessor: 'num_segments_to_load',
            filterable: false,
            width: 180,
            className: 'padded',
            Cell: ({ original }) => {
              const { num_segments_to_load, num_segments_to_drop } = original as Datasource;
              return formatLoadDrop(num_segments_to_load, num_segments_to_drop);
            },
          },
          {
            Header: twoLines('Total', 'data size'),
            show: visibleColumns.shown('Total data size'),
            accessor: 'total_data_size',
            filterable: false,
            width: 100,
            className: 'padded',
            Cell: ({ value }) => (
              <BracedText text={formatTotalDataSize(value)} braces={totalDataSizeValues} />
            ),
          },
          {
            Header: 'Running tasks',
            show: visibleColumns.shown('Running tasks'),
            id: 'running_tasks',
            accessor: d => countRunningTasks(d.runningTasks),
            filterable: false,
            width: 200,
            Cell: ({ original }) => {
              const { runningTasks } = original;
              if (!runningTasks) return;
              return (
                <TableClickableCell
                  onClick={() => goToTasks(original.datasource)}
                  hoverIcon={IconNames.ARROW_TOP_RIGHT}
                  tooltip="Go to tasks"
                >
                  {formatRunningTasks(runningTasks)}
                </TableClickableCell>
              );
            },
          },
          {
            Header: twoLines('Segment rows', 'minimum / average / maximum'),
            show: capabilities.hasSql() && visibleColumns.shown('Segment rows'),
            accessor: 'avg_segment_rows',
            filterable: false,
            width: 230,
            className: 'padded',
            Cell: ({ value, original }) => {
              const { min_segment_rows, max_segment_rows } = original as Datasource;
              if (
                isNumberLikeNaN(value) ||
                isNumberLikeNaN(min_segment_rows) ||
                isNumberLikeNaN(max_segment_rows)
              )
                return '-';
              return (
                <>
                  <BracedText
                    text={formatSegmentRows(min_segment_rows)}
                    braces={minSegmentRowsValues}
                  />{' '}
                  &nbsp;{' '}
                  <BracedText text={formatSegmentRows(value)} braces={avgSegmentRowsValues} />{' '}
                  &nbsp;{' '}
                  <BracedText
                    text={formatSegmentRows(max_segment_rows)}
                    braces={maxSegmentRowsValues}
                  />
                </>
              );
            },
          },
          {
            Header: twoLines('Segment size', 'minimum / average / maximum'),
            show: capabilities.hasSql() && visibleColumns.shown('Segment size'),
            accessor: 'avg_segment_size',
            filterable: false,
            width: 270,
            className: 'padded',
            Cell: ({ value, original }) => {
              const { min_segment_size, max_segment_size } = original as Datasource;
              if (
                isNumberLikeNaN(value) ||
                isNumberLikeNaN(min_segment_size) ||
                isNumberLikeNaN(max_segment_size)
              )
                return '-';
              return (
                <>
                  <BracedText
                    text={formatSegmentSize(min_segment_size)}
                    braces={minSegmentSizeValues}
                  />{' '}
                  &nbsp;{' '}
                  <BracedText text={formatSegmentSize(value)} braces={avgSegmentSizeValues} />{' '}
                  &nbsp;{' '}
                  <BracedText
                    text={formatSegmentSize(max_segment_size)}
                    braces={maxSegmentSizeValues}
                  />
                </>
              );
            },
          },
          {
            Header: twoLines('Segment', 'granularity'),
            show: capabilities.hasSql() && visibleColumns.shown('Segment granularity'),
            id: 'segment_granularity',
            accessor: segmentGranularityCountsToRank,
            filterable: false,
            width: 100,
            className: 'padded',
            Cell: ({ original }) => {
              const {
                num_segments,
                minute_aligned_segments,
                hour_aligned_segments,
                day_aligned_segments,
                month_aligned_segments,
                year_aligned_segments,
                all_granularity_segments,
              } = original as Datasource;
              const segmentGranularities: string[] = [];
              if (!num_segments || isNumberLikeNaN(year_aligned_segments)) return '-';
              if (all_granularity_segments) {
                segmentGranularities.push('All');
              }
              if (year_aligned_segments) {
                segmentGranularities.push('Year');
              }
              if (month_aligned_segments !== year_aligned_segments) {
                segmentGranularities.push('Month');
              }
              if (day_aligned_segments !== month_aligned_segments) {
                segmentGranularities.push('Day');
              }
              if (hour_aligned_segments !== day_aligned_segments) {
                segmentGranularities.push('Hour');
              }
              if (minute_aligned_segments !== hour_aligned_segments) {
                segmentGranularities.push('Minute');
              }
              if (
                Number(num_segments) - Number(all_granularity_segments) !==
                Number(minute_aligned_segments)
              ) {
                segmentGranularities.push('Sub minute');
              }
              return segmentGranularities.join(', ');
            },
          },
          {
            Header: twoLines('Total', 'rows'),
            show: capabilities.hasSql() && visibleColumns.shown('Total rows'),
            accessor: 'total_rows',
            filterable: false,
            width: 110,
            className: 'padded',
            Cell: ({ value }) => {
              if (isNumberLikeNaN(value)) return '-';
              return (
                <BracedText
                  text={formatTotalRows(value)}
                  braces={totalRowsValues}
                  unselectableThousandsSeparator
                />
              );
            },
          },
          {
            Header: twoLines('Avg. row size', '(bytes)'),
            show: capabilities.hasSql() && visibleColumns.shown('Avg. row size'),
            accessor: 'avg_row_size',
            filterable: false,
            width: 100,
            className: 'padded',
            Cell: ({ value }) => {
              if (isNumberLikeNaN(value)) return '-';
              return (
                <BracedText
                  text={formatAvgRowSize(value)}
                  braces={avgRowSizeValues}
                  unselectableThousandsSeparator
                />
              );
            },
          },
          {
            Header: twoLines('Replicated', 'size'),
            show: capabilities.hasSql() && visibleColumns.shown('Replicated size'),
            accessor: 'replicated_size',
            filterable: false,
            width: 100,
            className: 'padded',
            Cell: ({ value }) => {
              if (isNumberLikeNaN(value)) return '-';
              return (
                <BracedText text={formatReplicatedSize(value)} braces={replicatedSizeValues} />
              );
            },
          },
          {
            Header: 'Compaction',
            show: capabilities.hasCoordinatorAccess() && visibleColumns.shown('Compaction'),
            id: 'compactionStatus',
            accessor: row => Boolean(row.compaction?.status),
            filterable: false,
            width: 180,
            Cell: ({ original }) => {
              const { datasource, compaction } = original as Datasource;
              if (!compaction) return;
              return (
                <TableClickableCell
                  tooltip="Open compaction configuration"
                  disabled={!compaction}
                  onClick={() => {
                    if (!compaction) return;
                    this.setState({
                      compactionDialogOpenOn: {
                        datasource,
                        compactionConfig: compaction.config,
                      },
                    });
                  }}
                  hoverIcon={IconNames.EDIT}
                >
                  {formatCompactionInfo(compaction)}
                </TableClickableCell>
              );
            },
          },
          {
            Header: twoLines('% Compacted', 'bytes / segments / intervals'),
            show: capabilities.hasCoordinatorAccess() && visibleColumns.shown('% Compacted'),
            id: 'percentCompacted',
            width: 200,
            accessor: ({ compaction }) => {
              const status = compaction?.status;
              return status?.bytesCompacted
                ? status.bytesCompacted / (status.bytesAwaitingCompaction + status.bytesCompacted)
                : 0;
            },
            filterable: false,
            className: 'padded',
            Cell: ({ original }) => {
              const { compaction } = original as Datasource;
              if (!compaction) return;

              const { status } = compaction;
              if (!status || zeroCompactionStatus(status)) {
                return (
                  <>
                    <BracedText text="-" braces={PERCENT_BRACES} /> &nbsp;{' '}
                    <BracedText text="-" braces={PERCENT_BRACES} /> &nbsp;{' '}
                    <BracedText text="-" braces={PERCENT_BRACES} />
                  </>
                );
              }

              return (
                <>
                  <BracedText
                    text={formatPercent(
                      progress(status.bytesCompacted, status.bytesAwaitingCompaction),
                    )}
                    braces={PERCENT_BRACES}
                  />{' '}
                  &nbsp;{' '}
                  <BracedText
                    text={formatPercent(
                      progress(status.segmentCountCompacted, status.segmentCountAwaitingCompaction),
                    )}
                    braces={PERCENT_BRACES}
                  />{' '}
                  &nbsp;{' '}
                  <BracedText
                    text={formatPercent(
                      progress(
                        status.intervalCountCompacted,
                        status.intervalCountAwaitingCompaction,
                      ),
                    )}
                    braces={PERCENT_BRACES}
                  />
                </>
              );
            },
          },
          {
            Header: twoLines('Left to be', 'compacted'),
            show:
              capabilities.hasCoordinatorAccess() && visibleColumns.shown('Left to be compacted'),
            id: 'leftToBeCompacted',
            width: 100,
            accessor: ({ compaction }) => {
              const status = compaction?.status;
              return status?.bytesAwaitingCompaction || 0;
            },
            filterable: false,
            className: 'padded',
            Cell: ({ original }) => {
              const { compaction } = original as Datasource;
              if (!compaction) return;

              const { status } = compaction;
              if (!status) {
                return <BracedText text="-" braces={leftToBeCompactedValues} />;
              }

              return (
                <BracedText
                  text={formatLeftToBeCompacted(status.bytesAwaitingCompaction)}
                  braces={leftToBeCompactedValues}
                />
              );
            },
          },
          {
            Header: 'Retention',
            show: capabilities.hasCoordinatorAccess() && visibleColumns.shown('Retention'),
            id: 'retention',
            accessor: row => row.rules?.length || 0,
            filterable: false,
            width: 200,
            Cell: ({ original }) => {
              const { datasource, rules } = original as Datasource;
              if (!rules) return;

              return (
                <TableClickableCell
                  tooltip="Open retention (load rule) configuration"
                  disabled={!defaultRules}
                  onClick={() => {
                    if (!defaultRules) return;
                    this.setState({
                      retentionDialogOpenOn: {
                        datasource,
                        rules,
                        defaultRules,
                      },
                    });
                  }}
                  hoverIcon={IconNames.EDIT}
                >
                  {rules.length
                    ? DatasourcesView.formatRules(rules)
                    : defaultRules
                    ? `Cluster default: ${DatasourcesView.formatRules(defaultRules)}`
                    : ''}
                </TableClickableCell>
              );
            },
          },
          {
            Header: ACTION_COLUMN_LABEL,
            accessor: 'datasource',
            id: ACTION_COLUMN_ID,
            width: ACTION_COLUMN_WIDTH,
            filterable: false,
            sortable: false,
            Cell: ({ value: datasource, original }) => {
              const { unused, rules, compaction } = original as Datasource;
              const datasourceActions = this.getDatasourceActions(
                datasource,
                unused,
                rules,
                compaction,
              );
              return (
                <ActionCell
                  onDetail={() => {
                    this.onDetail(original);
                  }}
                  disableDetail={unused}
                  actions={datasourceActions}
                  menuTitle={datasource}
                />
              );
            },
          },
        ]}
      />
    );
  }