diff --git a/web-console/src/dialogs/compaction-dynamic-config-dialog/compaction-dynamic-config-dialog.tsx b/web-console/src/dialogs/compaction-dynamic-config-dialog/compaction-dynamic-config-dialog.tsx index 1b6bb587aab2..0e352784f625 100644 --- a/web-console/src/dialogs/compaction-dynamic-config-dialog/compaction-dynamic-config-dialog.tsx +++ b/web-console/src/dialogs/compaction-dynamic-config-dialog/compaction-dynamic-config-dialog.tsx @@ -20,35 +20,18 @@ import { Button, Classes, Code, Dialog, Intent } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import React, { useState } from 'react'; -import type { Field } from '../../components'; -import { AutoForm, ExternalLink, Loader } from '../../components'; +import type { FormJsonTabs } from '../../components'; +import { AutoForm, ExternalLink, FormJsonSelector, JsonInput, Loader } from '../../components'; +import type { CompactionDynamicConfig } from '../../druid-models'; +import { + COMPACTION_DYNAMIC_CONFIG_DEFAULT_MAX, + COMPACTION_DYNAMIC_CONFIG_DEFAULT_RATIO, + COMPACTION_DYNAMIC_CONFIG_FIELDS, +} from '../../druid-models'; import { useQueryManager } from '../../hooks'; import { getLink } from '../../links'; import { Api, AppToaster } from '../../singletons'; -import { getDruidErrorMessage } from '../../utils'; - -interface CompactionDynamicConfig { - compactionTaskSlotRatio: number; - maxCompactionTaskSlots: number; -} - -const DEFAULT_RATIO = 0.1; -const DEFAULT_MAX = 2147483647; -const COMPACTION_DYNAMIC_CONFIG_FIELDS: Field[] = [ - { - name: 'compactionTaskSlotRatio', - type: 'ratio', - defaultValue: DEFAULT_RATIO, - info: <>The ratio of the total task slots to the compaction task slots., - }, - { - name: 'maxCompactionTaskSlots', - type: 'number', - defaultValue: DEFAULT_MAX, - info: <>The maximum number of task slots for compaction tasks, - min: 0, - }, -]; +import { getDruidErrorMessage, wait } from '../../utils'; export interface CompactionDynamicConfigDialogProps { onClose(): void; @@ -58,21 +41,20 @@ export const CompactionDynamicConfigDialog = React.memo(function CompactionDynam props: CompactionDynamicConfigDialogProps, ) { const { onClose } = props; + const [currentTab, setCurrentTab] = useState('form'); const [dynamicConfig, setDynamicConfig] = useState< Partial | undefined >(); + const [jsonError, setJsonError] = useState(); useQueryManager>({ initQuery: null, processQuery: async (_, cancelToken) => { try { - const c = ( - await Api.instance.get('/druid/coordinator/v1/config/compaction', { cancelToken }) - ).data; - setDynamicConfig({ - compactionTaskSlotRatio: c.compactionTaskSlotRatio ?? DEFAULT_RATIO, - maxCompactionTaskSlots: c.maxCompactionTaskSlots ?? DEFAULT_MAX, + const configResp = await Api.instance.get('/druid/indexer/v1/compaction/config/cluster', { + cancelToken, }); + setDynamicConfig(configResp.data || {}); } catch (e) { AppToaster.show({ icon: IconNames.ERROR, @@ -88,26 +70,28 @@ export const CompactionDynamicConfigDialog = React.memo(function CompactionDynam async function saveConfig() { if (!dynamicConfig) return; try { - // This API is terrible. https://druid.apache.org/docs/latest/operations/api-reference#automatic-compaction-configuration - await Api.instance.post( - `/druid/coordinator/v1/config/compaction/taskslots?ratio=${ - dynamicConfig.compactionTaskSlotRatio ?? DEFAULT_RATIO - }&max=${dynamicConfig.maxCompactionTaskSlots ?? DEFAULT_MAX}`, - {}, - ); + await Api.instance.post('/druid/indexer/v1/compaction/config/cluster', dynamicConfig); } catch (e) { AppToaster.show({ icon: IconNames.ERROR, intent: Intent.DANGER, message: `Could not save compaction dynamic config: ${getDruidErrorMessage(e)}`, }); + return; } AppToaster.show({ message: 'Saved compaction dynamic config', intent: Intent.SUCCESS, }); + onClose(); + + // Reload the page also because the datasources page pulls from different APIs depending on the setting of supervisor based compaction + if (location.hash.includes('#datasources')) { + await wait(1000); // Wait for a second to give the user time to read the toast + location.reload(); + } } return ( @@ -135,17 +119,33 @@ export const CompactionDynamicConfigDialog = React.memo(function CompactionDynam

The maximum number of task slots used for compaction will be{' '} {`clamp(floor(${ - dynamicConfig.compactionTaskSlotRatio ?? DEFAULT_RATIO - } * total_task_slots), 1, ${ - dynamicConfig.maxCompactionTaskSlots ?? DEFAULT_MAX + dynamicConfig.compactionTaskSlotRatio ?? COMPACTION_DYNAMIC_CONFIG_DEFAULT_RATIO + } * total_task_slots), ${dynamicConfig.engine === 'msq' ? 2 : 1}, ${ + dynamicConfig.maxCompactionTaskSlots ?? COMPACTION_DYNAMIC_CONFIG_DEFAULT_MAX })`} .

- { + setJsonError(undefined); + setCurrentTab(t); + }} /> + {currentTab === 'form' ? ( + + ) : ( + + )}
@@ -154,6 +154,7 @@ export const CompactionDynamicConfigDialog = React.memo(function CompactionDynam onClick={() => void saveConfig()} intent={Intent.PRIMARY} rightIcon={IconNames.TICK} + disabled={Boolean(jsonError)} />
diff --git a/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.scss b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.scss index 620e7f6ff227..e6da0a9f3431 100644 --- a/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.scss +++ b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.scss @@ -36,11 +36,7 @@ .global-info { position: absolute; bottom: 10px; - left: 30px; - right: 10px; - width: auto; - white-space: pre; - background: $gray1; + right: 20px; } } diff --git a/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx index 8ee0ffdbfbc3..b77fc3d3dda2 100644 --- a/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx +++ b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx @@ -16,15 +16,16 @@ * limitations under the License. */ -import { Button, Callout, Classes, Dialog, Tab, Tabs, TabsExpander, Tag } from '@blueprintjs/core'; +import { Button, Classes, Dialog, Popover, Tab, Tabs, TabsExpander, Tag } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; import * as JSONBig from 'json-bigint-native'; import React, { useState } from 'react'; -import { Loader, ShowValue } from '../../components'; +import { Loader, PopoverText, ShowValue } from '../../components'; import type { CompactionConfig } from '../../druid-models'; import { useQueryManager } from '../../hooks'; import { Api } from '../../singletons'; -import { formatInteger, formatPercent, getApiArray } from '../../utils'; +import { formatInteger, formatPercent, getApiArrayFromKey } from '../../utils'; import { DiffDialog } from '../diff-dialog/diff-dialog'; import './compaction-history-dialog.scss'; @@ -46,7 +47,7 @@ function formatGlobalConfig(globalConfig: GlobalConfig): string { return [ `compactionTaskSlotRatio: ${formatPercent(globalConfig.compactionTaskSlotRatio)}`, `maxCompactionTaskSlots: ${formatInteger(globalConfig.maxCompactionTaskSlots)}`, - `useAutoScaleSlots: ${globalConfig.useAutoScaleSlots}`, + `useAutoScaleSlots: ${Boolean(globalConfig.useAutoScaleSlots)}`, ].join('\n'); } @@ -65,8 +66,11 @@ export const CompactionHistoryDialog = React.memo(function CompactionHistoryDial initQuery: datasource, processQuery: async (datasource, cancelToken) => { try { - return await getApiArray( - `/druid/coordinator/v1/config/compaction/${Api.encodePath(datasource)}/history?count=20`, + return await getApiArrayFromKey( + `/druid/indexer/v1/compaction/config/datasources/${Api.encodePath( + datasource, + )}/history?count=20`, + 'entries', cancelToken, ); } catch (e) { @@ -105,9 +109,16 @@ export const CompactionHistoryDialog = React.memo(function CompactionHistoryDial downloadFilename={`compaction-history-${datasource}-version-${historyEntry.auditTime}.json`} /> {historyEntry.globalConfig && ( - - {formatGlobalConfig(historyEntry.globalConfig)} - + +
{formatGlobalConfig(historyEntry.globalConfig)}
+ + } + > +