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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7e147ee905 Web console: Reset to specific offsets dialog (#14863)
7e147ee905 is described below

commit 7e147ee905443ce1f57f456f72b962740461fa15
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Thu Aug 17 15:38:56 2023 -0700

    Web console: Reset to specific offsets dialog (#14863)
    
    * add dialog
    
    * copy changes
---
 web-console/src/dialogs/index.ts                   |   1 +
 .../supervisor-reset-offsets-dialog.tsx            | 122 +++++++++++++++++++++
 .../views/supervisors-view/supervisors-view.tsx    |  25 ++++-
 3 files changed, 147 insertions(+), 1 deletion(-)

diff --git a/web-console/src/dialogs/index.ts b/web-console/src/dialogs/index.ts
index 468ccf8d62..f7e239b53f 100644
--- a/web-console/src/dialogs/index.ts
+++ b/web-console/src/dialogs/index.ts
@@ -34,6 +34,7 @@ export * from './retention-dialog/retention-dialog';
 export * from './snitch-dialog/snitch-dialog';
 export * from './spec-dialog/spec-dialog';
 export * from './string-input-dialog/string-input-dialog';
+export * from 
'./supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog';
 export * from 
'./supervisor-table-action-dialog/supervisor-table-action-dialog';
 export * from './table-action-dialog/table-action-dialog';
 export * from './task-table-action-dialog/task-table-action-dialog';
diff --git 
a/web-console/src/dialogs/supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog.tsx
 
b/web-console/src/dialogs/supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog.tsx
new file mode 100644
index 0000000000..8fd8fdba56
--- /dev/null
+++ 
b/web-console/src/dialogs/supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog.tsx
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Button, Classes, Code, ControlGroup, Dialog, FormGroup, Intent } from 
'@blueprintjs/core';
+import React, { useState } from 'react';
+
+import { FancyNumericInput } from 
'../../components/fancy-numeric-input/fancy-numeric-input';
+import { useQueryManager } from '../../hooks';
+import { Api, AppToaster } from '../../singletons';
+import { deepGet, getDruidErrorMessage } from '../../utils';
+
+type OffsetMap = Record<string, number>;
+
+interface SupervisorResetOffsetsDialogProps {
+  supervisorId: string;
+  supervisorType: string;
+  onClose: () => void;
+}
+
+export const SupervisorResetOffsetsDialog = React.memo(function 
SupervisorResetOffsetsDialog(
+  props: SupervisorResetOffsetsDialogProps,
+) {
+  const { supervisorId, supervisorType, onClose } = props;
+  const [offsetsToResetTo, setOffsetsToResetTo] = useState<OffsetMap>({});
+
+  const [statusResp] = useQueryManager<string, OffsetMap>({
+    initQuery: supervisorId,
+    processQuery: async supervisorId => {
+      const statusResp = await Api.instance.get(
+        `/druid/indexer/v1/supervisor/${Api.encodePath(supervisorId)}/status`,
+      );
+      return statusResp.data;
+    },
+  });
+
+  const stream = deepGet(statusResp.data || {}, 'payload.stream');
+  const latestOffsets = deepGet(statusResp.data || {}, 
'payload.latestOffsets');
+
+  async function onSave() {
+    if (!stream) return;
+    if (!Object.keys(offsetsToResetTo).length) return;
+
+    try {
+      await Api.instance.post(
+        
`/druid/indexer/v1/supervisor/${Api.encodePath(supervisorId)}/resetOffsets`,
+        {
+          type: supervisorType,
+          partitions: {
+            type: 'end',
+            stream,
+            partitionOffsetMap: offsetsToResetTo,
+          },
+        },
+      );
+    } catch (e) {
+      AppToaster.show({
+        message: `Failed to set offsets: ${getDruidErrorMessage(e)}`,
+        intent: Intent.DANGER,
+      });
+      return;
+    }
+
+    AppToaster.show({
+      message: `${supervisorId} offsets have been set`,
+      intent: Intent.SUCCESS,
+    });
+    onClose();
+  }
+
+  return (
+    <Dialog
+      className="supervisor-reset-offsets-dialog"
+      isOpen
+      onClose={onClose}
+      title={`Set supervisor offsets: ${supervisorId}`}
+    >
+      <div className={Classes.DIALOG_FOOTER}>
+        <div className={Classes.DIALOG_BODY}>
+          <p>
+            Set <Code>{supervisorId}</Code> to specific offsets
+          </p>
+          {latestOffsets &&
+            Object.entries(latestOffsets).map(([key, latestOffset]) => (
+              <FormGroup key={key}>
+                <ControlGroup>
+                  <Button text={`${key} (currently: ${latestOffset})`} 
disabled />
+                  <FancyNumericInput
+                    value={offsetsToResetTo[key]}
+                    onValueChange={valueAsNumber => {
+                      setOffsetsToResetTo({ ...offsetsToResetTo, [key]: 
valueAsNumber });
+                    }}
+                    min={0}
+                    fill
+                    placeholder={"Don't change"}
+                  />
+                </ControlGroup>
+              </FormGroup>
+            ))}
+        </div>
+        <div className={Classes.DIALOG_FOOTER_ACTIONS}>
+          <Button text="Close" onClick={onClose} />
+          <Button text="Save" intent={Intent.PRIMARY} onClick={() => void 
onSave()} />
+        </div>
+      </div>
+    </Dialog>
+  );
+});
diff --git a/web-console/src/views/supervisors-view/supervisors-view.tsx 
b/web-console/src/views/supervisors-view/supervisors-view.tsx
index c4bef912c3..2959e06f62 100644
--- a/web-console/src/views/supervisors-view/supervisors-view.tsx
+++ b/web-console/src/views/supervisors-view/supervisors-view.tsx
@@ -40,6 +40,7 @@ import {
   SpecDialog,
   SupervisorTableActionDialog,
 } from '../../dialogs';
+import { SupervisorResetOffsetsDialog } from 
'../../dialogs/supervisor-reset-offsets-dialog/supervisor-reset-offsets-dialog';
 import type { QueryWithContext } from '../../druid-models';
 import type { Capabilities } from '../../helpers';
 import { SMALL_TABLE_PAGE_SIZE, SMALL_TABLE_PAGE_SIZE_OPTIONS } from 
'../../react-table';
@@ -108,6 +109,7 @@ export interface SupervisorsViewState {
 
   resumeSupervisorId?: string;
   suspendSupervisorId?: string;
+  resetOffsetsSupervisorInfo?: { id: string; type: string };
   resetSupervisorId?: string;
   terminateSupervisorId?: string;
 
@@ -339,6 +341,11 @@ GROUP BY 1, 2`;
             ? this.setState({ resumeSupervisorId: id })
             : this.setState({ suspendSupervisorId: id }),
       },
+      {
+        icon: IconNames.STEP_BACKWARD,
+        title: 'Set offsets',
+        onAction: () => this.setState({ resetOffsetsSupervisorInfo: { id, type 
} }),
+      },
       {
         icon: IconNames.STEP_BACKWARD,
         title: 'Hard reset',
@@ -417,6 +424,21 @@ GROUP BY 1, 2`;
     );
   }
 
+  renderResetOffsetsSupervisorAction() {
+    const { resetOffsetsSupervisorInfo } = this.state;
+    if (!resetOffsetsSupervisorInfo) return;
+
+    return (
+      <SupervisorResetOffsetsDialog
+        supervisorId={resetOffsetsSupervisorInfo.id}
+        supervisorType={resetOffsetsSupervisorInfo.type}
+        onClose={() => {
+          this.setState({ resetOffsetsSupervisorInfo: undefined });
+        }}
+      />
+    );
+  }
+
   renderResetSupervisorAction() {
     const { resetSupervisorId } = this.state;
     if (!resetSupervisorId) return;
@@ -426,7 +448,7 @@ GROUP BY 1, 2`;
         action={async () => {
           const resp = await Api.instance.post(
             
`/druid/indexer/v1/supervisor/${Api.encodePath(resetSupervisorId)}/reset`,
-            {},
+            '',
           );
           return resp.data;
         }}
@@ -784,6 +806,7 @@ GROUP BY 1, 2`;
         {this.renderSupervisorTable()}
         {this.renderResumeSupervisorAction()}
         {this.renderSuspendSupervisorAction()}
+        {this.renderResetOffsetsSupervisorAction()}
         {this.renderResetSupervisorAction()}
         {this.renderTerminateSupervisorAction()}
         {supervisorSpecDialogOpen && (


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

Reply via email to