From d16ee17039059c2b0fb2ce37502d3dc3ad411774 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 15 Aug 2025 14:30:58 -0400 Subject: [PATCH 01/21] feat(compass-indexes): add index build progress COMPASS-9495 --- .../regular-index-actions.tsx | 103 ++++++++--- .../regular-indexes-table.tsx | 51 +++++- .../src/hooks/use-index-progress.ts | 65 +++++++ .../src/modules/regular-indexes.spec.ts | 82 +++++++++ .../src/modules/regular-indexes.ts | 160 +++++++++++++++++- packages/compass-indexes/src/stores/store.ts | 4 +- 6 files changed, 431 insertions(+), 34 deletions(-) create mode 100644 packages/compass-indexes/src/hooks/use-index-progress.ts diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index 891d224c7e8..79a9c82a717 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -1,18 +1,39 @@ import semver from 'semver'; import React, { useCallback, useMemo } from 'react'; import type { GroupedItemAction } from '@mongodb-js/compass-components'; -import { css, ItemActionGroup } from '@mongodb-js/compass-components'; +import { + css, + ItemActionGroup, + SpinLoader, + spacing, + Body, +} from '@mongodb-js/compass-components'; const styles = css({ // Align actions with the end of the table justifyContent: 'flex-end', }); +const combinedContainerStyles = css({ + display: 'flex', + alignItems: 'center', + gap: spacing[200], + minWidth: spacing[800], + justifyContent: 'flex-end', +}); + +const progressTextStyles = css({ + fontSize: '12px', + fontWeight: 'normal', +}); + type Index = { name: string; extra?: { hidden?: boolean; }; + status?: 'inprogress' | 'ready' | 'failed'; + progressPercentage?: number; }; type IndexActionsProps = { @@ -44,30 +65,42 @@ const IndexActions: React.FunctionComponent = ({ }) => { const indexActions: GroupedItemAction[] = useMemo(() => { const actions: GroupedItemAction[] = []; + const progressPercentage = index.progressPercentage ?? 0; - if (serverSupportsHideIndex(serverVersion)) { - actions.push( - index.extra?.hidden - ? { - action: 'unhide', - label: `Unhide Index ${index.name}`, - tooltip: `Unhide Index`, - icon: 'Visibility', - } - : { - action: 'hide', - label: `Hide Index ${index.name}`, - tooltip: `Hide Index`, - icon: 'VisibilityOff', - } - ); - } + if (progressPercentage < 100 && progressPercentage > 0) { + // partially built + actions.push({ + action: 'delete', + label: `Cancel Index ${index.name}`, + icon: 'XWithCircle', + variant: 'destructive', + }); + } else { + // completed + if (serverSupportsHideIndex(serverVersion)) { + actions.push( + index.extra?.hidden + ? { + action: 'unhide', + label: `Unhide Index ${index.name}`, + tooltip: `Unhide Index`, + icon: 'Visibility', + } + : { + action: 'hide', + label: `Hide Index ${index.name}`, + tooltip: `Hide Index`, + icon: 'VisibilityOff', + } + ); + } - actions.push({ - action: 'delete', - label: `Drop Index ${index.name}`, - icon: 'Trash', - }); + actions.push({ + action: 'delete', + label: `Drop Index ${index.name}`, + icon: 'Trash', + }); + } return actions; }, [index, serverVersion]); @@ -85,6 +118,30 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteIndexClick, onHideIndexClick, onUnhideIndexClick, index] ); + const progressPercentage = index.progressPercentage ?? 0; + if ( + index.status !== 'failed' && + progressPercentage < 100 && + progressPercentage > 0 + ) { + const progressText = `${Math.round(progressPercentage)}%`; + + return ( +
+ Building... {progressText} + + + data-testid="index-actions" + actions={indexActions} + onAction={onAction} + /> +
+ ); + } + return ( data-testid="index-actions" diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx index c1235866633..a645ed7d827 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx @@ -10,6 +10,7 @@ import type { } from '@mongodb-js/compass-components'; import type { RootState } from '../../modules'; +import { useIndexProgress } from '../../hooks/use-index-progress'; import TypeField from './type-field'; import SizeField from './size-field'; @@ -248,12 +249,16 @@ function mergeIndexes( const rollingIndexNames = new Set( rollingIndexes.map((index) => index.indexName) ); + const inProgressIndexNames = new Set( + inProgressIndexes.map(({ name }) => name) + ); const mappedIndexes: MappedRegularIndex[] = indexes // exclude partially-built indexes so that we don't include indexes that // only exist on the primary node and then duplicate those as rolling // builds in the same table .filter((index) => !rollingIndexNames.has(index.name)) + .filter((index) => !inProgressIndexNames.has(index.name)) .map((index) => { return { ...index, compassIndexType: 'regular-index' }; }); @@ -286,10 +291,44 @@ function getInProgressIndexInfo( index: MappedInProgressIndex, { onDeleteFailedIndexClick, + onDeleteIndexClick, + serverVersion, }: { onDeleteFailedIndexClick: (indexName: string) => void; + onDeleteIndexClick: (indexName: string) => void; + serverVersion: string; } ): CommonIndexInfo { + // Use progress directly from Redux state + const progressToUse = index.progressPercentage ?? 0; + + // Show spinner and progress only if: + // 1. Index is not failed (status !== 'failed') + // 2. Index has meaningful progress (> 0% and < 100%) + let actionsComponent; + if (index.status !== 'failed' && progressToUse > 0 && progressToUse < 100) { + actionsComponent = ( + {}} // No-op for building indexes + onUnhideIndexClick={() => {}} // No-op for building indexes + /> + ); + } else { + // For failed or pending indexes, use the InProgressIndexActions + actionsComponent = ( + + ); + } + return { id: index.id, name: index.name, @@ -300,12 +339,7 @@ function getInProgressIndexInfo( // TODO(COMPASS-8335): add properties for in-progress indexes properties: null, status: , - actions: ( - - ), + actions: actionsComponent, }; } @@ -387,6 +421,9 @@ export const RegularIndexesTable: React.FunctionComponent< }) => { const tabId = useWorkspaceTabId(); + // Use our custom hook to handle index progress tracking + useIndexProgress(inProgressIndexes); + useEffect(() => { onRegularIndexesOpened(tabId); return () => { @@ -407,6 +444,8 @@ export const RegularIndexesTable: React.FunctionComponent< if (index.compassIndexType === 'in-progress-index') { indexData = getInProgressIndexInfo(index, { onDeleteFailedIndexClick, + onDeleteIndexClick, + serverVersion, }); } else if (index.compassIndexType === 'rolling-index') { indexData = getRollingIndexInfo(index); diff --git a/packages/compass-indexes/src/hooks/use-index-progress.ts b/packages/compass-indexes/src/hooks/use-index-progress.ts new file mode 100644 index 00000000000..e6bf0d919e9 --- /dev/null +++ b/packages/compass-indexes/src/hooks/use-index-progress.ts @@ -0,0 +1,65 @@ +import { useEffect, useRef } from 'react'; +import { useDispatch } from 'react-redux'; +import type { InProgressIndex } from '../modules/regular-indexes'; +import { getIndexesProgress } from '../modules/regular-indexes'; +import type { IndexesThunkDispatch } from '../modules'; + +/** 1 seconds polling interval */ +const INDEX_INIT_PROGRESS_POLLING_INTERVAL_MS = 1 * 1000; +/** 10 seconds polling interval */ +const INDEX_PROGRESS_POLLING_INTERVAL_MS = 10 * 1000; + +/** + * Custom hook to manage index build progress tracking + * This hook automatically starts/stops progress polling for indexes that are: + * 1. Being created (status: 'inprogress') - monitors for when build starts + * 2. Currently building (progressPercentage > 0% and < 100%) - tracks build progress + */ +export function useIndexProgress(inProgressIndexes: InProgressIndex[]) { + const dispatch = useDispatch(); + const timeoutRef = useRef(undefined); + const checksRef = useRef(0); + + useEffect(() => { + const indexesToTrack = inProgressIndexes.filter((index) => { + const notFailed = index.status !== 'failed'; + const inProgress = index.status === 'inprogress'; + const progressPercentage = index.progressPercentage ?? 0; + const stillPartiallyBuilt = + progressPercentage > 0 && progressPercentage < 100; + + const shouldTrack = notFailed && (inProgress || stillPartiallyBuilt); + + return shouldTrack; + }); + + clearTimeout(timeoutRef.current); + timeoutRef.current = undefined; + + if (indexesToTrack.length) { + checksRef.current = 0; + + const updateIndexProgress = () => { + checksRef.current += 1; + void dispatch(getIndexesProgress(indexesToTrack)).finally(() => { + if (timeoutRef.current) { + // After the first 3 checks, slow down the poller + setTimeout( + updateIndexProgress, + checksRef.current < 3 + ? INDEX_INIT_PROGRESS_POLLING_INTERVAL_MS + : INDEX_PROGRESS_POLLING_INTERVAL_MS + ); + } + }); + }; + + if (!timeoutRef.current) updateIndexProgress(); + } + + return () => { + clearTimeout(timeoutRef.current); + timeoutRef.current = undefined; + }; + }, [inProgressIndexes, dispatch]); +} diff --git a/packages/compass-indexes/src/modules/regular-indexes.spec.ts b/packages/compass-indexes/src/modules/regular-indexes.spec.ts index 23f9075eab5..ff7150d0e26 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.spec.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.spec.ts @@ -9,6 +9,8 @@ import { unhideIndex, startPollingRegularIndexes, stopPollingRegularIndexes, + trackIndexProgress, + indexCreationStarted, } from './regular-indexes'; import { indexesList, @@ -547,4 +549,84 @@ describe('regular-indexes module', function () { expect(collection.fetch.callCount).to.equal(0); }); }); + + describe('#trackIndexProgress action', function () { + it('updates progress when currentOp returns matching index operation', async function () { + const currentOpResponse = { + inprog: [ + { + command: { + createIndexes: 'trips', + indexes: [{ name: 'test_index_1' }], + }, + progress: { done: 50, total: 100 }, + ns: 'citibike.trips', // Use the same namespace as the test setup + }, + ], + }; + + const store = setupStore( + {}, + { + currentOp: () => Promise.resolve(currentOpResponse), + } + ); + + // Add an in-progress index to track + const mockIndex = { + id: 'test-index-id', + name: 'test_index_1', + fields: [{ field: 'field1', value: 1 }], + status: 'inprogress' as const, + progressPercentage: 0, + }; + + store.dispatch(indexCreationStarted(mockIndex)); + + // Track progress + await store.dispatch(trackIndexProgress('test-index-id', 'test_index_1')); + + const state = store.getState(); + const inProgressIndex = state.regularIndexes.inProgressIndexes.find( + (index) => index.id === 'test-index-id' + ); + + expect(inProgressIndex?.progressPercentage).to.equal(50); + }); + + it('does not update progress when no matching index operation is found', async function () { + const currentOpResponse = { + inprog: [], // No operations in progress + }; + + const store = setupStore( + {}, + { + currentOp: () => Promise.resolve(currentOpResponse), + } + ); + + // Add an in-progress index to track + const mockIndex = { + id: 'test-index-id', + name: 'test_index_1', + fields: [{ field: 'field1', value: 1 }], + status: 'inprogress' as const, + progressPercentage: 0, + }; + + store.dispatch(indexCreationStarted(mockIndex)); + + // Track progress + await store.dispatch(trackIndexProgress('test-index-id', 'test_index_1')); + + const state = store.getState(); + const inProgressIndex = state.regularIndexes.inProgressIndexes.find( + (index) => index.id === 'test-index-id' + ); + + // Progress should remain unchanged + expect(inProgressIndex?.progressPercentage).to.equal(0); + }); + }); }); diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index 62d4cd5db53..b11c09d0932 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -42,6 +42,7 @@ export type InProgressIndex = Pick & { id: string; status: 'inprogress' | 'failed'; error?: string; + progressPercentage?: number; }; export type RollingIndex = Partial & @@ -85,6 +86,7 @@ export const prepareInProgressIndex = ( name: inProgressIndexName, // TODO(COMPASS-8335): we never mapped properties and the table does have // room to display them + progressPercentage: 0, // Default to 0% when index creation starts }; }; @@ -102,6 +104,7 @@ export enum ActionTypes { FailedIndexRemoved = 'compass-indexes/regular-indexes/failed-index-removed', IndexCreationStarted = 'compass-indexes/create-index/index-creation-started', + IndexCreationProgressUpdated = 'compass-indexes/create-index/index-creation-progress-updated', IndexCreationSucceeded = 'compass-indexes/create-index/index-creation-succeeded', IndexCreationFailed = 'compass-indexes/create-index/index-creation-failed', @@ -146,6 +149,12 @@ type IndexCreationStartedAction = { inProgressIndex: InProgressIndex; }; +type IndexCreationProgressUpdatedAction = { + type: ActionTypes.IndexCreationProgressUpdated; + inProgressIndexId: string; + progressPercentage: number; +}; + type IndexCreationSucceededAction = { type: ActionTypes.IndexCreationSucceeded; inProgressIndexId: string; @@ -240,11 +249,32 @@ export default function reducer( ...state, indexes: action.indexes, rollingIndexes: action.rollingIndexes, - // Remove in progress stubs when we got the "real" indexes from one of the - // backends. Keep the error ones around even if the name matches (should - // only be possible in cases of "index with the same name already exists") + // Keep in-progress indexes that are still actively building, but remove: + // 1. Failed indexes (status: 'failed') + // 2. Completed indexes (real index exists and progress >= 100%) inProgressIndexes: state.inProgressIndexes.filter((inProgress) => { - return !!inProgress.error || !allIndexNames.has(inProgress.name); + // Always keep indexes with explicit errors + if (inProgress.error) { + return true; + } + + // Remove failed indexes (they're done and should be cleaned up) + if (inProgress.status === 'failed') { + return false; + } + + // If real index doesn't exist, keep the in-progress one + if (!allIndexNames.has(inProgress.name)) { + return true; + } + + // Real index exists - only keep in-progress if it's still actively building + // (status: 'inprogress' AND progress < 100%) + const isActivelyBuilding = + inProgress.status === 'inprogress' && + (inProgress.progressPercentage ?? 0) < 100; + + return isActivelyBuilding; }), status: FetchStatuses.READY, }; @@ -287,6 +317,25 @@ export default function reducer( }; } + if ( + isAction( + action, + ActionTypes.IndexCreationProgressUpdated + ) + ) { + // Update the progress percentage for the in-progress index + const inProgressIndexes = state.inProgressIndexes.map((index) => + index.id === action.inProgressIndexId + ? { ...index, progressPercentage: action.progressPercentage } + : index + ); + + return { + ...state, + inProgressIndexes, + }; + } + if ( isAction(action, ActionTypes.FailedIndexRemoved) ) { @@ -318,6 +367,21 @@ export default function reducer( }; } + if ( + isAction( + action, + ActionTypes.IndexCreationSucceeded + ) + ) { + // Remove the completed index from in-progress list + return { + ...state, + inProgressIndexes: state.inProgressIndexes.filter( + (x) => x.id !== action.inProgressIndexId + ), + }; + } + if ( isAction( action, @@ -513,6 +577,17 @@ export const indexCreationStarted = ( inProgressIndex, }); +const indexCreationProgressUpdated = ( + inProgressIndexId: string, + progressPercentage: number +): IndexCreationProgressUpdatedAction => ({ + type: ActionTypes.IndexCreationProgressUpdated, + inProgressIndexId, + progressPercentage, +}); + +export { indexCreationProgressUpdated }; + const indexCreationSucceeded = ( inProgressIndexId: string ): IndexCreationSucceededAction => ({ @@ -529,6 +604,82 @@ const indexCreationFailed = ( error, }); +export const getIndexesProgress = ( + indexesToTrack: InProgressIndex[] +): IndexesThunkAction< + Promise, + IndexCreationProgressUpdatedAction | IndexCreationSucceededAction +> => { + return async (dispatch, getState, { dataService }) => { + const { namespace } = getState(); + + try { + const currentOps = await dataService.currentOp(); + + type IndexProgressOp = { + command?: { + createIndexes?: string; + indexes?: { name?: string }[]; + }; + progress?: { + done: number; + total: number; + }; + ns?: string; + }; + + // Filter for index creation operations in our namespace + const createIndexOps: IndexProgressOp[] = currentOps.inprog.filter( + (op: IndexProgressOp) => { + return ( + op.command && + op.command.createIndexes && + op.progress && + op.ns === namespace && + op.command.indexes + ); + } + ); + + // Create a map of index name to progress for quick lookup + const progressMap = new Map(); + + for (const op of createIndexOps) { + if (op.command?.indexes && op.progress) { + const percentage = Math.round( + (op.progress.done / op.progress.total) * 100 + ); + + // Add progress for all indexes in this operation + for (const idx of op.command.indexes) { + if (idx.name && typeof idx.name === 'string') { + progressMap.set(idx.name, percentage); + } + } + } + } + + // Update progress for all tracked indexes + for (const { id, name } of indexesToTrack) { + const percentage = progressMap.get(name); + if (percentage !== undefined) { + dispatch(indexCreationProgressUpdated(id, percentage)); + + // If index build is complete, mark it as succeeded + if (percentage >= 100) { + dispatch(indexCreationSucceeded(id)); + } + } + } + } catch (err) { + // If we can't get progress, the UI will continue with existing progress + // This ensures the UI doesn't break if currentOp fails + // Using void to indicate intentionally ignoring this error + void err; + } + }; +}; + /** * @internal exported only for testing */ @@ -549,6 +700,7 @@ export function createRegularIndex( ): IndexesThunkAction< Promise, | IndexCreationStartedAction + | IndexCreationProgressUpdatedAction | IndexCreationSucceededAction | IndexCreationFailedAction | RollingIndexTimeoutCheckAction diff --git a/packages/compass-indexes/src/stores/store.ts b/packages/compass-indexes/src/stores/store.ts index 5e547467495..6bed3fb3dbc 100644 --- a/packages/compass-indexes/src/stores/store.ts +++ b/packages/compass-indexes/src/stores/store.ts @@ -50,7 +50,9 @@ export type IndexesDataServiceProps = | 'collectionStats' | 'collectionInfo' | 'listCollections' - | 'isListSearchIndexesSupported'; + | 'isListSearchIndexesSupported' + // Required for tracking index build progress + | 'currentOp'; export type IndexesDataService = Pick; export type IndexesPluginServices = { From 3408a51edff1416bcb8269248f4e3ee00e497f60 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 15 Aug 2025 15:07:23 -0400 Subject: [PATCH 02/21] fix: tests --- .../regular-indexes-table.spec.tsx | 15 +++++++++++++-- .../src/modules/regular-indexes.spec.ts | 12 ++++++------ packages/compass-indexes/test/setup-store.ts | 3 +++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx index dc98401ac5c..04d5f9063ef 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx @@ -1,12 +1,13 @@ import React from 'react'; import { cleanup, - render, + renderWithConnections, screen, within, userEvent, } from '@mongodb-js/testing-library-compass'; import { expect } from 'chai'; +import sinon from 'sinon'; import { RegularIndexesTable } from './regular-indexes-table'; import type { @@ -15,6 +16,7 @@ import type { RollingIndex, } from '../../modules/regular-indexes'; import { mockRegularIndex } from '../../../test/helpers'; +import * as useIndexProgressModule from '../../hooks/use-index-progress'; const indexes: RegularIndex[] = [ { @@ -151,7 +153,7 @@ const rollingIndexes: RollingIndex[] = [ const renderIndexList = ( props: Partial> = {} ) => { - return render( + return renderWithConnections( Date: Fri, 15 Aug 2025 15:13:35 -0400 Subject: [PATCH 03/21] fix: switch back to 10s intervals and round the number at the component --- .../regular-indexes-table/regular-index-actions.tsx | 6 +++--- .../compass-indexes/src/hooks/use-index-progress.ts | 13 +------------ .../compass-indexes/src/modules/regular-indexes.ts | 4 +--- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index 79a9c82a717..f2661fa80c5 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -124,14 +124,14 @@ const IndexActions: React.FunctionComponent = ({ progressPercentage < 100 && progressPercentage > 0 ) { - const progressText = `${Math.round(progressPercentage)}%`; - return (
- Building... {progressText} + + Building... {progressPercentage | 0}% + data-testid="index-actions" diff --git a/packages/compass-indexes/src/hooks/use-index-progress.ts b/packages/compass-indexes/src/hooks/use-index-progress.ts index e6bf0d919e9..3b2c49ed138 100644 --- a/packages/compass-indexes/src/hooks/use-index-progress.ts +++ b/packages/compass-indexes/src/hooks/use-index-progress.ts @@ -4,8 +4,6 @@ import type { InProgressIndex } from '../modules/regular-indexes'; import { getIndexesProgress } from '../modules/regular-indexes'; import type { IndexesThunkDispatch } from '../modules'; -/** 1 seconds polling interval */ -const INDEX_INIT_PROGRESS_POLLING_INTERVAL_MS = 1 * 1000; /** 10 seconds polling interval */ const INDEX_PROGRESS_POLLING_INTERVAL_MS = 10 * 1000; @@ -18,7 +16,6 @@ const INDEX_PROGRESS_POLLING_INTERVAL_MS = 10 * 1000; export function useIndexProgress(inProgressIndexes: InProgressIndex[]) { const dispatch = useDispatch(); const timeoutRef = useRef(undefined); - const checksRef = useRef(0); useEffect(() => { const indexesToTrack = inProgressIndexes.filter((index) => { @@ -37,19 +34,11 @@ export function useIndexProgress(inProgressIndexes: InProgressIndex[]) { timeoutRef.current = undefined; if (indexesToTrack.length) { - checksRef.current = 0; - const updateIndexProgress = () => { - checksRef.current += 1; void dispatch(getIndexesProgress(indexesToTrack)).finally(() => { if (timeoutRef.current) { // After the first 3 checks, slow down the poller - setTimeout( - updateIndexProgress, - checksRef.current < 3 - ? INDEX_INIT_PROGRESS_POLLING_INTERVAL_MS - : INDEX_PROGRESS_POLLING_INTERVAL_MS - ); + setTimeout(updateIndexProgress, INDEX_PROGRESS_POLLING_INTERVAL_MS); } }); }; diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index b11c09d0932..e3a192d8a87 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -646,9 +646,7 @@ export const getIndexesProgress = ( for (const op of createIndexOps) { if (op.command?.indexes && op.progress) { - const percentage = Math.round( - (op.progress.done / op.progress.total) * 100 - ); + const percentage = (op.progress.done / op.progress.total) * 100; // Add progress for all indexes in this operation for (const idx of op.command.indexes) { From 7d4209f5660c792974d366f5792fb56e58299380 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 15 Aug 2025 15:15:24 -0400 Subject: [PATCH 04/21] chore: dont need math --- packages/compass-indexes/src/hooks/use-index-progress.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-indexes/src/hooks/use-index-progress.ts b/packages/compass-indexes/src/hooks/use-index-progress.ts index 3b2c49ed138..467240a2728 100644 --- a/packages/compass-indexes/src/hooks/use-index-progress.ts +++ b/packages/compass-indexes/src/hooks/use-index-progress.ts @@ -5,7 +5,7 @@ import { getIndexesProgress } from '../modules/regular-indexes'; import type { IndexesThunkDispatch } from '../modules'; /** 10 seconds polling interval */ -const INDEX_PROGRESS_POLLING_INTERVAL_MS = 10 * 1000; +const INDEX_PROGRESS_POLLING_INTERVAL_MS = 10_000; /** * Custom hook to manage index build progress tracking From df84f03a2d07f71b3e8f81dec34b4aac0590cd78 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 15 Aug 2025 15:22:22 -0400 Subject: [PATCH 05/21] chore: copilot --- packages/compass-indexes/src/hooks/use-index-progress.ts | 5 ++++- packages/compass-indexes/src/modules/regular-indexes.ts | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/compass-indexes/src/hooks/use-index-progress.ts b/packages/compass-indexes/src/hooks/use-index-progress.ts index 467240a2728..a8682d2523e 100644 --- a/packages/compass-indexes/src/hooks/use-index-progress.ts +++ b/packages/compass-indexes/src/hooks/use-index-progress.ts @@ -38,7 +38,10 @@ export function useIndexProgress(inProgressIndexes: InProgressIndex[]) { void dispatch(getIndexesProgress(indexesToTrack)).finally(() => { if (timeoutRef.current) { // After the first 3 checks, slow down the poller - setTimeout(updateIndexProgress, INDEX_PROGRESS_POLLING_INTERVAL_MS); + timeoutRef.current = +setTimeout( + updateIndexProgress, + INDEX_PROGRESS_POLLING_INTERVAL_MS + ); } }); }; diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index e3a192d8a87..145db3d1846 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -669,11 +669,10 @@ export const getIndexesProgress = ( } } } - } catch (err) { + } catch { // If we can't get progress, the UI will continue with existing progress // This ensures the UI doesn't break if currentOp fails // Using void to indicate intentionally ignoring this error - void err; } }; }; From 891d3d0b47a91fe5f76b92f91ebd4030426e7808 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 18 Aug 2025 16:11:08 -0400 Subject: [PATCH 06/21] fix: move index progress to data-service --- .../in-progress-index-actions.tsx | 40 +++++- .../regular-index-actions.tsx | 25 ++-- .../regular-indexes-table.spec.tsx | 11 -- .../regular-indexes-table.tsx | 63 +++------ .../src/hooks/use-index-progress.ts | 57 -------- .../src/modules/regular-indexes.spec.ts | 82 ------------ .../src/modules/regular-indexes.ts | 126 ++---------------- packages/data-service/src/data-service.ts | 44 +++++- .../data-service/src/index-detail-helper.ts | 11 +- 9 files changed, 123 insertions(+), 336 deletions(-) delete mode 100644 packages/compass-indexes/src/hooks/use-index-progress.ts diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index 1596a1e541c..ed1911b43ca 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -1,11 +1,25 @@ import React, { useCallback, useMemo } from 'react'; import type { GroupedItemAction } from '@mongodb-js/compass-components'; -import { ItemActionGroup } from '@mongodb-js/compass-components'; +import { + ItemActionGroup, + SpinLoader, + Body, + css, + spacing, +} from '@mongodb-js/compass-components'; import type { InProgressIndex } from '../../modules/regular-indexes'; +const buildingTextStyles = css({ + display: 'flex', + alignItems: 'center', + gap: spacing[1], + marginRight: spacing[2], +}); + type Index = { name: string; status: InProgressIndex['status']; + buildProgress?: number; }; type IndexActionsProps = { @@ -43,12 +57,26 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteFailedIndexClick, index] ); + const progress = (index.buildProgress ?? 0) * 100; + const isBuilding = progress > 0 && progress < 100; + return ( - - data-testid="index-actions" - actions={indexActions} - onAction={onAction} - > +
+ {isBuilding && ( +
+ + Building... {progress | 0}% +
+ )} + + data-testid="index-actions" + actions={indexActions} + onAction={onAction} + /> +
); }; diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index f2661fa80c5..5e9876caa93 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -8,6 +8,7 @@ import { spacing, Body, } from '@mongodb-js/compass-components'; +import type { RegularIndex } from '../../modules/regular-indexes'; const styles = css({ // Align actions with the end of the table @@ -27,17 +28,13 @@ const progressTextStyles = css({ fontWeight: 'normal', }); -type Index = { - name: string; - extra?: { - hidden?: boolean; - }; - status?: 'inprogress' | 'ready' | 'failed'; - progressPercentage?: number; +// Extended type to include buildProgress which might not be in the base RegularIndex type +type IndexWithProgress = RegularIndex & { + buildProgress?: number; }; type IndexActionsProps = { - index: Index; + index: IndexWithProgress; serverVersion: string; onDeleteIndexClick: (name: string) => void; onHideIndexClick: (name: string) => void; @@ -63,9 +60,10 @@ const IndexActions: React.FunctionComponent = ({ onHideIndexClick, onUnhideIndexClick, }) => { + const progressPercentage = (index.buildProgress ?? 0) * 100; + const indexActions: GroupedItemAction[] = useMemo(() => { const actions: GroupedItemAction[] = []; - const progressPercentage = index.progressPercentage ?? 0; if (progressPercentage < 100 && progressPercentage > 0) { // partially built @@ -103,7 +101,7 @@ const IndexActions: React.FunctionComponent = ({ } return actions; - }, [index, serverVersion]); + }, [index.name, index.extra?.hidden, serverVersion, progressPercentage]); const onAction = useCallback( (action: IndexAction) => { @@ -118,12 +116,7 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteIndexClick, onHideIndexClick, onUnhideIndexClick, index] ); - const progressPercentage = index.progressPercentage ?? 0; - if ( - index.status !== 'failed' && - progressPercentage < 100 && - progressPercentage > 0 - ) { + if (progressPercentage > 0 && progressPercentage < 100) { return (
name) ); + // Create a map of regular indexes by name to look up buildProgress + const regularIndexesByName = new Map(); + indexes.forEach((index) => { + regularIndexesByName.set(index.name, index); + }); + const mappedIndexes: MappedRegularIndex[] = indexes // exclude partially-built indexes so that we don't include indexes that // only exist on the primary node and then duplicate those as rolling @@ -263,9 +268,17 @@ function mergeIndexes( return { ...index, compassIndexType: 'regular-index' }; }); + // For in-progress indexes, merge in buildProgress from regular indexes if available const mappedInProgressIndexes: MappedInProgressIndex[] = inProgressIndexes.map((index) => { - return { ...index, compassIndexType: 'in-progress-index' }; + const regularIndex = regularIndexesByName.get(index.name); + const buildProgress = regularIndex?.buildProgress; + + return { + ...index, + buildProgress, + compassIndexType: 'in-progress-index', + }; }); const mappedRollingIndexes: MappedRollingIndex[] = rollingIndexes.map( @@ -291,44 +304,10 @@ function getInProgressIndexInfo( index: MappedInProgressIndex, { onDeleteFailedIndexClick, - onDeleteIndexClick, - serverVersion, }: { onDeleteFailedIndexClick: (indexName: string) => void; - onDeleteIndexClick: (indexName: string) => void; - serverVersion: string; } ): CommonIndexInfo { - // Use progress directly from Redux state - const progressToUse = index.progressPercentage ?? 0; - - // Show spinner and progress only if: - // 1. Index is not failed (status !== 'failed') - // 2. Index has meaningful progress (> 0% and < 100%) - let actionsComponent; - if (index.status !== 'failed' && progressToUse > 0 && progressToUse < 100) { - actionsComponent = ( - {}} // No-op for building indexes - onUnhideIndexClick={() => {}} // No-op for building indexes - /> - ); - } else { - // For failed or pending indexes, use the InProgressIndexActions - actionsComponent = ( - - ); - } - return { id: index.id, name: index.name, @@ -339,7 +318,12 @@ function getInProgressIndexInfo( // TODO(COMPASS-8335): add properties for in-progress indexes properties: null, status: , - actions: actionsComponent, + actions: ( + + ), }; } @@ -421,9 +405,6 @@ export const RegularIndexesTable: React.FunctionComponent< }) => { const tabId = useWorkspaceTabId(); - // Use our custom hook to handle index progress tracking - useIndexProgress(inProgressIndexes); - useEffect(() => { onRegularIndexesOpened(tabId); return () => { @@ -444,8 +425,6 @@ export const RegularIndexesTable: React.FunctionComponent< if (index.compassIndexType === 'in-progress-index') { indexData = getInProgressIndexInfo(index, { onDeleteFailedIndexClick, - onDeleteIndexClick, - serverVersion, }); } else if (index.compassIndexType === 'rolling-index') { indexData = getRollingIndexInfo(index); diff --git a/packages/compass-indexes/src/hooks/use-index-progress.ts b/packages/compass-indexes/src/hooks/use-index-progress.ts deleted file mode 100644 index a8682d2523e..00000000000 --- a/packages/compass-indexes/src/hooks/use-index-progress.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useEffect, useRef } from 'react'; -import { useDispatch } from 'react-redux'; -import type { InProgressIndex } from '../modules/regular-indexes'; -import { getIndexesProgress } from '../modules/regular-indexes'; -import type { IndexesThunkDispatch } from '../modules'; - -/** 10 seconds polling interval */ -const INDEX_PROGRESS_POLLING_INTERVAL_MS = 10_000; - -/** - * Custom hook to manage index build progress tracking - * This hook automatically starts/stops progress polling for indexes that are: - * 1. Being created (status: 'inprogress') - monitors for when build starts - * 2. Currently building (progressPercentage > 0% and < 100%) - tracks build progress - */ -export function useIndexProgress(inProgressIndexes: InProgressIndex[]) { - const dispatch = useDispatch(); - const timeoutRef = useRef(undefined); - - useEffect(() => { - const indexesToTrack = inProgressIndexes.filter((index) => { - const notFailed = index.status !== 'failed'; - const inProgress = index.status === 'inprogress'; - const progressPercentage = index.progressPercentage ?? 0; - const stillPartiallyBuilt = - progressPercentage > 0 && progressPercentage < 100; - - const shouldTrack = notFailed && (inProgress || stillPartiallyBuilt); - - return shouldTrack; - }); - - clearTimeout(timeoutRef.current); - timeoutRef.current = undefined; - - if (indexesToTrack.length) { - const updateIndexProgress = () => { - void dispatch(getIndexesProgress(indexesToTrack)).finally(() => { - if (timeoutRef.current) { - // After the first 3 checks, slow down the poller - timeoutRef.current = +setTimeout( - updateIndexProgress, - INDEX_PROGRESS_POLLING_INTERVAL_MS - ); - } - }); - }; - - if (!timeoutRef.current) updateIndexProgress(); - } - - return () => { - clearTimeout(timeoutRef.current); - timeoutRef.current = undefined; - }; - }, [inProgressIndexes, dispatch]); -} diff --git a/packages/compass-indexes/src/modules/regular-indexes.spec.ts b/packages/compass-indexes/src/modules/regular-indexes.spec.ts index 1a2cbad94ef..23f9075eab5 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.spec.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.spec.ts @@ -9,8 +9,6 @@ import { unhideIndex, startPollingRegularIndexes, stopPollingRegularIndexes, - getIndexesProgress, - indexCreationStarted, } from './regular-indexes'; import { indexesList, @@ -549,84 +547,4 @@ describe('regular-indexes module', function () { expect(collection.fetch.callCount).to.equal(0); }); }); - - describe('#getIndexesProgress action', function () { - it('updates progress when currentOp returns matching index operation', async function () { - const currentOpResponse = { - inprog: [ - { - command: { - createIndexes: 'trips', - indexes: [{ name: 'test_index_1' }], - }, - progress: { done: 50, total: 100 }, - ns: 'citibike.trips', // Use the same namespace as the test setup - }, - ], - }; - - const store = setupStore( - {}, - { - currentOp: () => Promise.resolve(currentOpResponse), - } - ); - - // Add an in-progress index to track - const mockIndex = { - id: 'test-index-id', - name: 'test_index_1', - fields: [{ field: 'field1', value: 1 }], - status: 'inprogress' as const, - progressPercentage: 0, - }; - - store.dispatch(indexCreationStarted(mockIndex)); - - // Track progress - pass array of indexes to track - await store.dispatch(getIndexesProgress([mockIndex])); - - const state = store.getState(); - const inProgressIndex = state.regularIndexes.inProgressIndexes.find( - (index) => index.id === 'test-index-id' - ); - - expect(inProgressIndex?.progressPercentage).to.equal(50); - }); - - it('does not update progress when no matching index operation is found', async function () { - const currentOpResponse = { - inprog: [], // No operations in progress - }; - - const store = setupStore( - {}, - { - currentOp: () => Promise.resolve(currentOpResponse), - } - ); - - // Add an in-progress index to track - const mockIndex = { - id: 'test-index-id', - name: 'test_index_1', - fields: [{ field: 'field1', value: 1 }], - status: 'inprogress' as const, - progressPercentage: 0, - }; - - store.dispatch(indexCreationStarted(mockIndex)); - - // Track progress - pass array of indexes to track - await store.dispatch(getIndexesProgress([mockIndex])); - - const state = store.getState(); - const inProgressIndex = state.regularIndexes.inProgressIndexes.find( - (index) => index.id === 'test-index-id' - ); - - // Progress should remain unchanged - expect(inProgressIndex?.progressPercentage).to.equal(0); - }); - }); }); diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index 145db3d1846..c92b1f79fff 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -36,13 +36,16 @@ export type RegularIndex = Partial & | 'size' | 'relativeSize' | 'usageCount' - >; + > & { + // Explicitly include buildProgress to ensure it's available + buildProgress?: number; + }; export type InProgressIndex = Pick & { id: string; status: 'inprogress' | 'failed'; error?: string; - progressPercentage?: number; + buildProgress?: number; }; export type RollingIndex = Partial & @@ -86,7 +89,6 @@ export const prepareInProgressIndex = ( name: inProgressIndexName, // TODO(COMPASS-8335): we never mapped properties and the table does have // room to display them - progressPercentage: 0, // Default to 0% when index creation starts }; }; @@ -104,7 +106,6 @@ export enum ActionTypes { FailedIndexRemoved = 'compass-indexes/regular-indexes/failed-index-removed', IndexCreationStarted = 'compass-indexes/create-index/index-creation-started', - IndexCreationProgressUpdated = 'compass-indexes/create-index/index-creation-progress-updated', IndexCreationSucceeded = 'compass-indexes/create-index/index-creation-succeeded', IndexCreationFailed = 'compass-indexes/create-index/index-creation-failed', @@ -149,12 +150,6 @@ type IndexCreationStartedAction = { inProgressIndex: InProgressIndex; }; -type IndexCreationProgressUpdatedAction = { - type: ActionTypes.IndexCreationProgressUpdated; - inProgressIndexId: string; - progressPercentage: number; -}; - type IndexCreationSucceededAction = { type: ActionTypes.IndexCreationSucceeded; inProgressIndexId: string; @@ -245,6 +240,7 @@ export default function reducer( }) ) ); + return { ...state, indexes: action.indexes, @@ -269,10 +265,8 @@ export default function reducer( } // Real index exists - only keep in-progress if it's still actively building - // (status: 'inprogress' AND progress < 100%) - const isActivelyBuilding = - inProgress.status === 'inprogress' && - (inProgress.progressPercentage ?? 0) < 100; + // (status: 'inprogress'). Progress is now tracked through the regular index data + const isActivelyBuilding = inProgress.status === 'inprogress'; return isActivelyBuilding; }), @@ -317,25 +311,6 @@ export default function reducer( }; } - if ( - isAction( - action, - ActionTypes.IndexCreationProgressUpdated - ) - ) { - // Update the progress percentage for the in-progress index - const inProgressIndexes = state.inProgressIndexes.map((index) => - index.id === action.inProgressIndexId - ? { ...index, progressPercentage: action.progressPercentage } - : index - ); - - return { - ...state, - inProgressIndexes, - }; - } - if ( isAction(action, ActionTypes.FailedIndexRemoved) ) { @@ -577,17 +552,6 @@ export const indexCreationStarted = ( inProgressIndex, }); -const indexCreationProgressUpdated = ( - inProgressIndexId: string, - progressPercentage: number -): IndexCreationProgressUpdatedAction => ({ - type: ActionTypes.IndexCreationProgressUpdated, - inProgressIndexId, - progressPercentage, -}); - -export { indexCreationProgressUpdated }; - const indexCreationSucceeded = ( inProgressIndexId: string ): IndexCreationSucceededAction => ({ @@ -604,79 +568,6 @@ const indexCreationFailed = ( error, }); -export const getIndexesProgress = ( - indexesToTrack: InProgressIndex[] -): IndexesThunkAction< - Promise, - IndexCreationProgressUpdatedAction | IndexCreationSucceededAction -> => { - return async (dispatch, getState, { dataService }) => { - const { namespace } = getState(); - - try { - const currentOps = await dataService.currentOp(); - - type IndexProgressOp = { - command?: { - createIndexes?: string; - indexes?: { name?: string }[]; - }; - progress?: { - done: number; - total: number; - }; - ns?: string; - }; - - // Filter for index creation operations in our namespace - const createIndexOps: IndexProgressOp[] = currentOps.inprog.filter( - (op: IndexProgressOp) => { - return ( - op.command && - op.command.createIndexes && - op.progress && - op.ns === namespace && - op.command.indexes - ); - } - ); - - // Create a map of index name to progress for quick lookup - const progressMap = new Map(); - - for (const op of createIndexOps) { - if (op.command?.indexes && op.progress) { - const percentage = (op.progress.done / op.progress.total) * 100; - - // Add progress for all indexes in this operation - for (const idx of op.command.indexes) { - if (idx.name && typeof idx.name === 'string') { - progressMap.set(idx.name, percentage); - } - } - } - } - - // Update progress for all tracked indexes - for (const { id, name } of indexesToTrack) { - const percentage = progressMap.get(name); - if (percentage !== undefined) { - dispatch(indexCreationProgressUpdated(id, percentage)); - - // If index build is complete, mark it as succeeded - if (percentage >= 100) { - dispatch(indexCreationSucceeded(id)); - } - } - } - } catch { - // If we can't get progress, the UI will continue with existing progress - // This ensures the UI doesn't break if currentOp fails - // Using void to indicate intentionally ignoring this error - } - }; -}; - /** * @internal exported only for testing */ @@ -697,7 +588,6 @@ export function createRegularIndex( ): IndexesThunkAction< Promise, | IndexCreationStartedAction - | IndexCreationProgressUpdatedAction | IndexCreationSucceededAction | IndexCreationFailedAction | RollingIndexTimeoutCheckAction diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index b0d9e87ad73..4e9abcdc0d3 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -2134,15 +2134,54 @@ class DataServiceImpl extends WithLogContext implements DataService { } } + private async _indexProgress(ns: string): Promise> { + type IndexProgressResult = { + _id: string; + progress: { done: number; total: number }; + }; + + const currentOps: IndexProgressResult[] = await this._database( + 'admin', + 'META' + ) + .aggregate([ + { $currentOp: { allUsers: true, localOps: true } }, // get all ops + { + $match: { + ns, + progress: { $type: 'object' }, + 'command.createIndexes': { $exists: true }, + }, + }, // filter for createIndexes + { $unwind: '$command.indexes' }, // explode the "indexes" array for each createIndexes command + { + $group: { + _id: '$command.indexes.name', + progress: { $first: '$progress' }, + }, + }, // group on index name + ]) + .toArray() + .then(undefined, () => []); + + const indexToProgress = Object.create(null); + for (const { _id, progress } of currentOps) { + indexToProgress[_id] = progress.done / progress.total; + } + + return indexToProgress; + } + @op(mongoLogId(1_001_000_047)) async indexes( ns: string, options?: IndexInformationOptions ): Promise { - const [indexes, indexStats, indexSizes] = await Promise.all([ + const [indexes, indexStats, indexSizes, indexProgress] = await Promise.all([ this._collection(ns, 'CRUD').indexes(options) as Promise, this._indexStats(ns), this._indexSizes(ns), + this._indexProgress(ns), ]); const maxSize = Math.max(...Object.values(indexSizes)); @@ -2154,7 +2193,8 @@ class DataServiceImpl extends WithLogContext implements DataService { index, indexStats[name], indexSizes[name], - maxSize + maxSize, + indexProgress[name] ); }); } diff --git a/packages/data-service/src/index-detail-helper.ts b/packages/data-service/src/index-detail-helper.ts index 3264e0b3a49..e8679573921 100644 --- a/packages/data-service/src/index-detail-helper.ts +++ b/packages/data-service/src/index-detail-helper.ts @@ -16,6 +16,10 @@ export type IndexStats = { type IndexSize = number; +type IndexProgress = { + buildProgress?: number; +}; + export type IndexDefinition = { ns: string; name: string; @@ -35,7 +39,8 @@ export type IndexDefinition = { extra: Record>; size: IndexSize; relativeSize: number; -} & IndexStats; +} & IndexStats & + IndexProgress; export function getIndexCardinality( index: Pick @@ -120,7 +125,8 @@ export function createIndexDefinition( { name, key, v, ...extra }: IndexInfo, indexStats?: IndexStats, indexSize?: number, - maxSize?: number + maxSize?: number, + buildProgress?: number ): IndexDefinition { indexStats ??= { name, @@ -149,5 +155,6 @@ export function createIndexDefinition( properties: getIndexProperties(index), size: indexSize, relativeSize: (indexSize / maxSize) * 100, + buildProgress, }; } From 9106a0aad477fe6e27faa63e66e1ac09e1edbe41 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 18 Aug 2025 16:19:04 -0400 Subject: [PATCH 07/21] fix hook deps --- .../regular-indexes-table/regular-index-actions.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index 5e9876caa93..e17e21856e6 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -60,12 +60,12 @@ const IndexActions: React.FunctionComponent = ({ onHideIndexClick, onUnhideIndexClick, }) => { - const progressPercentage = (index.buildProgress ?? 0) * 100; - const indexActions: GroupedItemAction[] = useMemo(() => { const actions: GroupedItemAction[] = []; + const buildProgress = index.buildProgress ?? 0; + const isBuilding = buildProgress > 0 && buildProgress < 1; - if (progressPercentage < 100 && progressPercentage > 0) { + if (isBuilding) { // partially built actions.push({ action: 'delete', @@ -101,7 +101,7 @@ const IndexActions: React.FunctionComponent = ({ } return actions; - }, [index.name, index.extra?.hidden, serverVersion, progressPercentage]); + }, [index.name, index.extra?.hidden, index.buildProgress, serverVersion]); const onAction = useCallback( (action: IndexAction) => { @@ -116,14 +116,15 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteIndexClick, onHideIndexClick, onUnhideIndexClick, index] ); - if (progressPercentage > 0 && progressPercentage < 100) { + const buildProgress = index.buildProgress ?? 0; + if (buildProgress > 0 && buildProgress < 1) { return (
- Building... {progressPercentage | 0}% + Building... {(buildProgress * 100) | 0}% From e10b79d1bdf200f6768be365718e3acb4d95ed5f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 18 Aug 2025 17:11:12 -0400 Subject: [PATCH 08/21] fix: styles and types --- .../in-progress-index-actions.tsx | 35 +++++++++--------- .../regular-index-actions.tsx | 36 ++++++------------- .../regular-indexes-table.tsx | 13 +++---- .../src/modules/regular-indexes.ts | 9 +++-- packages/compass-indexes/src/stores/store.ts | 4 +-- .../test/fixtures/regular-indexes.ts | 13 +++++++ packages/compass-indexes/test/helpers.ts | 2 ++ packages/compass-indexes/test/setup-store.ts | 3 -- .../data-service/src/index-detail-helper.ts | 10 ++---- 9 files changed, 54 insertions(+), 71 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index ed1911b43ca..db7f40fb9b4 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -4,22 +4,13 @@ import { ItemActionGroup, SpinLoader, Body, - css, - spacing, } from '@mongodb-js/compass-components'; import type { InProgressIndex } from '../../modules/regular-indexes'; -const buildingTextStyles = css({ - display: 'flex', - alignItems: 'center', - gap: spacing[1], - marginRight: spacing[2], -}); - type Index = { name: string; status: InProgressIndex['status']; - buildProgress?: number; + buildProgress: number; }; type IndexActionsProps = { @@ -57,19 +48,27 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteFailedIndexClick, index] ); - const progress = (index.buildProgress ?? 0) * 100; + const progress = index.buildProgress * 100; const isBuilding = progress > 0 && progress < 100; return ( -
+
{isBuilding && ( -
- + <> Building... {progress | 0}% -
+ + )} data-testid="index-actions" diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index e17e21856e6..75aeeb50988 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -5,7 +5,6 @@ import { css, ItemActionGroup, SpinLoader, - spacing, Body, } from '@mongodb-js/compass-components'; import type { RegularIndex } from '../../modules/regular-indexes'; @@ -15,26 +14,8 @@ const styles = css({ justifyContent: 'flex-end', }); -const combinedContainerStyles = css({ - display: 'flex', - alignItems: 'center', - gap: spacing[200], - minWidth: spacing[800], - justifyContent: 'flex-end', -}); - -const progressTextStyles = css({ - fontSize: '12px', - fontWeight: 'normal', -}); - -// Extended type to include buildProgress which might not be in the base RegularIndex type -type IndexWithProgress = RegularIndex & { - buildProgress?: number; -}; - type IndexActionsProps = { - index: IndexWithProgress; + index: RegularIndex; serverVersion: string; onDeleteIndexClick: (name: string) => void; onHideIndexClick: (name: string) => void; @@ -62,7 +43,7 @@ const IndexActions: React.FunctionComponent = ({ }) => { const indexActions: GroupedItemAction[] = useMemo(() => { const actions: GroupedItemAction[] = []; - const buildProgress = index.buildProgress ?? 0; + const buildProgress = index.buildProgress; const isBuilding = buildProgress > 0 && buildProgress < 1; if (isBuilding) { @@ -116,16 +97,19 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteIndexClick, onHideIndexClick, onUnhideIndexClick, index] ); - const buildProgress = index.buildProgress ?? 0; + const buildProgress = index.buildProgress; if (buildProgress > 0 && buildProgress < 1) { return (
- - Building... {(buildProgress * 100) | 0}% - + Building... {(buildProgress * 100) | 0}% data-testid="index-actions" diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx index 92e7bfb56ab..f1072cbb9ef 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx @@ -252,11 +252,9 @@ function mergeIndexes( inProgressIndexes.map(({ name }) => name) ); - // Create a map of regular indexes by name to look up buildProgress - const regularIndexesByName = new Map(); - indexes.forEach((index) => { - regularIndexesByName.set(index.name, index); - }); + const regularIndexesByName = new Map( + indexes.map((index) => [index.name, index]) + ); const mappedIndexes: MappedRegularIndex[] = indexes // exclude partially-built indexes so that we don't include indexes that @@ -271,12 +269,9 @@ function mergeIndexes( // For in-progress indexes, merge in buildProgress from regular indexes if available const mappedInProgressIndexes: MappedInProgressIndex[] = inProgressIndexes.map((index) => { - const regularIndex = regularIndexesByName.get(index.name); - const buildProgress = regularIndex?.buildProgress; - return { ...index, - buildProgress, + buildProgress: regularIndexesByName.get(index.name)?.buildProgress ?? 0, compassIndexType: 'in-progress-index', }; }); diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index c92b1f79fff..ef81dd6323e 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -36,16 +36,14 @@ export type RegularIndex = Partial & | 'size' | 'relativeSize' | 'usageCount' - > & { - // Explicitly include buildProgress to ensure it's available - buildProgress?: number; - }; + | 'buildProgress' + >; export type InProgressIndex = Pick & { id: string; status: 'inprogress' | 'failed'; error?: string; - buildProgress?: number; + buildProgress: number; }; export type RollingIndex = Partial & @@ -87,6 +85,7 @@ export const prepareInProgressIndex = ( status: 'inprogress', fields: inProgressIndexFields, name: inProgressIndexName, + buildProgress: 0, // TODO(COMPASS-8335): we never mapped properties and the table does have // room to display them }; diff --git a/packages/compass-indexes/src/stores/store.ts b/packages/compass-indexes/src/stores/store.ts index 6bed3fb3dbc..5e547467495 100644 --- a/packages/compass-indexes/src/stores/store.ts +++ b/packages/compass-indexes/src/stores/store.ts @@ -50,9 +50,7 @@ export type IndexesDataServiceProps = | 'collectionStats' | 'collectionInfo' | 'listCollections' - | 'isListSearchIndexesSupported' - // Required for tracking index build progress - | 'currentOp'; + | 'isListSearchIndexesSupported'; export type IndexesDataService = Pick; export type IndexesPluginServices = { diff --git a/packages/compass-indexes/test/fixtures/regular-indexes.ts b/packages/compass-indexes/test/fixtures/regular-indexes.ts index e29bc223a7c..2e4218d2d10 100644 --- a/packages/compass-indexes/test/fixtures/regular-indexes.ts +++ b/packages/compass-indexes/test/fixtures/regular-indexes.ts @@ -19,6 +19,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'CCCC', @@ -37,6 +38,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'AAAA', @@ -55,6 +57,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'BBBB', @@ -73,6 +76,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, ]; @@ -96,6 +100,7 @@ export const defaultSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { ns: 'citibike.trips', @@ -126,6 +131,7 @@ export const defaultSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { ns: 'citibike.trips', @@ -143,6 +149,7 @@ export const defaultSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, ]; @@ -167,6 +174,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'CCCC', @@ -185,6 +193,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { name: '_id_', @@ -203,6 +212,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'BBBB', @@ -234,6 +244,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, ]; @@ -244,6 +255,7 @@ export const inProgressIndexes: InProgressIndex[] = [ //version: 2, fields: [], status: 'inprogress', + buildProgress: 0, }, { id: 'in-progress-2', @@ -255,5 +267,6 @@ export const inProgressIndexes: InProgressIndex[] = [ }, ], status: 'inprogress', + buildProgress: 0, }, ]; diff --git a/packages/compass-indexes/test/helpers.ts b/packages/compass-indexes/test/helpers.ts index 4857ca8dbee..d8170769feb 100644 --- a/packages/compass-indexes/test/helpers.ts +++ b/packages/compass-indexes/test/helpers.ts @@ -12,6 +12,8 @@ export function mockRegularIndex(info: Partial): RegularIndex { relativeSize: 0, cardinality: 'single', properties: [], + buildProgress: 0, + usageCount: 0, ...info, extra: { ...info.extra, diff --git a/packages/compass-indexes/test/setup-store.ts b/packages/compass-indexes/test/setup-store.ts index 829447561b5..e0034988b0b 100644 --- a/packages/compass-indexes/test/setup-store.ts +++ b/packages/compass-indexes/test/setup-store.ts @@ -90,9 +90,6 @@ const NOOP_DATA_PROVIDER: IndexesDataService = { sample(namespace: string) { return Promise.resolve([]); }, - currentOp() { - return Promise.resolve({ inprog: [] }); - }, }; class FakeInstance extends EventEmitter { diff --git a/packages/data-service/src/index-detail-helper.ts b/packages/data-service/src/index-detail-helper.ts index e8679573921..fbe2c691f7a 100644 --- a/packages/data-service/src/index-detail-helper.ts +++ b/packages/data-service/src/index-detail-helper.ts @@ -16,10 +16,6 @@ export type IndexStats = { type IndexSize = number; -type IndexProgress = { - buildProgress?: number; -}; - export type IndexDefinition = { ns: string; name: string; @@ -39,8 +35,8 @@ export type IndexDefinition = { extra: Record>; size: IndexSize; relativeSize: number; -} & IndexStats & - IndexProgress; + buildProgress: number; +} & IndexStats; export function getIndexCardinality( index: Pick @@ -155,6 +151,6 @@ export function createIndexDefinition( properties: getIndexProperties(index), size: indexSize, relativeSize: (indexSize / maxSize) * 100, - buildProgress, + buildProgress: buildProgress ?? 0, }; } From 764f47473e126392fa699428fcb8f11328beee17 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 19 Aug 2025 10:50:34 -0400 Subject: [PATCH 09/21] test: buildProgress display --- .../src/components/indexes/indexes.spec.tsx | 4 + .../in-progress-index-actions.spec.tsx | 2 + .../regular-index-actions.spec.tsx | 109 +++++++++++++++++- .../regular-indexes-table.spec.tsx | 7 ++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/packages/compass-indexes/src/components/indexes/indexes.spec.tsx b/packages/compass-indexes/src/components/indexes/indexes.spec.tsx index 370e79e0edd..559bb5662ee 100644 --- a/packages/compass-indexes/src/components/indexes/indexes.spec.tsx +++ b/packages/compass-indexes/src/components/indexes/indexes.spec.tsx @@ -190,6 +190,7 @@ describe('Indexes Component', function () { }, ], usageCount: 20, + buildProgress: 0, }, ], inProgressIndexes: [ @@ -203,6 +204,7 @@ describe('Indexes Component', function () { }, ], status: 'inprogress', + buildProgress: 0, }, ], error: undefined, @@ -245,6 +247,7 @@ describe('Indexes Component', function () { }, ], usageCount: 20, + buildProgress: 0, }, ], inProgressIndexes: [ @@ -259,6 +262,7 @@ describe('Indexes Component', function () { ], status: 'failed', error: 'Error message', + buildProgress: 0, }, ], error: undefined, diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx index a9641c189de..f589c89d2dc 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx @@ -26,6 +26,7 @@ describe('IndexActions Component', function () { index={{ name: 'artist_id_index', status: 'inprogress', + buildProgress: 0, }} onDeleteFailedIndexClick={onDeleteSpy} /> @@ -41,6 +42,7 @@ describe('IndexActions Component', function () { index={{ name: 'artist_id_index', status: 'failed', + buildProgress: 0, }} onDeleteFailedIndexClick={onDeleteSpy} /> diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx index f0606d5affa..89c8d3d310b 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx @@ -10,6 +10,20 @@ import { spy } from 'sinon'; import type { SinonSpy } from 'sinon'; import RegularIndexActions from './regular-index-actions'; +import type { RegularIndex } from '../../modules/regular-indexes'; + +const commonIndexProperties: RegularIndex = { + name: 'artist_id_index', + type: 'regular', + cardinality: 'compound', + properties: [], + fields: [], + extra: {}, + size: 0, + relativeSize: 0, + usageCount: 0, + buildProgress: 0, +}; describe('IndexActions Component', function () { let onDeleteSpy: SinonSpy; @@ -24,10 +38,100 @@ describe('IndexActions Component', function () { onUnhideIndexSpy = spy(); }); + describe('build progress display', function () { + it('does not display progress percentage when buildProgress is 0', function () { + render( + + ); + + // Should not show building spinner or percentage + expect(() => screen.getByTestId('index-building-spinner')).to.throw; + expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; + }); + + it('displays progress percentage when buildProgress is 50% (0.5)', function () { + render( + + ); + + // Should show building spinner and percentage + const buildingSpinner = screen.getByTestId('index-building-spinner'); + expect(buildingSpinner).to.exist; + + const progressText = screen.getByText('Building... 50%'); + expect(progressText).to.exist; + }); + + it('does not display progress percentage when buildProgress is 100% (1.0)', function () { + render( + + ); + + // Should not show building spinner or percentage when complete + expect(() => screen.getByTestId('index-building-spinner')).to.throw; + expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; + }); + + it('displays cancel button when index is building', function () { + render( + + ); + + const cancelButton = screen.getByLabelText('Cancel Index building_index'); + expect(cancelButton).to.exist; + expect(onDeleteSpy.callCount).to.equal(0); + userEvent.click(cancelButton); + expect(onDeleteSpy.callCount).to.equal(1); + }); + }); + it('renders delete button for a regular index', function () { render( Date: Tue, 19 Aug 2025 10:54:18 -0400 Subject: [PATCH 10/21] fix: styles --- .../in-progress-index-actions.tsx | 17 +++++++++-------- .../regular-index-actions.tsx | 17 ++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index db7f40fb9b4..a2eab716dea 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -4,6 +4,7 @@ import { ItemActionGroup, SpinLoader, Body, + css, } from '@mongodb-js/compass-components'; import type { InProgressIndex } from '../../modules/regular-indexes'; @@ -13,6 +14,13 @@ type Index = { buildProgress: number; }; +const styles = css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + gap: '8px', +}); + type IndexActionsProps = { index: Index; onDeleteFailedIndexClick: (name: string) => void; @@ -52,14 +60,7 @@ const IndexActions: React.FunctionComponent = ({ const isBuilding = progress > 0 && progress < 100; return ( -
+
{isBuilding && ( <> Building... {progress | 0}% diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index 75aeeb50988..223d63ff5c0 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -14,6 +14,13 @@ const styles = css({ justifyContent: 'flex-end', }); +const buildProgressStyles = css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + gap: '8px', +}); + type IndexActionsProps = { index: RegularIndex; serverVersion: string; @@ -100,15 +107,7 @@ const IndexActions: React.FunctionComponent = ({ const buildProgress = index.buildProgress; if (buildProgress > 0 && buildProgress < 1) { return ( -
+
Building... {(buildProgress * 100) | 0}% From 0853225ab10ee3bca5e26be3abf2c9ade72a0bf6 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 22 Aug 2025 14:08:55 -0400 Subject: [PATCH 11/21] fix: rename indexActionsContainerStyles --- .../regular-indexes-table/in-progress-index-actions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index a2eab716dea..8d741df51c8 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -14,7 +14,7 @@ type Index = { buildProgress: number; }; -const styles = css({ +const indexActionsContainerStyles = css({ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', @@ -60,7 +60,7 @@ const IndexActions: React.FunctionComponent = ({ const isBuilding = progress > 0 && progress < 100; return ( -
+
{isBuilding && ( <> Building... {progress | 0}% From dbfa09ff65cbdec2f892fb3180da10f94558fad7 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 22 Aug 2025 14:10:40 -0400 Subject: [PATCH 12/21] =?UTF-8?q?fix:=20use=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../regular-indexes-table/in-progress-index-actions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index 8d741df51c8..1ff19955906 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -63,7 +63,7 @@ const IndexActions: React.FunctionComponent = ({
{isBuilding && ( <> - Building... {progress | 0}% + Building… {progress | 0}% Date: Fri, 22 Aug 2025 14:13:00 -0400 Subject: [PATCH 13/21] fix: spacing --- .../regular-indexes-table/in-progress-index-actions.tsx | 5 +++-- .../regular-indexes-table/regular-index-actions.tsx | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index 1ff19955906..8df1b1347a1 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -5,6 +5,7 @@ import { SpinLoader, Body, css, + spacing, } from '@mongodb-js/compass-components'; import type { InProgressIndex } from '../../modules/regular-indexes'; @@ -18,7 +19,7 @@ const indexActionsContainerStyles = css({ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', - gap: '8px', + gap: spacing[200], }); type IndexActionsProps = { @@ -65,7 +66,7 @@ const IndexActions: React.FunctionComponent = ({ <> Building… {progress | 0}% diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index 223d63ff5c0..e3cfff94d70 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -6,6 +6,7 @@ import { ItemActionGroup, SpinLoader, Body, + spacing, } from '@mongodb-js/compass-components'; import type { RegularIndex } from '../../modules/regular-indexes'; @@ -18,7 +19,7 @@ const buildProgressStyles = css({ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', - gap: '8px', + gap: spacing[200], }); type IndexActionsProps = { From d2a93a2cea8fd23417e45e75af4af5a3e7d95085 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 22 Aug 2025 14:20:43 -0400 Subject: [PATCH 14/21] docs: comments on filter --- packages/compass-indexes/src/modules/regular-indexes.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index ef81dd6323e..fc16ed1d58f 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -244,9 +244,6 @@ export default function reducer( ...state, indexes: action.indexes, rollingIndexes: action.rollingIndexes, - // Keep in-progress indexes that are still actively building, but remove: - // 1. Failed indexes (status: 'failed') - // 2. Completed indexes (real index exists and progress >= 100%) inProgressIndexes: state.inProgressIndexes.filter((inProgress) => { // Always keep indexes with explicit errors if (inProgress.error) { From a902b7f6466691a46c07d0f0d653b007abd7d55e Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 22 Aug 2025 14:55:15 -0400 Subject: [PATCH 15/21] feat: make the agg run the division --- packages/data-service/src/data-service.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index 4e9abcdc0d3..38d15076f8e 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -2137,7 +2137,7 @@ class DataServiceImpl extends WithLogContext implements DataService { private async _indexProgress(ns: string): Promise> { type IndexProgressResult = { _id: string; - progress: { done: number; total: number }; + progress: number; }; const currentOps: IndexProgressResult[] = await this._database( @@ -2157,7 +2157,15 @@ class DataServiceImpl extends WithLogContext implements DataService { { $group: { _id: '$command.indexes.name', - progress: { $first: '$progress' }, + progress: { + $first: { + $cond: { + if: { $gt: ['$progress.total', 0] }, + then: { $divide: ['$progress.done', '$progress.total'] }, + else: 0, + }, + }, + }, }, }, // group on index name ]) @@ -2166,7 +2174,7 @@ class DataServiceImpl extends WithLogContext implements DataService { const indexToProgress = Object.create(null); for (const { _id, progress } of currentOps) { - indexToProgress[_id] = progress.done / progress.total; + indexToProgress[_id] = progress; } return indexToProgress; From e2e309f12c49dac6a7deb6be45c89a498c2cb2b8 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 22 Aug 2025 15:08:40 -0400 Subject: [PATCH 16/21] fix: remove progress from "in-progress" --- .../in-progress-index-actions.tsx | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index 8df1b1347a1..8cc5d533661 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -1,12 +1,6 @@ import React, { useCallback, useMemo } from 'react'; import type { GroupedItemAction } from '@mongodb-js/compass-components'; -import { - ItemActionGroup, - SpinLoader, - Body, - css, - spacing, -} from '@mongodb-js/compass-components'; +import { ItemActionGroup, css, spacing } from '@mongodb-js/compass-components'; import type { InProgressIndex } from '../../modules/regular-indexes'; type Index = { @@ -57,21 +51,8 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteFailedIndexClick, index] ); - const progress = index.buildProgress * 100; - const isBuilding = progress > 0 && progress < 100; - return (
- {isBuilding && ( - <> - Building… {progress | 0}% - - - )} data-testid="index-actions" actions={indexActions} From 990a128a4a8a01d871b85d5a91edafd6b78a9382 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 26 Aug 2025 15:09:11 -0400 Subject: [PATCH 17/21] fix: assert thrown error correctly --- .../regular-indexes-table/regular-index-actions.spec.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx index 89c8d3d310b..7fe6983dc6f 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx @@ -55,8 +55,12 @@ describe('IndexActions Component', function () { ); // Should not show building spinner or percentage - expect(() => screen.getByTestId('index-building-spinner')).to.throw; - expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; + expect(() => screen.getByTestId('index-building-spinner')).to.throw( + /Unable to find/ + ); + expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw( + /Unable to find/ + ); }); it('displays progress percentage when buildProgress is 50% (0.5)', function () { From 4cced3e6ebf36fbae1d9984d1ccb4c68a6fbd283 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 26 Aug 2025 15:12:42 -0400 Subject: [PATCH 18/21] fix: use trunc --- .../components/regular-indexes-table/regular-index-actions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx index e3cfff94d70..8f50640a208 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx @@ -109,7 +109,7 @@ const IndexActions: React.FunctionComponent = ({ if (buildProgress > 0 && buildProgress < 1) { return (
- Building... {(buildProgress * 100) | 0}% + Building... {Math.trunc(buildProgress * 100)}% data-testid="index-actions" From c92237b269e327b6ffb2e0d05df0ab547a71223f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 26 Aug 2025 15:19:27 -0400 Subject: [PATCH 19/21] remove double pass --- .../regular-indexes-table/regular-indexes-table.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx index f1072cbb9ef..31cdbf9eea6 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx @@ -260,8 +260,11 @@ function mergeIndexes( // exclude partially-built indexes so that we don't include indexes that // only exist on the primary node and then duplicate those as rolling // builds in the same table - .filter((index) => !rollingIndexNames.has(index.name)) - .filter((index) => !inProgressIndexNames.has(index.name)) + .filter( + (index) => + !rollingIndexNames.has(index.name) && + !inProgressIndexNames.has(index.name) + ) .map((index) => { return { ...index, compassIndexType: 'regular-index' }; }); From 163e40c901b1c91c39b3efdc903c6d104b9cf33c Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 27 Aug 2025 12:54:50 -0400 Subject: [PATCH 20/21] fix: return index filtering to what it was --- .../src/modules/regular-indexes.ts | 40 ++----------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index fc16ed1d58f..c7aa94abaa8 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -239,32 +239,15 @@ export default function reducer( }) ) ); - return { ...state, indexes: action.indexes, rollingIndexes: action.rollingIndexes, + // Remove in progress stubs when we got the "real" indexes from one of the + // backends. Keep the error ones around even if the name matches (should + // only be possible in cases of "index with the same name already exists") inProgressIndexes: state.inProgressIndexes.filter((inProgress) => { - // Always keep indexes with explicit errors - if (inProgress.error) { - return true; - } - - // Remove failed indexes (they're done and should be cleaned up) - if (inProgress.status === 'failed') { - return false; - } - - // If real index doesn't exist, keep the in-progress one - if (!allIndexNames.has(inProgress.name)) { - return true; - } - - // Real index exists - only keep in-progress if it's still actively building - // (status: 'inprogress'). Progress is now tracked through the regular index data - const isActivelyBuilding = inProgress.status === 'inprogress'; - - return isActivelyBuilding; + return !!inProgress.error || !allIndexNames.has(inProgress.name); }), status: FetchStatuses.READY, }; @@ -338,21 +321,6 @@ export default function reducer( }; } - if ( - isAction( - action, - ActionTypes.IndexCreationSucceeded - ) - ) { - // Remove the completed index from in-progress list - return { - ...state, - inProgressIndexes: state.inProgressIndexes.filter( - (x) => x.id !== action.inProgressIndexId - ), - }; - } - if ( isAction( action, From c179d47bb0c0c555066982e5c12d39c8c7d6b7ba Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 27 Aug 2025 12:59:16 -0400 Subject: [PATCH 21/21] chore: diff --- .../components/regular-indexes-table/regular-indexes-table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx index 31cdbf9eea6..1ce4bfe65a3 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx @@ -320,7 +320,7 @@ function getInProgressIndexInfo( + > ), }; }