This is an automated email from the ASF dual-hosted git repository.

vogievetsky pushed a commit to branch better_supervisor_view
in repository https://gitbox.apache.org/repos/asf/druid.git

commit 06ef03402b6636fd06389be89f25c24dab2fb2aa
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Mon Apr 22 15:24:37 2024 -0700

    add recent errors
---
 .../src/utils/local-storage-backed-visibility.tsx  |  4 +-
 .../views/datasources-view/datasources-view.tsx    |  5 +-
 .../src/views/segments-view/segments-view.tsx      |  2 +-
 .../views/supervisors-view/supervisors-view.scss   |  4 ++
 .../views/supervisors-view/supervisors-view.tsx    | 68 ++++++++++++++++++----
 5 files changed, 66 insertions(+), 17 deletions(-)

diff --git a/web-console/src/utils/local-storage-backed-visibility.tsx 
b/web-console/src/utils/local-storage-backed-visibility.tsx
index c335180056b..f20031f2b8d 100644
--- a/web-console/src/utils/local-storage-backed-visibility.tsx
+++ b/web-console/src/utils/local-storage-backed-visibility.tsx
@@ -65,7 +65,7 @@ export class LocalStorageBackedVisibility {
     return new LocalStorageBackedVisibility(this.key, defaultHidden, 
newVisibility);
   }
 
-  public shown(value: string): boolean {
-    return this.visibility[value] ?? !this.defaultHidden.includes(value);
+  public shown(...values: string[]): boolean {
+    return values.some(value => this.visibility[value] ?? 
!this.defaultHidden.includes(value));
   }
 }
diff --git a/web-console/src/views/datasources-view/datasources-view.tsx 
b/web-console/src/views/datasources-view/datasources-view.tsx
index 75541b82999..997db1d91af 100644
--- a/web-console/src/views/datasources-view/datasources-view.tsx
+++ b/web-console/src/views/datasources-view/datasources-view.tsx
@@ -338,12 +338,11 @@ export class DatasourcesView extends React.PureComponent<
     const columns = compact(
       [
         visibleColumns.shown('Datasource name') && `datasource`,
-        (visibleColumns.shown('Availability') || visibleColumns.shown('Segment 
granularity')) && [
+        visibleColumns.shown('Availability', 'Segment granularity') && [
           `COUNT(*) FILTER (WHERE is_active = 1) AS num_segments`,
           `COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND 
replication_factor = 0) AS num_zero_replica_segments`,
         ],
-        (visibleColumns.shown('Availability') ||
-          visibleColumns.shown('Historical load/drop queues')) && [
+        visibleColumns.shown('Availability', 'Historical load/drop queues') && 
[
           `COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND 
is_available = 0 AND replication_factor > 0) AS num_segments_to_load`,
           `COUNT(*) FILTER (WHERE is_available = 1 AND is_active = 0) AS 
num_segments_to_drop`,
         ],
diff --git a/web-console/src/views/segments-view/segments-view.tsx 
b/web-console/src/views/segments-view/segments-view.tsx
index ba4b84b76c4..0e3fe1c6f4b 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -201,7 +201,7 @@ export class SegmentsView extends 
React.PureComponent<SegmentsViewProps, Segment
   WHEN "start" LIKE '%:00.000Z' AND "end" LIKE '%:00.000Z' THEN 'Minute'
   ELSE 'Sub minute'
 END AS "time_span"`,
-      (visibleColumns.shown('Shard type') || visibleColumns.shown('Shard 
spec')) && `"shard_spec"`,
+      visibleColumns.shown('Shard type', 'Shard spec') && `"shard_spec"`,
       visibleColumns.shown('Partition') && `"partition_num"`,
       visibleColumns.shown('Size') && `"size"`,
       visibleColumns.shown('Num rows') && `"num_rows"`,
diff --git a/web-console/src/views/supervisors-view/supervisors-view.scss 
b/web-console/src/views/supervisors-view/supervisors-view.scss
index 7b03df4f186..25c55e69b44 100644
--- a/web-console/src/views/supervisors-view/supervisors-view.scss
+++ b/web-console/src/views/supervisors-view/supervisors-view.scss
@@ -37,5 +37,9 @@
       font-style: italic;
       opacity: 0.6;
     }
+
+    .warning-line {
+      color: $orange4;
+    }
   }
 }
diff --git a/web-console/src/views/supervisors-view/supervisors-view.tsx 
b/web-console/src/views/supervisors-view/supervisors-view.tsx
index 6622cc99e11..316080641ce 100644
--- a/web-console/src/views/supervisors-view/supervisors-view.tsx
+++ b/web-console/src/views/supervisors-view/supervisors-view.tsx
@@ -92,6 +92,7 @@ const supervisorTableColumns: string[] = [
   'Configured tasks',
   'Running tasks',
   'Aggregate lag',
+  'Recent errors',
   'Stats',
   ACTION_COLUMN_LABEL,
 ];
@@ -102,6 +103,10 @@ function getRowStatsKeyTitle(key: RowStatsKey) {
   return `Rate over past ${pluralIfNeeded(parseInt(key, 10), 'minute')}`;
 }
 
+function getRowStatsKeySeconds(key: RowStatsKey): number {
+  return parseInt(key, 10) * 60;
+}
+
 interface SupervisorQuery extends TableState {
   capabilities: Capabilities;
   visibleColumns: LocalStorageBackedVisibility;
@@ -288,7 +293,7 @@ export class SupervisorsView extends React.PureComponent<
         }
 
         if (capabilities.hasOverlordAccess()) {
-          if (visibleColumns.shown('Running tasks') || 
visibleColumns.shown('Aggregate lag')) {
+          if (visibleColumns.shown('Running tasks', 'Aggregate lag', 'Recent 
errors')) {
             try {
               for (const supervisor of supervisors) {
                 cancelToken.throwIfRequested();
@@ -674,7 +679,7 @@ export class SupervisorsView extends React.PureComponent<
           {
             Header: 'Status',
             id: 'detailed_state',
-            width: 150,
+            width: 130,
             accessor: 'detailed_state',
             Cell: ({ value }) => (
               <TableFilterableCell
@@ -758,7 +763,7 @@ export class SupervisorsView extends React.PureComponent<
           },
           {
             Header: 'Aggregate lag',
-            accessor: 'status.aggregateLag',
+            accessor: 'status.payload.aggregateLag',
             width: 200,
             filterable: false,
             sortable: false,
@@ -800,23 +805,64 @@ export class SupervisorsView extends React.PureComponent<
             Cell: ({ value }) => {
               if (!value) return;
               const c = getTotalSupervisorStats(value, statsKey);
-              const isRate = statsKey !== 'totals';
-              const formatNumber = isRate ? formatRate : formatInteger;
-              const formatData = isRate ? formatByteRate : formatBytes;
-              const bytes = c.processedBytes ? ` 
(${formatData(c.processedBytes)})` : '';
+              const seconds = getRowStatsKeySeconds(statsKey);
+              const totalLabel = `Total over ${statsKey}: `;
+              const bytes = c.processedBytes ? ` 
(${formatByteRate(c.processedBytes)})` : '';
               return (
                 <div>
-                  <div>{`Processed: 
${formatNumber(c.processed)}${bytes}`}</div>
+                  <div
+                    title={`${totalLabel}${formatInteger(c.processed * 
seconds)} (${formatBytes(
+                      c.processedBytes * seconds,
+                    )})`}
+                  >{`Processed: ${formatRate(c.processed)}${bytes}`}</div>
                   {Boolean(c.processedWithError) && (
-                    <div>Processed with error: 
{formatNumber(c.processedWithError)}</div>
+                    <div
+                      className="warning-line"
+                      
title={`${totalLabel}${formatInteger(c.processedWithError * seconds)}`}
+                    >
+                      Processed with error: {formatRate(c.processedWithError)}
+                    </div>
+                  )}
+                  {Boolean(c.thrownAway) && (
+                    <div
+                      className="warning-line"
+                      title={`${totalLabel}${formatInteger(c.thrownAway * 
seconds)}`}
+                    >
+                      Thrown away: {formatRate(c.thrownAway)}
+                    </div>
+                  )}
+                  {Boolean(c.unparseable) && (
+                    <div
+                      className="warning-line"
+                      title={`${totalLabel}${formatInteger(c.unparseable * 
seconds)}`}
+                    >
+                      Unparseable: {formatRate(c.unparseable)}
+                    </div>
                   )}
-                  {Boolean(c.thrownAway) && <div>Thrown away: 
{formatNumber(c.thrownAway)}</div>}
-                  {Boolean(c.unparseable) && <div>Unparseable: 
{formatNumber(c.unparseable)}</div>}
                 </div>
               );
             },
             show: visibleColumns.shown('Stats'),
           },
+          {
+            Header: 'Recent errors',
+            accessor: 'status.payload.recentErrors',
+            width: 200,
+            filterable: false,
+            sortable: false,
+            show: visibleColumns.shown('Recent errors'),
+            Cell: ({ value, original }) => {
+              return (
+                <TableClickableCell
+                  onClick={() => this.onSupervisorDetail(original)}
+                  hoverIcon={IconNames.SEARCH_TEMPLATE}
+                  title="See errors"
+                >
+                  {pluralIfNeeded(value?.length, 'error')}
+                </TableClickableCell>
+              );
+            },
+          },
           {
             Header: ACTION_COLUMN_LABEL,
             id: ACTION_COLUMN_ID,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to