-
Notifications
You must be signed in to change notification settings - Fork 229
feat(compass-indexes): add index build progress COMPASS-9495 #7200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds index build progress tracking to the MongoDB Compass indexes table, showing a percentage indicator when indexes are being built. The implementation introduces a new hook for polling index progress via currentOp
command and updates the UI to display build progress and a spinner for in-progress indexes.
- Adds
useIndexProgress
hook for tracking index build progress via polling - Updates Redux state to track progress percentage for in-progress indexes
- Modifies UI components to display progress indicators and spinners for building indexes
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
test/setup-store.ts |
Adds mock currentOp method to test data service |
src/stores/store.ts |
Extends data service type to include currentOp for progress tracking |
src/modules/regular-indexes.ts |
Adds progress tracking logic, new actions, and getIndexesProgress thunk |
src/modules/regular-indexes.spec.ts |
Adds tests for index progress tracking functionality |
src/hooks/use-index-progress.ts |
New custom hook for managing index build progress polling |
src/components/regular-indexes-table/regular-indexes-table.tsx |
Integrates progress hook and updates index filtering logic |
src/components/regular-indexes-table/regular-indexes-table.spec.tsx |
Updates tests to mock the progress hook |
src/components/regular-indexes-table/regular-index-actions.tsx |
Adds progress display UI with spinner and percentage |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx
Outdated
Show resolved
Hide resolved
@@ -43,12 +56,27 @@ const IndexActions: React.FunctionComponent<IndexActionsProps> = ({ | |||
[onDeleteFailedIndexClick, index] | |||
); | |||
|
|||
const progress = index.buildProgress * 100; | |||
const isBuilding = progress > 0 && progress < 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow this logic, we either have these in "in-progress" bucket or in "regular", how are we expecting this UI to show up for both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps I've over engineered a bit, and I can confirm this again. IIUC the inprogress state is a "fake" one to make the UI show the user's index in the table right away after they create it. It changes to "ready" as soon as we see the index in a listIndexes response. I think the progress logic is left over here from when I would show users 0% when we had a true 0 value. I think I can remove this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, your understanding is totally right here, although I guess now that we actually include in progress in "real" ones, the "in progress" name doesn't make much sense anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't had a chance to track this down, but I'm definitely seeing some weird behaviors locally: if I start the index creation (so it first gets into the "in progress" bucket in the state) I never see the progress shown for it, it just shows "in progress" badge until the index finished building fully, if I look at the indexes for the same collection in a separate tab (so the index is fetched from the beginning, not created as a virtual "in progress" one) it correctly shows the building progress, but the badge shows ready. Screenshots below show how this looks, notice that title_1
is in progress but without the progress in one tab, and ready but with progress in another


if (buildProgress > 0 && buildProgress < 1) { | ||
return ( | ||
<div className={buildProgressStyles} data-testid="index-building-spinner"> | ||
<Body>Building... {(buildProgress * 100) | 0}%</Body> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: we're not using much of bitwise operators in the codebase, can we use truncate here instead for readability?
<Body>Building... {(buildProgress * 100) | 0}%</Body> | |
<Body>Building… {Math.trunc(buildProgress * 100)}%</Body> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
.filter((index) => !rollingIndexNames.has(index.name)) | ||
.filter((index) => !inProgressIndexNames.has(index.name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super nit, to avoid two passes
.filter((index) => !rollingIndexNames.has(index.name)) | |
.filter((index) => !inProgressIndexNames.has(index.name)) | |
.filter((index) => !rollingIndexNames.has(index.name) && !inProgressIndexNames.has(index.name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More than a nit to me, good catch.
Someday we'll use the new Iterator to stack complex set of filters and maps perhaps...
@@ -43,12 +56,27 @@ const IndexActions: React.FunctionComponent<IndexActionsProps> = ({ | |||
[onDeleteFailedIndexClick, index] | |||
); | |||
|
|||
const progress = index.buildProgress * 100; | |||
const isBuilding = progress > 0 && progress < 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, your understanding is totally right here, although I guess now that we actually include in progress in "real" ones, the "in progress" name doesn't make much sense anymore
expect(() => screen.getByTestId('index-building-spinner')).to.throw; | ||
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to be called to validate
expect(() => screen.getByTestId('index-building-spinner')).to.throw; | |
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; | |
expect(() => screen.getByTestId('index-building-spinner')).to.throw(); | |
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, dunno how I missed this. On the node team I recall we either had a lint rule or we monkey-patched this assertion to require it has an argument because you could start throwing something else and not realize.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, do you mind digging up what ruleset that was if you have time for this? I just did a quick search through the codebase and seems like we have a bunch of those uncalled ones, I didn't realize that this is easy to miss... 😬 "Worst" case we can probably just add our own custom rule for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it, we modified the chai assertion: https://github.com/mongodb/js-bson/blob/d32397730426cc8423b89ddc1dd20ef4180f1677/test/register-bson.js#L14-L41
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which I realize won't help bc you won't hit this unless you call it, I think I fixed those mistakes with search and replace back when I introduced this. No "throw;" was pretty easy to eliminate 😅
I am now seeing it display the build percentage correctly by undoing the filtering changes. This does mean the index now has a "ready" badge while there's a "building... x%" string right next to it. Not ideal 🤔 We also have a "building" state but for rolling indexes only, I'm not sure whats the best approach here because it seems like it would be a heavy lift to reframe the statuses from how they are currently used since they're bound to how the UI treats them (like how in-progress is really a dummy state until we get a ping from the server) Any thoughts on the best investments we can make here? Could the status badge also rely on |
Badge can depend on progress, this is probably the easiest way to deal with this quickly and we can do this if we want to resolve this ticket sooner rather than later. Just generally I think the problem here is that your change changes the definition of in progress completely for compass but we're not adjusting for that in a more general sense: what compass was calling in progress before is not in progress anymore, so if we're changing that, it would also make sense to either remove this confusion from the codebase or treat the raw-ish output of data service in a way that maps to this concept properly. Your changes are somewhere in between right now, we're getting the indexes as part of what we now incorrectly call "ready", then we update the in progress ones to get the build progress, but at the same time we're still rendering the "ready" ones, not the "in progress" ones. So either we change the code to make it clear that in progress is not in progress and ready is not always ready, or when receiveing data from data service we continue to map in progress to in progress group without mixing it with the ready one. If the ui wasn't so tied to the state, we'd probably have easier time going the first route (which is also preferable I'd say), but as that's not the case, we can probably try the latter and separate data service returned indexes before they end up in plugin state |
expect(() => screen.getByTestId('index-building-spinner')).to.throw( | ||
/Unable to find/ | ||
); | ||
expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Free ellipsis to copy if you like: …
(it does look a small bit nicer than three separate dots)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! ty
'META' | ||
) | ||
.aggregate([ | ||
{ $currentOp: { allUsers: true, localOps: true } }, // get all ops |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
allUsers: true
requires extra privileges. Do we want to try to fall back to allUsers: false
if we're getting a permissions (or Atlas proxy) error?
localOps: true
means we don't see the index build if it happened on a mongos that's different from the one hit by this aggregation, which seems quite realistic in clusters with multiple mongos instances. Do we care about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catches, we should do both these things I think. I switched to localOps false and I retry the agg with allUsers: false.
Should we parallelize both aggs and take the one with more information? I also didn't check the error message because I imagine there's no good way to make that not brittle
I have started a new PR on top of this one to refactor the index status handling to be more accurate to reality. There is now a Maybe we can land this one as is, or if we're happy with the changes in that PR we can bundle them into one, I just didn't want to cloud the important bits of this work with refactorings. It seems to work pretty well! ⬇️ |
Description
Add a percentage to the index build row in the table if it is in progress.
https://jira.mongodb.org/browse/COMPASS-9495
Checklist
Motivation and Context
Open Questions
For reviewers:
useIndexProgress
?getInProgressIndexInfo
used to just list stub indexes because even in progress ones would be listed as "ready" while they continued to build on the server. We now will list "real" indexes via this function. Is that the right approach?Dependents
Types of changes
Screen.Recording.2025-08-15.at.2.29.25.PM.mov
index_finish.mov